refactoring ahndler, and cgihandler start

This commit is contained in:
whaffman 2025-10-16 17:10:32 +02:00
parent a8dd8a77a5
commit b8e3e16f3d
12 changed files with 222 additions and 105 deletions

View File

@ -26,6 +26,7 @@ Client::Client(std::unique_ptr<ClientSocket> socket, Server &server)
Log::trace(LOCATION); Log::trace(LOCATION);
Log::info("New client connected, fd: " + std::to_string(clientSocket_->getFd())); Log::info("New client connected, fd: " + std::to_string(clientSocket_->getFd()));
clientSocket_->setCallback([this]() { request(); }); clientSocket_->setCallback([this]() { request(); });
sockets_[clientSocket_->getFd()] = clientSocket_.get();
} }
Client::~Client() Client::~Client()
@ -35,20 +36,15 @@ Client::~Client()
server_.remove(*clientSocket_); server_.remove(*clientSocket_);
}; };
ASocket &Client::getSocket(int fd) const ASocket &Client::getSocket(int fd) const
{ {
if (fd == -1 || clientSocket_->getFd() == fd) if (fd == -1)
{ {
return *clientSocket_; return *clientSocket_;
} }
if (cgiStdIn_ && cgiStdIn_->getFd() == fd) if (sockets_.contains(fd))
{ {
return *cgiStdIn_; return *sockets_.at(fd);
}
if (cgiStdOut_ && cgiStdOut_->getFd() == fd)
{
return *cgiStdOut_;
} }
Log::error("Socket not found for fd: " + std::to_string(fd)); Log::error("Socket not found for fd: " + std::to_string(fd));
throw std::runtime_error("Socket not found for fd: " + std::to_string(fd)); throw std::runtime_error("Socket not found for fd: " + std::to_string(fd));
@ -88,7 +84,8 @@ void Client::request()
{"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))}, {"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))},
}); });
// server_.responseReady(client_socket_->getFd()); // server_.responseReady(client_socket_->getFd());
router_->handleRequest(); handler_ = router_->handleRequest();
handler_->handle();
} }
else else
{ {
@ -100,80 +97,23 @@ void Client::request()
} }
} }
void Client::writeToCgi() //
void Client::setCgiSockets(CgiSocket *cgiStdIn, CgiSocket *cgiStdOut)
{ {
Log::trace(LOCATION); server_.add(*cgiStdIn, EPOLLOUT, this); // write
if (cgiStdIn_ == nullptr) server_.add(*cgiStdOut, EPOLLIN, this); // read
{
Log::error("CGI stdin socket is null"); sockets_[cgiStdIn->getFd()] = cgiStdIn;
return; sockets_[cgiStdOut->getFd()] = cgiStdOut;
}
if (httpRequest_->getBody().empty())
{
Log::debug("No body to write to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
server_.remove(*cgiStdIn_);
cgiStdIn_ = nullptr;
return;
}
ssize_t bytesWritten = cgiStdIn_->write(httpRequest_->getBody().data(), httpRequest_->getBody().size());
if (bytesWritten < 0)
{
Log::error("Failed to write to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
}
else
{
Log::debug("Wrote " + std::to_string(bytesWritten)
+ " bytes to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
}
server_.remove(*cgiStdIn_);
cgiStdIn_ = nullptr;
} }
void Client::readFromCgi() void Client::removeCgiSocket(CgiSocket *cgiSocket)
{ {
Log::trace(LOCATION); server_.remove(*cgiSocket); // write
if (cgiStdOut_ == nullptr)
{
Log::error("CGI stdout socket is null");
return;
}
char buffer[bufferSize_] = {}; // NOLINT(cppcoreguidelines-avoid-c-arrays)
ssize_t bytesRead
= cgiStdOut_->read(buffer, sizeof(buffer) - 1); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
if (bytesRead < 0)
{
Log::error("Failed to read from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
}
else if (bytesRead == 0)
{
Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
server_.remove(*cgiStdOut_);
cgiStdOut_ = nullptr;
httpResponse_->addHeader("Content-Type", "text/html");
httpResponse_->setComplete();
return;
}
else
{
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
httpResponse_->appendBody(std::string(buffer, static_cast<size_t>(bytesRead)));
Log::debug("Read " + std::to_string(bytesRead)
+ " bytes from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
}
}
void Client::setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut) sockets_.erase(cgiSocket->getFd());
{ // sockets_[cgiStdIn->getFd()] = cgiStdIn;
cgiStdIn->setCallback([this]() { writeToCgi(); }); // sockets_[cgiStdOut->getFd()] = cgiStdOut;
cgiStdOut->setCallback([this]() { readFromCgi(); });
cgiStdOut_ = std::move(cgiStdOut);
cgiStdIn_ = std::move(cgiStdIn);
server_.add(*cgiStdOut_, EPOLLIN, this); // read
server_.add(*cgiStdIn_, EPOLLOUT, this); // write
// TODO add to handler
} }
void Client::poll() const void Client::poll() const

View File

@ -2,7 +2,9 @@
// #include <webserv/http/HttpResponse.hpp> // #include <webserv/http/HttpResponse.hpp>
#include "webserv/handler/AHandler.hpp"
#include "webserv/router/Router.hpp" #include "webserv/router/Router.hpp"
#include "webserv/socket/ASocket.hpp"
#include "webserv/socket/CgiSocket.hpp" #include "webserv/socket/CgiSocket.hpp"
#include <webserv/config/ServerConfig.hpp> // for ServerConfig #include <webserv/config/ServerConfig.hpp> // for ServerConfig
@ -36,16 +38,13 @@ class Client
void respond() const; void respond() const;
void poll() const; void poll() const;
void writeToCgi();
void readFromCgi();
// [[nodiscard]] int getStatusCode() const noexcept; // [[nodiscard]] int getStatusCode() const noexcept;
[[nodiscard]] ASocket &getSocket(int fd = -1) const; [[nodiscard]] ASocket &getSocket(int fd = -1) const;
// void setStatusCode(int code); // void setStatusCode(int code);
void setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut); void setCgiSockets(CgiSocket *cgiStdIn, CgiSocket *cgiStdOut);
void removeCgiSocket(CgiSocket *cgiSocket);
[[nodiscard]] HttpRequest &getHttpRequest() const noexcept; [[nodiscard]] HttpRequest &getHttpRequest() const noexcept;
[[nodiscard]] HttpResponse &getHttpResponse() const noexcept; [[nodiscard]] HttpResponse &getHttpResponse() const noexcept;
@ -56,8 +55,14 @@ 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::unique_ptr<CgiSocket> cgiStdIn_ = nullptr; std::unique_ptr<CgiSocket> cgiStdIn_ = nullptr;
std::unique_ptr<CgiSocket> cgiStdOut_ = nullptr; std::unique_ptr<CgiSocket> cgiStdOut_ = nullptr;
std::unordered_map<int, ASocket *> sockets_;
Server &server_; Server &server_;
void writeToCgi();
void readFromCgi();
}; };

View File

@ -0,0 +1,7 @@
#include <webserv/handler/AHandler.hpp>
#include <webserv/http/HttpRequest.hpp>
#include <webserv/http/HttpResponse.hpp>
AHandler::AHandler(const HttpRequest &request, HttpResponse &response) : request_(request), response_(response) {}

View File

@ -0,0 +1,26 @@
#pragma once
class HttpRequest;
class HttpResponse;
class AHandler
{
public:
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;
virtual void handle() = 0;
protected:
const HttpRequest &request_;
HttpResponse &response_;
};

View File

@ -0,0 +1,98 @@
#include <webserv/client/Client.hpp> // for Client
#include <webserv/handler/CgiHandler.hpp>
#include <webserv/handler/CgiProcess.hpp> // for CgiProcess
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
#include <webserv/log/Log.hpp> // for Log
#include <webserv/socket/CgiSocket.hpp> // for CgiSocket
CgiHandler::CgiHandler(const HttpRequest &request, HttpResponse &response)
: AHandler(request, response), cgiProcess_(nullptr), cgiStdIn_(nullptr), cgiStdOut_(nullptr)
{
Log::debug("CgiHandler constructed");
}
void CgiHandler::handle()
{
Log::info("CgiHandler handling request");
// Initialize CGI process
cgiProcess_ = std::make_unique<CgiProcess>(request_, *this);
Log::info("CGI process started and sockets registered");
}
void CgiHandler::write()
{
Log::trace(LOCATION);
if (cgiStdIn_ == nullptr)
{
Log::error("CGI stdin socket is null");
return;
}
if (request_.getBody().empty())
{
Log::debug("No body to write to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
request_.getClient().removeCgiSocket(cgiStdIn_.get());
cgiStdIn_ = nullptr;
return;
}
ssize_t bytesWritten = cgiStdIn_->write(request_.getBody().data(), request_.getBody().size());
if (bytesWritten < 0)
{
Log::error("Failed to write to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
}
else
{
Log::debug("Wrote " + std::to_string(bytesWritten)
+ " bytes to CGI stdin, fd: " + std::to_string(cgiStdIn_->getFd()));
}
request_.getClient().removeCgiSocket(cgiStdIn_.get());
cgiStdIn_ = nullptr;
}
void CgiHandler::read()
{
Log::trace(LOCATION);
if (cgiStdOut_ == nullptr)
{
Log::error("CGI stdout socket is null");
return;
}
char buffer[bufferSize_] = {}; // NOLINT(cppcoreguidelines-avoid-c-arrays)
ssize_t bytesRead
= cgiStdOut_->read(buffer, sizeof(buffer) - 1); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
if (bytesRead < 0)
{
Log::error("Failed to read from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
}
else if (bytesRead == 0)
{
Log::info("CGI process closed stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
request_.getClient().removeCgiSocket(cgiStdOut_.get());
cgiStdOut_ = nullptr;
response_.addHeader("Content-Type", "text/html");
response_.setComplete();
return;
}
else
{
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
response_.appendBody(std::string(buffer, static_cast<size_t>(bytesRead)));
Log::debug("Read " + std::to_string(bytesRead)
+ " bytes from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd()));
}
}
void CgiHandler::setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut)
{
cgiStdIn->setCallback([this]() { write(); });
cgiStdOut->setCallback([this]() { read(); });
cgiStdOut_ = std::move(cgiStdOut);
cgiStdIn_ = std::move(cgiStdIn);
request_.getClient().setCgiSockets(cgiStdIn_.get(), cgiStdOut_.get()); // write
// TODO add to handler
}

View File

@ -1 +1,33 @@
#pragma once #pragma once
#include "webserv/handler/AHandler.hpp"
#include "webserv/socket/CgiSocket.hpp"
#include <memory>
class CgiProcess;
class CgiHandler : public AHandler
{
public:
CgiHandler(const HttpRequest &request, HttpResponse &response);
CgiHandler(const CgiHandler &other) = delete;
CgiHandler &operator=(const CgiHandler &other) = delete;
CgiHandler(CgiHandler &&other) noexcept = delete;
CgiHandler &operator=(CgiHandler &&other) noexcept = delete;
~CgiHandler() = default;
void handle() override;
void setCgiSockets(std::unique_ptr<CgiSocket> cgiStdIn, std::unique_ptr<CgiSocket> cgiStdOut);
private:
constexpr static size_t bufferSize_ = 8192; // TODO: remove duplicate definition and move to configmanager
std::unique_ptr<CgiProcess> cgiProcess_;
std::unique_ptr<CgiSocket> cgiStdIn_;
std::unique_ptr<CgiSocket> cgiStdOut_;
void write();
void read();
};

View File

@ -12,7 +12,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
CgiProcess::CgiProcess(const HttpRequest &request) : request_(request), _pid(-1) CgiProcess::CgiProcess(const HttpRequest &request, CgiHandler &handler) : request_(request), handler_(handler), _pid(-1)
{ {
if (!request_.getUri().isCgi()) if (!request_.getUri().isCgi())
{ {
@ -78,6 +78,8 @@ void CgiProcess::spawn()
Log::debug("CGI process forked with PID: " + std::to_string(_pid)); Log::debug("CGI process forked with PID: " + std::to_string(_pid));
request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the client // request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the
// client
handler_.setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut));
} }
} }

View File

@ -1,11 +1,12 @@
#pragma once #pragma once
#include "webserv/handler/CgiHandler.hpp"
#include "webserv/http/HttpRequest.hpp" #include "webserv/http/HttpRequest.hpp"
class CgiProcess class CgiProcess
{ {
public: public:
CgiProcess(const HttpRequest &request); CgiProcess(const HttpRequest &request, CgiHandler &handler);
CgiProcess(const CgiProcess &other) = delete; CgiProcess(const CgiProcess &other) = delete;
CgiProcess(CgiProcess &&other) noexcept = delete; CgiProcess(CgiProcess &&other) noexcept = delete;
@ -17,6 +18,7 @@ class CgiProcess
private: private:
const HttpRequest &request_; const HttpRequest &request_;
CgiHandler &handler_;
int _pid; int _pid;
// int _cgiFd; // int _cgiFd;

View File

@ -15,7 +15,7 @@
#include <vector> // for vector #include <vector> // for vector
FileHandler::FileHandler(const HttpRequest &request, HttpResponse &response) FileHandler::FileHandler(const HttpRequest &request, HttpResponse &response)
: config_(request.getUri().getConfig()), uri_(request.getUri()), response_(response) : AHandler(request, response), uri_(request.getUri()), config_(uri_.getConfig())
{ {
Log::trace(LOCATION); Log::trace(LOCATION);
} }
@ -71,7 +71,7 @@ void FileHandler::handleDirectory(const std::string &dirpath, ResourceType type)
ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_);
} }
void FileHandler::handle() const void FileHandler::handle()
{ {
Log::trace(LOCATION); Log::trace(LOCATION);
if (!uri_.isValid()) if (!uri_.isValid())
@ -85,10 +85,16 @@ void FileHandler::handle() const
switch (resourceType) switch (resourceType)
{ {
case FILE: handleFile(filepath); return; case FILE:
handleFile(filepath);
return;
case DIRECTORY_AUTOINDEX: case DIRECTORY_AUTOINDEX:
case DIRECTORY_INDEX: handleDirectory(filepath, resourceType); return; case DIRECTORY_INDEX:
case NOT_FOUND: ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); return; handleDirectory(filepath, resourceType);
return;
case NOT_FOUND:
ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_);
return;
} }
ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_);
} }

View File

@ -3,6 +3,7 @@
#include "webserv/http/HttpRequest.hpp" #include "webserv/http/HttpRequest.hpp"
#include <webserv/config/AConfig.hpp> #include <webserv/config/AConfig.hpp>
#include <webserv/handler/AHandler.hpp>
#include <webserv/config/LocationConfig.hpp> #include <webserv/config/LocationConfig.hpp>
#include <webserv/handler/URI.hpp> #include <webserv/handler/URI.hpp>
#include <webserv/http/HttpResponse.hpp> // for HttpResponse #include <webserv/http/HttpResponse.hpp> // for HttpResponse
@ -14,26 +15,23 @@
class AConfig; class AConfig;
class URI; class URI;
class FileHandler class FileHandler : public AHandler
{ {
public: public:
FileHandler(const HttpRequest &request, HttpResponse &response); FileHandler(const HttpRequest &request, HttpResponse &response);
FileHandler(const FileHandler &other) = delete; FileHandler(const FileHandler &other) = delete;
FileHandler(FileHandler &&other) noexcept = delete;
FileHandler &operator=(const FileHandler &other) = delete; FileHandler &operator=(const FileHandler &other) = delete;
FileHandler(FileHandler &&other) noexcept = delete;
FileHandler &operator=(FileHandler &&other) noexcept = delete; FileHandler &operator=(FileHandler &&other) noexcept = delete;
~FileHandler() = default; ~FileHandler() = default;
void handle() const; void handle() override;
private: private:
const AConfig *config_;
const URI &uri_; const URI &uri_;
const AConfig *config_;
HttpResponse &response_;
enum ResourceType : uint8_t enum ResourceType : uint8_t
{ {
FILE, FILE,

View File

@ -33,7 +33,7 @@ bool Router::isMethodSupported(const std::string &method, const AConfig &config)
return std::ranges::find(methods, method) != methods.end(); return std::ranges::find(methods, method) != methods.end();
} }
void Router::handleRequest() std::unique_ptr<AHandler> Router::handleRequest()
{ {
Log::trace(LOCATION); Log::trace(LOCATION);
@ -48,6 +48,7 @@ void Router::handleRequest()
if (!isMethodSupported(method, *config)) if (!isMethodSupported(method, *config))
{ {
return nullptr;
// return ErrorHandler::getErrorResponse(405, config); // return ErrorHandler::getErrorResponse(405, config);
} }
if (request.getUri().isCgi()) if (request.getUri().isCgi())
@ -55,8 +56,7 @@ void Router::handleRequest()
try try
{ {
Log::debug("Starting CGI process"); Log::debug("Starting CGI process");
CgiProcess cgiProcess(request); return std::make_unique<CgiHandler>(request, response);
// return nullptr; // Response will be handled asynchronously
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -66,7 +66,7 @@ void Router::handleRequest()
} }
else else
{ {
FileHandler fileHandler(request, response); return std::make_unique<FileHandler>(request, response);
fileHandler.handle();
} }
return nullptr;
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "webserv/handler/AHandler.hpp"
#include <webserv/config/AConfig.hpp> // for AConfig #include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/LocationConfig.hpp> #include <webserv/config/LocationConfig.hpp>
#include <webserv/http/HttpRequest.hpp> // for HttpRequest #include <webserv/http/HttpRequest.hpp> // for HttpRequest
@ -15,7 +16,7 @@ class Router
{ {
public: public:
Router(Client *client); Router(Client *client);
void handleRequest(); [[nodiscard]] std::unique_ptr<AHandler> handleRequest();
private: private:
Client *client_; Client *client_;