Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions src/http/cgi/execute.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include "../../../include/http/CgiHandler.hpp"
#include <unistd.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <fcntl.h>
#include <signal.h>

#define CGI_TIMEOUT_SEC 5

static std::string scriptPathFrom(const HttpRequest& req, const LocationConfig& loc)
{
Expand Down Expand Up @@ -32,14 +36,40 @@ static void runChild(const std::string& interpreter, const std::string& scriptPa
_exit(1);
}

static std::string readOutput(int stdout_pipe[2])
static std::string readOutputWithTimeout(int fd, pid_t pid)
{
std::string output;
char buf[4096];
ssize_t n;
while ((n = read(stdout_pipe[0], buf, sizeof(buf))) > 0)

while (true)
{
fd_set readfds;
struct timeval timeout;

FD_ZERO(&readfds);
FD_SET(fd, &readfds);
timeout.tv_sec = CGI_TIMEOUT_SEC;
timeout.tv_usec = 0;

int ready = select(fd + 1, &readfds, NULL, NULL, &timeout);

if (ready == 0)
{
// timeout : tuer le script
kill(pid, SIGKILL);
waitpid(pid, NULL, 0);
close(fd);
return "";
}
if (ready == -1)
break;

ssize_t n = read(fd, buf, sizeof(buf));
if (n <= 0)
break;
output.append(buf, n);
close(stdout_pipe[0]);
}
close(fd);
return output;
}

Expand Down Expand Up @@ -81,15 +111,20 @@ HttpResponse CgiHandler::execute(const HttpRequest& req, const LocationConfig& l
if (pid == 0)
runChild(interpreter, scriptPath, argv, envp.data(), stdin_pipe, stdout_pipe);

// parent
close(stdin_pipe[0]);
close(stdout_pipe[1]);

if (!req.body.empty())
write(stdin_pipe[1], req.body.c_str(), req.body.size());
close(stdin_pipe[1]);

std::string output = readOutput(stdout_pipe);
std::string output = readOutputWithTimeout(stdout_pipe[0], pid);

if (output.empty())
{
// timeout ou sortie vide — le waitpid a déjà été fait dans readOutputWithTimeout
return buildError(504, "Gateway Timeout");
}

int status;
waitpid(pid, &status, 0);
Expand Down
7 changes: 7 additions & 0 deletions tests/http/test_cgi_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ int main()
check("Non-CGI: pas de CGI pour .html", res.body.find("WebServ") != std::string::npos);
}

// ── CAS 8 : script qui freeze → 504 Gateway Timeout ─────────────
{
std::cout << " (attente timeout 5s...)" << std::endl;
HttpResponse res = handler.handle(makeReq("GET", "/cgi-bin/infinite.py"), loc, server);
check("CGI timeout: 504", res.status_code == 504);
}

std::cout << std::endl << passed << " passed, " << failed << " failed" << std::endl;
return failed > 0 ? 1 : 0;
}
4 changes: 4 additions & 0 deletions www/cgi-bin/infinite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env python3
import time
time.sleep(30)
print("Content-Type: text/html\n\nnever")
Loading