timeout, but please refactor yes thankyou
This commit is contained in:
parent
c148636b1e
commit
e7e913a32d
@ -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">
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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_;
|
||||
};
|
||||
@ -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);
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -29,6 +29,9 @@ class FileHandler : public AHandler
|
||||
|
||||
void handle() override;
|
||||
|
||||
protected:
|
||||
void handleTimeout() override;
|
||||
|
||||
private:
|
||||
const URI &uri_;
|
||||
const AConfig *config_;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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_)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
46
webserv/socket/TimerSocket.cpp
Normal file
46
webserv/socket/TimerSocket.cpp
Normal 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_;
|
||||
}
|
||||
18
webserv/socket/TimerSocket.hpp
Normal file
18
webserv/socket/TimerSocket.hpp
Normal 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_;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
Loading…
Reference in New Issue
Block a user