feat(CgiHandler, TimerSocket): enhance error handling and add read/write methods

This commit is contained in:
whaffman 2025-10-22 15:18:15 +02:00
parent cd33bb1ce6
commit 656abd24e7
7 changed files with 60 additions and 18 deletions

View File

@ -140,7 +140,7 @@ void Client::poll() const
auto *cgiHandler = dynamic_cast<CgiHandler *>(handler_.get()); auto *cgiHandler = dynamic_cast<CgiHandler *>(handler_.get());
if (cgiHandler != nullptr) if (cgiHandler != nullptr)
{ {
Log::debug("Polling CGI handler for client, fd: " + std::to_string(clientSocket_->getFd())); // Log::debug("Polling CGI handler for client, fd: " + std::to_string(clientSocket_->getFd()));
// CGI handler polling logic if needed // CGI handler polling logic if needed
cgiHandler->wait(); cgiHandler->wait();
} }

View File

@ -58,9 +58,9 @@ class Client
std::unique_ptr<HttpRequest> httpRequest_; std::unique_ptr<HttpRequest> httpRequest_;
std::unique_ptr<HttpResponse> httpResponse_; std::unique_ptr<HttpResponse> httpResponse_;
std::unique_ptr<Router> router_; std::unique_ptr<Router> router_;
std::unique_ptr<AHandler> handler_ = nullptr;
std::unique_ptr<ClientSocket> clientSocket_; std::unique_ptr<ClientSocket> clientSocket_;
std::unordered_map<int, ASocket *> sockets_; std::unordered_map<int, ASocket *> sockets_;
std::unique_ptr<AHandler> handler_ = nullptr;
Server &server_; Server &server_;
void writeToCgi(); void writeToCgi();

View File

@ -10,6 +10,10 @@
AHandler::AHandler(const HttpRequest &request, HttpResponse &response) : request_(request), response_(response) {} AHandler::AHandler(const HttpRequest &request, HttpResponse &response) : request_(request), response_(response) {}
AHandler::~AHandler()
{
cancelTimer();
}
void AHandler::startTimer() void AHandler::startTimer()
{ {
timerSocket_ = std::make_unique<TimerSocket>(std::chrono::milliseconds(5000)); timerSocket_ = std::make_unique<TimerSocket>(std::chrono::milliseconds(5000));

View File

@ -10,7 +10,7 @@ class AHandler
{ {
public: public:
AHandler(const HttpRequest &request, HttpResponse &response); AHandler(const HttpRequest &request, HttpResponse &response);
virtual ~AHandler() = default; virtual ~AHandler();
AHandler(const AHandler &other) = delete; AHandler(const AHandler &other) = delete;
AHandler &operator=(const AHandler &other) = delete; AHandler &operator=(const AHandler &other) = delete;

View File

@ -1,4 +1,5 @@
#include "webserv/handler/ErrorHandler.hpp" #include "webserv/handler/ErrorHandler.hpp"
#include <webserv/client/Client.hpp> // for Client #include <webserv/client/Client.hpp> // for Client
#include <webserv/handler/CgiHandler.hpp> #include <webserv/handler/CgiHandler.hpp>
#include <webserv/handler/CgiProcess.hpp> // for CgiProcess #include <webserv/handler/CgiProcess.hpp> // for CgiProcess
@ -8,6 +9,8 @@
#include <webserv/socket/CgiSocket.hpp> // for CgiSocket #include <webserv/socket/CgiSocket.hpp> // for CgiSocket
#include <webserv/utils/utils.hpp> // for stoul #include <webserv/utils/utils.hpp> // for stoul
#include <sys/types.h>
CgiHandler::CgiHandler(const HttpRequest &request, HttpResponse &response) CgiHandler::CgiHandler(const HttpRequest &request, HttpResponse &response)
: AHandler(request, response), cgiProcess_(nullptr), cgiStdIn_(nullptr), cgiStdOut_(nullptr) : AHandler(request, response), cgiProcess_(nullptr), cgiStdIn_(nullptr), cgiStdOut_(nullptr)
{ {
@ -74,7 +77,7 @@ void CgiHandler::read()
{ {
Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd())); Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
request_.getClient().removeSocket(cgiStdOut_.get()); request_.getClient().removeSocket(cgiStdOut_.get());
request_.getClient().removeSocket(timerSocket_.get()); // request_.getClient().removeSocket(timerSocket_.get());
cgiStdOut_ = nullptr; cgiStdOut_ = nullptr;
parseCgiOutput(); parseCgiOutput();
return; return;
@ -107,19 +110,20 @@ void CgiHandler::error()
{ {
Log::info("CGI process closed stderr, fd: " + std::to_string(cgiStdErr_->getFd())); Log::info("CGI process closed stderr, fd: " + std::to_string(cgiStdErr_->getFd()));
request_.getClient().removeSocket(cgiStdErr_.get()); request_.getClient().removeSocket(cgiStdErr_.get());
request_.getClient().removeSocket(timerSocket_.get()); // todo maybe this dangerous // request_.getClient().removeSocket(timerSocket_.get()); // todo maybe this dangerous
cgiStdErr_ = nullptr; cgiStdErr_ = nullptr;
return; return;
} }
else else
{ {
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
Log::error("CGI stderr output (fd: " + std::to_string(cgiStdErr_->getFd()) + "): " Log::error("CGI stderr output (fd: " + std::to_string(cgiStdErr_->getFd())
+ std::string(buffer, static_cast<size_t>(bytesRead))); + "): " + std::string(buffer, static_cast<size_t>(bytesRead)));
} }
} }
void CgiHandler::setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut, std::unique_ptr<CgiSocket> cgiStdErr) void CgiHandler::setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut,
std::unique_ptr<CgiSocket> cgiStdErr)
{ {
cgiStdIn->setCallback([this]() { write(); }); cgiStdIn->setCallback([this]() { write(); });
cgiStdOut->setCallback([this]() { read(); }); cgiStdOut->setCallback([this]() { read(); });
@ -221,7 +225,14 @@ void CgiHandler::parseCgiHeaders(std::string &headers)
void CgiHandler::handleTimeout() void CgiHandler::handleTimeout()
{ {
Log::warning("CGI handler timeout occurred for PID: " + std::to_string(pid_)); Log::warning("CGI handler timeout occurred for PID: " + std::to_string(pid_));
char buffer[9] = {}; // NOLINT(cppcoreguidelines-avoid-c-arrays)
ssize_t bytesRead = timerSocket_->read(buffer, sizeof(buffer) - 1);
buffer[bytesRead] = '\0';
if (bytesRead <= 0)
{
// NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
return;
}
// Terminate the CGI process if it's still running // Terminate the CGI process if it's still running
if (cgiProcess_) if (cgiProcess_)
{ {

View File

@ -44,3 +44,25 @@ bool TimerSocket::isActive() const noexcept
{ {
return active_; return active_;
} }
ssize_t TimerSocket::read(void *buf, size_t len) const
{
Log::trace(LOCATION);
ssize_t bytesRead = ::read(getFd(), buf, len);
if (bytesRead == -1)
{
throw std::system_error(errno, std::generic_category(), "Socket: Read error");
}
return bytesRead;
}
ssize_t TimerSocket::write(const void *buf, size_t len) const
{
Log::trace(LOCATION);
ssize_t bytesSent = ::write(getFd(), buf, len);
if (bytesSent == -1)
{
throw std::system_error(errno, std::generic_category(), "Socket: Write error");
}
return bytesSent;
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "webserv/socket/ASocket.hpp" #include "webserv/socket/ASocket.hpp"
#include <chrono> #include <chrono>
class TimerSocket : public ASocket class TimerSocket : public ASocket
@ -11,7 +12,11 @@ class TimerSocket : public ASocket
[[nodiscard]] ASocket::Type getType() const noexcept override; [[nodiscard]] ASocket::Type getType() const noexcept override;
[[nodiscard]] bool isActive() const noexcept; [[nodiscard]] bool isActive() const noexcept;
ssize_t read(void *buf, size_t len) const override;
ssize_t write(const void *buf, size_t len) const override;
void activate(); void activate();
private: private:
bool active_ = false; bool active_ = false;
std::chrono::milliseconds timeout_; std::chrono::milliseconds timeout_;