diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index e3f877c..bebe8e7 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -26,6 +26,7 @@ Client::Client(std::unique_ptr socket, Server &server) Log::trace(LOCATION); Log::info("New client connected, fd: " + std::to_string(clientSocket_->getFd())); clientSocket_->setCallback([this]() { request(); }); + sockets_[clientSocket_->getFd()] = clientSocket_.get(); } Client::~Client() @@ -35,20 +36,15 @@ Client::~Client() server_.remove(*clientSocket_); }; - ASocket &Client::getSocket(int fd) const { - if (fd == -1 || clientSocket_->getFd() == fd) + if (fd == -1) { return *clientSocket_; } - if (cgiStdIn_ && cgiStdIn_->getFd() == fd) + if (sockets_.contains(fd)) { - return *cgiStdIn_; - } - if (cgiStdOut_ && cgiStdOut_->getFd() == fd) - { - return *cgiStdOut_; + return *sockets_.at(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)); @@ -88,7 +84,8 @@ void Client::request() {"state", std::to_string(static_cast(httpRequest_->getState()))}, }); // server_.responseReady(client_socket_->getFd()); - router_->handleRequest(); + handler_ = router_->handleRequest(); + handler_->handle(); } else { @@ -100,80 +97,23 @@ void Client::request() } } -void Client::writeToCgi() +// +void Client::setCgiSockets(CgiSocket *cgiStdIn, CgiSocket *cgiStdOut) { - Log::trace(LOCATION); - if (cgiStdIn_ == nullptr) - { - Log::error("CGI stdin socket is null"); - return; - } - 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; + server_.add(*cgiStdIn, EPOLLOUT, this); // write + server_.add(*cgiStdOut, EPOLLIN, this); // read + + sockets_[cgiStdIn->getFd()] = cgiStdIn; + sockets_[cgiStdOut->getFd()] = cgiStdOut; } -void Client::readFromCgi() +void Client::removeCgiSocket(CgiSocket *cgiSocket) { - 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())); - 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(bytesRead))); - Log::debug("Read " + std::to_string(bytesRead) - + " bytes from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd())); - } -} + server_.remove(*cgiSocket); // write -void Client::setCgiSockets(std::unique_ptr cgiStdIn, std::unique_ptr cgiStdOut) -{ - cgiStdIn->setCallback([this]() { writeToCgi(); }); - 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 + sockets_.erase(cgiSocket->getFd()); + // sockets_[cgiStdIn->getFd()] = cgiStdIn; + // sockets_[cgiStdOut->getFd()] = cgiStdOut; } void Client::poll() const diff --git a/webserv/client/Client.hpp b/webserv/client/Client.hpp index 6c35df0..1a09c2f 100644 --- a/webserv/client/Client.hpp +++ b/webserv/client/Client.hpp @@ -2,7 +2,9 @@ // #include +#include "webserv/handler/AHandler.hpp" #include "webserv/router/Router.hpp" +#include "webserv/socket/ASocket.hpp" #include "webserv/socket/CgiSocket.hpp" #include // for ServerConfig @@ -36,16 +38,13 @@ class Client void respond() const; void poll() const; - void writeToCgi(); - void readFromCgi(); - - // [[nodiscard]] int getStatusCode() const noexcept; [[nodiscard]] ASocket &getSocket(int fd = -1) const; // void setStatusCode(int code); - void setCgiSockets(std::unique_ptr cgiStdIn, std::unique_ptr cgiStdOut); + void setCgiSockets(CgiSocket *cgiStdIn, CgiSocket *cgiStdOut); + void removeCgiSocket(CgiSocket *cgiSocket); [[nodiscard]] HttpRequest &getHttpRequest() const noexcept; [[nodiscard]] HttpResponse &getHttpResponse() const noexcept; @@ -56,8 +55,14 @@ class Client std::unique_ptr httpRequest_; std::unique_ptr httpResponse_; std::unique_ptr router_; + std::unique_ptr handler_ = nullptr; std::unique_ptr clientSocket_; std::unique_ptr cgiStdIn_ = nullptr; std::unique_ptr cgiStdOut_ = nullptr; + + std::unordered_map sockets_; + Server &server_; + void writeToCgi(); + void readFromCgi(); }; \ No newline at end of file diff --git a/webserv/handler/AHandler.cpp b/webserv/handler/AHandler.cpp new file mode 100644 index 0000000..bbb2f7d --- /dev/null +++ b/webserv/handler/AHandler.cpp @@ -0,0 +1,7 @@ +#include + +#include +#include + +AHandler::AHandler(const HttpRequest &request, HttpResponse &response) : request_(request), response_(response) {} + diff --git a/webserv/handler/AHandler.hpp b/webserv/handler/AHandler.hpp new file mode 100644 index 0000000..1a5b89c --- /dev/null +++ b/webserv/handler/AHandler.hpp @@ -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_; + + + + +}; \ No newline at end of file diff --git a/webserv/handler/CgiHandler.cpp b/webserv/handler/CgiHandler.cpp new file mode 100644 index 0000000..2431dc4 --- /dev/null +++ b/webserv/handler/CgiHandler.cpp @@ -0,0 +1,98 @@ +#include // for Client +#include +#include // for CgiProcess +#include // for HttpRequest +#include // for HttpResponse +#include // for Log +#include // 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(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(bytesRead))); + Log::debug("Read " + std::to_string(bytesRead) + + " bytes from CGI stdout, fd: " + std::to_string(cgiStdOut_->getFd())); + } +} + +void CgiHandler::setCgiSockets(std::unique_ptr cgiStdIn, std::unique_ptr 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 +} \ No newline at end of file diff --git a/webserv/handler/CgiHandler.hpp b/webserv/handler/CgiHandler.hpp index 7b9637e..c507af0 100644 --- a/webserv/handler/CgiHandler.hpp +++ b/webserv/handler/CgiHandler.hpp @@ -1 +1,33 @@ -#pragma once \ No newline at end of file +#pragma once + +#include "webserv/handler/AHandler.hpp" +#include "webserv/socket/CgiSocket.hpp" + +#include + +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 cgiStdIn, std::unique_ptr cgiStdOut); + + private: + constexpr static size_t bufferSize_ = 8192; // TODO: remove duplicate definition and move to configmanager + std::unique_ptr cgiProcess_; + std::unique_ptr cgiStdIn_; + std::unique_ptr cgiStdOut_; + + void write(); + void read(); +}; \ No newline at end of file diff --git a/webserv/handler/CgiProcess.cpp b/webserv/handler/CgiProcess.cpp index 7e4d8da..cd30c68 100644 --- a/webserv/handler/CgiProcess.cpp +++ b/webserv/handler/CgiProcess.cpp @@ -12,7 +12,7 @@ #include #include -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()) { @@ -78,6 +78,8 @@ void CgiProcess::spawn() 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)); } } \ No newline at end of file diff --git a/webserv/handler/CgiProcess.hpp b/webserv/handler/CgiProcess.hpp index 8b6639c..c90b9e3 100644 --- a/webserv/handler/CgiProcess.hpp +++ b/webserv/handler/CgiProcess.hpp @@ -1,11 +1,12 @@ #pragma once +#include "webserv/handler/CgiHandler.hpp" #include "webserv/http/HttpRequest.hpp" class CgiProcess { public: - CgiProcess(const HttpRequest &request); + CgiProcess(const HttpRequest &request, CgiHandler &handler); CgiProcess(const CgiProcess &other) = delete; CgiProcess(CgiProcess &&other) noexcept = delete; @@ -17,6 +18,7 @@ class CgiProcess private: const HttpRequest &request_; + CgiHandler &handler_; int _pid; // int _cgiFd; diff --git a/webserv/handler/FileHandler.cpp b/webserv/handler/FileHandler.cpp index db4c44c..27fb190 100644 --- a/webserv/handler/FileHandler.cpp +++ b/webserv/handler/FileHandler.cpp @@ -15,7 +15,7 @@ #include // for vector 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); } @@ -71,7 +71,7 @@ void FileHandler::handleDirectory(const std::string &dirpath, ResourceType type) ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); } -void FileHandler::handle() const +void FileHandler::handle() { Log::trace(LOCATION); if (!uri_.isValid()) @@ -85,10 +85,16 @@ void FileHandler::handle() const switch (resourceType) { - case FILE: handleFile(filepath); return; + case FILE: + handleFile(filepath); + return; case DIRECTORY_AUTOINDEX: - case DIRECTORY_INDEX: handleDirectory(filepath, resourceType); return; - case NOT_FOUND: ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); return; + case DIRECTORY_INDEX: + handleDirectory(filepath, resourceType); + return; + case NOT_FOUND: + ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); + return; } ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); } diff --git a/webserv/handler/FileHandler.hpp b/webserv/handler/FileHandler.hpp index 705efce..8903258 100644 --- a/webserv/handler/FileHandler.hpp +++ b/webserv/handler/FileHandler.hpp @@ -3,6 +3,7 @@ #include "webserv/http/HttpRequest.hpp" #include +#include #include #include #include // for HttpResponse @@ -14,26 +15,23 @@ class AConfig; class URI; -class FileHandler +class FileHandler : public AHandler { public: FileHandler(const HttpRequest &request, HttpResponse &response); FileHandler(const FileHandler &other) = delete; - FileHandler(FileHandler &&other) noexcept = delete; FileHandler &operator=(const FileHandler &other) = delete; + FileHandler(FileHandler &&other) noexcept = delete; FileHandler &operator=(FileHandler &&other) noexcept = delete; ~FileHandler() = default; - void handle() const; + void handle() override; private: + const URI &uri_; const AConfig *config_; - const URI &uri_; - - HttpResponse &response_; - enum ResourceType : uint8_t { FILE, diff --git a/webserv/router/Router.cpp b/webserv/router/Router.cpp index 718a5f3..57ebb87 100644 --- a/webserv/router/Router.cpp +++ b/webserv/router/Router.cpp @@ -33,7 +33,7 @@ bool Router::isMethodSupported(const std::string &method, const AConfig &config) return std::ranges::find(methods, method) != methods.end(); } -void Router::handleRequest() +std::unique_ptr Router::handleRequest() { Log::trace(LOCATION); @@ -48,6 +48,7 @@ void Router::handleRequest() if (!isMethodSupported(method, *config)) { + return nullptr; // return ErrorHandler::getErrorResponse(405, config); } if (request.getUri().isCgi()) @@ -55,8 +56,7 @@ void Router::handleRequest() try { Log::debug("Starting CGI process"); - CgiProcess cgiProcess(request); - // return nullptr; // Response will be handled asynchronously + return std::make_unique(request, response); } catch (const std::exception &e) { @@ -66,7 +66,7 @@ void Router::handleRequest() } else { - FileHandler fileHandler(request, response); - fileHandler.handle(); + return std::make_unique(request, response); } + return nullptr; } \ No newline at end of file diff --git a/webserv/router/Router.hpp b/webserv/router/Router.hpp index 4a14b61..f622e91 100644 --- a/webserv/router/Router.hpp +++ b/webserv/router/Router.hpp @@ -1,5 +1,6 @@ #pragma once +#include "webserv/handler/AHandler.hpp" #include // for AConfig #include #include // for HttpRequest @@ -15,7 +16,7 @@ class Router { public: Router(Client *client); - void handleRequest(); + [[nodiscard]] std::unique_ptr handleRequest(); private: Client *client_;