timeout, but please refactor yes thankyou

This commit is contained in:
Quinten 2025-10-21 22:17:17 +02:00
parent c148636b1e
commit e7e913a32d
20 changed files with 177 additions and 39 deletions

View File

@ -13,6 +13,8 @@ if (!isset($_SESSION['request_count'])) {
// Increment on each request
$_SESSION['request_count']++;
sleep(8); // Simulate some processing delay
?>
<!DOCTYPE html>
<html lang="en">

View File

@ -93,7 +93,7 @@ void Client::request()
}
else
{
ErrorHandler::createErrorResponse(500, *httpResponse_);
ErrorHandler::createErrorResponse(500, *httpResponse_);
}
}
else
@ -149,7 +149,7 @@ void Client::poll() const
Log::info("Response is ready to be sent to client, fd: " + std::to_string(clientSocket_->getFd()));
clientSocket_->setCallback([this]() { respond(); });
// server_.writable(clientSocket_->getFd());
clientSocket_->setIOState(ASocket::IOState::WRITE);
clientSocket_->setIOState(ASocket::IoState::WRITE);
}
}

View File

@ -1,7 +1,31 @@
#include "webserv/log/Log.hpp"
#include <webserv/handler/AHandler.hpp>
#include <webserv/http/HttpRequest.hpp>
#include <webserv/http/HttpResponse.hpp>
#include <webserv/client/Client.hpp>
#include <memory>
#include <chrono>
#include <string>
AHandler::AHandler(const HttpRequest &request, HttpResponse &response) : request_(request), response_(response) {}
void AHandler::startTimer()
{
timerSocket_ = std::make_unique<TimerSocket>(std::chrono::milliseconds(5000));
timerSocket_->setCallback([this]() { handleTimeout(); });
timerSocket_->activate();
request_.getClient().addSocket(timerSocket_.get());
Log::debug("Timer started for handler: " + std::to_string(timerSocket_->getFd()));
}
void AHandler::cancelTimer()
{
if (timerSocket_)
{
request_.getClient().removeSocket(timerSocket_.get());
timerSocket_ = nullptr;
}
}

View File

@ -1,26 +1,31 @@
#pragma once
#include "webserv/socket/TimerSocket.hpp"
#include <memory>
class HttpRequest;
class HttpResponse;
class AHandler
{
public:
AHandler(const HttpRequest &request, HttpResponse &response);
virtual ~AHandler() = default;
AHandler(const HttpRequest &request, HttpResponse &response);
virtual ~AHandler() = default;
AHandler(const AHandler &other) = delete;
AHandler &operator=(const AHandler &other) = delete;
AHandler(AHandler &&other) noexcept = delete;
AHandler &operator=(AHandler &&other) noexcept = delete;
AHandler(const AHandler &other) = delete;
AHandler &operator=(const AHandler &other) = delete;
AHandler(AHandler &&other) noexcept = delete;
AHandler &operator=(AHandler &&other) noexcept = delete;
virtual void handle() = 0;
protected:
const HttpRequest &request_;
HttpResponse &response_;
void startTimer();
void cancelTimer();
protected:
virtual void handleTimeout() = 0;
const HttpRequest &request_;
HttpResponse &response_;
std::unique_ptr<TimerSocket> timerSocket_;
};

View File

@ -1,3 +1,4 @@
#include "webserv/handler/ErrorHandler.hpp"
#include <webserv/client/Client.hpp> // for Client
#include <webserv/handler/CgiHandler.hpp>
#include <webserv/handler/CgiProcess.hpp> // for CgiProcess
@ -20,6 +21,8 @@ void CgiHandler::handle()
// Initialize CGI process
cgiProcess_ = std::make_unique<CgiProcess>(request_, *this);
startTimer();
Log::info("CGI process started and sockets registered");
}
@ -71,6 +74,7 @@ void CgiHandler::read()
{
Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
request_.getClient().removeSocket(cgiStdOut_.get());
request_.getClient().removeSocket(timerSocket_.get());
cgiStdOut_ = nullptr;
parseCgiOutput();
return;
@ -103,6 +107,7 @@ void CgiHandler::error()
{
Log::info("CGI process closed stderr, fd: " + std::to_string(cgiStdErr_->getFd()));
request_.getClient().removeSocket(cgiStdErr_.get());
request_.getClient().removeSocket(timerSocket_.get()); // todo maybe this dangerous
cgiStdErr_ = nullptr;
return;
}
@ -213,6 +218,21 @@ void CgiHandler::parseCgiHeaders(std::string &headers)
}
}
void CgiHandler::handleTimeout()
{
Log::warning("CGI handler timeout occurred for PID: " + std::to_string(pid_));
// Terminate the CGI process if it's still running
if (cgiProcess_)
{
cgiProcess_->kill();
Log::info("Terminated CGI process with PID: " + std::to_string(pid_));
}
ErrorHandler::createErrorResponse(504, response_);
// cancelTimer();
}
void CgiHandler::parseCgiBody()
{
Log::trace(LOCATION);

View File

@ -24,6 +24,9 @@ class CgiHandler : public AHandler
void setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut, std::unique_ptr<CgiSocket> cgiStdErr);
void setPid(int pid);
protected:
void handleTimeout() override;
private:
constexpr static size_t bufferSize_ = 8192; // TODO: remove duplicate definition and move to configmanager
std::vector<uint8_t> buffer_;

View File

@ -82,9 +82,9 @@ void CgiProcess::spawn()
else
{
// Parent process
auto cgiStdIn = std::make_unique<CgiSocket>(pipeStdin[1], ASocket::IOState::WRITE);
auto cgiStdOut = std::make_unique<CgiSocket>(pipeStdout[0], ASocket::IOState::READ);
auto cgiStdErr = std::make_unique<CgiSocket>(pipeStderr[0], ASocket::IOState::READ);
auto cgiStdIn = std::make_unique<CgiSocket>(pipeStdin[1], ASocket::IoState::WRITE);
auto cgiStdOut = std::make_unique<CgiSocket>(pipeStdout[0], ASocket::IoState::READ);
auto cgiStdErr = std::make_unique<CgiSocket>(pipeStderr[0], ASocket::IoState::READ);
close(pipeStdin[0]);
close(pipeStdout[1]);
@ -95,7 +95,7 @@ void CgiProcess::spawn()
// request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the
// client
handler_.setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut), std::move(cgiStdErr));
handler_.setPid(_pid);
}
}

View File

@ -20,6 +20,12 @@ FileHandler::FileHandler(const HttpRequest &request, HttpResponse &response)
Log::trace(LOCATION);
}
void FileHandler::handleTimeout()
{
Log::warning("File handler timeout occurred for path: " + uri_.getFullPath());
ErrorHandler::createErrorResponse(504, response_, config_);
}
void FileHandler::handleFile(const std::string &filepath) const
{
Log::trace(LOCATION);

View File

@ -29,6 +29,9 @@ class FileHandler : public AHandler
void handle() override;
protected:
void handleTimeout() override;
private:
const URI &uri_;
const AConfig *config_;

View File

@ -49,7 +49,7 @@ class Log
void log(Level level, const std::string &message, const std::map<std::string, std::string> &context);
static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Trace;
static constexpr Log::Level COMPILE_TIME_LOG_LEVEL = Log::Level::Debug;
static void setFileChannel(const std::string &filename, std::ios_base::openmode mode = std::ios_base::app);
static void setStdoutChannel();

View File

@ -89,7 +89,12 @@ void Server::remove(ASocket &socket)
int fd = socket.getFd();
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1)
{
Log::error("epoll_ctl DEL failed for fd: " + std::to_string(fd));
if (errno == EBADF || errno == ENOENT)
{
Log::debug("Socket fd " + std::to_string(fd) + " was already closed or removed from epoll");
return;
}
Log::error("epoll_ctl DEL failed for fd: " + std::to_string(fd) + " with error: " + std::strerror(errno));
throw std::runtime_error("epoll_ctl DEL failed");
}
socketToClient_.erase(fd);
@ -133,7 +138,7 @@ void Server::handleConnection(struct epoll_event *event)
Log::trace(LOCATION);
ServerSocket &listener = getListener(event->data.fd);
std::unique_ptr<ClientSocket> clientSocket = listener.accept();
clientSocket->setIOState(ASocket::IoState::READ);
auto client = std::make_unique<Client>(std::move(clientSocket), *this);
add(client->getSocket(), client.get());
clients_.emplace_back(std::move(client));
@ -198,7 +203,12 @@ void Server::update(const ASocket &socket) const
evt.data.fd = socketFd;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, socketFd, &evt) == -1)
{
Log::error("epoll_ctl MOD failed for fd: " + std::to_string(socketFd));
if (errno == EBADF || errno == ENOENT)
{
Log::debug("Socket fd " + std::to_string(socketFd) + " was already closed or removed from epoll");
return;
}
Log::error("epoll_ctl MOD failed for fd: " + std::to_string(socketFd) + " with error: " + std::strerror(errno));
throw std::runtime_error("epoll_ctl MOD failed");
}
}

View File

@ -10,7 +10,7 @@
#include <sys/socket.h> // for recv, send
#include <unistd.h> // for close
ASocket::ASocket(int fd, IOState event) : fd_(fd), ioState_(event)
ASocket::ASocket(int fd, IoState event) : fd_(fd), ioState_(event)
{
Log::trace(LOCATION);
if (fd_ == -1)
@ -65,7 +65,7 @@ int ASocket::getFd() const noexcept
return fd_;
}
ASocket::IOState ASocket::getEvent() const noexcept
ASocket::IoState ASocket::getEvent() const noexcept
{
return ioState_;
}
@ -75,7 +75,7 @@ bool ASocket::isDirty() const noexcept
return dirty_;
}
void ASocket::setIOState(IOState event)
void ASocket::setIOState(IoState event)
{
if (event == ioState_)
{

View File

@ -13,10 +13,11 @@ class ASocket
{
CLIENT_SOCKET,
SERVER_SOCKET,
CGI_SOCKET
CGI_SOCKET,
TIMER_SOCKET
};
enum class IOState : uint32_t
enum class IoState : uint32_t
{
NONE = 0,
READ = 1 << 0,
@ -24,7 +25,7 @@ class ASocket
};
ASocket() = delete;
explicit ASocket(int fd, IOState state = IOState::READ);
explicit ASocket(int fd, IoState state = IoState::NONE);
ASocket(const ASocket &other) = delete;
ASocket &operator=(const ASocket &other) = delete;
@ -35,7 +36,7 @@ class ASocket
[[nodiscard]] virtual Type getType() const noexcept = 0;
[[nodiscard]] int getFd() const noexcept;
[[nodiscard]] IOState getEvent() const noexcept;
[[nodiscard]] IoState getEvent() const noexcept;
[[nodiscard]] bool isDirty() const noexcept;
void callback() const;
@ -44,7 +45,7 @@ class ASocket
virtual ssize_t read(void *buf, size_t len) const;
virtual ssize_t write(const void *buf, size_t len) const;
void setIOState(IOState event);
void setIOState(IoState event);
void processed();
protected:
@ -54,6 +55,6 @@ class ASocket
private:
int fd_;
bool dirty_ = false;
IOState ioState_;
IoState ioState_;
std::function<void()> callback_ = nullptr;
};

View File

@ -4,7 +4,7 @@
#include <unistd.h>
CgiSocket::CgiSocket(int fd, ASocket::IOState event) : ASocket(fd, event)
CgiSocket::CgiSocket(int fd, ASocket::IoState event) : ASocket(fd, event)
{
Log::trace(LOCATION);
}

View File

@ -9,7 +9,7 @@
class CgiSocket : public ASocket
{
public:
explicit CgiSocket(int fd, ASocket::IOState event);
explicit CgiSocket(int fd, ASocket::IoState event);
[[nodiscard]] ASocket::Type getType() const noexcept override;

View File

@ -11,7 +11,7 @@
#include <sys/socket.h> // for AF_INET, accept, bind, listen, setsockopt, socket, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
#include <unistd.h> // for close
ServerSocket::ServerSocket() : ASocket(socket(AF_INET, SOCK_STREAM, 0))
ServerSocket::ServerSocket() : ASocket(socket(AF_INET, SOCK_STREAM, 0), ASocket::IoState::READ)
{
Log::trace(LOCATION);
if (getFd() == -1)

View File

@ -0,0 +1,46 @@
#include "webserv/log/Log.hpp"
#include "webserv/socket/ASocket.hpp"
#include <webserv/socket/TimerSocket.hpp>
#include <stdexcept>
#include <sys/time.h>
#include <sys/timerfd.h>
TimerSocket::TimerSocket(std::chrono::milliseconds timeout)
: ASocket(timerfd_create(CLOCK_MONOTONIC, 0), ASocket::IoState::NONE), timeout_(timeout)
{
}
void TimerSocket::activate()
{
Log::trace(LOCATION);
struct itimerspec timerSpec;
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout_);
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout_ - seconds);
timerSpec.it_value.tv_sec = seconds.count();
timerSpec.it_value.tv_nsec = nanoseconds.count();
timerSpec.it_interval.tv_sec = 0;
timerSpec.it_interval.tv_nsec = 0;
if (timerfd_settime(getFd(), 0, &timerSpec, nullptr) == -1)
{
throw std::runtime_error("Failed to set timerfd time");
}
active_ = true;
setIOState(IoState::READ);
}
ASocket::Type TimerSocket::getType() const noexcept
{
return ASocket::Type::TIMER_SOCKET;
}
bool TimerSocket::isActive() const noexcept
{
return active_;
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "webserv/socket/ASocket.hpp"
#include <chrono>
class TimerSocket : public ASocket
{
public:
explicit TimerSocket(std::chrono::milliseconds timeout);
[[nodiscard]] ASocket::Type getType() const noexcept override;
[[nodiscard]] bool isActive() const noexcept;
void activate();
private:
bool active_ = false;
std::chrono::milliseconds timeout_;
};

View File

@ -139,15 +139,15 @@ std::string implode(const std::vector<std::string> &elements, const std::string
return stream.str();
}
uint32_t stateToEpoll(const ASocket::IOState &event)
uint32_t stateToEpoll(const ASocket::IoState &event)
{
uint32_t epollEvents = 0;
using EventType = std::underlying_type_t<ASocket::IOState>;
if ((static_cast<EventType>(event) & static_cast<EventType>(ASocket::IOState::READ)) != 0U)
using EventType = std::underlying_type_t<ASocket::IoState>;
if ((static_cast<EventType>(event) & static_cast<EventType>(ASocket::IoState::READ)) != 0U)
{
epollEvents |= EPOLLIN;
}
if ((static_cast<EventType>(event) & static_cast<EventType>(ASocket::IOState::WRITE)) != 0U)
if ((static_cast<EventType>(event) & static_cast<EventType>(ASocket::IoState::WRITE)) != 0U)
{
epollEvents |= EPOLLOUT;
}

View File

@ -18,5 +18,5 @@ void removeComments(std::string &str);
std::vector<std::string> split(const std::string &str, char delimiter);
std::string implode(const std::vector<std::string> &elements, const std::string &delimiter);
uint32_t stateToEpoll(const ASocket::IOState &event);
uint32_t stateToEpoll(const ASocket::IoState &event);
} // namespace utils