diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index bba52d0..14b4a29 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -1,5 +1,4 @@ #include - #include // for ConfigManager #include // for ServerConfig #include // for ErrorHandler @@ -16,7 +15,8 @@ #include // for ssize_t Client::Client(std::unique_ptr socket, Server &server) - : client_socket_(std::move(socket)), server_(std::ref(server)), httpRequest_(std::make_unique(this)) + : client_socket_(std::move(socket)), server_(std::ref(server)), httpRequest_(std::make_unique(this)), + httpResponse_(std::make_unique(this)) { Log::info("New client connected, fd: " + std::to_string(client_socket_->getFd())); } @@ -49,7 +49,7 @@ void Client::request() } if (bytesRead == 0) { - Log::info("Client disconnected, fd: " + std::to_string(client_socket_->getFd())); //TODO weird + Log::info("Client disconnected, fd: " + std::to_string(client_socket_->getFd())); // TODO weird return; } @@ -68,12 +68,15 @@ void Client::request() {"body", httpRequest_->getBody()}, {"state", std::to_string(static_cast(httpRequest_->getState()))}, }); - server_config_ = ConfigManager::getInstance().getMatchingServerConfig(httpRequest_->getHeaders().get("Host")); + server_config_ = + ConfigManager::getInstance().getMatchingServerConfig(httpRequest_->getHeaders().getHost().value_or("")); if (server_config_ == nullptr) { - Log::warning("No matching server config found for Host: " + httpRequest_->getHeaders().get("Host")); + Log::warning("No matching server config found for Host: " + + httpRequest_->getHeaders().getHost().value_or("unknown host")); httpRequest_->setState(HttpRequest::State::ParseError); } + // Example usage, replace with actual host and port extraction from request server_.responseReady(client_socket_->getFd()); } @@ -87,23 +90,31 @@ void Client::request() } } -std::string Client::getResponse() const +bool Client::isResponseReady() const +{ + // todo: poll the httpResponse_ object +} + +std::vector Client::getResponse() const { Log::trace(LOCATION); - if (httpRequest_->getState() == HttpRequest::State::ParseError) - { - return ErrorHandler::generateErrorPage(Http::StatusCode::BAD_REQUEST); - } - std::string response = "HTTP/1.1 "; - response += "200 OK\r\n"; + // if (httpRequest_->getState() == HttpRequest::State::ParseError) + // { + // return ErrorHandler::generateErrorPage(Http::StatusCode::BAD_REQUEST); + // } + // std::string response = "HTTP/1.1 "; + // response += "200 OK\r\n"; - auto serverName = server_config_->getDirectiveValue("server_name"); - auto port = server_config_->getDirectiveValue("listen"); - std::string body = "Server Name " + serverName + "\r\n"; - body += "Server port " + std::to_string(port) + "\r\n"; - response += "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n"; - response += body; - Log::info("Prepared response for client fd: " + std::to_string(client_socket_->getFd())); - Log::debug("Sending response:\n" + response); - return response; + // auto serverName = server_config_->getDirectiveValue("server_name"); + // auto port = server_config_->getDirectiveValue("listen"); + // std::string body = "Server Name " + serverName + "\r\n"; + // body += "Server port " + std::to_string(port) + "\r\n"; + // response += "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n"; + // response += body; + // Log::info("Prepared response for client fd: " + std::to_string(client_socket_->getFd())); + // Log::debug("Sending response:\n" + response); + httpResponse_->setStatus(200); + httpResponse_->addHeader("Content-Type", "text/plain"); + httpResponse_->appendBody("Hello, World!\n"); + return httpResponse_->toBytes(); } \ No newline at end of file diff --git a/webserv/client/Client.hpp b/webserv/client/Client.hpp index c428782..47319ae 100644 --- a/webserv/client/Client.hpp +++ b/webserv/client/Client.hpp @@ -1,5 +1,9 @@ #pragma once +// #include "webserv/http/HttpResponse.hpp" + +#include "webserv/http/HttpResponse.hpp" + #include // for ServerConfig #include // for OK #include // for HttpRequest @@ -7,12 +11,15 @@ #include #include // for size_t +#include #include // for unique_ptr #include // for string +#include class Server; class Socket; class ServerConfig; +class HttpResponse; class Client { @@ -27,15 +34,20 @@ class Client ~Client(); void request(); - [[nodiscard]] std::string getResponse() const; + [[nodiscard]] std::vector getResponse() const; + [[nodiscard]] bool isResponseReady() const; [[nodiscard]] int getStatusCode() const; + + [[nodiscard]] Socket &getSocket() const { return *client_socket_; } + void setStatusCode(int code); 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 client_socket_; Server &server_; mutable const ServerConfig *server_config_ = nullptr; diff --git a/webserv/http/HttpConstants.cpp b/webserv/http/HttpConstants.cpp new file mode 100644 index 0000000..6c0b85e --- /dev/null +++ b/webserv/http/HttpConstants.cpp @@ -0,0 +1,18 @@ +#include "HttpConstants.hpp" + +#include + +namespace Http +{ +std::string getStatusCodeReason(uint16_t statusCode) +{ + for (const auto &info : statusCodeInfos) + { + if (info.code == statusCode) + { + return std::string(info.reason); + } + } + return "Unknown Status"; +} +} // namespace Http \ No newline at end of file diff --git a/webserv/http/HttpConstants.hpp b/webserv/http/HttpConstants.hpp index bd215cd..2ac0519 100644 --- a/webserv/http/HttpConstants.hpp +++ b/webserv/http/HttpConstants.hpp @@ -63,6 +63,8 @@ static constexpr std::array statusCodeInfos = { {.code = StatusCode::BAD_GATEWAY, .reason = "Bad Gateway"}, {.code = StatusCode::SERVICE_UNAVAILABLE, .reason = "Service Unavailable"}}}; +std::string getStatusCodeReason(uint16_t statusCode); + // Header Names namespace Header { diff --git a/webserv/http/HttpResponse.cpp b/webserv/http/HttpResponse.cpp index 5170b35..0b3486a 100644 --- a/webserv/http/HttpResponse.cpp +++ b/webserv/http/HttpResponse.cpp @@ -1,3 +1,55 @@ +#include "webserv/http/HttpConstants.hpp" + #include -HttpResponse::HttpResponse() {} +#include +#include + +HttpResponse::HttpResponse(Client *client) : client_(client), headers_(std::make_unique()) {} + +void HttpResponse::addHeader(const std::string &key, const std::string &value) +{ + headers_->add(key, value); +} + +void HttpResponse::appendBody(const std::vector &data) +{ + body_.insert(body_.end(), data.begin(), data.end()); +} + +void HttpResponse::appendBody(const std::string &body) +{ + body_.insert(body_.end(), body.begin(), body.end()); +} + +void HttpResponse::setStatus(int statusCode) +{ + statusCode_ = statusCode; +} + +const HttpHeaders &HttpResponse::getHeaders() const +{ + return *headers_; +} + +std::string HttpResponse::getContentLength() const +{ + return "Content-Length: " + std::to_string(body_.size()) + "\r\n"; +} + +std::vector HttpResponse::toBytes() const +{ + std::string headerStr; + std::string reason; + + reason = Http::getStatusCodeReason(statusCode_); + + headerStr = "HTTP/1.1 " + std::to_string(statusCode_) + " " + reason + "\r\n"; // todo: status line + headerStr += getContentLength(); + headerStr += headers_->toString(); + + std::vector responseData(headerStr.begin(), headerStr.end()); + responseData.insert(responseData.end(), body_.begin(), body_.end()); + + return responseData; +} \ No newline at end of file diff --git a/webserv/http/HttpResponse.hpp b/webserv/http/HttpResponse.hpp index 1865c99..45ecef5 100644 --- a/webserv/http/HttpResponse.hpp +++ b/webserv/http/HttpResponse.hpp @@ -1,7 +1,48 @@ #pragma once +#include "webserv/http/HttpHeaders.hpp" + +#include +#include +#include +#include + +class Client; + class HttpResponse { public: - HttpResponse(); + HttpResponse(Client *client); + + HttpResponse(const HttpResponse &other) = delete; // Disable copy constructor + HttpResponse &operator=(const HttpResponse &other) = delete; // Disable copy assignment + HttpResponse(HttpResponse &&other) noexcept = delete; // Move constructor + HttpResponse &operator=(HttpResponse &&other) noexcept = delete; // Move assignment + + ~HttpResponse() = default; + + void addHeader(const std::string &key, const std::string &value); + + void appendBody(const std::vector &data); + void appendBody(const std::string &body); + + void setComplete(); + + void setStatus(int statusCode); + + [[nodiscard]] bool isComplete() const; + + [[nodiscard]] const HttpHeaders &getHeaders() const; + + [[nodiscard]] std::vector toBytes() const; + + private: + [[nodiscard]] std::string getStatusLine() const; + [[nodiscard]] std::string getContentLength() const; + + Client *client_; + std::vector body_; + std::unique_ptr headers_; + bool complete_ = false; + int statusCode_ = 200; }; \ No newline at end of file diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index e12120d..88426c5 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -1,20 +1,19 @@ -#include - #include // for Client #include // for ConfigManager #include // for ServerConfig #include // for Log +#include #include // for Socket -#include // for errno -#include // for strerror, strlen -#include // for exception -#include // for unique_ptr, allocator, make_unique -#include // for runtime_error -#include // for basic_string, operator+, to_string, char_traits, string -#include // for move, pair -#include // for vector +#include // for errno +#include // for strerror, strlen +#include // for exception +#include // for unique_ptr, allocator, make_unique +#include // for runtime_error +#include // for basic_string, operator+, to_string, char_traits, string #include // for unordered_map, unordered_map<>::container_type +#include // for move, pair +#include // for vector #include // for epoll_event, epoll_ctl, EPOLLIN, EPOLLOUT, epoll_create1, epoll_wait, EPOLLERR, EPOLLHUP, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD #include // for send, SOMAXCONN @@ -212,9 +211,8 @@ void Server::eventLoop() { Log::debug("Attempting to send data to fd: " + std::to_string(event.data.fd)); Client &client = getClient(event.data.fd); - std::string response = client.getResponse(); - const char *httpResponse = response.c_str(); - ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0); + auto httpResponse = client.getResponse(); + ssize_t bytesSent = send(event.data.fd, httpResponse.data(), httpResponse.size(), 0); if (bytesSent < 0) { Log::error("Send failed for fd: " + std::to_string(event.data.fd) +