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_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/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp> // for Log, LOCATION
@ -9,6 +11,7 @@
#include <exception>
#include <functional> // for ref, reference_wrapper
#include <map> // for map
#include <memory>
#include <string> // for basic_string, to_string, operator+, operator<=>
#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
}

View File

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

View File

@ -5,6 +5,9 @@
#include <webserv/handler/URI.hpp>
#include <memory>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
@ -47,17 +50,18 @@ void CgiProcess::spawn()
close(sv[1]);
// 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);
}
else
{
// Parent process
CgiSocket cgiSocket(sv[0]);
close(sv[0]);
auto cgiSocket = std::make_unique<CgiSocket>(sv[0]); // CgiSocket wraps 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
}
}

View File

@ -1,4 +1,5 @@
#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
@ -155,9 +156,16 @@ bool URI::isCgi() const
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")
Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_ +
", 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 "";
}
auto cgiPath = config_->getCGIPath(getExtension());

View File

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

View File

@ -73,7 +73,7 @@ void Server::add(const ASocket &socket, uint32_t events, Client *client)
event.data.fd = fd;
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");
}
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));
Client &client = getClient(event->data.fd);
auto httpResponse = client.getResponse();
ssize_t bytesSent = send(event->data.fd, httpResponse.data(), httpResponse.size(), 0);
auto payload = client.getResponse();
ssize_t bytesSent = send(event->data.fd, payload.data(), payload.size(), 0);
if (bytesSent < 0)
{
Log::error("Send failed for fd: " + std::to_string(event->data.fd) + " with error: " + std::strerror(errno));