refactor: make response async

This commit is contained in:
Quinten 2025-10-15 15:28:09 +02:00
parent 35432729dc
commit 7134e26f6f
29 changed files with 299 additions and 142 deletions

View File

@ -86,5 +86,5 @@ server {
# }
# cgi_enabled yes;
# cgi_ext .php /usr/bin/php-cgi;
cgi_ext .php /usr/bin/php-cgi;
}

View File

@ -12,11 +12,12 @@
#include <string> // for basic_string, to_string, operator+, operator<=>
#include <utility> // for pair, move
#include <sys/epoll.h>
#include <sys/types.h> // for ssize_t
Client::Client(std::unique_ptr<ClientSocket> socket, Server &server)
: httpRequest_(std::make_unique<HttpRequest>(this)), httpResponse_(std::make_unique<HttpResponse>()),
client_socket_(std::move(socket)), server_(std::ref(server))
router_(std::make_unique<Router>(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<uint8_t>(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<uint8_t> 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_;
}

View File

@ -2,6 +2,9 @@
// #include <webserv/http/HttpResponse.hpp>
#include "webserv/router/Router.hpp"
#include "webserv/socket/CgiSocket.hpp"
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
#include <webserv/http/HttpConstants.hpp> // for OK
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
@ -32,23 +35,25 @@ class Client
~Client();
void request();
[[nodiscard]] std::vector<uint8_t> getResponse() const;
void poll() const;
[[nodiscard]] bool isResponseReady() const;
[[nodiscard]] std::vector<uint8_t> 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> httpRequest_ = nullptr;
std::unique_ptr<HttpResponse> httpResponse_ = nullptr;
std::unique_ptr<HttpRequest> httpRequest_;
std::unique_ptr<HttpResponse> httpResponse_;
std::unique_ptr<Router> router_;
std::unique_ptr<ClientSocket> client_socket_;
Server &server_;
// mutable const ServerConfig *server_config_ = nullptr;
};

View File

@ -1,14 +1,13 @@
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory
#include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValue
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/utils/utils.hpp> // for trim
#include <ranges> // for filter
#include <sstream> // for basic_stringstream, stringstream
#include <utility> // for pair, move
#include <ranges> // 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<std::vector<std::string>>())
{
continue;
}
auto exts = directive->getValue().try_get<std::vector<std::string>>().value();
{
continue;
}
auto cgiPath = exts.back();
exts.pop_back(); // Last element is the CGI path
auto it = std::ranges::find(exts, extension);

View File

@ -1,6 +1,5 @@
#include <webserv/config/LocationConfig.hpp>
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/LocationConfig.hpp>
#include <webserv/log/Log.hpp> // 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);
}

View File

@ -17,7 +17,6 @@ class ADirective;
std::unique_ptr<ADirective> DirectiveFactory::createDirective(const std::string &line)
{
Log::trace(LOCATION);
std::stringstream ss(line);
std::string name;
ss >> name;

View File

@ -1,9 +1,8 @@
#include <webserv/config/validation/ValidationEngine.hpp>
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/GlobalConfig.hpp> // for GlobalConfig
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
#include <webserv/config/validation/ValidationEngine.hpp>
#include <webserv/config/validation/ValidationResult.hpp> // for ValidationResult
#include <webserv/config/validation/directive_rules/AValidationRule.hpp> // for AValidationRule
#include <webserv/config/validation/structural_rules/AStructuralValidationRule.hpp> // for AStructuralValidationRule
@ -14,25 +13,21 @@
void ValidationEngine::addGlobalRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
{
Log::trace(LOCATION);
addRule(globalRules_, directiveName, std::move(rule));
}
void ValidationEngine::addServerRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
{
Log::trace(LOCATION);
addRule(serverRules_, directiveName, std::move(rule));
}
void ValidationEngine::addLocationRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
{
Log::trace(LOCATION);
addRule(locationRules_, directiveName, std::move(rule));
}
void ValidationEngine::addStructuralRule(std::unique_ptr<AStructuralValidationRule> rule)
{
Log::trace(LOCATION);
if (rule != nullptr)
{
structuralRules_.push_back(std::move(rule));
@ -42,7 +37,6 @@ void ValidationEngine::addStructuralRule(std::unique_ptr<AStructuralValidationRu
void ValidationEngine::addRule(RuleMap &ruleMap, const std::string &directiveName,
std::unique_ptr<AValidationRule> rule)
{
Log::trace(LOCATION);
ruleMap[directiveName].emplace_back(std::move(rule));
}

View File

@ -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");

View File

@ -0,0 +1,63 @@
#include "webserv/handler/CgiProcess.hpp"
#include "webserv/http/HttpRequest.hpp"
#include "webserv/socket/CgiSocket.hpp"
#include <webserv/handler/URI.hpp>
#include <sys/socket.h>
#include <unistd.h>
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<char *>(cgiPath.c_str()), nullptr};
execve(const_cast<char *>(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
}
}

View File

@ -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();
};

View File

@ -1,8 +1,7 @@
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
#include <webserv/config/GlobalConfig.hpp> // for GlobalConfig
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
#include <webserv/http/HttpConstants.hpp> // for getStatusCodeReason, INTERNAL_SERVER_ERROR
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
#include <webserv/log/Log.hpp> // for Log, LOCATION
@ -12,18 +11,15 @@
#include <sstream> // for basic_stringstream
#include <string> // for basic_string, char_traits, operator+, string, to_string
std::unique_ptr<HttpResponse> 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<HttpResponse>();
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)

View File

@ -14,7 +14,7 @@ class AConfig;
class ErrorHandler
{
public:
static std::unique_ptr<HttpResponse> 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);

View File

@ -1,7 +1,6 @@
#include <webserv/handler/FileHandler.hpp>
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
#include <webserv/handler/FileHandler.hpp>
#include <webserv/handler/MIMETypes.hpp> // for MIMETypes
#include <webserv/handler/URI.hpp> // for URI
#include <webserv/http/HttpConstants.hpp> // for NOT_FOUND, FORBIDDEN, OK
@ -15,33 +14,35 @@
#include <string> // for basic_string, string, operator+, char_traits
#include <vector> // 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<HttpResponse> FileHandler::handleFile(const std::string &filepath) const
void FileHandler::handleFile(const std::string &filepath) const
{
Log::trace(LOCATION);
auto response = std::make_unique<HttpResponse>();
// auto response = std::make_unique<HttpResponse>();
std::string extension = FileUtils::getExtension(filepath);
std::string mimeType = MIMETypes().getType(extension);
response->addHeader("Content-Type", mimeType);
response_.addHeader("Content-Type", mimeType);
std::vector<char> 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<char> is preferred, but for http data vector<uint8_t> is preferred
response->setBody(std::vector<uint8_t>{fileData.begin(), fileData.end()});
response->setStatus(Http::StatusCode::OK);
return response;
response_.setBody(std::vector<uint8_t>{fileData.begin(), fileData.end()});
response_.setStatus(Http::StatusCode::OK);
// return response;
}
std::unique_ptr<HttpResponse> 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<HttpResponse> 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<HttpResponse> FileHandler::getResponse() const
void FileHandler::handle() const
{
Log::trace(LOCATION);
std::string filepath = uri_.getFullPath();
@ -73,12 +77,12 @@ std::unique_ptr<HttpResponse> 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

View File

@ -1,5 +1,7 @@
#pragma once
#include "webserv/http/HttpRequest.hpp"
#include <webserv/config/AConfig.hpp>
#include <webserv/config/LocationConfig.hpp>
#include <webserv/handler/URI.hpp>
@ -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<HttpResponse> 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<HttpResponse> handleFile(const std::string &filepath) const;
[[nodiscard]] std::unique_ptr<HttpResponse> handleDirectory(const std::string &dirpath, ResourceType type) const;
void handleFile(const std::string &filepath) const;
void handleDirectory(const std::string &dirpath, ResourceType type) const;
};

View File

@ -1,3 +1,4 @@
#include "webserv/log/Log.hpp"
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
#include <webserv/config/ServerConfig.hpp> // 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<std::string>("cgi_enabled").has_value()
|| config_->get<std::string>("cgi_enabled").value() != "on")
{
return false;
return "";
}
auto cgiPath = config_->getCGIPath(getExtension());
return !cgiPath.empty();
return cgiPath;
}
const std::string &URI::getBaseName() const

View File

@ -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;

View File

@ -1,6 +1,5 @@
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/http/HttpConstants.hpp> // for CRLF
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp>
#include <webserv/utils/utils.hpp> // for trim
@ -10,7 +9,6 @@
std::optional<size_t> HttpHeaders::getContentLength() const
{
Log::trace(LOCATION);
const auto &value = this->get("Content-Length");
if (value.empty())
{
@ -21,7 +19,6 @@ std::optional<size_t> HttpHeaders::getContentLength() const
std::optional<std::string> HttpHeaders::getContentType() const
{
Log::trace(LOCATION);
const auto &value = this->get("Content-Type");
if (value.empty())
{
@ -32,7 +29,6 @@ std::optional<std::string> HttpHeaders::getContentType() const
std::optional<std::string> HttpHeaders::getHost() const
{
Log::trace(LOCATION);
const auto &value = this->get("Host");
if (value.empty())
{
@ -43,7 +39,6 @@ std::optional<std::string> 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_)
{

View File

@ -1,17 +1,20 @@
#include <webserv/http/HttpRequest.hpp>
#include "webserv/config/ConfigManager.hpp"
#include "webserv/handler/URI.hpp"
#include <webserv/client/Client.hpp> // for Client
#include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF, BAD_REQUEST
#include <webserv/http/HttpRequest.hpp>
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/utils/utils.hpp> // for stoul
#include <map> // for map
#include <map> // for map
#include <memory>
#include <optional> // for optional
#include <sstream> // for basic_stringstream, basic_istream, stringstream
#include <utility> // for pair
#include <vector> // 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<URI>(
*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_;
}

View File

@ -5,10 +5,12 @@
#include <cstddef> // for size_t
#include <cstdint> // for uint8_t
#include <string> // for string, basic_string
#include <memory>
#include <string> // 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> uri_;
std::string buffer_;
std::string body_;
std::string method_;

View File

@ -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<uint8_t> &data)
void HttpResponse::setBody(const std::vector<uint8_t> &data) // TODO: validate headers
{
body_ = data;
setComplete();

View File

@ -1,4 +1,4 @@
#include <webserv/router/Router.hpp>
#include "webserv/handler/CgiProcess.hpp"
#include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
@ -9,6 +9,7 @@
#include <webserv/handler/URI.hpp> // for URI
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp> // for LOCATION, Log
#include <webserv/router/Router.hpp>
#include <memory> // for unique_ptr
#include <optional> // for optional
@ -16,6 +17,11 @@
#include <string> // for basic_string, string
#include <vector> // 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<HttpResponse> 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<void>(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();
}

View File

@ -14,8 +14,10 @@ class ServerConfig;
class Router
{
public:
[[nodiscard]] static std::unique_ptr<HttpResponse> 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);
};

View File

@ -1,9 +1,8 @@
#include <webserv/server/Server.hpp>
#include <webserv/client/Client.hpp> // for Client
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/server/Server.hpp>
#include <webserv/socket/ASocket.hpp>
#include <webserv/socket/ClientSocket.hpp> // for ClientSocket
#include <webserv/socket/ServerSocket.hpp> // 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);
}
}

View File

@ -49,6 +49,9 @@ class Server
std::vector<std::unique_ptr<Client>> clients_;
std::unordered_map<int, Client *> 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;

View File

@ -1,6 +1,5 @@
#include <webserv/socket/ASocket.hpp>
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/socket/ASocket.hpp>
#include <stdexcept> // for runtime_error
#include <string> // 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_;
}

View File

@ -1,14 +1,13 @@
#include <webserv/socket/CGISocket.hpp>
#include <webserv/log/Log.hpp> // for LOCATION, Log
#include <webserv/socket/ASocket.hpp> // for ASocket
#include <webserv/socket/CgiSocket.hpp>
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;
}

View File

@ -6,10 +6,10 @@
#include <sys/types.h>
#include <unistd.h>
class CGISocket : public ASocket
class CgiSocket : public ASocket
{
public:
explicit CGISocket(int fd);
explicit CgiSocket(int fd);
[[nodiscard]] ASocket::Type getType() const override;
};

View File

@ -1,7 +1,6 @@
#include <webserv/socket/ClientSocket.hpp>
#include <webserv/log/Log.hpp> // for LOCATION, Log
#include <webserv/socket/ASocket.hpp> // for ASocket
#include <webserv/socket/ClientSocket.hpp>
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;
}

View File

@ -1,8 +1,7 @@
#include <webserv/socket/ServerSocket.hpp>
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/socket/ASocket.hpp> // for ASocket
#include <webserv/socket/ClientSocket.hpp> // for ClientSocket
#include <webserv/socket/ServerSocket.hpp>
#include <memory> // for allocator, make_unique, unique_ptr
#include <stdexcept> // 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;
}