diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index 47f4f07..90b24cb 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -6,8 +6,10 @@ #include Client::Client(std::unique_ptr socket, Server &server, const ServerConfig &server_config) - : client_socket_(std::move(socket)), server_(std::ref(server)), server_config_(std::cref(server_config)) + : client_socket_(std::move(socket)), server_(std::ref(server)), server_config_(std::cref(server_config)), + httpRequest_(std::make_unique(&server_config, this)) { + } Client::~Client() @@ -16,23 +18,6 @@ Client::~Client() server_.removeFromEpoll(*client_socket_); }; -int Client::parseHeaderforContentLength(const std::string &request) // NOLINT -{ - std::string header = "Content-Length: "; - size_t pos = request.find(header); - Log::debug("Parsing header for Content-Length...\n" + header); - if (pos != std::string::npos) - { - size_t start = pos + header.length(); - size_t end = request.find("\r\n", start); - if (end != std::string::npos) - { - std::string lengthStr = request.substr(start, end - start); - return std::atoi(lengthStr.c_str()); - } - } - return -1; // Not found -} void Client::request() { @@ -51,33 +36,15 @@ void Client::request() } buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) - requestBuffer_ += buffer; // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay) - if (header_.empty()) - { - auto headerEnd = requestBuffer_.find("\r\n\r\n"); - if (headerEnd != std::string::npos) - { - header_ = requestBuffer_.substr(0, headerEnd + 4); - contentLength_ = parseHeaderforContentLength(header_); - if (contentLength_ == -1) - { - Log::info("Received complete request:\n" + requestBuffer_ + "\n=== HEADER FINISHED\n"); - server_.responseReady(client_socket_->getFd()); - } - requestBuffer_.erase(0, headerEnd + 4); - return; - } + httpRequest_->receiveData(buffer, static_cast(bytesRead)); + + if(httpRequest_->getState() == HttpRequest::State::Complete) { + Log::info("Received complete request:\n" + httpRequest_->getHeaders() + httpRequest_->getBody() + "\n=== FULL REQUEST FINISHED\n"); + server_.responseReady(client_socket_->getFd()); + httpRequest_->reset(); } - else - { - content_ += requestBuffer_; - if (content_.size() >= contentLength_) - { - Log::info("Received complete request:\n" + header_ + content_ + "\n=== FULL REQUEST FINISHED\n"); - server_.responseReady(client_socket_->getFd()); - requestBuffer_.clear(); - contentLength_ = -1; - } + else { + Log::debug("Received partial request:\n" + httpRequest_->getHeaders() + httpRequest_->getBody() + "\n=== PARTIAL REQUEST\n"); } } diff --git a/webserv/client/Client.hpp b/webserv/client/Client.hpp index 8c6db95..08cd1d9 100644 --- a/webserv/client/Client.hpp +++ b/webserv/client/Client.hpp @@ -4,7 +4,7 @@ #include #include - +#include #include class Server; @@ -25,11 +25,12 @@ class Client [[nodiscard]] std::string getResponse() const; private: + std::unique_ptr httpRequest_ = nullptr; int parseHeaderforContentLength(const std::string &request); - int contentLength_{-1}; - std::string requestBuffer_; - std::string header_; - std::string content_; + // int contentLength_{-1}; + // std::string requestBuffer_; + // std::string header_; + // std::string content_; std::unique_ptr client_socket_; const Server &server_; const ServerConfig &server_config_; diff --git a/webserv/http/HttpRequest.cpp b/webserv/http/HttpRequest.cpp index bdcc334..19a0e05 100644 --- a/webserv/http/HttpRequest.cpp +++ b/webserv/http/HttpRequest.cpp @@ -1,5 +1,130 @@ #include +#include -HttpRequest::HttpRequest() +HttpRequest::HttpRequest(const ServerConfig *serverConfig, const Client *client) + : serverConfig_(serverConfig), client_(client) { + Log::trace("HttpRequest constructor called"); +} +HttpRequest::~HttpRequest() +{ + Log::trace("HttpRequest destructor called"); +} + +HttpRequest::State HttpRequest::getState() const +{ + Log::trace("HttpRequest::getState() called"); + return state_; +} + +const std::string &HttpRequest::getHeaders() const +{ + return headers_; +} + +const std::string &HttpRequest::getBody() const +{ + return body_; +} + +size_t HttpRequest::getContentLength() const +{ + return contentLength_; +} + +void HttpRequest::receiveData(const char *data, size_t length) +{ + Log::trace("HttpRequest::receiveData() called"); + buffer_.append(data, length); + parseBuffer(); +} + +void HttpRequest::parseContentLength() +{ + // Parse headers to find Content-Length + size_t pos = headers_.find("Content-Length:"); + if (pos != std::string::npos) + { + pos += 15; + while (pos < headers_.size() && (headers_[pos] == ' ' || headers_[pos] == '\t')) + { + ++pos; // Skip whitespace + } + size_t endPos = headers_.find("\r\n", pos); + std::string contentLengthValue = headers_.substr(pos, endPos - pos); + contentLength_ = std::stoul(contentLengthValue); + } + else + { + contentLength_ = 0; // No body expected + } +} + +void HttpRequest::parseBuffer() +{ + while (true) + { + if (state_ == State::RequestLine) + { + size_t pos = buffer_.find("\r\n"); + if (pos == std::string::npos) + { + Log::debug("HttpRequest::parseBuffer() in state RequestLine waiting for more data"); + return; // Wait for more data + } + requestLine_ = buffer_.substr(0, pos); + buffer_.erase(0, pos + 2); + state_ = State::Headers; + } + else if (state_ == State::Headers) + { + size_t pos = buffer_.find("\r\n\r\n"); + if (pos == std::string::npos) + { + Log::debug("HttpRequest::parseBuffer() in state Headers waiting for more data"); + return; // Wait for more data + } + headers_ = buffer_.substr(0, pos + 2); // Include the last \r\n + buffer_.erase(0, pos + 4); + parseContentLength(); + + if (contentLength_ > 0) + { + state_ = State::Body; + } + else + { + Log::debug("HttpRequest::parseBuffer() in state Headers no body to read"); + state_ = State::Complete; + return; // No body to read + } + } + else if (state_ == State::Body) + { + if (buffer_.size() < contentLength_) + { + Log::debug("HttpRequest::parseBuffer() in state Body waiting for more data"); + return; // Wait for more data + } + body_ = buffer_.substr(0, contentLength_); + buffer_.erase(0, contentLength_); + state_ = State::Complete; + } + else if (state_ == State::Complete) + { + Log::debug("HttpRequest::parseBuffer() request is complete"); + return; // Request is complete + } + } +} + +void HttpRequest::reset() +{ + Log::trace("HttpRequest::reset() called"); + state_ = State::RequestLine; + buffer_.clear(); + requestLine_.clear(); + headers_.clear(); + body_.clear(); + contentLength_ = 0; } diff --git a/webserv/http/HttpRequest.hpp b/webserv/http/HttpRequest.hpp index 4849c2e..a218624 100644 --- a/webserv/http/HttpRequest.hpp +++ b/webserv/http/HttpRequest.hpp @@ -1,7 +1,51 @@ #pragma once +#include "webserv/config/ServerConfig.hpp" + +#include +#include +#include + +class Client; class HttpRequest { public: - HttpRequest(); -}; \ No newline at end of file + enum class State : std::uint8_t + { + RequestLine, + Headers, + Body, + Complete + }; + + HttpRequest(const ServerConfig *serverConfig, const Client *client); + + HttpRequest(const HttpRequest &other) = delete; + HttpRequest(HttpRequest &&other) noexcept = delete; + HttpRequest &operator=(const HttpRequest &other) = delete; + HttpRequest &operator=(HttpRequest &&other) noexcept = delete; + ~HttpRequest(); + + [[nodiscard]] State getState() const; + [[nodiscard]] const std::string &getHeaders() const; + [[nodiscard]] const std::string &getBody() const; + [[nodiscard]] size_t getContentLength() const; + + void receiveData(const char *data, size_t length); + void reset(); + + private: + void parseBuffer(); + void parseContentLength(); + const ServerConfig *serverConfig_; + const Client *client_; + + State state_ = State::RequestLine; + + std::string buffer_; + + std::string requestLine_; + std::string headers_; + std::string body_; + size_t contentLength_ = 0; +};