diff --git a/config/default.conf b/config/default.conf index 43c62f1..70c69d4 100644 --- a/config/default.conf +++ b/config/default.conf @@ -86,5 +86,5 @@ server { # } # cgi_enabled yes; - # cgi_ext .php /usr/bin/php-cgi; + cgi_ext .php /usr/bin/php-cgi; } \ No newline at end of file diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index a460da7..07313ae 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -12,11 +12,12 @@ #include // for basic_string, to_string, operator+, operator<=> #include // for pair, move +#include #include // for ssize_t Client::Client(std::unique_ptr socket, Server &server) : httpRequest_(std::make_unique(this)), httpResponse_(std::make_unique()), - client_socket_(std::move(socket)), server_(std::ref(server)) + router_(std::make_unique(this)), client_socket_(std::move(socket)), server_(std::ref(server)) { Log::trace(LOCATION); Log::info("New client connected, fd: " + std::to_string(client_socket_->getFd())); @@ -31,13 +32,11 @@ Client::~Client() int Client::getStatusCode() const { - Log::trace(LOCATION); return statusCode_; } void Client::setStatusCode(int code) { - Log::trace(LOCATION); statusCode_ = code; } @@ -74,7 +73,8 @@ void Client::request() {"body", httpRequest_->getBody()}, {"state", std::to_string(static_cast(httpRequest_->getState()))}, }); - server_.responseReady(client_socket_->getFd()); + // server_.responseReady(client_socket_->getFd()); + router_->handleRequest(); } else { @@ -86,17 +86,33 @@ void Client::request() } } -bool Client::isResponseReady() const +void Client::setCgiSocket(CgiSocket &cgiSocket) { - Log::trace(LOCATION); - // todo: poll the httpResponse_ object - return httpResponse_->isComplete(); + server_.add(cgiSocket, EPOLLIN | EPOLLOUT); + // TODO add to handler +} + +void Client::poll() const +{ + if (httpResponse_->isComplete()) + { + Log::info("Response is ready to be sent to client, fd: " + std::to_string(client_socket_->getFd())); + server_.responseReady(client_socket_->getFd()); + } } std::vector Client::getResponse() const { - Log::trace(LOCATION); - auto response = Router::handleRequest(*httpRequest_); - return response->toBytes(); + return httpResponse_->toBytes(); +} + +HttpRequest &Client::getHttpRequest() const +{ + return *httpRequest_; +} + +HttpResponse &Client::getHttpResponse() const +{ + return *httpResponse_; } \ No newline at end of file diff --git a/webserv/client/Client.hpp b/webserv/client/Client.hpp index a300919..2907e07 100644 --- a/webserv/client/Client.hpp +++ b/webserv/client/Client.hpp @@ -2,6 +2,9 @@ // #include +#include "webserv/router/Router.hpp" +#include "webserv/socket/CgiSocket.hpp" + #include // for ServerConfig #include // for OK #include // for HttpRequest @@ -32,23 +35,25 @@ class Client ~Client(); void request(); - [[nodiscard]] std::vector getResponse() const; + void poll() const; - [[nodiscard]] bool isResponseReady() const; + [[nodiscard]] std::vector getResponse() const; [[nodiscard]] int getStatusCode() const; [[nodiscard]] ClientSocket &getSocket() const { return *client_socket_; } - // void setError(int statusCode); - void setStatusCode(int code); + void setCgiSocket(CgiSocket &cgiSocket); + + [[nodiscard]] HttpRequest &getHttpRequest() const; + [[nodiscard]] HttpResponse &getHttpResponse() const; private: int statusCode_ = Http::StatusCode::OK; constexpr static size_t bufferSize_ = 4096; - std::unique_ptr httpRequest_ = nullptr; - std::unique_ptr httpResponse_ = nullptr; + std::unique_ptr httpRequest_; + std::unique_ptr httpResponse_; + std::unique_ptr router_; std::unique_ptr client_socket_; Server &server_; - // mutable const ServerConfig *server_config_ = nullptr; }; \ No newline at end of file diff --git a/webserv/config/AConfig.cpp b/webserv/config/AConfig.cpp index 27b1935..1f71dd6 100644 --- a/webserv/config/AConfig.cpp +++ b/webserv/config/AConfig.cpp @@ -1,14 +1,13 @@ #include // for AConfig - #include // for ADirective #include // for DirectiveFactory #include // for DirectiveValue #include // for Log, LOCATION #include // for trim +#include // for filter #include // for basic_stringstream, stringstream #include // for pair, move -#include // for filter AConfig::AConfig(const AConfig *parent) : parent_(parent) {} @@ -118,19 +117,17 @@ std::string AConfig::getErrorPage(int statusCode) const std::string AConfig::getCGIPath(const std::string &extension) const { Log::trace(LOCATION); - for (const auto &directive : directives_ | std::views::filter([](const auto &d) { - return d->getName() == "cgi_ext"; - })) + for (const auto &directive : directives_) { - + if (directive->getName() != "cgi_ext") + { + continue; + } if (!directive->getValue().holds>()) { continue; } auto exts = directive->getValue().try_get>().value(); - { - continue; - } auto cgiPath = exts.back(); exts.pop_back(); // Last element is the CGI path auto it = std::ranges::find(exts, extension); diff --git a/webserv/config/LocationConfig.cpp b/webserv/config/LocationConfig.cpp index ce9e6e6..53e2f4b 100644 --- a/webserv/config/LocationConfig.cpp +++ b/webserv/config/LocationConfig.cpp @@ -1,6 +1,5 @@ -#include - #include // for AConfig +#include #include // for Log, LOCATION LocationConfig::LocationConfig(const std::string &block, const std::string &path, const AConfig *parent) @@ -22,6 +21,5 @@ std::string LocationConfig::getType() const void LocationConfig::parseBlock(const std::string &block) { - Log::trace(LOCATION); parseDirectives(block); } \ No newline at end of file diff --git a/webserv/config/directive/DirectiveFactory.cpp b/webserv/config/directive/DirectiveFactory.cpp index 18fc06c..9976bcc 100644 --- a/webserv/config/directive/DirectiveFactory.cpp +++ b/webserv/config/directive/DirectiveFactory.cpp @@ -17,7 +17,6 @@ class ADirective; std::unique_ptr DirectiveFactory::createDirective(const std::string &line) { - Log::trace(LOCATION); std::stringstream ss(line); std::string name; ss >> name; diff --git a/webserv/config/validation/ValidationEngine.cpp b/webserv/config/validation/ValidationEngine.cpp index c7a62b4..f8d0b4f 100644 --- a/webserv/config/validation/ValidationEngine.cpp +++ b/webserv/config/validation/ValidationEngine.cpp @@ -1,9 +1,8 @@ -#include - #include // for AConfig #include // for GlobalConfig #include // for LocationConfig #include // for ServerConfig +#include #include // for ValidationResult #include // for AValidationRule #include // for AStructuralValidationRule @@ -14,25 +13,21 @@ void ValidationEngine::addGlobalRule(const std::string &directiveName, std::unique_ptr rule) { - Log::trace(LOCATION); addRule(globalRules_, directiveName, std::move(rule)); } void ValidationEngine::addServerRule(const std::string &directiveName, std::unique_ptr rule) { - Log::trace(LOCATION); addRule(serverRules_, directiveName, std::move(rule)); } void ValidationEngine::addLocationRule(const std::string &directiveName, std::unique_ptr rule) { - Log::trace(LOCATION); addRule(locationRules_, directiveName, std::move(rule)); } void ValidationEngine::addStructuralRule(std::unique_ptr rule) { - Log::trace(LOCATION); if (rule != nullptr) { structuralRules_.push_back(std::move(rule)); @@ -42,7 +37,6 @@ void ValidationEngine::addStructuralRule(std::unique_ptr rule) { - Log::trace(LOCATION); ruleMap[directiveName].emplace_back(std::move(rule)); } diff --git a/webserv/config/validation/directive_rules/AValidationRule.cpp b/webserv/config/validation/directive_rules/AValidationRule.cpp index 45b01d8..1838020 100644 --- a/webserv/config/validation/directive_rules/AValidationRule.cpp +++ b/webserv/config/validation/directive_rules/AValidationRule.cpp @@ -13,7 +13,6 @@ AValidationRule::AValidationRule(std::string ruleName, std::string description, ValidationResult AValidationRule::validate(const AConfig *config, const std::string &directiveName) const { - Log::trace(LOCATION); if (config == nullptr || directiveName.empty()) { return ValidationResult::error("Invalid config or directive name"); diff --git a/webserv/handler/CgiProcess.cpp b/webserv/handler/CgiProcess.cpp new file mode 100644 index 0000000..8c31a13 --- /dev/null +++ b/webserv/handler/CgiProcess.cpp @@ -0,0 +1,63 @@ +#include "webserv/handler/CgiProcess.hpp" + +#include "webserv/http/HttpRequest.hpp" +#include "webserv/socket/CgiSocket.hpp" + +#include + +#include +#include + +CgiProcess::CgiProcess(const HttpRequest &request) : request_(request), _pid(-1), _cgiFd(-1) +{ + if (!request_.getUri().isCgi()) + { + throw std::runtime_error("URI is not a CGI"); + } + + spawn(); +} + +void CgiProcess::spawn() +{ + const URI &uri = request_.getUri(); + auto cgiPath = uri.getCgiPath(); + auto environment = uri.getCGIEnvironment(); + + int sv[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) + { + throw std::runtime_error("Failed to create socket pair"); + } + + _cgiFd = sv[1]; + _pid = fork(); + if (_pid < 0) + { + close(sv[0]); + close(sv[1]); + throw std::runtime_error("Failed to fork"); + } + if (_pid == 0) + { + dup2(sv[1], STDIN_FILENO); + dup2(sv[1], STDOUT_FILENO); + + close(sv[0]); + close(sv[1]); + + // Prepare arguments + char *args[] = {const_cast(cgiPath.c_str()), nullptr}; + execve(const_cast(cgiPath.c_str()), args, nullptr); + } + else + { + // Parent process + CgiSocket cgiSocket(sv[0]); + close(sv[0]); + + request_.getClient().setCgiSocket(cgiSocket); // move the socket to the client + cgiSocket.write(request_.getBody().data(), request_.getBody().size()); + // _cgiFd can be used to communicate with the CGI process + } +} \ No newline at end of file diff --git a/webserv/handler/CgiProcess.hpp b/webserv/handler/CgiProcess.hpp new file mode 100644 index 0000000..5ff87e9 --- /dev/null +++ b/webserv/handler/CgiProcess.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "webserv/http/HttpRequest.hpp" + +class CgiProcess +{ + public: + CgiProcess(const HttpRequest &request); + + CgiProcess(const CgiProcess &other) = delete; + CgiProcess(CgiProcess &&other) noexcept = delete; + + CgiProcess &operator=(const CgiProcess &other) = delete; + CgiProcess &operator=(CgiProcess &&other) noexcept = delete; + + ~CgiProcess() = default; + + private: + const HttpRequest &request_; + + int _pid; + int _cgiFd; + + void spawn(); +}; \ No newline at end of file diff --git a/webserv/handler/ErrorHandler.cpp b/webserv/handler/ErrorHandler.cpp index 221e525..1ccfb98 100644 --- a/webserv/handler/ErrorHandler.cpp +++ b/webserv/handler/ErrorHandler.cpp @@ -1,8 +1,7 @@ -#include // for ErrorHandler - #include // for AConfig #include // for ConfigManager #include // for GlobalConfig +#include // for ErrorHandler #include // for getStatusCodeReason, INTERNAL_SERVER_ERROR #include // for HttpResponse #include // for Log, LOCATION @@ -12,18 +11,15 @@ #include // for basic_stringstream #include // for basic_string, char_traits, operator+, string, to_string -std::unique_ptr ErrorHandler::getErrorResponse(uint16_t statusCode, const AConfig *config) +void ErrorHandler::createErrorResponse(uint16_t statusCode, HttpResponse &response, const AConfig *config) { std::string statusMessage = Http::getStatusCodeReason(statusCode); Log::warning("Generating error response: " + std::to_string(statusCode) + " " + statusMessage); - auto response = std::make_unique(); - std::string body = generateErrorPage(statusCode, config); - response->appendBody(body); - response->setStatus(statusCode); - response->addHeader("Content-Type", "text/html"); - response->addHeader("Connection", "close"); - return response; + response.setStatus(statusCode); + response.addHeader("Content-Type", "text/html"); + response.addHeader("Connection", "close"); + response.appendBody(generateErrorPage(statusCode, config)); } std::string ErrorHandler::generateErrorPage(uint16_t statusCode, const AConfig *config) diff --git a/webserv/handler/ErrorHandler.hpp b/webserv/handler/ErrorHandler.hpp index cc5886b..a469cda 100644 --- a/webserv/handler/ErrorHandler.hpp +++ b/webserv/handler/ErrorHandler.hpp @@ -14,7 +14,7 @@ class AConfig; class ErrorHandler { public: - static std::unique_ptr getErrorResponse(uint16_t statusCode, const AConfig *config = nullptr); + static void createErrorResponse(uint16_t statusCode, HttpResponse &response, const AConfig *config = nullptr); private: static std::string generateErrorPage(uint16_t statusCode, const AConfig *config = nullptr); diff --git a/webserv/handler/FileHandler.cpp b/webserv/handler/FileHandler.cpp index 4597a8b..496e09d 100644 --- a/webserv/handler/FileHandler.cpp +++ b/webserv/handler/FileHandler.cpp @@ -1,7 +1,6 @@ -#include - #include // for AConfig #include // for ErrorHandler +#include #include // for MIMETypes #include // for URI #include // for NOT_FOUND, FORBIDDEN, OK @@ -15,33 +14,35 @@ #include // for basic_string, string, operator+, char_traits #include // for vector -FileHandler::FileHandler(const AConfig *config, const URI &URI) : config_(config), uri_(URI) +FileHandler::FileHandler(const HttpRequest &request, HttpResponse &response) + : config_(request.getUri().getConfig()), uri_(request.getUri()), response_(response) { Log::trace(LOCATION); } -std::unique_ptr FileHandler::handleFile(const std::string &filepath) const +void FileHandler::handleFile(const std::string &filepath) const { Log::trace(LOCATION); - auto response = std::make_unique(); + // auto response = std::make_unique(); std::string extension = FileUtils::getExtension(filepath); std::string mimeType = MIMETypes().getType(extension); - response->addHeader("Content-Type", mimeType); + response_.addHeader("Content-Type", mimeType); std::vector fileData = FileUtils::readBinaryFile(filepath); Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType); if (fileData.empty()) { - return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_); + ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); + return; } // TODO: annoying: For reading files, vector is preferred, but for http data vector is preferred - response->setBody(std::vector{fileData.begin(), fileData.end()}); - response->setStatus(Http::StatusCode::OK); - return response; + response_.setBody(std::vector{fileData.begin(), fileData.end()}); + response_.setStatus(Http::StatusCode::OK); + // return response; } -std::unique_ptr FileHandler::handleDirectory(const std::string &dirpath, ResourceType type) const +void FileHandler::handleDirectory(const std::string &dirpath, ResourceType type) const { Log::trace(LOCATION); @@ -53,19 +54,22 @@ std::unique_ptr FileHandler::handleDirectory(const std::string &di }); if (first_matching == possible_indexes.end()) { - return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, config_); + ErrorHandler::createErrorResponse(Http::StatusCode::FORBIDDEN, response_, config_); + return; } - return handleFile(FileUtils::joinPath(dirpath, *first_matching)); + handleFile(FileUtils::joinPath(dirpath, *first_matching)); + return; } if (type == DIRECTORY_AUTOINDEX) { Log::debug("Requested path is a directory: " + dirpath); - return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, config_); + ErrorHandler::createErrorResponse(Http::StatusCode::FORBIDDEN, response_, config_); + return; } - return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_); + ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); } -std::unique_ptr FileHandler::getResponse() const +void FileHandler::handle() const { Log::trace(LOCATION); std::string filepath = uri_.getFullPath(); @@ -73,12 +77,12 @@ std::unique_ptr FileHandler::getResponse() const switch (resourceType) { - case FILE: return handleFile(filepath); + case FILE: handleFile(filepath); return; case DIRECTORY_AUTOINDEX: - case DIRECTORY_INDEX: return handleDirectory(filepath, resourceType); - case NOT_FOUND: return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_); + case DIRECTORY_INDEX: handleDirectory(filepath, resourceType); return; + case NOT_FOUND: ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); return; } - return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, config_); + ErrorHandler::createErrorResponse(Http::StatusCode::NOT_FOUND, response_, config_); } FileHandler::ResourceType FileHandler::getResourceType(const std::string &path) const diff --git a/webserv/handler/FileHandler.hpp b/webserv/handler/FileHandler.hpp index 20a5533..705efce 100644 --- a/webserv/handler/FileHandler.hpp +++ b/webserv/handler/FileHandler.hpp @@ -1,5 +1,7 @@ #pragma once +#include "webserv/http/HttpRequest.hpp" + #include #include #include @@ -15,7 +17,7 @@ class URI; class FileHandler { public: - FileHandler(const AConfig *config, const URI &uri); + FileHandler(const HttpRequest &request, HttpResponse &response); FileHandler(const FileHandler &other) = delete; FileHandler(FileHandler &&other) noexcept = delete; @@ -24,12 +26,14 @@ class FileHandler ~FileHandler() = default; - [[nodiscard]] std::unique_ptr getResponse() const; + void handle() const; private: const AConfig *config_; const URI &uri_; + HttpResponse &response_; + enum ResourceType : uint8_t { FILE, @@ -39,6 +43,6 @@ class FileHandler }; [[nodiscard]] ResourceType getResourceType(const std::string &path) const; - [[nodiscard]] std::unique_ptr handleFile(const std::string &filepath) const; - [[nodiscard]] std::unique_ptr handleDirectory(const std::string &dirpath, ResourceType type) const; + void handleFile(const std::string &filepath) const; + void handleDirectory(const std::string &dirpath, ResourceType type) const; }; \ No newline at end of file diff --git a/webserv/handler/URI.cpp b/webserv/handler/URI.cpp index 8950e64..38b0f59 100644 --- a/webserv/handler/URI.cpp +++ b/webserv/handler/URI.cpp @@ -1,3 +1,4 @@ +#include "webserv/log/Log.hpp" #include // for AConfig #include // for LocationConfig #include // for ServerConfig @@ -13,6 +14,7 @@ URI::URI(const HttpRequest &request, const ServerConfig &serverConfig) : uriTrimmed_(utils::trim(request.getTarget(), "/")), config_(matchConfig(uriTrimmed_, serverConfig)) { + Log::trace(LOCATION); parseUri(request.getTarget()); parseFullpath(); @@ -147,14 +149,19 @@ bool URI::isValid() const } bool URI::isCgi() const +{ + return !getCgiPath().empty(); +} + +std::string URI::getCgiPath() const { if (isFile() || getExtension().empty() || !config_->get("cgi_enabled").has_value() || config_->get("cgi_enabled").value() != "on") { - return false; + return ""; } auto cgiPath = config_->getCGIPath(getExtension()); - return !cgiPath.empty(); + return cgiPath; } const std::string &URI::getBaseName() const diff --git a/webserv/handler/URI.hpp b/webserv/handler/URI.hpp index 6a17b13..88a771c 100644 --- a/webserv/handler/URI.hpp +++ b/webserv/handler/URI.hpp @@ -26,6 +26,7 @@ class URI [[nodiscard]] bool isCgi() const; [[nodiscard]] std::string getExtension() const; + [[nodiscard]] std::string getCgiPath() const; [[nodiscard]] const AConfig *getConfig() const; [[nodiscard]] const std::string &getBaseName() const; [[nodiscard]] const std::string &getFullPath() const; diff --git a/webserv/http/HttpHeaders.cpp b/webserv/http/HttpHeaders.cpp index 4c1d606..b8760e9 100644 --- a/webserv/http/HttpHeaders.cpp +++ b/webserv/http/HttpHeaders.cpp @@ -1,6 +1,5 @@ -#include // for HttpHeaders - #include // for CRLF +#include // for HttpHeaders #include #include // for trim @@ -10,7 +9,6 @@ std::optional HttpHeaders::getContentLength() const { - Log::trace(LOCATION); const auto &value = this->get("Content-Length"); if (value.empty()) { @@ -21,7 +19,6 @@ std::optional HttpHeaders::getContentLength() const std::optional HttpHeaders::getContentType() const { - Log::trace(LOCATION); const auto &value = this->get("Content-Type"); if (value.empty()) { @@ -32,7 +29,6 @@ std::optional HttpHeaders::getContentType() const std::optional HttpHeaders::getHost() const { - Log::trace(LOCATION); const auto &value = this->get("Host"); if (value.empty()) { @@ -43,7 +39,6 @@ std::optional HttpHeaders::getHost() const void HttpHeaders::add(const std::string &name, const std::string &value) // NOLINT(bugprone-easily-swappable-parameters) { - Log::trace(LOCATION); std::string lower = name; std::ranges::transform(lower, lower.begin(), ::tolower); headers_[lower] = value; @@ -51,13 +46,11 @@ void HttpHeaders::add(const std::string &name, const std::string &value) // NOLI void HttpHeaders::remove(const std::string &name) { - Log::trace(LOCATION); headers_.erase(name); } const std::string &HttpHeaders::get(const std::string &name) const { - Log::trace(LOCATION); std::string lower = name; std::ranges::transform(lower, lower.begin(), ::tolower); auto it = headers_.find(lower); @@ -71,7 +64,6 @@ const std::string &HttpHeaders::get(const std::string &name) const bool HttpHeaders::has(const std::string &name) const { - Log::trace(LOCATION); std::string lower = name; std::ranges::transform(lower, lower.begin(), ::tolower); return headers_.contains(lower); @@ -102,7 +94,6 @@ void HttpHeaders::parse(const std::string &rawHeaders) std::string HttpHeaders::toString() const { - Log::trace(LOCATION); std::string result; for (const auto &pair : headers_) { diff --git a/webserv/http/HttpRequest.cpp b/webserv/http/HttpRequest.cpp index 99bd481..8b17391 100644 --- a/webserv/http/HttpRequest.cpp +++ b/webserv/http/HttpRequest.cpp @@ -1,17 +1,20 @@ -#include +#include "webserv/config/ConfigManager.hpp" +#include "webserv/handler/URI.hpp" #include // for Client #include // for CRLF, DOUBLE_CRLF, BAD_REQUEST +#include #include // for Log, LOCATION #include // for stoul -#include // for map +#include // for map +#include #include // for optional #include // for basic_stringstream, basic_istream, stringstream #include // for pair #include // for vector -HttpRequest::HttpRequest(Client *client) : client_(client) +HttpRequest::HttpRequest(Client *client) : client_(client), uri_(nullptr) { Log::trace(LOCATION); } @@ -23,12 +26,16 @@ HttpRequest::~HttpRequest() HttpRequest::State HttpRequest::getState() const { - Log::trace(LOCATION); return state_; } void HttpRequest::setState(State state) { + if (state == State::Complete) + { + uri_ = std::make_unique( + *this, *ConfigManager::getInstance().getMatchingServerConfig(getHeaders().getHost().value_or(""))); + } state_ = state; } @@ -51,6 +58,7 @@ void HttpRequest::receiveData(const char *data, size_t length) void HttpRequest::parseBuffer() { + Log::trace(LOCATION); while (true) { try @@ -156,7 +164,8 @@ bool HttpRequest::parseBufferforHeaders() state_ = State::Chunked; return true; } - state_ = State::Complete; + Log::debug("No body to read, marking request as complete"); + setState(State::Complete); return false; // No body to read } @@ -177,7 +186,7 @@ bool HttpRequest::parseBufferforChunkedBody() Log::warning("Invalid chunk size: " + chunkSizeStr); if (chunkSize == 0) { - state_ = State::Complete; // Last chunk + setState(State::Complete); // Last chunk buffer_.erase(0, pos + Http::Protocol::CRLF.size()); return true; } @@ -197,7 +206,7 @@ bool HttpRequest::parseBufferforBody() if (!headers_.getContentLength().has_value()) { Log::warning("HttpRequest::parseBuffer() in state Body but no Content-Length header found"); - state_ = State::Complete; + setState(State::Complete); return true; } Log::trace(LOCATION, {{"Content-Length", std::to_string(*headers_.getContentLength())}}); @@ -208,7 +217,7 @@ bool HttpRequest::parseBufferforBody() } body_ = buffer_.substr(0, *headers_.getContentLength()); buffer_.erase(0, *headers_.getContentLength()); - state_ = State::Complete; + setState(State::Complete); return true; } @@ -224,6 +233,11 @@ void HttpRequest::reset() // contentLength_ = 0; } +const URI &HttpRequest::getUri() const +{ + return *uri_; +} + const std::string &HttpRequest::getMethod() const { return method_; @@ -239,3 +253,7 @@ const std::string &HttpRequest::getHttpVersion() const return httpVersion_; } +Client &HttpRequest::getClient() const +{ + return *client_; +} diff --git a/webserv/http/HttpRequest.hpp b/webserv/http/HttpRequest.hpp index 1893ed4..1be5718 100644 --- a/webserv/http/HttpRequest.hpp +++ b/webserv/http/HttpRequest.hpp @@ -5,10 +5,12 @@ #include // for size_t #include // for uint8_t -#include // for string, basic_string +#include +#include // for string, basic_string class Client; class ServerConfig; +class URI; class HttpRequest { @@ -32,11 +34,13 @@ class HttpRequest ~HttpRequest(); [[nodiscard]] State getState() const; + [[nodiscard]] const URI &getUri() const; [[nodiscard]] const HttpHeaders &getHeaders() const; [[nodiscard]] const std::string &getBody() const; [[nodiscard]] const std::string &getMethod() const; [[nodiscard]] const std::string &getTarget() const; [[nodiscard]] const std::string &getHttpVersion() const; + [[nodiscard]] Client &getClient() const; void setState(State state); void receiveData(const char *data, size_t length); @@ -58,6 +62,8 @@ class HttpRequest HttpHeaders headers_; + std::unique_ptr uri_; + std::string buffer_; std::string body_; std::string method_; diff --git a/webserv/http/HttpResponse.cpp b/webserv/http/HttpResponse.cpp index eedce15..e4ce718 100644 --- a/webserv/http/HttpResponse.cpp +++ b/webserv/http/HttpResponse.cpp @@ -22,7 +22,7 @@ void HttpResponse::appendBody(const std::string &body) body_.insert(body_.end(), body.begin(), body.end()); } -void HttpResponse::setBody(const std::vector &data) +void HttpResponse::setBody(const std::vector &data) // TODO: validate headers { body_ = data; setComplete(); diff --git a/webserv/router/Router.cpp b/webserv/router/Router.cpp index 3d1197d..dd2733f 100644 --- a/webserv/router/Router.cpp +++ b/webserv/router/Router.cpp @@ -1,4 +1,4 @@ -#include +#include "webserv/handler/CgiProcess.hpp" #include // for AConfig #include // for ConfigManager @@ -9,6 +9,7 @@ #include // for URI #include // for HttpHeaders #include // for LOCATION, Log +#include #include // for unique_ptr #include // for optional @@ -16,6 +17,11 @@ #include // for basic_string, string #include // for vector +Router::Router(Client *client) : client_(client) +{ + Log::trace(LOCATION); +} + bool Router::isMethodSupported(const std::string &method, const AConfig &config) { const ADirective *allowedMethods = config.getDirective("allowed_methods"); @@ -27,28 +33,45 @@ bool Router::isMethodSupported(const std::string &method, const AConfig &config) return std::ranges::find(methods, method) != methods.end(); } -std::unique_ptr Router::handleRequest(const HttpRequest &request) +void Router::handleRequest() { Log::trace(LOCATION); - ServerConfig *serverConfig - = ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or("")); + HttpRequest &request = client_->getHttpRequest(); + HttpResponse &response = client_->getHttpResponse(); - if (serverConfig == nullptr) - { - return ErrorHandler::getErrorResponse(400); - } - URI uri{request, *serverConfig}; + // ServerConfig *serverConfig + // = ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or("")); + + // if (serverConfig == nullptr) + // { + // response = ErrorHandler::getErrorResponse(400); + // } + // URI uri{request, *serverConfig}; const std::string &target = request.getTarget(); static_cast(target); // Suppress unused variable warning const std::string &method = request.getMethod(); - const AConfig *config = uri.getConfig(); + const AConfig *config = request.getUri().getConfig(); + if (!isMethodSupported(method, *config)) { - return ErrorHandler::getErrorResponse(405, config); + // return ErrorHandler::getErrorResponse(405, config); } - FileHandler fileHandler(config, uri); - return fileHandler.getResponse(); + if (request.getUri().isCgi()) + { + try + { + CgiProcess cgiProcess(request); + // return nullptr; // Response will be handled asynchronously + } + catch (const std::exception &e) + { + Log::error("CGI process failed: " + std::string(e.what())); + // return ErrorHandler::getErrorResponse(500, config); + } + } + FileHandler fileHandler(request, response); + fileHandler.handle(); } \ No newline at end of file diff --git a/webserv/router/Router.hpp b/webserv/router/Router.hpp index 5f5412d..c4bde12 100644 --- a/webserv/router/Router.hpp +++ b/webserv/router/Router.hpp @@ -14,8 +14,10 @@ class ServerConfig; class Router { public: - [[nodiscard]] static std::unique_ptr handleRequest(const HttpRequest &request); + Router(Client *client); + void handleRequest(); private: - [[nodiscard]] static bool isMethodSupported(const std::string &method, const AConfig &config); + Client *client_; + [[nodiscard]] bool isMethodSupported(const std::string &method, const AConfig &config); }; \ No newline at end of file diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index 916c22d..4cb177b 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -1,9 +1,8 @@ -#include - #include // for Client #include // for ConfigManager #include // for ServerConfig #include // for Log, LOCATION +#include #include #include // for ClientSocket #include // for ServerSocket @@ -26,8 +25,7 @@ class Router; -Server::Server(const ConfigManager &configManager) - : epoll_fd_(epoll_create1(0)), configManager_(configManager) +Server::Server(const ConfigManager &configManager) : epoll_fd_(epoll_create1(0)), configManager_(configManager) { Log::trace(LOCATION); const auto &serverConfigs = configManager.getServerConfigs(); @@ -152,7 +150,6 @@ ServerSocket &Server::getListener(int fd) const Client &Server::getClient(int fd) const { - Log::trace(LOCATION); if (socketToClient_.contains(fd)) { return *(socketToClient_.at(fd)); @@ -224,6 +221,28 @@ void Server::handleEvent(struct epoll_event *event) } } +void Server::handleEpoll(struct epoll_event *events, int max_events) +{ + int nfds = epoll_wait(epoll_fd_, events, max_events, 0); // NOLINT + if (nfds == -1) + { + Log::error("epoll_wait failed"); + throw std::runtime_error("epoll_wait failed"); + } + for (int i = 0; i < nfds; ++i) + { + handleEvent(&events[i]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } +} + +void Server::pollClients() const +{ + for (const auto &client : clients_) + { + client->poll(); + } +} + void Server::run() { Log::trace(LOCATION); @@ -232,15 +251,8 @@ void Server::run() struct epoll_event events[MAX_EVENTS]; // NOLINT while (true) { - int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1); // NOLINT - if (nfds == -1) - { - Log::error("epoll_wait failed"); - throw std::runtime_error("epoll_wait failed"); - } - for (int i = 0; i < nfds; ++i) - { - handleEvent(&events[i]); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) - } + pollClients(); + handleEpoll(events, MAX_EVENTS); + usleep(1000); } } \ No newline at end of file diff --git a/webserv/server/Server.hpp b/webserv/server/Server.hpp index d5a16f6..d47e773 100644 --- a/webserv/server/Server.hpp +++ b/webserv/server/Server.hpp @@ -49,6 +49,9 @@ class Server std::vector> clients_; std::unordered_map socketToClient_; + void pollClients() const; + void handleEpoll(struct epoll_event *events, int max_events); + void handleEvent(struct epoll_event *event); void handleConnection(struct epoll_event *event); void handleRequest(struct epoll_event *event) const; diff --git a/webserv/socket/ASocket.cpp b/webserv/socket/ASocket.cpp index 36334a6..e02406e 100644 --- a/webserv/socket/ASocket.cpp +++ b/webserv/socket/ASocket.cpp @@ -1,6 +1,5 @@ -#include - #include // for Log, LOCATION +#include #include // for runtime_error #include // for basic_string @@ -33,6 +32,7 @@ ASocket::~ASocket() ssize_t ASocket::read(void *buf, size_t len) const { + Log::trace(LOCATION); ssize_t bytesRead = ::recv(fd_, buf, len, 0); if (bytesRead == -1) { @@ -62,7 +62,6 @@ void ASocket::setNonBlocking() const int ASocket::getFd() const { - Log::trace(LOCATION); return fd_; } diff --git a/webserv/socket/CGISocket.cpp b/webserv/socket/CgiSocket.cpp similarity index 59% rename from webserv/socket/CGISocket.cpp rename to webserv/socket/CgiSocket.cpp index 89da2b6..cc464d0 100644 --- a/webserv/socket/CGISocket.cpp +++ b/webserv/socket/CgiSocket.cpp @@ -1,14 +1,13 @@ -#include - #include // for LOCATION, Log #include // for ASocket +#include -CGISocket::CGISocket(int fd) : ASocket(fd) +CgiSocket::CgiSocket(int fd) : ASocket(fd) { Log::trace(LOCATION); } -ASocket::Type CGISocket::getType() const +ASocket::Type CgiSocket::getType() const { return ASocket::Type::CGI_SOCKET; } diff --git a/webserv/socket/CGISocket.hpp b/webserv/socket/CgiSocket.hpp similarity index 76% rename from webserv/socket/CGISocket.hpp rename to webserv/socket/CgiSocket.hpp index ac000ff..b69b5c0 100644 --- a/webserv/socket/CGISocket.hpp +++ b/webserv/socket/CgiSocket.hpp @@ -6,10 +6,10 @@ #include #include -class CGISocket : public ASocket +class CgiSocket : public ASocket { public: - explicit CGISocket(int fd); + explicit CgiSocket(int fd); [[nodiscard]] ASocket::Type getType() const override; }; \ No newline at end of file diff --git a/webserv/socket/ClientSocket.cpp b/webserv/socket/ClientSocket.cpp index f7669ed..5ceac03 100644 --- a/webserv/socket/ClientSocket.cpp +++ b/webserv/socket/ClientSocket.cpp @@ -1,7 +1,6 @@ -#include - #include // for LOCATION, Log #include // for ASocket +#include ClientSocket::ClientSocket(int fd) : ASocket(fd) { @@ -10,6 +9,5 @@ ClientSocket::ClientSocket(int fd) : ASocket(fd) ASocket::Type ClientSocket::getType() const { - Log::trace(LOCATION); return ASocket::Type::CLIENT_SOCKET; } diff --git a/webserv/socket/ServerSocket.cpp b/webserv/socket/ServerSocket.cpp index 50aef59..e38c92d 100644 --- a/webserv/socket/ServerSocket.cpp +++ b/webserv/socket/ServerSocket.cpp @@ -1,8 +1,7 @@ -#include - #include // for Log, LOCATION #include // for ASocket #include // for ClientSocket +#include #include // for allocator, make_unique, unique_ptr #include // for runtime_error @@ -65,7 +64,6 @@ void ServerSocket::bind(const std::string &host, const int port) const ASocket::Type ServerSocket::getType() const { - Log::trace(LOCATION); return ASocket::Type::SERVER_SOCKET; }