refactor: make response async
This commit is contained in:
parent
35432729dc
commit
7134e26f6f
@ -86,5 +86,5 @@ server {
|
||||
# }
|
||||
|
||||
# cgi_enabled yes;
|
||||
# cgi_ext .php /usr/bin/php-cgi;
|
||||
cgi_ext .php /usr/bin/php-cgi;
|
||||
}
|
||||
@ -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_;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
63
webserv/handler/CgiProcess.cpp
Normal file
63
webserv/handler/CgiProcess.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
25
webserv/handler/CgiProcess.hpp
Normal file
25
webserv/handler/CgiProcess.hpp
Normal 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();
|
||||
};
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_)
|
||||
{
|
||||
|
||||
@ -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_;
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user