feat(config): add new server configuration for site-3 and implement CGI support

feat(site-3): create index.php for site-3 with a greeting message
refactor(client): change setCgiSocket to accept unique_ptr for better memory management
fix(AConfig): adjust getCGIPath to correctly prepend dot to extension
refactor(CgiProcess): update CGI process handling to use unique_ptr for CgiSocket
refactor(URI): enhance logging for CGI checks and improve condition checks
refactor(Router): clean up commented code and add debug logging for CGI process
fix(Server): improve error logging for epoll_ctl failures
This commit is contained in:
Quinten 2025-10-15 17:03:49 +02:00
parent 7134e26f6f
commit 0887acd81a
9 changed files with 72 additions and 28 deletions

View File

@ -88,3 +88,32 @@ server {
# cgi_enabled yes; # cgi_enabled yes;
cgi_ext .php /usr/bin/php-cgi; cgi_ext .php /usr/bin/php-cgi;
} }
server {
listen 8082;
host 127.0.0.1;
server_name localhost;
root ./htdocs/site-3/;
index index.html index2.htm;
error_page 400;
error_page 403 /403.html;
error_page 404 /404.html;
error_page 405 /405.html;
error_page 413 /413.html;
error_page 500 /500.html;
error_page 502 /502.html;
error_page 504 /504.html;
client_max_body_size 1M;
location / {
autoindex off;
index index.php;
allowed_methods GET POST DELETE;
}
cgi_enabled yes;
cgi_ext .php /usr/bin/php-cgi;
}

3
htdocs/site-3/index.php Normal file
View File

@ -0,0 +1,3 @@
<?php
echo "Hello, World! This is site-3.";

View File

@ -1,3 +1,5 @@
#include "webserv/socket/CgiSocket.hpp"
#include <webserv/client/Client.hpp> #include <webserv/client/Client.hpp>
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders #include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp> // for Log, LOCATION #include <webserv/log/Log.hpp> // for Log, LOCATION
@ -9,6 +11,7 @@
#include <exception> #include <exception>
#include <functional> // for ref, reference_wrapper #include <functional> // for ref, reference_wrapper
#include <map> // for map #include <map> // for map
#include <memory>
#include <string> // for basic_string, to_string, operator+, operator<=> #include <string> // for basic_string, to_string, operator+, operator<=>
#include <utility> // for pair, move #include <utility> // for pair, move
@ -86,9 +89,10 @@ void Client::request()
} }
} }
void Client::setCgiSocket(CgiSocket &cgiSocket) void Client::setCgiSocket(std::unique_ptr<CgiSocket> cgiSocket)
{ {
server_.add(cgiSocket, EPOLLIN | EPOLLOUT); server_.add(*cgiSocket, EPOLLIN, this);
cgi_socket_ = std::move(cgiSocket);
// TODO add to handler // TODO add to handler
} }

View File

@ -43,7 +43,7 @@ class Client
[[nodiscard]] ClientSocket &getSocket() const { return *client_socket_; } [[nodiscard]] ClientSocket &getSocket() const { return *client_socket_; }
void setStatusCode(int code); void setStatusCode(int code);
void setCgiSocket(CgiSocket &cgiSocket); void setCgiSocket(std::unique_ptr<CgiSocket> cgiSocket);
[[nodiscard]] HttpRequest &getHttpRequest() const; [[nodiscard]] HttpRequest &getHttpRequest() const;
[[nodiscard]] HttpResponse &getHttpResponse() const; [[nodiscard]] HttpResponse &getHttpResponse() const;
@ -55,5 +55,6 @@ class Client
std::unique_ptr<HttpResponse> httpResponse_; std::unique_ptr<HttpResponse> httpResponse_;
std::unique_ptr<Router> router_; std::unique_ptr<Router> router_;
std::unique_ptr<ClientSocket> client_socket_; std::unique_ptr<ClientSocket> client_socket_;
std::unique_ptr<CgiSocket> cgi_socket_ = nullptr;
Server &server_; Server &server_;
}; };

View File

@ -130,7 +130,7 @@ std::string AConfig::getCGIPath(const std::string &extension) const
auto exts = directive->getValue().try_get<std::vector<std::string>>().value(); auto exts = directive->getValue().try_get<std::vector<std::string>>().value();
auto cgiPath = exts.back(); auto cgiPath = exts.back();
exts.pop_back(); // Last element is the CGI path exts.pop_back(); // Last element is the CGI path
auto it = std::ranges::find(exts, extension); auto it = std::ranges::find(exts, "." + extension);
if (it != exts.end()) if (it != exts.end())
{ {
return cgiPath; return cgiPath;

View File

@ -5,6 +5,9 @@
#include <webserv/handler/URI.hpp> #include <webserv/handler/URI.hpp>
#include <memory>
#include <string>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
@ -47,17 +50,18 @@ void CgiProcess::spawn()
close(sv[1]); close(sv[1]);
// Prepare arguments // Prepare arguments
char *args[] = {const_cast<char *>(cgiPath.c_str()), nullptr}; std::string fullPath = uri.getFullPath();
char *args[] = {const_cast<char *>(cgiPath.c_str()), const_cast<char *>(fullPath.c_str())};
execve(const_cast<char *>(cgiPath.c_str()), args, nullptr); execve(const_cast<char *>(cgiPath.c_str()), args, nullptr);
} }
else else
{ {
// Parent process // Parent process
CgiSocket cgiSocket(sv[0]); auto cgiSocket = std::make_unique<CgiSocket>(sv[0]); // CgiSocket wraps sv[0]
close(sv[0]); close(sv[1]);
request_.getClient().setCgiSocket(cgiSocket); // move the socket to the client // cgiSocket->write(request_.getBody().data(), request_.getBody().size());
cgiSocket.write(request_.getBody().data(), request_.getBody().size()); request_.getClient().setCgiSocket(std::move(cgiSocket)); // move the socket to the client
// _cgiFd can be used to communicate with the CGI process // _cgiFd can be used to communicate with the CGI process
} }
} }

View File

@ -1,4 +1,5 @@
#include "webserv/log/Log.hpp" #include "webserv/log/Log.hpp"
#include <webserv/config/AConfig.hpp> // for AConfig #include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/LocationConfig.hpp> // for LocationConfig #include <webserv/config/LocationConfig.hpp> // for LocationConfig
#include <webserv/config/ServerConfig.hpp> // for ServerConfig #include <webserv/config/ServerConfig.hpp> // for ServerConfig
@ -155,9 +156,16 @@ bool URI::isCgi() const
std::string URI::getCgiPath() const std::string URI::getCgiPath() const
{ {
if (isFile() || getExtension().empty() || !config_->get<std::string>("cgi_enabled").has_value() Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_ +
|| config_->get<std::string>("cgi_enabled").value() != "on") ", Extension: " + getExtension());
if (!isFile() || getExtension().empty() || !config_->get<bool>("cgi_enabled").has_value()
|| !config_->get<bool>("cgi_enabled").value())
{ {
Log::debug("CGI not enabled or not a file or no extension",
{{"isFile", isFile() ? "true" : "false"},
{"extension", getExtension()},
{"cgi_enabled", config_->get<bool>("cgi_enabled").has_value() ? "true" : "false"},
{"cgi_enabled_value", config_->get<bool>("cgi_enabled").value() ? "true" : "false"}});
return ""; return "";
} }
auto cgiPath = config_->getCGIPath(getExtension()); auto cgiPath = config_->getCGIPath(getExtension());

View File

@ -40,15 +40,6 @@ void Router::handleRequest()
HttpRequest &request = client_->getHttpRequest(); HttpRequest &request = client_->getHttpRequest();
HttpResponse &response = client_->getHttpResponse(); HttpResponse &response = client_->getHttpResponse();
// 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(); const std::string &target = request.getTarget();
static_cast<void>(target); // Suppress unused variable warning static_cast<void>(target); // Suppress unused variable warning
const std::string &method = request.getMethod(); const std::string &method = request.getMethod();
@ -63,6 +54,7 @@ void Router::handleRequest()
{ {
try try
{ {
Log::debug("Starting CGI process");
CgiProcess cgiProcess(request); CgiProcess cgiProcess(request);
// return nullptr; // Response will be handled asynchronously // return nullptr; // Response will be handled asynchronously
} }
@ -72,6 +64,9 @@ void Router::handleRequest()
// return ErrorHandler::getErrorResponse(500, config); // return ErrorHandler::getErrorResponse(500, config);
} }
} }
else
{
FileHandler fileHandler(request, response); FileHandler fileHandler(request, response);
fileHandler.handle(); fileHandler.handle();
}
} }

View File

@ -73,7 +73,7 @@ void Server::add(const ASocket &socket, uint32_t events, Client *client)
event.data.fd = fd; event.data.fd = fd;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == -1) if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event) == -1)
{ {
Log::error("epoll_ctl ADD failed for fd: " + std::to_string(fd)); Log::error("epoll_ctl ADD failed for fd: " + std::to_string(fd) + " with error: " + std::strerror(errno));
throw std::runtime_error("epoll_ctl ADD failed"); throw std::runtime_error("epoll_ctl ADD failed");
} }
socketToClient_[fd] = client; socketToClient_[fd] = client;
@ -185,8 +185,8 @@ void Server::handleResponse(struct epoll_event *event)
{ {
Log::debug("Attempting to send data to fd: " + std::to_string(event->data.fd)); Log::debug("Attempting to send data to fd: " + std::to_string(event->data.fd));
Client &client = getClient(event->data.fd); Client &client = getClient(event->data.fd);
auto httpResponse = client.getResponse(); auto payload = client.getResponse();
ssize_t bytesSent = send(event->data.fd, httpResponse.data(), httpResponse.size(), 0); ssize_t bytesSent = send(event->data.fd, payload.data(), payload.size(), 0);
if (bytesSent < 0) if (bytesSent < 0)
{ {
Log::error("Send failed for fd: " + std::to_string(event->data.fd) + " with error: " + std::strerror(errno)); Log::error("Send failed for fd: " + std::to_string(event->data.fd) + " with error: " + std::strerror(errno));