feat: better status hanlding cgi and custom env

This commit is contained in:
Quinten 2025-10-30 15:15:06 +01:00
parent c2bdbeb307
commit 6d3941ddf2
8 changed files with 76 additions and 27 deletions

View File

@ -17,7 +17,7 @@ CgiExtValidationRule::CgiExtValidationRule(bool requiresValue)
bool isAllowedCGIExtension(const std::string &extension) bool isAllowedCGIExtension(const std::string &extension)
{ {
static const std::vector<std::string> allowedExtensions = {".php", ".py", ".bla", ".sh", ".cgi"}; static const std::vector<std::string> allowedExtensions = {".php", ".py", ".bla", ".cgi", ".sh"};
return std::ranges::any_of(allowedExtensions, [&extension](const auto &it) { return extension == it; }); return std::ranges::any_of(allowedExtensions, [&extension](const auto &it) { return extension == it; });
} }

View File

@ -1,14 +1,19 @@
#include <webserv/handler/CgiEnvironment.hpp> #include "webserv/http/HttpRequest.hpp"
#include "webserv/log/Log.hpp"
#include <webserv/handler/CgiEnvironment.hpp>
#include <webserv/handler/URI.hpp> // for URI #include <webserv/handler/URI.hpp> // for URI
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders #include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <algorithm>
#include <cctype>
#include <cstring> // for strcpy, size_t #include <cstring> // for strcpy, size_t
#include <optional> // for optional #include <optional> // for optional
#include <utility> // for pair #include <utility> // for pair
CgiEnvironment::CgiEnvironment(const URI &uri, const HttpRequest &request) CgiEnvironment::CgiEnvironment(const URI &uri, const HttpRequest &request)
{ {
Log::trace(LOCATION);
env_["GATEWAY_INTERFACE"] = "CGI/1.1"; env_["GATEWAY_INTERFACE"] = "CGI/1.1";
env_["SERVER_PROTOCOL"] = "HTTP/1.1"; env_["SERVER_PROTOCOL"] = "HTTP/1.1";
env_["REQUEST_METHOD"] = request.getMethod(); env_["REQUEST_METHOD"] = request.getMethod();
@ -38,7 +43,7 @@ CgiEnvironment::CgiEnvironment(const URI &uri, const HttpRequest &request)
env_["SERVER_NAME"] = host; env_["SERVER_NAME"] = host;
env_["SERVER_PORT"] = std::to_string(port); env_["SERVER_PORT"] = std::to_string(port);
env_["REMOTE_ADDR"] = request.getClient().getClientAddress(); // Placeholder, should be set to actual remote address env_["REMOTE_ADDR"] = request.getClient().getClientAddress(); // Placeholder, should be set to actual remote address
env_["REDIRECT_STATUS"] = "200"; // Required by PHP with force-cgi-redirect enabled env_["REDIRECT_STATUS"] = "200"; // Required by PHP with force-cgi-redirect enabled
env_["SERVER_SOFTWARE"] = "Webserv/1.0"; env_["SERVER_SOFTWARE"] = "Webserv/1.0";
env_["REQUEST_SCHEME"] = "HTTP"; env_["REQUEST_SCHEME"] = "HTTP";
env_["HTTP_VERSION"] = "1.1"; env_["HTTP_VERSION"] = "1.1";
@ -49,6 +54,8 @@ CgiEnvironment::CgiEnvironment(const URI &uri, const HttpRequest &request)
env_["HTTP_ACCEPT"] = headers.get("Accept"); env_["HTTP_ACCEPT"] = headers.get("Accept");
env_["HTTP_ACCEPT_LANGUAGE"] = headers.get("Accept-Language"); env_["HTTP_ACCEPT_LANGUAGE"] = headers.get("Accept-Language");
env_["HTTP_ACCEPT_ENCODING"] = headers.get("Accept-Encoding"); env_["HTTP_ACCEPT_ENCODING"] = headers.get("Accept-Encoding");
appendCustomHeaders(headers);
} }
char **CgiEnvironment::toEnvp() const char **CgiEnvironment::toEnvp() const
@ -64,3 +71,20 @@ char **CgiEnvironment::toEnvp() const
envp[index] = nullptr; // Null-terminate the array envp[index] = nullptr; // Null-terminate the array
return envp; return envp;
} }
void CgiEnvironment::appendCustomHeaders(const HttpHeaders &headers)
{
Log::trace(LOCATION);
for (const auto &header : headers.getAll())
{
if (!header.first.starts_with("x-"))
{
continue;
}
std::string key = "HTTP_" + header.first;
std::transform(key.begin(), key.end(), key.begin(), ::toupper);
std::replace(key.begin(), key.end(), '-', '_');
env_[key] = header.second;
Log::debug("Added custom header with key: " + key + " And value: " + header.second);
}
}

View File

@ -18,5 +18,6 @@ class CgiEnvironment
[[nodiscard]] char **toEnvp() const; [[nodiscard]] char **toEnvp() const;
private: private:
void appendCustomHeaders(const HttpHeaders &headers);
std::map<std::string, std::string> env_; std::map<std::string, std::string> env_;
}; };

View File

@ -1,6 +1,5 @@
#include <webserv/handler/CgiHandler.hpp>
#include <webserv/client/Client.hpp> // for Client #include <webserv/client/Client.hpp> // for Client
#include <webserv/handler/CgiHandler.hpp>
#include <webserv/handler/CgiProcess.hpp> // for CgiProcess #include <webserv/handler/CgiProcess.hpp> // for CgiProcess
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler #include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
#include <webserv/http/HttpRequest.hpp> // for HttpRequest #include <webserv/http/HttpRequest.hpp> // for HttpRequest
@ -11,6 +10,8 @@
#include <webserv/utils/utils.hpp> // for trim #include <webserv/utils/utils.hpp> // for trim
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <functional> // for function #include <functional> // for function
#include <utility> // for move #include <utility> // for move
@ -268,7 +269,16 @@ void CgiHandler::parseCgiBody()
{ {
Log::trace(LOCATION); Log::trace(LOCATION);
// Append the body to the response auto status = response_.getHeaders().get("Status");
if (cgiProcess_->getExitCode() > 0 && !status.empty())
{
response_.setStatus(500);
}
else if (!status.empty())
{
response_.setStatus(std::atoi(status.c_str()));
}
response_.appendBody(buffer_); response_.appendBody(buffer_);
response_.setComplete(); response_.setComplete();
buffer_.clear(); buffer_.clear();

View File

@ -1,7 +1,6 @@
#include <webserv/handler/CgiProcess.hpp>
#include <webserv/handler/CgiEnvironment.hpp> // for CgiEnvironment #include <webserv/handler/CgiEnvironment.hpp> // for CgiEnvironment
#include <webserv/handler/CgiHandler.hpp> // for CgiHandler #include <webserv/handler/CgiHandler.hpp> // for CgiHandler
#include <webserv/handler/CgiProcess.hpp>
#include <webserv/handler/URI.hpp> // for URI #include <webserv/handler/URI.hpp> // for URI
#include <webserv/http/HttpRequest.hpp> // for HttpRequest #include <webserv/http/HttpRequest.hpp> // for HttpRequest
#include <webserv/log/Log.hpp> // for Log #include <webserv/log/Log.hpp> // for Log
@ -19,7 +18,7 @@
#include <sys/wait.h> // for waitpid, WNOHANG #include <sys/wait.h> // for waitpid, WNOHANG
#include <unistd.h> // for close, dup2, pipe2, execve, fork, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO #include <unistd.h> // for close, dup2, pipe2, execve, fork, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO
CgiProcess::CgiProcess(const HttpRequest &request, CgiHandler &handler) : request_(request), handler_(handler), _pid(-1) CgiProcess::CgiProcess(const HttpRequest &request, CgiHandler &handler) : request_(request), handler_(handler), pid_(-1), status_(-1)
{ {
if (!request_.getUri().isCgi()) if (!request_.getUri().isCgi())
{ {
@ -49,8 +48,8 @@ void CgiProcess::spawn()
} }
// NOLINTEND // NOLINTEND
CgiEnvironment cgiEnv(uri, request_); CgiEnvironment cgiEnv(uri, request_);
_pid = fork(); pid_ = fork();
if (_pid < 0) if (pid_ < 0)
{ {
close(pipeStdin[0]); close(pipeStdin[0]);
close(pipeStdin[1]); close(pipeStdin[1]);
@ -60,7 +59,7 @@ void CgiProcess::spawn()
close(pipeStderr[1]); close(pipeStderr[1]);
throw std::runtime_error("Failed to fork"); throw std::runtime_error("Failed to fork");
} }
if (_pid == 0) if (pid_ == 0)
{ {
dup2(pipeStdin[0], STDIN_FILENO); dup2(pipeStdin[0], STDIN_FILENO);
dup2(pipeStdout[1], STDOUT_FILENO); dup2(pipeStdout[1], STDOUT_FILENO);
@ -98,34 +97,34 @@ void CgiProcess::spawn()
close(pipeStdout[1]); close(pipeStdout[1]);
close(pipeStderr[1]); close(pipeStderr[1]);
Log::debug("CGI process forked with PID: " + std::to_string(_pid)); Log::debug("CGI process forked with PID: " + std::to_string(pid_));
// request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the // request_.getClient().setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut)); // move the sockets to the
// client // client
handler_.setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut), std::move(cgiStdErr)); handler_.setCgiSockets(std::move(cgiStdIn), std::move(cgiStdOut), std::move(cgiStdErr));
handler_.setPid(_pid); handler_.setPid(pid_);
} }
} }
void CgiProcess::kill() const noexcept void CgiProcess::kill() const noexcept
{ {
if (_pid > 0) if (pid_ > 0)
{ {
::kill(_pid, SIGKILL); ::kill(pid_, SIGKILL);
Log::debug("Killed CGI process with PID: " + std::to_string(_pid)); Log::debug("Killed CGI process with PID: " + std::to_string(pid_));
} }
} }
void CgiProcess::wait() noexcept void CgiProcess::wait() noexcept
{ {
if (_pid > 0) if (pid_ > 0)
{ {
int status; int status;
int waitResult = ::waitpid(_pid, &status, WNOHANG); int waitResult = ::waitpid(pid_, &status, WNOHANG);
if (waitResult == -1) if (waitResult == -1)
{ {
Log::error("Error while waiting for CGI process with PID: " + std::to_string(_pid)); Log::error("Error while waiting for CGI process with PID: " + std::to_string(pid_));
return; return;
} }
if (waitResult == 0) if (waitResult == 0)
@ -134,8 +133,13 @@ void CgiProcess::wait() noexcept
return; return;
} }
Log::debug("CGI process with PID " + std::to_string(_pid) + " has terminated"); Log::debug("CGI process with PID " + std::to_string(pid_) + " has terminated with status " + std::to_string(status));
; status_ = status;
_pid = -1; pid_ = -1;
} }
} }
int CgiProcess::getExitCode() const noexcept
{
return status_;
}

View File

@ -20,11 +20,14 @@ class CgiProcess
void kill() const noexcept; void kill() const noexcept;
void wait() noexcept; void wait() noexcept;
[[nodiscard]] int getExitCode() const noexcept;
private: private:
const HttpRequest &request_; const HttpRequest &request_;
CgiHandler &handler_; CgiHandler &handler_;
int _pid; int pid_;
int status_;
// int _cgiFd; // int _cgiFd;
void spawn(); void spawn();

View File

@ -1,12 +1,13 @@
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/http/HttpConstants.hpp> // for CRLF #include <webserv/http/HttpConstants.hpp> // for CRLF
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp> #include <webserv/log/Log.hpp>
#include <webserv/utils/utils.hpp> // for trim #include <webserv/utils/utils.hpp> // for trim
#include <algorithm> // for __transform_fn, transform #include <algorithm> // for __transform_fn, transform
#include <cctype> // for tolower #include <cctype> // for tolower
#include <utility> // for pair #include <string>
#include <unordered_map>
#include <utility> // for pair
std::optional<size_t> HttpHeaders::getContentLength() const std::optional<size_t> HttpHeaders::getContentLength() const
{ {
@ -94,6 +95,11 @@ void HttpHeaders::parse(const std::string &rawHeaders) noexcept
} }
} }
const std::unordered_map<std::string, std::string> &HttpHeaders::getAll() const noexcept
{
return headers_;
}
std::string HttpHeaders::toString() const noexcept std::string HttpHeaders::toString() const noexcept
{ {
std::string result; std::string result;

View File

@ -29,6 +29,7 @@ class HttpHeaders
[[nodiscard]] std::optional<size_t> getContentLength() const; [[nodiscard]] std::optional<size_t> getContentLength() const;
[[nodiscard]] std::optional<std::string> getContentType() const noexcept; [[nodiscard]] std::optional<std::string> getContentType() const noexcept;
[[nodiscard]] std::optional<std::string> getHost() const noexcept; [[nodiscard]] std::optional<std::string> getHost() const noexcept;
[[nodiscard]] const std::unordered_map<std::string, std::string> &getAll() const noexcept;
private: private:
std::unordered_map<std::string, std::string> headers_; std::unordered_map<std::string, std::string> headers_;