From c1c6ce37f9e49e73966fe402a588725a127e3d4a Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 9 Nov 2023 15:20:39 +0100 Subject: [PATCH 01/33] draft CGI but need to split it up and implement with poll --- include/CGI.hpp | 4 +- src/CGI.cpp | 233 +++++++++++++++++++++++++++++++----------------- src/main.cpp | 53 ++++++++--- 3 files changed, 193 insertions(+), 97 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 1c709bc..f7a0dbd 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -4,6 +4,8 @@ #define READ_END 0 #define WRITE_END 1 +#include + // Common gateway interface class CGI @@ -14,7 +16,7 @@ class CGI CGI(const CGI &src) = delete; CGI &operator=(const CGI &rhs) = delete; ~CGI(); - void execute(); + void execute(const char *executable, bool get, std::string &body); }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index e3983ff..fb687a0 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -6,6 +6,7 @@ #include #include + CGI::CGI() { } @@ -14,93 +15,159 @@ CGI::~CGI() { } -void CGI::execute() +// Every step in the CGI must be done through poll +// First step: separate on read, write and execve +// First read is already been done by the request parser, +// so after creating the pipe in the execute function return to poll and +// wait till it's says it's ready to write. +void CGI::execute(const char *executable, bool get, std::string &body) { - int infile = open("in.txt", O_RDONLY, 0666); - int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); - Logger &logger = Logger::getInstance(); - int in[2]; - int out[2]; - pid_t pid; - - logger.log(INFO, "launcging CGI"); - if (pipe(in) == SYSTEM_ERROR) - throw SystemException("Pipe"); - if (pipe(out) == SYSTEM_ERROR) + Logger &logger = Logger::getInstance(); + int fd[2]; + pid_t pid; + + // STEP 1 + if (pipe(fd) == SYSTEM_ERROR) throw SystemException("Pipe"); + // *return to poll with fd[write_end] - pid = fork(); - if (pid == SYSTEM_ERROR) - throw SystemException("Fork"); - else if (pid == 0) - { - if (close(in[WRITE_END]) == SYSTEM_ERROR) - { - logger.log(ERROR, "close in[WRITE_END] error" + - std::string(strerror(errno))); - _exit(127); - } - if (close(out[READ_END]) == SYSTEM_ERROR) - { - logger.log(ERROR, "close out[READ_END] error" + - std::string(strerror(errno))); - _exit(127); - } - if (dup2(in[READ_END], STDIN_FILENO) == SYSTEM_ERROR) - { - logger.log(ERROR, "dup2 in[READ_END] error" + - std::string(strerror(errno))); - _exit(127); - } - if (close(in[READ_END]) == SYSTEM_ERROR) - { - logger.log(ERROR, "close in[READ_END] error" + - std::string(strerror(errno))); - _exit(127); - } - if (dup2(out[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) - { - logger.log(ERROR, "dup2 out[WRITE_END] error" + - std::string(strerror(errno))); - _exit(127); - } - if (close(out[WRITE_END]) == SYSTEM_ERROR) - { - logger.log(ERROR, "close out[WRITE_END] error" + - std::string(strerror(errno))); + // STEP 2 + logger.log(INFO, "launching CGI"); + if (get) { + logger.log(INFO, "handling CGI GET Request"); + + pid = fork(); + if (pid == SYSTEM_ERROR) + throw SystemException("Fork"); + else if (pid == 0) { + if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) + { + logger.log(ERROR, "dup2 out[WRITE_END] error" + + std::string(strerror(errno))); + _exit(127); + } + std::string bin = "python3"; + const char *const argv[] = {bin.c_str(), executable, NULL}; + char *const envp[] = {NULL}; + const char *path = "/usr/bin/python3"; + execve(path, (char *const *)argv, envp); + logger.log(ERROR, "execve error" + std::string(strerror(errno))); _exit(127); } - std::string bin = "python3"; - std::string script = "./cgi-bin/print.py"; - const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; - char *const envp[] = {NULL}; - const char *path = "/usr/bin/python3"; - execve(path, (char *const *)argv, envp); - logger.log(ERROR, "execve error" + std::string(strerror(errno))); - _exit(127); + // *return to poll with fd[read_end] + + // STEP 3 + char buffer[1024]; + int read_bytes; + bzero(buffer, sizeof(buffer)); + read_bytes = read(fd[READ_END], buffer, sizeof(buffer)); + logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); + body += buffer; + // logger.log(INFO, "Output: " + std::string(buffer)); + // *return to poll + + // FINAL STEP + // response + + } + else { + // implementation for post request + logger.log(INFO, "handling CGI POST Request"); } - assert(close(in[READ_END]) && "close in[READ_END] error"); - assert(close(out[WRITE_END]) && "close out[WRITE_END] error"); - - char buffer_out[1024]; - char buffer_in[1024]; - int read_bytes; - bzero(buffer_in, sizeof(buffer_in)); - bzero(buffer_out, sizeof(buffer_out)); - - read_bytes = read(infile, buffer_in, sizeof(buffer_in)); - logger.log(INFO, "buffer_in: " + std::string(buffer_in)); - if (write(in[WRITE_END], buffer_in, read_bytes) == SYSTEM_ERROR) - logger.log(ERROR, "write error" + std::string(strerror(errno))); - - assert(close(in[WRITE_END]) && "close in[WRITE_END] error"); - if ((read_bytes = read(out[READ_END], buffer_out, sizeof(buffer_out))) == - SYSTEM_ERROR) - logger.log(ERROR, "read error" + std::string(strerror(errno))); - logger.log(INFO, "buffer_out: " + std::string(buffer_out)); - write(outfile, buffer_out, read_bytes); - close(out[READ_END]); - - assert(close(infile) && "close infile error"); - assert(close(outfile) && "close outfile error"); + close(fd[READ_END]); + close(fd[WRITE_END]); } + +// void CGI::execute(const char *executable) +// { + // int infile = open("in.txt", O_RDONLY, 0666); + // int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); + // Logger &logger = Logger::getInstance(); + // int in[2]; + // int out[2]; + // pid_t pid; + + // logger.log(INFO, "launching CGI"); + // if (pipe(in) == SYSTEM_ERROR) + // throw SystemException("Pipe"); + // if (pipe(out) == SYSTEM_ERROR) + // throw SystemException("Pipe"); + + // pid = fork(); + // if (pid == SYSTEM_ERROR) + // throw SystemException("Fork"); + // else if (pid == 0) + // { + // if (close(in[WRITE_END]) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "close in[WRITE_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // if (close(out[READ_END]) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "close out[READ_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // if (dup2(in[READ_END], STDIN_FILENO) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "dup2 in[READ_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // if (close(in[READ_END]) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "close in[READ_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // if (dup2(out[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "dup2 out[WRITE_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // if (close(out[WRITE_END]) == SYSTEM_ERROR) + // { + // logger.log(ERROR, "close out[WRITE_END] error" + + // std::string(strerror(errno))); + // _exit(127); + // } + // std::string bin = "python3"; + // + // std::string script = "./cgi-bin/print.py"; + // const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; + // + // const char *const argv[] = {bin.c_str(), executable, NULL}; + // char *const envp[] = {NULL}; + // const char *path = "/usr/bin/python3"; + // execve(path, (char *const *)argv, envp); + // logger.log(ERROR, "execve error" + std::string(strerror(errno))); + // _exit(127); + // } + // assert(close(in[READ_END]) && "close in[READ_END] error"); + // assert(close(out[WRITE_END]) && "close out[WRITE_END] error"); + + // char buffer_out[1024]; + // char buffer_in[1024]; + // int read_bytes; + // bzero(buffer_in, sizeof(buffer_in)); + // bzero(buffer_out, sizeof(buffer_out)); + + // read_bytes = read(infile, buffer_in, sizeof(buffer_in)); + // logger.log(INFO, "buffer_in: " + std::string(buffer_in)); + // if (write(in[WRITE_END], buffer_in, read_bytes) == SYSTEM_ERROR) + // logger.log(ERROR, "write error" + std::string(strerror(errno))); + + // assert(close(in[WRITE_END]) && "close in[WRITE_END] error"); + // if ((read_bytes = read(out[READ_END], buffer_out, sizeof(buffer_out))) == + // SYSTEM_ERROR) + // logger.log(ERROR, "read error" + std::string(strerror(errno))); + // logger.log(INFO, "buffer_out: " + std::string(buffer_out)); + // write(outfile, buffer_out, read_bytes); + // close(out[READ_END]); + + // assert(close(infile) && "close infile error"); + // assert(close(outfile) && "close outfile error"); +// } diff --git a/src/main.cpp b/src/main.cpp index 7b8357f..3b6f660 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,21 +3,48 @@ #include #include -int main(int argc, char **argv) -{ - Logger &logger = Logger::getInstance(); - const std::string program_name(argv[0]); - - logger.log(LogLevel::INFO, "Launching server"); - if (argc != 1 && argc != 2) - { - logger.log(LogLevel::ERROR, "Usage: " + program_name + "[config_path]"); - return (EXIT_FAILURE); - } - HTTPServer server(argc == 2 ? argv[1] : "config/default.conf"); - return (server.run()); +// int main(int argc, char **argv) +// { +// Logger &logger = Logger::getInstance(); +// const std::string program_name(argv[0]); + +// logger.log(LogLevel::INFO, "Launching server"); +// if (argc != 1 && argc != 2) +// { +// logger.log(LogLevel::ERROR, "Usage: " + program_name + "[config_path]"); +// return (EXIT_FAILURE); +// } +// HTTPServer server(argc == 2 ? argv[1] : "config/default.conf"); +// return (server.run()); +// } + + +#include +#include +int main(void) { + + Logger &logger = Logger::getInstance(); + CGI cgi_request; + std::string body; + + cgi_request.execute("data/www/python/test.py", true, body); + logger.log(INFO, "Body: " + body); + + return 0; + + // std::string bin = "python3"; + // std::string script = "data/www/python/test.py"; + // const char *path = "/usr/bin/python3"; + + // const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; + // char *const envp[] = {NULL}; + + // execve(path, (char *const *)argv, envp); + // return 0; } +// ###### + // //#define REQUEST "GET /index HTTP/1.1 balllen \r\nHost: // 192.168.0.199:80\r\nConnection: keep-alive\r\nAccept: */*\r\nUser-Agent: // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) From 04305c588148aa9f7e69e35bcdec4944c73f6fff Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 9 Nov 2023 17:16:23 +0100 Subject: [PATCH 02/33] added comments in handle connection for structure cgi --- src/CGI.cpp | 7 +++++-- src/Client.cpp | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/CGI.cpp b/src/CGI.cpp index fb687a0..fdfb0d9 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -25,11 +25,15 @@ void CGI::execute(const char *executable, bool get, std::string &body) Logger &logger = Logger::getInstance(); int fd[2]; pid_t pid; + + // WHEN POST: extra step --> parse body and write to pipe + // *return to poll with fd[] with addPollFD // STEP 1 if (pipe(fd) == SYSTEM_ERROR) throw SystemException("Pipe"); - // *return to poll with fd[write_end] + // *return to poll with fd[write_end] with addPollFD + // STEP 2 logger.log(INFO, "launching CGI"); @@ -54,7 +58,6 @@ void CGI::execute(const char *executable, bool get, std::string &body) logger.log(ERROR, "execve error" + std::string(strerror(errno))); _exit(127); } - // *return to poll with fd[read_end] // STEP 3 char buffer[1024]; diff --git a/src/Client.cpp b/src/Client.cpp index 1911c29..bf71912 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -27,7 +27,12 @@ ClientState Client::handleConnection(short events) { if (events & POLLIN) { + // if pipe + // - read pipe fd + // otherwise _state = _request.receive(_socket.getFD()); + // resolve location + // return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) @@ -45,6 +50,8 @@ ClientState Client::handleConnection(short events) } else if (events & POLLOUT && _state == ClientState::Sending) { + // if pipe + // - write to body to pipe or execute program, otherwise: _state = _response.send(_socket.getFD(), _file_manager.getResponse()); return (_state); From 9a80ae91b466796b09acd4f6e1652847a6950bd9 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 15 Nov 2023 17:34:44 +0100 Subject: [PATCH 03/33] took some steps in the right direction --- include/CGI.hpp | 12 ++++++++-- include/Client.hpp | 12 +++++----- include/ClientState.hpp | 2 ++ include/FileManager.hpp | 3 +++ out.txt | 2 -- src/CGI.cpp | 24 +++++++++++++++++++ src/Client.cpp | 16 +++++++++---- src/HTTPServer.cpp | 9 ++++++-- src/main.cpp | 51 ++++++++++++++++++++++------------------- tests/get_request.sh | 2 +- 10 files changed, 91 insertions(+), 42 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index f7a0dbd..f0ed53e 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -1,22 +1,30 @@ #ifndef CGI_HPP #define CGI_HPP +#include +#include + #define READ_END 0 #define WRITE_END 1 -#include - // Common gateway interface class CGI { private: + std::string _post_body; + public: CGI(); CGI(const CGI &src) = delete; CGI &operator=(const CGI &rhs) = delete; ~CGI(); + + // execute is probably redundant void execute(const char *executable, bool get, std::string &body); + + ClientState receive(int pipe_fd, std::string body); + ClientState send(int fd); }; #endif diff --git a/include/Client.hpp b/include/Client.hpp index fb0e010..ce7e283 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -20,12 +20,12 @@ class Client int getFD(void) const; private: - HTTPRequest _request; - HTTPResponse _response; - FileManager _file_manager; - CGI _cgi; - Socket _socket; - ClientState _state; + HTTPRequest _request; + HTTPResponse _response; + FileManager _file_manager; + CGI _cgi; + Socket _socket; + ClientState _state; }; #endif diff --git a/include/ClientState.hpp b/include/ClientState.hpp index 829999f..d035b82 100644 --- a/include/ClientState.hpp +++ b/include/ClientState.hpp @@ -4,6 +4,8 @@ enum class ClientState { Receiving, + CGI_Write, + CGI_Read, Loading, Sending, Done, diff --git a/include/FileManager.hpp b/include/FileManager.hpp index 8f08a4c..740d9d8 100644 --- a/include/FileManager.hpp +++ b/include/FileManager.hpp @@ -11,6 +11,7 @@ class FileManager private: std::string _response; std::fstream _request_target; + // CGI _cgi; public: FileManager(); @@ -30,6 +31,8 @@ class FileManager ClientState managePost(const std::string &body); ClientState manageDelete(const std::string &reqest_target_path); + // CGI APPEND FUNCTION + const std::string &getResponse(void) const; }; diff --git a/out.txt b/out.txt index 40f3220..e69de29 100644 --- a/out.txt +++ b/out.txt @@ -1,2 +0,0 @@ -['hello world!\n', 'goodbye world!\n'] -Content-type: text/html diff --git a/src/CGI.cpp b/src/CGI.cpp index fdfb0d9..510403e 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -81,6 +81,30 @@ void CGI::execute(const char *executable, bool get, std::string &body) close(fd[WRITE_END]); } +ClientState CGI::send(int fd) +{ + // 1) write body to stdin (if it is a POST request) + // if method == post && body_has_been_sent == false + // write body to stdin + // set body_has_been_sent to true + // or (else) + // 2) execve +} + +ClientState CGI::receive(int fd, std::string body) +{ + Logger &logger = Logger::getInstance(); + char buffer[1024]; + int read_bytes; + bzero(buffer, sizeof(buffer)); + read_bytes = read(fd, buffer, sizeof(buffer)); + logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); + + body += buffer; + return (ClientState::Loading); +} + + // void CGI::execute(const char *executable) // { // int infile = open("in.txt", O_RDONLY, 0666); diff --git a/src/Client.cpp b/src/Client.cpp index bf71912..00cae6d 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -27,12 +28,17 @@ ClientState Client::handleConnection(short events) { if (events & POLLIN) { - // if pipe - // - read pipe fd - // otherwise _state = _request.receive(_socket.getFD()); - // resolve location - // + return (_state); + } + else if (events & POLLOUT && _state == ClientState::CGI_Write) + { + _state = _cgi.send(int fd); + return (_state); + } + else if (events & POLLIN && _state == ClientState::CGI_Read) + { + _state = _cgi.receive(int fd); return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 5aec298..ba559e8 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -32,8 +32,6 @@ int HTTPServer::run() logger.log(FATAL, e.what()); return (EXIT_FAILURE); } - return (EXIT_SUCCESS); - while (true) { try @@ -46,6 +44,7 @@ int HTTPServer::run() return (EXIT_FAILURE); } } + return (EXIT_SUCCESS); } void HTTPServer::setupServers(void) @@ -115,6 +114,12 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd) case ClientState::Error: _poll.setEvents(poll_fd.fd, POLLOUT); break; + case ClientState::CGI_Write: + _poll.setEvents(poll_fd.fd, POLLOUT); + break; + case ClientState::CGI_Read: + _poll.setEvents(poll_fd.fd, POLLIN); + break; case ClientState::Done: _poll.removeFD(poll_fd.fd); _active_clients.erase(poll_fd.fd); diff --git a/src/main.cpp b/src/main.cpp index 3b6f660..f4b7db3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,34 +3,37 @@ #include #include -// int main(int argc, char **argv) -// { -// Logger &logger = Logger::getInstance(); -// const std::string program_name(argv[0]); - -// logger.log(LogLevel::INFO, "Launching server"); -// if (argc != 1 && argc != 2) -// { -// logger.log(LogLevel::ERROR, "Usage: " + program_name + "[config_path]"); -// return (EXIT_FAILURE); -// } -// HTTPServer server(argc == 2 ? argv[1] : "config/default.conf"); -// return (server.run()); -// } +int main(int argc, char **argv) +{ + Logger &logger = Logger::getInstance(); + const std::string program_name(argv[0]); + + logger.log(LogLevel::INFO, "Launching server"); + if (argc != 1 && argc != 2) + { + logger.log(LogLevel::ERROR, "Usage: " + program_name + "[config_path]"); + return (EXIT_FAILURE); + } + HTTPServer server(argc == 2 ? argv[1] : "config/default.conf"); + return (server.run()); +} + +// &&&&&&&&&&&&&& +// #include +// #include +// int main(void) { -#include -#include -int main(void) { +// Logger &logger = Logger::getInstance(); +// CGI cgi_request; +// std::string body; - Logger &logger = Logger::getInstance(); - CGI cgi_request; - std::string body; +// cgi_request.execute("data/www/python/test.py", true, body); +// logger.log(INFO, "Body: " + body); - cgi_request.execute("data/www/python/test.py", true, body); - logger.log(INFO, "Body: " + body); +// return 0; - return 0; + // & // std::string bin = "python3"; // std::string script = "data/www/python/test.py"; @@ -41,7 +44,7 @@ int main(void) { // execve(path, (char *const *)argv, envp); // return 0; -} +// } // ###### diff --git a/tests/get_request.sh b/tests/get_request.sh index 8923074..95f6053 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/delete.txt" +REQUEST_FILE="tests/request/post.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From 3f21c8bc552eec071a0c3cc3ea35e5af78ef49e2 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 16 Nov 2023 12:18:07 +0100 Subject: [PATCH 04/33] cgi send and receive almost there --- include/CGI.hpp | 9 +++++++-- include/HTTPRequest.hpp | 2 ++ src/CGI.cpp | 40 +++++++++++++++++++++++++++++++++------- src/Client.cpp | 19 +++++++++++++++---- src/FileManager.cpp | 2 +- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index f0ed53e..885cdac 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -2,6 +2,7 @@ #define CGI_HPP #include +#include #include #define READ_END 0 @@ -12,7 +13,7 @@ class CGI { private: - std::string _post_body; + // std::string _body; public: CGI(); @@ -24,7 +25,11 @@ class CGI void execute(const char *executable, bool get, std::string &body); ClientState receive(int pipe_fd, std::string body); - ClientState send(int fd); + ClientState send(int fd, HTTPMethod methodType, std::string requestBody); + + std::string body; + bool body_has_been_sent; + int pipe_fd[2]; }; #endif diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index ca893a7..b9a13f1 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -47,6 +47,8 @@ class HTTPRequest const std::string &getBody(void) const; ClientState receive(int fd); + bool cgi; + private: ssize_t _bytes_read; size_t _content_length; diff --git a/src/CGI.cpp b/src/CGI.cpp index 510403e..956993f 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -9,6 +9,7 @@ CGI::CGI() { + body_has_been_sent = false; } CGI::~CGI() @@ -81,14 +82,38 @@ void CGI::execute(const char *executable, bool get, std::string &body) close(fd[WRITE_END]); } -ClientState CGI::send(int fd) +ClientState CGI::send(int fd, HTTPMethod methodType, std::string requestBody) { - // 1) write body to stdin (if it is a POST request) - // if method == post && body_has_been_sent == false - // write body to stdin - // set body_has_been_sent to true - // or (else) - // 2) execve + pid_t pid; + + if (methodType == HTTPMethod::POST && body_has_been_sent == false) + { + write(STDIN_FILENO, &requestBody, sizeof(requestBody)); + return (ClientState::CGI_Write); + } + else + { + // parse URI and optionally save PATH_INFO and QUERY_STRING in env + // char **env = NULL; + pid = fork(); + if (pid == SYSTEM_ERROR) + throw SystemException("Fork"); + else if (pid == 0) { + if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) + { + logger.log(ERROR, "dup2 out[WRITE_END] error" + + std::string(strerror(errno))); + _exit(127); + } + std::string bin = "python3"; + const char *const argv[] = {bin.c_str(), executable, NULL}; + char *const envp[] = {NULL}; + const char *path = "/usr/bin/python3"; + execve(path, (char *const *)argv, envp); + logger.log(ERROR, "execve error" + std::string(strerror(errno))); + _exit(127); + } + } } ClientState CGI::receive(int fd, std::string body) @@ -101,6 +126,7 @@ ClientState CGI::receive(int fd, std::string body) logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); body += buffer; + // is it necesssary to check here whether the eof is reached on the fd? return (ClientState::Loading); } diff --git a/src/Client.cpp b/src/Client.cpp index 00cae6d..c617fc3 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -29,20 +29,29 @@ ClientState Client::handleConnection(short events) if (events & POLLIN) { _state = _request.receive(_socket.getFD()); + if (_request.cgi == true) + { + // create pipe to read to? how does this work with poll? + // if (pipe(fd) == SYSTEM_ERROR) + // throw SystemException("Pipe"); + } return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) { - _state = _cgi.send(int fd); + _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); return (_state); } else if (events & POLLIN && _state == ClientState::CGI_Read) { - _state = _cgi.receive(int fd); + _state = _cgi.receive(_socket.getFD(), _cgi.body); return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) { + // if (_request._cgi == true) + // _state = _cgi.createResponse(); + // else _state = _file_manager.manage( _request.getMethodType(), "./data/www" + _request.getRequestTarget(), @@ -56,8 +65,10 @@ ClientState Client::handleConnection(short events) } else if (events & POLLOUT && _state == ClientState::Sending) { - // if pipe - // - write to body to pipe or execute program, otherwise: + // if CGI + // _state = + // _response.send(_socket.getFD(), _cgi.getResponse()); + // else _state = _response.send(_socket.getFD(), _file_manager.getResponse()); return (_state); diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 03328bf..cff166e 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -131,7 +131,7 @@ ClientState FileManager::manage(HTTPMethod method, openGetFile(request_target_path); return (manageGet()); } - else if (method == HTTPMethod::POST) + else if (method == HTTPMethod::POST) // && CGI == false { if (!_request_target.is_open()) openPostFile(request_target_path); From 5746c4e88bf8420d826bf1924369071e9cc845ef Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 17 Nov 2023 15:57:56 +0100 Subject: [PATCH 05/33] parseURIForCGI is finished and did some things in CGI cpp as well and added some comments --- include/CGI.hpp | 5 +- include/ClientState.hpp | 1 + include/HTTPRequest.hpp | 4 + include/Socket.hpp | 3 + src/CGI.cpp | 319 ++++++++++++++++++++-------------------- src/Client.cpp | 22 ++- src/HTTPRequest.cpp | 50 ++++++- src/main.cpp | 4 + 8 files changed, 237 insertions(+), 171 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 885cdac..8227d49 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -22,10 +22,11 @@ class CGI ~CGI(); // execute is probably redundant - void execute(const char *executable, bool get, std::string &body); + void start_CGI(const char *executable, const char **env, size_t body_length); + // void execute(const char *executable); ClientState receive(int pipe_fd, std::string body); - ClientState send(int fd, HTTPMethod methodType, std::string requestBody); + // ClientState send(int fd, HTTPMethod methodType, std::string requestBody); std::string body; bool body_has_been_sent; diff --git a/include/ClientState.hpp b/include/ClientState.hpp index d035b82..29b9ab9 100644 --- a/include/ClientState.hpp +++ b/include/ClientState.hpp @@ -4,6 +4,7 @@ enum class ClientState { Receiving, + start_CGI, CGI_Write, CGI_Read, Loading, diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index b9a13f1..8bc589f 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -47,6 +47,8 @@ class HTTPRequest const std::string &getBody(void) const; ClientState receive(int fd); + void parseURIForCGI(); + bool cgi; private: @@ -58,6 +60,8 @@ class HTTPRequest std::string _http_version; std::string _body; std::unordered_map _headers; + std::string _executable; + char **_env; size_t parseStartLine(size_t &i); size_t parseHeaders(size_t &i); diff --git a/include/Socket.hpp b/include/Socket.hpp index 14ca090..e426656 100644 --- a/include/Socket.hpp +++ b/include/Socket.hpp @@ -4,6 +4,9 @@ #include #include +// MAX_PENDING_CONNECTION SHOULDN'T BE 1 +// WHY 10? WE CAN ALSO SET IT ON 1024, THAT'S +// MORE SAFE, CONCERNING THE AVAILABILITY (siege -b) #define MAX_PENDING_CONNECTIONS 10 typedef struct sockaddr_in t_sockaddr_in; diff --git a/src/CGI.cpp b/src/CGI.cpp index 956993f..d924b9b 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -7,6 +7,13 @@ #include +// TO DO +// - make in startCGI connection (pollFD) for out[READ_END] and optionally for in[WRITE_END] (in case of post request) +// - insert in handleConnection CGI_WRITE and CGI_READ +// - create some kind of a CGI_LOAD that serves as replacement for the file manager +// - create python program +// * check out the wiki and github that Sander has sent + CGI::CGI() { body_has_been_sent = false; @@ -21,100 +28,100 @@ CGI::~CGI() // First read is already been done by the request parser, // so after creating the pipe in the execute function return to poll and // wait till it's says it's ready to write. -void CGI::execute(const char *executable, bool get, std::string &body) -{ - Logger &logger = Logger::getInstance(); - int fd[2]; - pid_t pid; +// void CGI::execute(const char *executable, bool get, std::string &body) +// { +// Logger &logger = Logger::getInstance(); +// int fd[2]; +// pid_t pid; - // WHEN POST: extra step --> parse body and write to pipe - // *return to poll with fd[] with addPollFD +// // WHEN POST: extra step --> parse body and write to pipe +// // *return to poll with fd[] with addPollFD - // STEP 1 - if (pipe(fd) == SYSTEM_ERROR) - throw SystemException("Pipe"); - // *return to poll with fd[write_end] with addPollFD - - - // STEP 2 - logger.log(INFO, "launching CGI"); - if (get) { - logger.log(INFO, "handling CGI GET Request"); - - pid = fork(); - if (pid == SYSTEM_ERROR) - throw SystemException("Fork"); - else if (pid == 0) { - if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) - { - logger.log(ERROR, "dup2 out[WRITE_END] error" + - std::string(strerror(errno))); - _exit(127); - } - std::string bin = "python3"; - const char *const argv[] = {bin.c_str(), executable, NULL}; - char *const envp[] = {NULL}; - const char *path = "/usr/bin/python3"; - execve(path, (char *const *)argv, envp); - logger.log(ERROR, "execve error" + std::string(strerror(errno))); - _exit(127); - } - - // STEP 3 - char buffer[1024]; - int read_bytes; - bzero(buffer, sizeof(buffer)); - read_bytes = read(fd[READ_END], buffer, sizeof(buffer)); - logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); - body += buffer; - // logger.log(INFO, "Output: " + std::string(buffer)); - // *return to poll - - // FINAL STEP - // response - - } - else { - // implementation for post request - logger.log(INFO, "handling CGI POST Request"); - } - close(fd[READ_END]); - close(fd[WRITE_END]); -} - -ClientState CGI::send(int fd, HTTPMethod methodType, std::string requestBody) -{ - pid_t pid; +// // STEP 1 +// if (pipe(fd) == SYSTEM_ERROR) +// throw SystemException("Pipe"); +// // *return to poll with fd[write_end] with addPollFD + + +// // STEP 2 +// logger.log(INFO, "launching CGI"); +// if (get) { +// logger.log(INFO, "handling CGI GET Request"); + +// pid = fork(); +// if (pid == SYSTEM_ERROR) +// throw SystemException("Fork"); +// else if (pid == 0) { +// if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) +// { +// logger.log(ERROR, "dup2 out[WRITE_END] error" + +// std::string(strerror(errno))); +// _exit(127); +// } +// std::string bin = "python3"; +// const char *const argv[] = {bin.c_str(), executable, NULL}; +// char *const envp[] = {NULL}; +// const char *path = "/usr/bin/python3"; +// execve(path, (char *const *)argv, envp); +// logger.log(ERROR, "execve error" + std::string(strerror(errno))); +// _exit(127); +// } + +// // STEP 3 +// char buffer[1024]; +// int read_bytes; +// bzero(buffer, sizeof(buffer)); +// read_bytes = read(fd[READ_END], buffer, sizeof(buffer)); +// logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); +// body += buffer; +// // logger.log(INFO, "Output: " + std::string(buffer)); +// // *return to poll + +// // FINAL STEP +// // response + +// } +// else { +// // implementation for post request +// logger.log(INFO, "handling CGI POST Request"); +// } +// close(fd[READ_END]); +// close(fd[WRITE_END]); +// } - if (methodType == HTTPMethod::POST && body_has_been_sent == false) - { - write(STDIN_FILENO, &requestBody, sizeof(requestBody)); - return (ClientState::CGI_Write); - } - else - { - // parse URI and optionally save PATH_INFO and QUERY_STRING in env - // char **env = NULL; - pid = fork(); - if (pid == SYSTEM_ERROR) - throw SystemException("Fork"); - else if (pid == 0) { - if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) - { - logger.log(ERROR, "dup2 out[WRITE_END] error" + - std::string(strerror(errno))); - _exit(127); - } - std::string bin = "python3"; - const char *const argv[] = {bin.c_str(), executable, NULL}; - char *const envp[] = {NULL}; - const char *path = "/usr/bin/python3"; - execve(path, (char *const *)argv, envp); - logger.log(ERROR, "execve error" + std::string(strerror(errno))); - _exit(127); - } - } -} +// ClientState CGI::send(int fd, HTTPMethod methodType, std::string requestBody) +// { +// pid_t pid; + +// if (methodType == HTTPMethod::POST && body_has_been_sent == false) +// { +// write(STDIN_FILENO, &requestBody, sizeof(requestBody)); +// return (ClientState::CGI_Write); +// } +// else +// { +// // parse URI and optionally save PATH_INFO and QUERY_STRING in env +// // char **env = NULL; +// pid = fork(); +// if (pid == SYSTEM_ERROR) +// throw SystemException("Fork"); +// else if (pid == 0) { +// if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) +// { +// logger.log(ERROR, "dup2 out[WRITE_END] error" + +// std::string(strerror(errno))); +// _exit(127); +// } +// std::string bin = "python3"; +// const char *const argv[] = {bin.c_str(), executable, NULL}; +// char *const envp[] = {NULL}; +// const char *path = "/usr/bin/python3"; +// execve(path, (char *const *)argv, envp); +// logger.log(ERROR, "execve error" + std::string(strerror(errno))); +// _exit(127); +// } +// } +// } ClientState CGI::receive(int fd, std::string body) { @@ -130,78 +137,70 @@ ClientState CGI::receive(int fd, std::string body) return (ClientState::Loading); } - -// void CGI::execute(const char *executable) +// void CGI::execute(const char *executable) // { +// // EXECUTE **** +// std::string bin = "python3"; + +// std::string script = "./cgi-bin/print.py"; +// const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; + +// const char *const argv[] = {bin.c_str(), executable, NULL}; +// char *const envp[] = {NULL}; +// const char *path = "/usr/bin/python3"; +// execve(path, (char *const *)argv, envp); +// logger.log(ERROR, "execve error" + std::string(strerror(errno))); +// _exit(127); +// // **** +// } + + +void CGI::start_CGI(const char *executable, const char **env, size_t body_length) +{ + (void)executable; + (void)env; + (void)body_length; // int infile = open("in.txt", O_RDONLY, 0666); // int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); - // Logger &logger = Logger::getInstance(); - // int in[2]; - // int out[2]; - // pid_t pid; - - // logger.log(INFO, "launching CGI"); - // if (pipe(in) == SYSTEM_ERROR) - // throw SystemException("Pipe"); - // if (pipe(out) == SYSTEM_ERROR) - // throw SystemException("Pipe"); - - // pid = fork(); - // if (pid == SYSTEM_ERROR) - // throw SystemException("Fork"); - // else if (pid == 0) - // { - // if (close(in[WRITE_END]) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "close in[WRITE_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // if (close(out[READ_END]) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "close out[READ_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // if (dup2(in[READ_END], STDIN_FILENO) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "dup2 in[READ_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // if (close(in[READ_END]) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "close in[READ_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // if (dup2(out[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "dup2 out[WRITE_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // if (close(out[WRITE_END]) == SYSTEM_ERROR) - // { - // logger.log(ERROR, "close out[WRITE_END] error" + - // std::string(strerror(errno))); - // _exit(127); - // } - // std::string bin = "python3"; - // - // std::string script = "./cgi-bin/print.py"; - // const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; - // - // const char *const argv[] = {bin.c_str(), executable, NULL}; - // char *const envp[] = {NULL}; - // const char *path = "/usr/bin/python3"; - // execve(path, (char *const *)argv, envp); - // logger.log(ERROR, "execve error" + std::string(strerror(errno))); - // _exit(127); - // } - // assert(close(in[READ_END]) && "close in[READ_END] error"); - // assert(close(out[WRITE_END]) && "close out[WRITE_END] error"); + Logger &logger = Logger::getInstance(); + int in[2]; + int out[2]; + pid_t pid; + + logger.log(INFO, "launching CGI"); + if (pipe(in) == SYSTEM_ERROR) + throw SystemException("Pipe"); + if (pipe(out) == SYSTEM_ERROR) + throw SystemException("Pipe"); + + pid = fork(); + if (pid == SYSTEM_ERROR) + throw SystemException("Fork"); + else if (pid == 0) + { + if (close(in[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(out[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(in[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(in[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(out[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(out[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + + // execute(executable); + } + assert(close(in[READ_END]) && "close in[READ_END] error"); + assert(close(out[WRITE_END]) && "close out[WRITE_END] error"); + + // if () + // if request body + // make new connection for out[WRITE_END] + // else + // close in[WRITE_END] + // make new connection for out[READ_END] + + + + // are you alowed to execute and read in one function? NO // char buffer_out[1024]; // char buffer_in[1024]; // int read_bytes; @@ -223,4 +222,4 @@ ClientState CGI::receive(int fd, std::string body) // assert(close(infile) && "close infile error"); // assert(close(outfile) && "close outfile error"); -// } +} diff --git a/src/Client.cpp b/src/Client.cpp index c617fc3..05d81be 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -31,17 +31,23 @@ ClientState Client::handleConnection(short events) _state = _request.receive(_socket.getFD()); if (_request.cgi == true) { - // create pipe to read to? how does this work with poll? - // if (pipe(fd) == SYSTEM_ERROR) - // throw SystemException("Pipe"); + _request.parseURIForCGI(); + // parse request_line for: + // - executable + // - PATH_INFO + // - QUERY_STRING } return (_state); } - else if (events & POLLOUT && _state == ClientState::CGI_Write) - { - _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); - return (_state); - } + // else if (events & POLLOUT && _state == ClientState::start_CGI) + // { + // _cgi.start_CGI(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); + // } + // else if (events & POLLOUT && _state == ClientState::CGI_Write) + // { + // _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); + // return (_state); + // } else if (events & POLLIN && _state == ClientState::CGI_Read) { _state = _cgi.receive(_socket.getFD(), _cgi.body); diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 25c80e4..2c701c4 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -8,7 +8,9 @@ HTTPRequest::HTTPRequest() : _bytes_read(0), _content_length(0), _methodType(HTTPMethod::UNKNOWN), - _http_request(), _request_target(), _http_version(), _body(), _headers() + _http_request(), _request_target(), _http_version(), _body(), _headers(), + _executable() + // ,_env(NULL) { } @@ -152,3 +154,49 @@ ClientState HTTPRequest::receive(int client_fd) } return (ClientState::Receiving); } + +void HTTPRequest::parseURIForCGI(void) +{ + Logger &logger = Logger::getInstance(); + size_t pyMarkPos = _request_target.find("py"); + std::string executable = _request_target.substr(0, pyMarkPos + 2); + bool skip = false; + size_t env_num = 1; + size_t i = 0; + + logger.log(DEBUG, "Executable is: " + executable); + if (pyMarkPos + 2 >= std::strlen(_request_target.c_str()) - 1) + return ; + + std::string remaining = _request_target.substr(pyMarkPos + 2, std::string::npos); + size_t questionMarkPos = remaining.find('?'); + + if (remaining.at(0) == '/') + { + env_num++; + if (questionMarkPos != std::string::npos) + env_num++; + } + else if (remaining.at(0) == '?') + env_num++; + _env = new char *[env_num]; + if (remaining.at(0) == '/' && !skip) + { + std::string pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); + _env[i] = new char[pathInfo.length() + 1]; + std::strcpy(_env[i], pathInfo.c_str()); + if (env_num == 2) + { _env[i + 1] = nullptr; skip = true;} + i++; + } + if (!skip) { + std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); + _env[i] = new char[queryString.length() + 1]; + std::strcpy(_env[i], queryString.c_str()); + _env[i + 1] = nullptr; } + + for (size_t i = 0; _env[i]; i++) { + logger.log(DEBUG, _env[i]); + } + // delete[] env; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f4b7db3..7ee367c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,10 @@ #include #include +#include +#include +#include + int main(int argc, char **argv) { Logger &logger = Logger::getInstance(); From af2315f7ccb5cd152115b9f74f8ed3d163f454e9 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 22 Nov 2023 11:19:23 +0100 Subject: [PATCH 06/33] update parseURIForCGI --- src/HTTPRequest.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 2c701c4..ace65f0 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -155,20 +155,24 @@ ClientState HTTPRequest::receive(int client_fd) return (ClientState::Receiving); } +// !! need to free _env and it's arguments somewhere !! void HTTPRequest::parseURIForCGI(void) { + std::string fileToExecute = ".py"; // or something like: "data/www/python/test.py" to specify it better + size_t lengthFileToExecute = std::strlen(fileToExecute.c_str()); Logger &logger = Logger::getInstance(); - size_t pyMarkPos = _request_target.find("py"); - std::string executable = _request_target.substr(0, pyMarkPos + 2); + size_t pyMarkPos = _request_target.find(fileToExecute); + logger.log(DEBUG, "Length of fileToExecute: %", lengthFileToExecute); + std::string executable = _request_target.substr(0, pyMarkPos + lengthFileToExecute); bool skip = false; size_t env_num = 1; size_t i = 0; logger.log(DEBUG, "Executable is: " + executable); - if (pyMarkPos + 2 >= std::strlen(_request_target.c_str()) - 1) + if (pyMarkPos + lengthFileToExecute >= std::strlen(_request_target.c_str()) - 1) return ; - std::string remaining = _request_target.substr(pyMarkPos + 2, std::string::npos); + std::string remaining = _request_target.substr(pyMarkPos + lengthFileToExecute, std::string::npos); size_t questionMarkPos = remaining.find('?'); if (remaining.at(0) == '/') From a690ab7f79f3c453323d0160cfe857c04b4cf827 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 22 Nov 2023 11:42:37 +0100 Subject: [PATCH 07/33] added getters for the parameters of execve (startCGI), also made a setter for CGI boolean --- include/HTTPRequest.hpp | 10 ++++++++-- src/Client.cpp | 16 +++++----------- src/HTTPRequest.cpp | 22 +++++++++++++++++++--- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index 8bc589f..d1c83f6 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -47,9 +47,14 @@ class HTTPRequest const std::string &getBody(void) const; ClientState receive(int fd); - void parseURIForCGI(); + // CGI stuff + void parseURIForCGI(void); + const std::string &getExecutable(void) const; + const char **getEnv(void) const; + void setCGIToTrue(void); + const bool &CGITrue(void) const; - bool cgi; + private: ssize_t _bytes_read; @@ -62,6 +67,7 @@ class HTTPRequest std::unordered_map _headers; std::string _executable; char **_env; + bool _cgi; size_t parseStartLine(size_t &i); size_t parseHeaders(size_t &i); diff --git a/src/Client.cpp b/src/Client.cpp index 05d81be..b4fd9f0 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -29,20 +29,14 @@ ClientState Client::handleConnection(short events) if (events & POLLIN) { _state = _request.receive(_socket.getFD()); - if (_request.cgi == true) - { + if (_request.CGITrue() == true) _request.parseURIForCGI(); - // parse request_line for: - // - executable - // - PATH_INFO - // - QUERY_STRING - } return (_state); } - // else if (events & POLLOUT && _state == ClientState::start_CGI) - // { - // _cgi.start_CGI(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); - // } + else if (events & POLLOUT && _state == ClientState::start_CGI) + { + _cgi.start_CGI(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); + } // else if (events & POLLOUT && _state == ClientState::CGI_Write) // { // _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index ace65f0..730af32 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -9,8 +9,7 @@ HTTPRequest::HTTPRequest() : _bytes_read(0), _content_length(0), _methodType(HTTPMethod::UNKNOWN), _http_request(), _request_target(), _http_version(), _body(), _headers(), - _executable() - // ,_env(NULL) + _executable(),_env(NULL), _cgi(false) { } @@ -70,6 +69,23 @@ const std::string &HTTPRequest::getBody(void) const return (_body); } +const std::string &HTTPRequest::getExecutable(void) const { + return (_executable); +} + +const char **HTTPRequest::getEnv(void) const { + return (_env); +} + +void HTTPRequest::setCGIToTrue(void) { + _cgi = true; +} + +const bool &HTTPRequest::CGITrue(void) const { + return (_cgi); +} + + size_t HTTPRequest::parseStartLine(size_t &i) { Logger &logger = Logger::getInstance(); @@ -158,7 +174,7 @@ ClientState HTTPRequest::receive(int client_fd) // !! need to free _env and it's arguments somewhere !! void HTTPRequest::parseURIForCGI(void) { - std::string fileToExecute = ".py"; // or something like: "data/www/python/test.py" to specify it better + std::string fileToExecute = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! size_t lengthFileToExecute = std::strlen(fileToExecute.c_str()); Logger &logger = Logger::getInstance(); size_t pyMarkPos = _request_target.find(fileToExecute); From 35a723cb2f0f70648f02b23bb2fdc929b778e02a Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 22 Nov 2023 11:51:49 +0100 Subject: [PATCH 08/33] added getBodyLength method to HTTPRequest class --- include/HTTPRequest.hpp | 1 + src/Client.cpp | 2 +- src/HTTPRequest.cpp | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index d1c83f6..ffabaa5 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -53,6 +53,7 @@ class HTTPRequest const char **getEnv(void) const; void setCGIToTrue(void); const bool &CGITrue(void) const; + const size_t &getBodyLength(void) const; diff --git a/src/Client.cpp b/src/Client.cpp index b4fd9f0..80c6854 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -35,7 +35,7 @@ ClientState Client::handleConnection(short events) } else if (events & POLLOUT && _state == ClientState::start_CGI) { - _cgi.start_CGI(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); + _cgi.start_CGI(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); } // else if (events & POLLOUT && _state == ClientState::CGI_Write) // { diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 730af32..297239e 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -69,6 +69,10 @@ const std::string &HTTPRequest::getBody(void) const return (_body); } +const size_t &HTTPRequest::getBodyLength(void) const { + return (_content_length); +} + const std::string &HTTPRequest::getExecutable(void) const { return (_executable); } From cce7485403c846594bf3987f699f58a0745a3c79 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 23 Nov 2023 12:07:23 +0100 Subject: [PATCH 09/33] server back running but the file manager is giving problems --- include/CGI.hpp | 2 +- include/HTTPRequest.hpp | 2 +- src/CGI.cpp | 3 +-- src/HTTPRequest.cpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 8227d49..774414b 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -22,7 +22,7 @@ class CGI ~CGI(); // execute is probably redundant - void start_CGI(const char *executable, const char **env, size_t body_length); + void start_CGI(const char *executable, char **env, size_t body_length); // void execute(const char *executable); ClientState receive(int pipe_fd, std::string body); diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index ffabaa5..d636ce7 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -50,7 +50,7 @@ class HTTPRequest // CGI stuff void parseURIForCGI(void); const std::string &getExecutable(void) const; - const char **getEnv(void) const; + char **getEnv(void); void setCGIToTrue(void); const bool &CGITrue(void) const; const size_t &getBodyLength(void) const; diff --git a/src/CGI.cpp b/src/CGI.cpp index d924b9b..59bc5ca 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -154,8 +154,7 @@ ClientState CGI::receive(int fd, std::string body) // // **** // } - -void CGI::start_CGI(const char *executable, const char **env, size_t body_length) +void CGI::start_CGI(const char *executable, char **env, size_t body_length) { (void)executable; (void)env; diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 297239e..90c97dd 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -77,7 +77,7 @@ const std::string &HTTPRequest::getExecutable(void) const { return (_executable); } -const char **HTTPRequest::getEnv(void) const { +char **HTTPRequest::getEnv(void) { return (_env); } From 9321a8b119129d849f01f1d14c9bbeffb2b95743 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 23 Nov 2023 14:56:51 +0100 Subject: [PATCH 10/33] managePost now functional with the brains of sala --- include/FileManager.hpp | 5 +++-- src/Client.cpp | 22 +++++++++++----------- src/FileManager.cpp | 19 ++++++++++++++----- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/FileManager.hpp b/include/FileManager.hpp index 740d9d8..55b81cb 100644 --- a/include/FileManager.hpp +++ b/include/FileManager.hpp @@ -9,8 +9,9 @@ class FileManager { private: - std::string _response; - std::fstream _request_target; + std::string _response; + std::fstream _request_target; + size_t _bytes_sent; // CGI _cgi; public: diff --git a/src/Client.cpp b/src/Client.cpp index 80c6854..bc8f2bc 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -29,24 +29,24 @@ ClientState Client::handleConnection(short events) if (events & POLLIN) { _state = _request.receive(_socket.getFD()); - if (_request.CGITrue() == true) - _request.parseURIForCGI(); + // if (_request.CGITrue() == true) + // _request.parseURIForCGI(); return (_state); } - else if (events & POLLOUT && _state == ClientState::start_CGI) - { - _cgi.start_CGI(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); - } + // else if (events & POLLOUT && _state == ClientState::start_CGI) + // { + // _cgi.start_CGI(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); + // } // else if (events & POLLOUT && _state == ClientState::CGI_Write) // { // _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); // return (_state); // } - else if (events & POLLIN && _state == ClientState::CGI_Read) - { - _state = _cgi.receive(_socket.getFD(), _cgi.body); - return (_state); - } + // else if (events & POLLIN && _state == ClientState::CGI_Read) + // { + // _state = _cgi.receive(_socket.getFD(), _cgi.body); + // return (_state); + // } else if (events & POLLOUT && _state == ClientState::Loading) { // if (_request._cgi == true) diff --git a/src/FileManager.cpp b/src/FileManager.cpp index cff166e..94e0b60 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -5,7 +5,7 @@ #include #include -FileManager::FileManager() : _response(), _request_target() +FileManager::FileManager() : _response(), _request_target(), _bytes_sent(0) { } @@ -95,14 +95,23 @@ ClientState FileManager::manageGet(void) ClientState FileManager::managePost(const std::string &body) { Logger &logger = Logger::getInstance(); - size_t pos = _request_target.tellp(); + ssize_t pos = _request_target.tellp(); + size_t bytes_to_send = 0; + + logger.log(DEBUG, "managePost method is called"); - _request_target.write(body.c_str() + pos, BUFFER_SIZE); - logger.log(DEBUG, "post buffer: " + body.substr(pos, BUFFER_SIZE)); + logger.log(DEBUG, "pos: %", pos); + if (body.length() - _bytes_sent < BUFFER_SIZE) + bytes_to_send = body.length() - _bytes_sent; + else + bytes_to_send = BUFFER_SIZE; + + _request_target.write(body.c_str() + pos, bytes_to_send); + _bytes_sent += bytes_to_send; if (_request_target.fail()) throw ClientException(StatusCode::InternalServerError); - if (_request_target.eof()) + if (_bytes_sent == body.size()) return (ClientState::Sending); return (ClientState::Loading); } From cf2e772f0c3efabb481791e01cbe96f237c44e42 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 23 Nov 2023 17:23:33 +0100 Subject: [PATCH 11/33] some improvements on the CGI, currently stuck on parseURIForCGI method of HTTPRequest class --- include/CGI.hpp | 15 ++- include/ClientState.hpp | 3 +- src/CGI.cpp | 207 +++++++++++----------------------------- src/Client.cpp | 27 +++--- 4 files changed, 83 insertions(+), 169 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 774414b..f2c7589 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -12,8 +12,12 @@ class CGI { - private: - // std::string _body; +private: + pid_t _pid; + int _serverToPython[2]; + int _pythonToServer[2]; + bool _bodyIsSent; + size_t _bodyBytesWritten; public: CGI(); @@ -22,14 +26,15 @@ class CGI ~CGI(); // execute is probably redundant - void start_CGI(const char *executable, char **env, size_t body_length); - // void execute(const char *executable); + ClientState start(const char *executable, char **env, size_t body_length); + void execute(const char *executable, char **env); + + ClientState send(std::string body, size_t bodyLength); ClientState receive(int pipe_fd, std::string body); // ClientState send(int fd, HTTPMethod methodType, std::string requestBody); std::string body; - bool body_has_been_sent; int pipe_fd[2]; }; diff --git a/include/ClientState.hpp b/include/ClientState.hpp index 29b9ab9..a432d34 100644 --- a/include/ClientState.hpp +++ b/include/ClientState.hpp @@ -4,9 +4,10 @@ enum class ClientState { Receiving, - start_CGI, + CGI_Start, CGI_Write, CGI_Read, + CGI_Load, Loading, Sending, Done, diff --git a/src/CGI.cpp b/src/CGI.cpp index 59bc5ca..b433268 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -14,115 +14,27 @@ // - create python program // * check out the wiki and github that Sander has sent -CGI::CGI() -{ - body_has_been_sent = false; -} +CGI::CGI() : _bodyIsSent(false), _bodyBytesWritten(0) {} + +CGI::~CGI() {} -CGI::~CGI() +ClientState CGI::send(std::string body, size_t bodyLength) { + ssize_t writeSize = 0; + + if (!_bodyIsSent) + writeSize = write(_serverToPython[WRITE_END], &body, BUFFER_SIZE); + if (writeSize == SYSTEM_ERROR) + throw SystemException("write to Python"); + _bodyBytesWritten += writeSize; + if (_bodyBytesWritten == bodyLength) + { + close(_serverToPython[WRITE_END]); + return (ClientState::CGI_Read); + } + return (ClientState::CGI_Write); } -// Every step in the CGI must be done through poll -// First step: separate on read, write and execve -// First read is already been done by the request parser, -// so after creating the pipe in the execute function return to poll and -// wait till it's says it's ready to write. -// void CGI::execute(const char *executable, bool get, std::string &body) -// { -// Logger &logger = Logger::getInstance(); -// int fd[2]; -// pid_t pid; - -// // WHEN POST: extra step --> parse body and write to pipe -// // *return to poll with fd[] with addPollFD - -// // STEP 1 -// if (pipe(fd) == SYSTEM_ERROR) -// throw SystemException("Pipe"); -// // *return to poll with fd[write_end] with addPollFD - - -// // STEP 2 -// logger.log(INFO, "launching CGI"); -// if (get) { -// logger.log(INFO, "handling CGI GET Request"); - -// pid = fork(); -// if (pid == SYSTEM_ERROR) -// throw SystemException("Fork"); -// else if (pid == 0) { -// if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) -// { -// logger.log(ERROR, "dup2 out[WRITE_END] error" + -// std::string(strerror(errno))); -// _exit(127); -// } -// std::string bin = "python3"; -// const char *const argv[] = {bin.c_str(), executable, NULL}; -// char *const envp[] = {NULL}; -// const char *path = "/usr/bin/python3"; -// execve(path, (char *const *)argv, envp); -// logger.log(ERROR, "execve error" + std::string(strerror(errno))); -// _exit(127); -// } - -// // STEP 3 -// char buffer[1024]; -// int read_bytes; -// bzero(buffer, sizeof(buffer)); -// read_bytes = read(fd[READ_END], buffer, sizeof(buffer)); -// logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); -// body += buffer; -// // logger.log(INFO, "Output: " + std::string(buffer)); -// // *return to poll - -// // FINAL STEP -// // response - -// } -// else { -// // implementation for post request -// logger.log(INFO, "handling CGI POST Request"); -// } -// close(fd[READ_END]); -// close(fd[WRITE_END]); -// } - -// ClientState CGI::send(int fd, HTTPMethod methodType, std::string requestBody) -// { -// pid_t pid; - -// if (methodType == HTTPMethod::POST && body_has_been_sent == false) -// { -// write(STDIN_FILENO, &requestBody, sizeof(requestBody)); -// return (ClientState::CGI_Write); -// } -// else -// { -// // parse URI and optionally save PATH_INFO and QUERY_STRING in env -// // char **env = NULL; -// pid = fork(); -// if (pid == SYSTEM_ERROR) -// throw SystemException("Fork"); -// else if (pid == 0) { -// if (dup2(fd[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) -// { -// logger.log(ERROR, "dup2 out[WRITE_END] error" + -// std::string(strerror(errno))); -// _exit(127); -// } -// std::string bin = "python3"; -// const char *const argv[] = {bin.c_str(), executable, NULL}; -// char *const envp[] = {NULL}; -// const char *path = "/usr/bin/python3"; -// execve(path, (char *const *)argv, envp); -// logger.log(ERROR, "execve error" + std::string(strerror(errno))); -// _exit(127); -// } -// } -// } - ClientState CGI::receive(int fd, std::string body) { Logger &logger = Logger::getInstance(); @@ -137,66 +49,57 @@ ClientState CGI::receive(int fd, std::string body) return (ClientState::Loading); } -// void CGI::execute(const char *executable) -// { -// // EXECUTE **** -// std::string bin = "python3"; - -// std::string script = "./cgi-bin/print.py"; -// const char *const argv[] = {bin.c_str(), script.c_str(), NULL}; - -// const char *const argv[] = {bin.c_str(), executable, NULL}; -// char *const envp[] = {NULL}; -// const char *path = "/usr/bin/python3"; -// execve(path, (char *const *)argv, envp); -// logger.log(ERROR, "execve error" + std::string(strerror(errno))); -// _exit(127); -// // **** -// } - -void CGI::start_CGI(const char *executable, char **env, size_t body_length) +void CGI::execute(const char *executable, char **env) +{ + Logger &logger = Logger::getInstance(); + std::string bin = "python3"; + + const char *const argv[] = {bin.c_str(), executable, NULL}; + const char *path = "/usr/bin/python3"; + execve(path, (char *const *)argv, env); + + // if error throw exception? + logger.log(ERROR, "execve error" + std::string(strerror(errno))); + _exit(127); +} + +ClientState CGI::start(const char *executable, char **env, size_t bodyLength) { - (void)executable; - (void)env; - (void)body_length; // int infile = open("in.txt", O_RDONLY, 0666); // int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); Logger &logger = Logger::getInstance(); - int in[2]; - int out[2]; - pid_t pid; + + logger.log(DEBUG, "CGI::start called"); + return (ClientState::Loading); logger.log(INFO, "launching CGI"); - if (pipe(in) == SYSTEM_ERROR) + if (pipe(_serverToPython) == SYSTEM_ERROR) throw SystemException("Pipe"); - if (pipe(out) == SYSTEM_ERROR) + if (pipe(_pythonToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); - pid = fork(); - if (pid == SYSTEM_ERROR) + _pid = fork(); + if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); - else if (pid == 0) + else if (_pid == 0) { - if (close(in[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(out[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(in[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(in[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(out[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(out[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_serverToPython[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_pythonToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_serverToPython[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(_serverToPython[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_pythonToServer[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(_pythonToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - // execute(executable); + execute(executable, env); } - assert(close(in[READ_END]) && "close in[READ_END] error"); - assert(close(out[WRITE_END]) && "close out[WRITE_END] error"); - - // if () - // if request body - // make new connection for out[WRITE_END] - // else - // close in[WRITE_END] - - // make new connection for out[READ_END] - + assert(close(_serverToPython[READ_END]) && "close in[READ_END] error"); + assert(close(_pythonToServer[WRITE_END]) && "close out[WRITE_END] error"); + + if (bodyLength != 0) + return (ClientState::CGI_Write); + if (close(_serverToPython[WRITE_END]) == SYSTEM_ERROR) + throw SystemException("close"); + return (ClientState::CGI_Read); // are you alowed to execute and read in one function? NO diff --git a/src/Client.cpp b/src/Client.cpp index bc8f2bc..d1839ef 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -29,19 +29,23 @@ ClientState Client::handleConnection(short events) if (events & POLLIN) { _state = _request.receive(_socket.getFD()); - // if (_request.CGITrue() == true) - // _request.parseURIForCGI(); + // implement method Martijn for verifying that we are dealing with a CGI + _request.setCGIToTrue(); // settting CGI to true for testing purposes + logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); + if (_request.CGITrue() == true) + _request.parseURIForCGI(); + return (_state); + } + else if (events & POLLOUT && _state == ClientState::CGI_Start) + { + _state = _cgi.start(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); + return (_state); + } + else if (events & POLLOUT && _state == ClientState::CGI_Write) + { + _state = _cgi.send(_request.getBody(), _request.getBodyLength()); return (_state); } - // else if (events & POLLOUT && _state == ClientState::start_CGI) - // { - // _cgi.start_CGI(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); - // } - // else if (events & POLLOUT && _state == ClientState::CGI_Write) - // { - // _state = _cgi.send(_socket.getFD(), _request.getMethodType(), _request.getBody()); - // return (_state); - // } // else if (events & POLLIN && _state == ClientState::CGI_Read) // { // _state = _cgi.receive(_socket.getFD(), _cgi.body); @@ -71,6 +75,7 @@ ClientState Client::handleConnection(short events) // else _state = _response.send(_socket.getFD(), _file_manager.getResponse()); + logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); return (_state); } } From 61c2e9561fe4c5c7060af002c68c8dde0c68f2e4 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 24 Nov 2023 17:01:52 +0100 Subject: [PATCH 12/33] stuck at the executable but big steps are taken --- include/CGI.hpp | 12 +++-- include/HTTPRequest.hpp | 2 +- src/CGI.cpp | 106 +++++++++++++++++++++++++++------------- src/Client.cpp | 25 +++++++--- src/HTTPRequest.cpp | 45 +++++++++++------ src/HTTPServer.cpp | 1 + tests/get_request.sh | 2 +- 7 files changed, 131 insertions(+), 62 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index f2c7589..309d7b8 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -14,8 +14,8 @@ class CGI { private: pid_t _pid; - int _serverToPython[2]; - int _pythonToServer[2]; + int _serverToExternalProgram[2]; + int _externalProgramToServer[2]; bool _bodyIsSent; size_t _bodyBytesWritten; @@ -26,12 +26,14 @@ class CGI ~CGI(); // execute is probably redundant - ClientState start(const char *executable, char **env, size_t body_length); - void execute(const char *executable, char **env); + ClientState start(std::string executable, char **env, size_t body_length); + void execute(std::string executable, char **env); + bool fileExists(const std::string& filePath); + bool isExecutable(const std::string& filePath); ClientState send(std::string body, size_t bodyLength); - ClientState receive(int pipe_fd, std::string body); + ClientState receive(std::string body); // ClientState send(int fd, HTTPMethod methodType, std::string requestBody); std::string body; diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index d636ce7..ad00aa3 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -48,7 +48,7 @@ class HTTPRequest ClientState receive(int fd); // CGI stuff - void parseURIForCGI(void); + ClientState parseURIForCGI(void); const std::string &getExecutable(void) const; char **getEnv(void); void setCGIToTrue(void); diff --git a/src/CGI.cpp b/src/CGI.cpp index b433268..b617c06 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -5,10 +5,12 @@ #include #include #include +#include // TO DO -// - make in startCGI connection (pollFD) for out[READ_END] and optionally for in[WRITE_END] (in case of post request) +// - get execute functional +// - make sure that the test.py is closing the STDIN if that is not already been done // - insert in handleConnection CGI_WRITE and CGI_READ // - create some kind of a CGI_LOAD that serves as replacement for the file manager // - create python program @@ -20,42 +22,73 @@ CGI::~CGI() {} ClientState CGI::send(std::string body, size_t bodyLength) { - ssize_t writeSize = 0; + Logger &logger = Logger::getInstance(); + ssize_t bytesWritten = 0; + logger.log(INFO, "GCI::send is called"); if (!_bodyIsSent) - writeSize = write(_serverToPython[WRITE_END], &body, BUFFER_SIZE); - if (writeSize == SYSTEM_ERROR) + bytesWritten = write(_serverToExternalProgram[WRITE_END], &body, BUFFER_SIZE); + if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); - _bodyBytesWritten += writeSize; + _bodyBytesWritten += bytesWritten; if (_bodyBytesWritten == bodyLength) { - close(_serverToPython[WRITE_END]); + close(_serverToExternalProgram[WRITE_END]); return (ClientState::CGI_Read); } + logger.log(INFO, "CGI body: " + std::to_string(bytesWritten)); return (ClientState::CGI_Write); } -ClientState CGI::receive(int fd, std::string body) +ClientState CGI::receive(std::string body) { - Logger &logger = Logger::getInstance(); - char buffer[1024]; - int read_bytes; - bzero(buffer, sizeof(buffer)); - read_bytes = read(fd, buffer, sizeof(buffer)); - logger.log(INFO, "Bytes read: " + std::to_string(read_bytes)); + Logger &logger = Logger::getInstance(); + ssize_t bytesRead = 0; + logger.log(INFO, "GCI::receive is called"); + char buffer[1024]; + bzero(buffer, sizeof(buffer)); + bytesRead = read(_externalProgramToServer[READ_END], buffer, sizeof(buffer)); + logger.log(INFO, "Bytes read: " + std::to_string(bytesRead)); body += buffer; - // is it necesssary to check here whether the eof is reached on the fd? + if (bytesRead == 0) + return (ClientState::CGI_Read); + if (bytesRead == SYSTEM_ERROR) + return (ClientState::Error); return (ClientState::Loading); } -void CGI::execute(const char *executable, char **env) +bool CGI::fileExists(const std::string& filePath) { + return (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)); +} + +bool CGI::isExecutable(const std::string& filePath) +{ + std::filesystem::file_status fileStat = std::filesystem::status(filePath); + + return std::filesystem::is_regular_file(fileStat) && (fileStat.permissions() & + std::filesystem::perms::owner_exec) != std::filesystem::perms::none; +} + +void CGI::execute(std::string executable, char **env) { Logger &logger = Logger::getInstance(); std::string bin = "python3"; - const char *const argv[] = {bin.c_str(), executable, NULL}; - const char *path = "/usr/bin/python3"; + logger.log(ERROR, "CGI::execute is called"); + logger.log(ERROR, "Executable: %", executable); + // if (!fileExists(std::string(executable))) { + // logger.log(ERROR, "Path to external program does not exist"); + // _exit(127); + // } + // if (!isExecutable(std::string(executable))) { + // logger.log(ERROR, "External program is not executable"); + // _exit(127); + // } + + const char *const argv[] = {bin.c_str(), executable.c_str(), NULL}; + const char *path = "/data/www"; + // const char *path = "/usr/bin/python3"; execve(path, (char *const *)argv, env); // if error throw exception? @@ -63,43 +96,50 @@ void CGI::execute(const char *executable, char **env) _exit(127); } -ClientState CGI::start(const char *executable, char **env, size_t bodyLength) +ClientState CGI::start(std::string executable, char **env, size_t bodyLength) { // int infile = open("in.txt", O_RDONLY, 0666); // int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); Logger &logger = Logger::getInstance(); logger.log(DEBUG, "CGI::start called"); - return (ClientState::Loading); + logger.log(DEBUG, "Executable: %", executable); + + // return (ClientState::Loading); - logger.log(INFO, "launching CGI"); - if (pipe(_serverToPython) == SYSTEM_ERROR) - throw SystemException("Pipe"); - if (pipe(_pythonToServer) == SYSTEM_ERROR) - throw SystemException("Pipe"); + if (pipe(_serverToExternalProgram) == SYSTEM_ERROR) throw SystemException("Pipe"); + if (pipe(_externalProgramToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); else if (_pid == 0) { - if (close(_serverToPython[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(_pythonToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_serverToPython[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(_serverToPython[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_pythonToServer[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(_pythonToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_serverToExternalProgram[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_externalProgramToServer[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); execute(executable, env); } - assert(close(_serverToPython[READ_END]) && "close in[READ_END] error"); - assert(close(_pythonToServer[WRITE_END]) && "close out[WRITE_END] error"); + logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); + if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + // + // if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + logger.log(DEBUG, "CGI::start after closing"); + // assert(close(_serverToExternalProgram[READ_END])); + // assert(close(_externalProgramToServer[WRITE_END])); if (bodyLength != 0) return (ClientState::CGI_Write); - if (close(_serverToPython[WRITE_END]) == SYSTEM_ERROR) + if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); return (ClientState::CGI_Read); + // return (ClientState::Done); // are you alowed to execute and read in one function? NO diff --git a/src/Client.cpp b/src/Client.cpp index d1839ef..e34618c 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -30,15 +30,22 @@ ClientState Client::handleConnection(short events) { _state = _request.receive(_socket.getFD()); // implement method Martijn for verifying that we are dealing with a CGI + // also get fileExtension from Martijn and save in HTTPRequest class + _request.setCGIToTrue(); // settting CGI to true for testing purposes + // if load then if cgi + + // _request.setRequestTarget("/python/test.py"); logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); - if (_request.CGITrue() == true) - _request.parseURIForCGI(); + if (_state == ClientState::Loading && _request.CGITrue() == true) + _state = _request.parseURIForCGI(); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) { - _state = _cgi.start(_request.getExecutable().c_str(), _request.getEnv(), _request.getBodyLength()); + logger.log(DEBUG, "Executable of external program is: %", _request.getExecutable()); + _state = _cgi.start(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); + // _state = ClientState::Done; return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) @@ -46,16 +53,18 @@ ClientState Client::handleConnection(short events) _state = _cgi.send(_request.getBody(), _request.getBodyLength()); return (_state); } - // else if (events & POLLIN && _state == ClientState::CGI_Read) - // { - // _state = _cgi.receive(_socket.getFD(), _cgi.body); - // return (_state); - // } + else if (events & POLLIN && _state == ClientState::CGI_Read) + { + _state = _cgi.receive(_cgi.body); + logger.log(INFO, "_cgi.body: %", _cgi.body); + return (_state); + } else if (events & POLLOUT && _state == ClientState::Loading) { // if (_request._cgi == true) // _state = _cgi.createResponse(); // else + // logger.log(INFO, "request target from loading else if: %", _request.getRequestTarget()); _state = _file_manager.manage( _request.getMethodType(), "./data/www" + _request.getRequestTarget(), diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 90c97dd..9365f5d 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "ClientState.hpp" #include @@ -95,6 +96,7 @@ size_t HTTPRequest::parseStartLine(size_t &i) Logger &logger = Logger::getInstance(); size_t pos; + logger.log(DEBUG, "_http_request: %", _http_request); pos = _http_request.find(' ', i); setMethodType(_http_request.substr(i, pos - i)); i = pos + 1; @@ -134,8 +136,8 @@ ClientState HTTPRequest::receive(int client_fd) Logger &logger = Logger::getInstance(); char buffer[BUFFER_SIZE]; std::string header_end; - size_t pos; size_t i = 0; + size_t pos; _bytes_read = read(client_fd, buffer, BUFFER_SIZE); if (_bytes_read == SYSTEM_ERROR) @@ -176,25 +178,37 @@ ClientState HTTPRequest::receive(int client_fd) } // !! need to free _env and it's arguments somewhere !! -void HTTPRequest::parseURIForCGI(void) +ClientState HTTPRequest::parseURIForCGI(void) { - std::string fileToExecute = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! - size_t lengthFileToExecute = std::strlen(fileToExecute.c_str()); Logger &logger = Logger::getInstance(); - size_t pyMarkPos = _request_target.find(fileToExecute); - logger.log(DEBUG, "Length of fileToExecute: %", lengthFileToExecute); - std::string executable = _request_target.substr(0, pyMarkPos + lengthFileToExecute); + logger.log(DEBUG, "parseURIForCGI is called"); + logger.log(DEBUG, "_request_target: %", _request_target); + // return (ClientState::Loading); + + std::string filenameExtension = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! + size_t lengthFilenameExtension = std::strlen(filenameExtension.c_str()); + size_t filenameExtensionPos = _request_target.find(filenameExtension); + logger.log(DEBUG, "Length of filenameExtension: %", lengthFilenameExtension); + if (filenameExtensionPos == std::string::npos) + return (ClientState::Error); + // return (ClientState::Done); + + _executable = _request_target.substr(0, filenameExtensionPos + lengthFilenameExtension); bool skip = false; size_t env_num = 1; size_t i = 0; - logger.log(DEBUG, "Executable is: " + executable); - if (pyMarkPos + lengthFileToExecute >= std::strlen(_request_target.c_str()) - 1) - return ; + logger.log(DEBUG, "Executable is: " + _executable); + if (filenameExtensionPos + lengthFilenameExtension >= std::strlen(_request_target.c_str()) - 1) { + // logger.log(DEBUG, "filenameExtensionPos + lengthFilenameExtension >= std::strlen(_request_target.c_str()) - 1"); + return (ClientState::CGI_Start); + // return (ClientState::Done); + } - std::string remaining = _request_target.substr(pyMarkPos + lengthFileToExecute, std::string::npos); + logger.log(DEBUG, "filenameExtensionPos + lengthfilenameExtension: %", filenameExtensionPos + lengthFilenameExtension); + + std::string remaining = _request_target.substr(filenameExtensionPos + lengthFilenameExtension, std::string::npos); size_t questionMarkPos = remaining.find('?'); - if (remaining.at(0) == '/') { env_num++; @@ -207,6 +221,7 @@ void HTTPRequest::parseURIForCGI(void) if (remaining.at(0) == '/' && !skip) { std::string pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); + // vector string instead of new char shit _env[i] = new char[pathInfo.length() + 1]; std::strcpy(_env[i], pathInfo.c_str()); if (env_num == 2) @@ -214,7 +229,7 @@ void HTTPRequest::parseURIForCGI(void) i++; } if (!skip) { - std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); + std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); _env[i] = new char[queryString.length() + 1]; std::strcpy(_env[i], queryString.c_str()); _env[i + 1] = nullptr; } @@ -222,5 +237,7 @@ void HTTPRequest::parseURIForCGI(void) for (size_t i = 0; _env[i]; i++) { logger.log(DEBUG, _env[i]); } - // delete[] env; + logger.log(INFO, "Do we reach the end of parseURIForCGI"); + return (ClientState::CGI_Start); + // return (ClientState::CGI_Start); } \ No newline at end of file diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index ba559e8..0df077b 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -115,6 +115,7 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd) _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::CGI_Write: + case ClientState::CGI_Start: _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::CGI_Read: diff --git a/tests/get_request.sh b/tests/get_request.sh index 95f6053..242eb53 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/post.txt" +REQUEST_FILE="tests/request/get_cgi.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From f38b39045c9f18f3a549ba473b8ffe9bb6088f16 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 29 Nov 2023 16:40:41 +0100 Subject: [PATCH 13/33] some improvents, new focus: CGI::receive --- include/CGI.hpp | 18 ++-- include/HTTPRequest.hpp | 7 -- src/CGI.cpp | 179 +++++++++++++++++++++++++++------------- src/Client.cpp | 13 ++- src/HTTPRequest.cpp | 75 +---------------- src/HTTPServer.cpp | 12 ++- 6 files changed, 149 insertions(+), 155 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 309d7b8..2d847d7 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -13,11 +13,12 @@ class CGI { private: - pid_t _pid; - int _serverToExternalProgram[2]; - int _externalProgramToServer[2]; - bool _bodyIsSent; - size_t _bodyBytesWritten; + pid_t _pid; + int _serverToExternalProgram[2]; + int _externalProgramToServer[2]; + bool _bodyIsSent; + size_t _bodyBytesWritten; + std::string _executable; public: CGI(); @@ -26,11 +27,15 @@ class CGI ~CGI(); // execute is probably redundant - ClientState start(std::string executable, char **env, size_t body_length); + ClientState start(size_t body_length); + ClientState parseURIForCGI(std::string requestTarget); void execute(std::string executable, char **env); bool fileExists(const std::string& filePath); bool isExecutable(const std::string& filePath); + const std::string& getExecutable(void) const; + const pid_t& getPid(void) const; + ClientState send(std::string body, size_t bodyLength); ClientState receive(std::string body); @@ -38,6 +43,7 @@ class CGI std::string body; int pipe_fd[2]; + char **_env; }; #endif diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index ad00aa3..c10fe46 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -47,16 +47,11 @@ class HTTPRequest const std::string &getBody(void) const; ClientState receive(int fd); - // CGI stuff - ClientState parseURIForCGI(void); const std::string &getExecutable(void) const; - char **getEnv(void); void setCGIToTrue(void); const bool &CGITrue(void) const; const size_t &getBodyLength(void) const; - - private: ssize_t _bytes_read; size_t _content_length; @@ -66,8 +61,6 @@ class HTTPRequest std::string _http_version; std::string _body; std::unordered_map _headers; - std::string _executable; - char **_env; bool _cgi; size_t parseStartLine(size_t &i); diff --git a/src/CGI.cpp b/src/CGI.cpp index b617c06..a47b83e 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -1,8 +1,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -20,6 +22,14 @@ CGI::CGI() : _bodyIsSent(false), _bodyBytesWritten(0) {} CGI::~CGI() {} +const std::string &CGI::getExecutable(void) const { + return (_executable); +} + +const pid_t &CGI::getPid(void) const { + return (_pid); +} + ClientState CGI::send(std::string body, size_t bodyLength) { Logger &logger = Logger::getInstance(); @@ -27,7 +37,7 @@ ClientState CGI::send(std::string body, size_t bodyLength) logger.log(INFO, "GCI::send is called"); if (!_bodyIsSent) - bytesWritten = write(_serverToExternalProgram[WRITE_END], &body, BUFFER_SIZE); + bytesWritten = write(_serverToExternalProgram[WRITE_END], body.c_str(), BUFFER_SIZE); if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); _bodyBytesWritten += bytesWritten; @@ -49,12 +59,19 @@ ClientState CGI::receive(std::string body) char buffer[1024]; bzero(buffer, sizeof(buffer)); bytesRead = read(_externalProgramToServer[READ_END], buffer, sizeof(buffer)); + if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); logger.log(INFO, "Bytes read: " + std::to_string(bytesRead)); body += buffer; - if (bytesRead == 0) + if (bytesRead != 0) return (ClientState::CGI_Read); - if (bytesRead == SYSTEM_ERROR) - return (ClientState::Error); + // if (bytesRead == SYSTEM_ERROR) + // return (ClientState::Error); + logger.log(DEBUG, "body in GCI::receive:\n" + body); + close(_externalProgramToServer[READ_END]); + // int status; + // waitpid(_pid, &status, 0); + // if (WEXITSTATUS(status) == -1) + // return (ClientState::Error); return (ClientState::Loading); } @@ -72,8 +89,10 @@ bool CGI::isExecutable(const std::string& filePath) void CGI::execute(std::string executable, char **env) { + (void)env; Logger &logger = Logger::getInstance(); std::string bin = "python3"; + logger.log(ERROR, "CGI::execute is called"); logger.log(ERROR, "Executable: %", executable); @@ -86,34 +105,35 @@ void CGI::execute(std::string executable, char **env) // _exit(127); // } - const char *const argv[] = {bin.c_str(), executable.c_str(), NULL}; - const char *path = "/data/www"; - // const char *path = "/usr/bin/python3"; - execve(path, (char *const *)argv, env); + std::string executableWithPath = "data/www" + executable; + logger.log(ERROR, "executableWithPath: " + executableWithPath); + + // const char *const argv[] = {bin.c_str(), executable.c_str(), NULL}; + const char *const argv[] = {bin.c_str(), executableWithPath.c_str(), NULL}; + // const char *path = "/data/www"; + const char *path = "/usr/bin/python3"; + // execve(path, (char *const *)argv, env); + if (execve(path, (char *const *)argv, NULL) == SYSTEM_ERROR) throw SystemException("Execve"); // if error throw exception? logger.log(ERROR, "execve error" + std::string(strerror(errno))); _exit(127); } -ClientState CGI::start(std::string executable, char **env, size_t bodyLength) +ClientState CGI::start(size_t bodyLength) { - // int infile = open("in.txt", O_RDONLY, 0666); - // int outfile = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); Logger &logger = Logger::getInstance(); logger.log(DEBUG, "CGI::start called"); - logger.log(DEBUG, "Executable: %", executable); + logger.log(DEBUG, "Executable: %", _executable); - // return (ClientState::Loading); if (pipe(_serverToExternalProgram) == SYSTEM_ERROR) throw SystemException("Pipe"); if (pipe(_externalProgramToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); _pid = fork(); - if (_pid == SYSTEM_ERROR) - throw SystemException("Fork"); - else if (_pid == 0) + if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); + if (_pid == 0) { if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); @@ -122,46 +142,91 @@ ClientState CGI::start(std::string executable, char **env, size_t bodyLength) if (dup2(_externalProgramToServer[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - execute(executable, env); + execute(_executable, _env); } - logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); - if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - // - // if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - logger.log(DEBUG, "CGI::start after closing"); - // assert(close(_serverToExternalProgram[READ_END])); - // assert(close(_externalProgramToServer[WRITE_END])); - - if (bodyLength != 0) - return (ClientState::CGI_Write); - if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) - throw SystemException("close"); - return (ClientState::CGI_Read); - // return (ClientState::Done); - - - // are you alowed to execute and read in one function? NO - // char buffer_out[1024]; - // char buffer_in[1024]; - // int read_bytes; - // bzero(buffer_in, sizeof(buffer_in)); - // bzero(buffer_out, sizeof(buffer_out)); - - // read_bytes = read(infile, buffer_in, sizeof(buffer_in)); - // logger.log(INFO, "buffer_in: " + std::string(buffer_in)); - // if (write(in[WRITE_END], buffer_in, read_bytes) == SYSTEM_ERROR) - // logger.log(ERROR, "write error" + std::string(strerror(errno))); - - // assert(close(in[WRITE_END]) && "close in[WRITE_END] error"); - // if ((read_bytes = read(out[READ_END], buffer_out, sizeof(buffer_out))) == - // SYSTEM_ERROR) - // logger.log(ERROR, "read error" + std::string(strerror(errno))); - // logger.log(INFO, "buffer_out: " + std::string(buffer_out)); - // write(outfile, buffer_out, read_bytes); - // close(out[READ_END]); - - // assert(close(infile) && "close infile error"); - // assert(close(outfile) && "close outfile error"); + else + { + + logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); + if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + logger.log(DEBUG, "CGI::start after closing"); + + if (bodyLength != 0) + return (ClientState::CGI_Write); + logger.log(DEBUG, "GCI::start after writing"); + if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_externalProgramToServer[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); + // int status; + // waitpid() + return (ClientState::CGI_Read); + // return (ClientState::Done); + } + return (ClientState::Error); } + +// !! need to free _env and it's arguments somewhere !! +ClientState CGI::parseURIForCGI(std::string requestTarget) +{ + Logger &logger = Logger::getInstance(); + logger.log(DEBUG, "parseURIForCGI is called"); + logger.log(DEBUG, "requestTarget: %", requestTarget); + // return (ClientState::Loading); + + std::string filenameExtension = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! + size_t lengthFilenameExtension = std::strlen(filenameExtension.c_str()); + size_t filenameExtensionPos = requestTarget.find(filenameExtension); + logger.log(DEBUG, "Length of filenameExtension: %", lengthFilenameExtension); + if (filenameExtensionPos == std::string::npos) + return (ClientState::Error); + // return (ClientState::Done); + + _executable = requestTarget.substr(0, filenameExtensionPos + lengthFilenameExtension); + bool skip = false; + size_t env_num = 1; + size_t i = 0; + + logger.log(DEBUG, "Executable is: " + _executable); + if (filenameExtensionPos + lengthFilenameExtension >= std::strlen(requestTarget.c_str()) - 1) { + // logger.log(DEBUG, "filenameExtensionPos + lengthFilenameExtension >= std::strlen(requestTarget.c_str()) - 1"); + return (ClientState::CGI_Start); + // return (ClientState::Done); + } + + logger.log(DEBUG, "filenameExtensionPos + lengthfilenameExtension: %", filenameExtensionPos + lengthFilenameExtension); + + std::string remaining = requestTarget.substr(filenameExtensionPos + lengthFilenameExtension, std::string::npos); + size_t questionMarkPos = remaining.find('?'); + if (remaining.at(0) == '/') + { + env_num++; + if (questionMarkPos != std::string::npos) + env_num++; + } + else if (remaining.at(0) == '?') + env_num++; + _env = new char *[env_num]; + if (remaining.at(0) == '/' && !skip) + { + std::string pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); + // vector string instead of new char shit + _env[i] = new char[pathInfo.length() + 1]; + std::strcpy(_env[i], pathInfo.c_str()); + if (env_num == 2) + { _env[i + 1] = nullptr; skip = true;} + i++; + } + if (!skip) { + std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); + _env[i] = new char[queryString.length() + 1]; + std::strcpy(_env[i], queryString.c_str()); + _env[i + 1] = nullptr; } + + for (size_t i = 0; _env[i]; i++) { + logger.log(DEBUG, _env[i]); + } + logger.log(INFO, "Do we reach the end of parseURIForCGI"); + return (ClientState::CGI_Start); + // return (ClientState::CGI_Start); +} \ No newline at end of file diff --git a/src/Client.cpp b/src/Client.cpp index e34618c..164cabe 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -26,7 +27,7 @@ ClientState Client::handleConnection(short events) std::to_string(_socket.getFD())); try { - if (events & POLLIN) + if (events & POLLIN && _state == ClientState::Receiving) { _state = _request.receive(_socket.getFD()); // implement method Martijn for verifying that we are dealing with a CGI @@ -38,13 +39,13 @@ ClientState Client::handleConnection(short events) // _request.setRequestTarget("/python/test.py"); logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); if (_state == ClientState::Loading && _request.CGITrue() == true) - _state = _request.parseURIForCGI(); + _state = _cgi.parseURIForCGI(_request.getRequestTarget()); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) { - logger.log(DEBUG, "Executable of external program is: %", _request.getExecutable()); - _state = _cgi.start(_request.getExecutable(), _request.getEnv(), _request.getBodyLength()); + logger.log(DEBUG, "Executable of external program is: %", _cgi.getExecutable()); + _state = _cgi.start(_request.getBodyLength()); // _state = ClientState::Done; return (_state); } @@ -55,8 +56,12 @@ ClientState Client::handleConnection(short events) } else if (events & POLLIN && _state == ClientState::CGI_Read) { + logger.log(ERROR, "TEST FROM HANDLECONNECTION"); _state = _cgi.receive(_cgi.body); logger.log(INFO, "_cgi.body: %", _cgi.body); + // int status; + // waitpid(_cgi.getPid(), &status, 0); + // WEXITSTATUS(status); return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 9365f5d..695a7d7 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -10,7 +10,7 @@ HTTPRequest::HTTPRequest() : _bytes_read(0), _content_length(0), _methodType(HTTPMethod::UNKNOWN), _http_request(), _request_target(), _http_version(), _body(), _headers(), - _executable(),_env(NULL), _cgi(false) + _cgi(false) { } @@ -74,14 +74,6 @@ const size_t &HTTPRequest::getBodyLength(void) const { return (_content_length); } -const std::string &HTTPRequest::getExecutable(void) const { - return (_executable); -} - -char **HTTPRequest::getEnv(void) { - return (_env); -} - void HTTPRequest::setCGIToTrue(void) { _cgi = true; } @@ -175,69 +167,4 @@ ClientState HTTPRequest::receive(int client_fd) pos = parseHeaders(i); } return (ClientState::Receiving); -} - -// !! need to free _env and it's arguments somewhere !! -ClientState HTTPRequest::parseURIForCGI(void) -{ - Logger &logger = Logger::getInstance(); - logger.log(DEBUG, "parseURIForCGI is called"); - logger.log(DEBUG, "_request_target: %", _request_target); - // return (ClientState::Loading); - - std::string filenameExtension = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! - size_t lengthFilenameExtension = std::strlen(filenameExtension.c_str()); - size_t filenameExtensionPos = _request_target.find(filenameExtension); - logger.log(DEBUG, "Length of filenameExtension: %", lengthFilenameExtension); - if (filenameExtensionPos == std::string::npos) - return (ClientState::Error); - // return (ClientState::Done); - - _executable = _request_target.substr(0, filenameExtensionPos + lengthFilenameExtension); - bool skip = false; - size_t env_num = 1; - size_t i = 0; - - logger.log(DEBUG, "Executable is: " + _executable); - if (filenameExtensionPos + lengthFilenameExtension >= std::strlen(_request_target.c_str()) - 1) { - // logger.log(DEBUG, "filenameExtensionPos + lengthFilenameExtension >= std::strlen(_request_target.c_str()) - 1"); - return (ClientState::CGI_Start); - // return (ClientState::Done); - } - - logger.log(DEBUG, "filenameExtensionPos + lengthfilenameExtension: %", filenameExtensionPos + lengthFilenameExtension); - - std::string remaining = _request_target.substr(filenameExtensionPos + lengthFilenameExtension, std::string::npos); - size_t questionMarkPos = remaining.find('?'); - if (remaining.at(0) == '/') - { - env_num++; - if (questionMarkPos != std::string::npos) - env_num++; - } - else if (remaining.at(0) == '?') - env_num++; - _env = new char *[env_num]; - if (remaining.at(0) == '/' && !skip) - { - std::string pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); - // vector string instead of new char shit - _env[i] = new char[pathInfo.length() + 1]; - std::strcpy(_env[i], pathInfo.c_str()); - if (env_num == 2) - { _env[i + 1] = nullptr; skip = true;} - i++; - } - if (!skip) { - std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); - _env[i] = new char[queryString.length() + 1]; - std::strcpy(_env[i], queryString.c_str()); - _env[i + 1] = nullptr; } - - for (size_t i = 0; _env[i]; i++) { - logger.log(DEBUG, _env[i]); - } - logger.log(INFO, "Do we reach the end of parseURIForCGI"); - return (ClientState::CGI_Start); - // return (ClientState::CGI_Start); } \ No newline at end of file diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 0df077b..67888f5 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -107,19 +107,17 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd) switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events)) { case ClientState::Receiving: - _poll.setEvents(poll_fd.fd, POLLIN); + case ClientState::CGI_Read: + { + _poll.setEvents(poll_fd.fd, POLLIN); break; + } case ClientState::Loading: case ClientState::Sending: case ClientState::Error: - _poll.setEvents(poll_fd.fd, POLLOUT); - break; case ClientState::CGI_Write: case ClientState::CGI_Start: - _poll.setEvents(poll_fd.fd, POLLOUT); - break; - case ClientState::CGI_Read: - _poll.setEvents(poll_fd.fd, POLLIN); + _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::Done: _poll.removeFD(poll_fd.fd); From 21759b609ea325a0cbc1e99e3955dd584b1629f7 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 30 Nov 2023 13:33:00 +0100 Subject: [PATCH 14/33] improvents cgi --- include/CGI.hpp | 3 ++- src/CGI.cpp | 64 ++++++++++++++++++++++++++++++++-------------- src/Client.cpp | 32 ++++++++++++++++------- src/HTTPServer.cpp | 9 ++++--- 4 files changed, 76 insertions(+), 32 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 2d847d7..55ebf7c 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -38,7 +38,8 @@ class CGI ClientState send(std::string body, size_t bodyLength); - ClientState receive(std::string body); + // ClientState receive(std::string body); + ClientState receive(void); // ClientState send(int fd, HTTPMethod methodType, std::string requestBody); std::string body; diff --git a/src/CGI.cpp b/src/CGI.cpp index a47b83e..6247143 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -50,29 +50,50 @@ ClientState CGI::send(std::string body, size_t bodyLength) return (ClientState::CGI_Write); } -ClientState CGI::receive(std::string body) +ClientState CGI::receive(void) { Logger &logger = Logger::getInstance(); ssize_t bytesRead = 0; - - logger.log(INFO, "GCI::receive is called"); char buffer[1024]; + + // if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { + // perror("fcntl"); + // throw SystemException("fcntl"); + // } + bzero(buffer, sizeof(buffer)); + logger.log(INFO, "CGI::receive is called"); bytesRead = read(_externalProgramToServer[READ_END], buffer, sizeof(buffer)); - if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); - logger.log(INFO, "Bytes read: " + std::to_string(bytesRead)); - body += buffer; - if (bytesRead != 0) - return (ClientState::CGI_Read); - // if (bytesRead == SYSTEM_ERROR) - // return (ClientState::Error); - logger.log(DEBUG, "body in GCI::receive:\n" + body); - close(_externalProgramToServer[READ_END]); - // int status; - // waitpid(_pid, &status, 0); - // if (WEXITSTATUS(status) == -1) - // return (ClientState::Error); - return (ClientState::Loading); + + // if (bytesRead == -1) { + // if (errno == EAGAIN || errno == EWOULDBLOCK) { + // logger.log(DEBUG, "No data available to read.\n"); + // return (ClientState::CGI_Read); + + // } else { + // // Handle other errors + // perror("read"); + // throw SystemException("errno"); + // } + // } + // else { + // bytesRead = read(STDIN_FILENO, buffer, sizeof(buffer)); + // if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); + logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); + logger.log(DEBUG, "buffer:\n" + std::string(buffer)); + body += buffer; + if (bytesRead != 0) + return (ClientState::CGI_Read); + // if (bytesRead == SYSTEM_ERROR) + // return (ClientState::Error); + logger.log(DEBUG, "body in GCI::receive:\n" + body); + close(_externalProgramToServer[READ_END]); + // int status; + // waitpid(_pid, &status, 0); + // if (WEXITSTATUS(status) == -1) + // return (ClientState::Error); + // } + return (ClientState::CGI_Load); } bool CGI::fileExists(const std::string& filePath) { @@ -135,6 +156,7 @@ ClientState CGI::start(size_t bodyLength) if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); if (_pid == 0) { + logger.log(ERROR, "_pid == 0"); if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(_serverToExternalProgram[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); @@ -143,6 +165,7 @@ ClientState CGI::start(size_t bodyLength) if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); execute(_executable, _env); + logger.log(DEBUG, "after execute"); } else { @@ -158,12 +181,15 @@ ClientState CGI::start(size_t bodyLength) if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(_externalProgramToServer[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); + + // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); // int status; // waitpid() - return (ClientState::CGI_Read); + return (receive()); + // return (ClientState::CGI_Read); // return (ClientState::Done); } - return (ClientState::Error); + return (ClientState::CGI_Read); } // !! need to free _env and it's arguments somewhere !! diff --git a/src/Client.cpp b/src/Client.cpp index 164cabe..2498e61 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -9,6 +9,7 @@ Client::Client(const int &server_fd) : _socket(server_fd) { _socket.setupClient(); + _state = ClientState::Receiving; } Client::~Client() @@ -29,35 +30,33 @@ ClientState Client::handleConnection(short events) { if (events & POLLIN && _state == ClientState::Receiving) { + logger.log(ERROR, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); + logger.log(DEBUG, "_request_target: " + _request.getRequestTarget()); + // implement method Martijn for verifying that we are dealing with a CGI // also get fileExtension from Martijn and save in HTTPRequest class _request.setCGIToTrue(); // settting CGI to true for testing purposes - // if load then if cgi - - // _request.setRequestTarget("/python/test.py"); - logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); - if (_state == ClientState::Loading && _request.CGITrue() == true) - _state = _cgi.parseURIForCGI(_request.getRequestTarget()); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) { - logger.log(DEBUG, "Executable of external program is: %", _cgi.getExecutable()); + logger.log(ERROR, "ClientState::CGI_Start"); _state = _cgi.start(_request.getBodyLength()); // _state = ClientState::Done; return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) { + logger.log(ERROR, "ClientState::CGI_Write"); _state = _cgi.send(_request.getBody(), _request.getBodyLength()); return (_state); } else if (events & POLLIN && _state == ClientState::CGI_Read) { - logger.log(ERROR, "TEST FROM HANDLECONNECTION"); - _state = _cgi.receive(_cgi.body); + logger.log(ERROR, "ClientState::CGI_Read"); + _state = _cgi.receive(); logger.log(INFO, "_cgi.body: %", _cgi.body); // int status; // waitpid(_cgi.getPid(), &status, 0); @@ -66,6 +65,12 @@ ClientState Client::handleConnection(short events) } else if (events & POLLOUT && _state == ClientState::Loading) { + logger.log(DEBUG, "ClientState::Loading"); + if (_state == ClientState::Loading && _request.CGITrue() == true) { + _state = _cgi.parseURIForCGI(_request.getRequestTarget()); + logger.log(DEBUG, "executable: " + _cgi.getExecutable()); + return (_state); + } // if (_request._cgi == true) // _state = _cgi.createResponse(); // else @@ -76,6 +81,15 @@ ClientState Client::handleConnection(short events) _request.getBody()); // TODO: resolve location return (_state); } + else if (events & POLLOUT && _state == ClientState::CGI_Load) + { + logger.log(DEBUG, "ClientState::CGI_Load"); + _state = _file_manager.manage( + _request.getMethodType(), + "./data/www" + _request.getRequestTarget(), + _cgi.body); + return (_state); + } else if (events & POLLOUT && _state == ClientState::Error) { _state = _file_manager.loadErrorPage(); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 67888f5..4dab784 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -67,6 +67,8 @@ void HTTPServer::setupServers(void) void HTTPServer::handleActivePollFDs() { Logger &logger = Logger::getInstance(); + logger.log(DEBUG, "HTTPServer::handleActivePollFDs"); + _poll.pollFDs(); for (const auto &poll_fd : _poll.getPollFDs()) { @@ -104,14 +106,15 @@ void HTTPServer::handleNewConnection(int fd) void HTTPServer::handleExistingConnection(const pollfd &poll_fd) { + Logger &logger = Logger::getInstance(); + logger.log(DEBUG, "HTTPServer::handleExistingConnection"); + switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events)) { case ClientState::Receiving: case ClientState::CGI_Read: - { - _poll.setEvents(poll_fd.fd, POLLIN); + _poll.setEvents(poll_fd.fd, POLLIN); break; - } case ClientState::Loading: case ClientState::Sending: case ClientState::Error: From 66077eb4a1e657385add7729b7c951702299e886 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 30 Nov 2023 13:36:14 +0100 Subject: [PATCH 15/33] python file and get_cgi text file --- data/www/test.py | 18 ++++++++++++++++++ tests/request/get_cgi.txt | 6 ++++++ 2 files changed, 24 insertions(+) create mode 100755 data/www/test.py create mode 100755 tests/request/get_cgi.txt diff --git a/data/www/test.py b/data/www/test.py new file mode 100755 index 0000000..7d937e6 --- /dev/null +++ b/data/www/test.py @@ -0,0 +1,18 @@ +import sys + +def main(): + # while 1: + + # data = sys.stdin.read() + # print("Received data from stdin:", data) + print('Error message 1', file=sys.stderr) + + print("Hello World!", file=sys.stdout) + sys.stdout.write('Hello world!') + + # sys.stdout.close() + + print('Error message 2', file=sys.stderr) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/request/get_cgi.txt b/tests/request/get_cgi.txt new file mode 100755 index 0000000..0bb316d --- /dev/null +++ b/tests/request/get_cgi.txt @@ -0,0 +1,6 @@ +GET /test.py HTTP/1.1 +User-Agent: custom-client +Host: weebserv +Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 +Connection: Keep-Alive + From ba44ce6bded886a01d5a11be18870613828b27a0 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 30 Nov 2023 14:13:48 +0100 Subject: [PATCH 16/33] added fcntl to GCI start --- src/CGI.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CGI.cpp b/src/CGI.cpp index 6247143..54740cf 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -152,6 +152,11 @@ ClientState CGI::start(size_t bodyLength) if (pipe(_serverToExternalProgram) == SYSTEM_ERROR) throw SystemException("Pipe"); if (pipe(_externalProgramToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); + if (fcntl(_serverToExternalProgram[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + if (fcntl(_serverToExternalProgram[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + if (fcntl(_externalProgramToServer[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); if (_pid == 0) @@ -185,8 +190,8 @@ ClientState CGI::start(size_t bodyLength) // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); // int status; // waitpid() - return (receive()); - // return (ClientState::CGI_Read); + // return (receive()); + return (ClientState::CGI_Read); // return (ClientState::Done); } return (ClientState::CGI_Read); From 422ec6d981aa9b9904da415dac8e690edd05d79d Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 30 Nov 2023 17:40:44 +0100 Subject: [PATCH 17/33] fixed findClientByFd, next step is using this in handleExistingConnection and get the Client class with in his own handle connection --- include/Client.hpp | 2 ++ include/HTTPServer.hpp | 1 + src/CGI.cpp | 52 +++++++++++++++++------------------------- src/Client.cpp | 1 - src/HTTPServer.cpp | 34 +++++++++++++++++++++++++++ src/Poll.cpp | 2 ++ 6 files changed, 60 insertions(+), 32 deletions(-) diff --git a/include/Client.hpp b/include/Client.hpp index ce7e283..651d0d5 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -26,6 +26,8 @@ class Client CGI _cgi; Socket _socket; ClientState _state; + // int _serverToExternalProgram[2]; + // int _externalProgramToServer[2]; }; #endif diff --git a/include/HTTPServer.hpp b/include/HTTPServer.hpp index e1d7e18..a51b987 100644 --- a/include/HTTPServer.hpp +++ b/include/HTTPServer.hpp @@ -30,6 +30,7 @@ class HTTPServer void handleActivePollFDs(); void handleNewConnection(int fd); void handleExistingConnection(const pollfd &poll_fd); + std::shared_ptr findClientByFd(int targetFd); }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index 54740cf..e195083 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -135,10 +135,6 @@ void CGI::execute(std::string executable, char **env) const char *path = "/usr/bin/python3"; // execve(path, (char *const *)argv, env); if (execve(path, (char *const *)argv, NULL) == SYSTEM_ERROR) throw SystemException("Execve"); - - // if error throw exception? - logger.log(ERROR, "execve error" + std::string(strerror(errno))); - _exit(127); } ClientState CGI::start(size_t bodyLength) @@ -152,10 +148,10 @@ ClientState CGI::start(size_t bodyLength) if (pipe(_serverToExternalProgram) == SYSTEM_ERROR) throw SystemException("Pipe"); if (pipe(_externalProgramToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); - if (fcntl(_serverToExternalProgram[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - if (fcntl(_serverToExternalProgram[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - if (fcntl(_externalProgramToServer[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(_serverToExternalProgram[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(_serverToExternalProgram[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(_externalProgramToServer[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); @@ -170,31 +166,25 @@ ClientState CGI::start(size_t bodyLength) if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); execute(_executable, _env); - logger.log(DEBUG, "after execute"); - } - else - { - - logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); - if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - logger.log(DEBUG, "CGI::start after closing"); - - if (bodyLength != 0) - return (ClientState::CGI_Write); - logger.log(DEBUG, "GCI::start after writing"); - if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_externalProgramToServer[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); - - // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - // int status; - // waitpid() - // return (receive()); - return (ClientState::CGI_Read); - // return (ClientState::Done); } + logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); + if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + logger.log(DEBUG, "CGI::start after closing"); + + if (bodyLength != 0) + return (ClientState::CGI_Write); + logger.log(DEBUG, "GCI::start after writing"); + if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(_externalProgramToServer[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); + + // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + // int status; + // waitpid() + // return (receive()); return (ClientState::CGI_Read); + // return (ClientState::Done); } // !! need to free _env and it's arguments somewhere !! diff --git a/src/Client.cpp b/src/Client.cpp index 2498e61..f694f5d 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -3,7 +3,6 @@ #include #include #include - #include Client::Client(const int &server_fd) : _socket(server_fd) diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 4dab784..5c51ea3 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -17,6 +17,19 @@ HTTPServer::~HTTPServer() { } +std::shared_ptr HTTPServer::findClientByFd(int targetFd) +{ + auto it = _active_clients.find(targetFd); + + if (it != _active_clients.end()) { + // Found the Client with the specified fd + return it->second; + } else { + // Client with the specified fd not found + return nullptr; + } +} + int HTTPServer::run() { Logger &logger = Logger::getInstance(); @@ -34,6 +47,7 @@ int HTTPServer::run() } while (true) { + logger.log(DEBUG, "while loop HTTPServer::run"); try { handleActivePollFDs(); @@ -72,6 +86,7 @@ void HTTPServer::handleActivePollFDs() _poll.pollFDs(); for (const auto &poll_fd : _poll.getPollFDs()) { + logger.log(DEBUG, "HTTPServer::handleActivePollFDs -- in for loop"); if (poll_fd.revents == 0) continue; try @@ -95,10 +110,14 @@ void HTTPServer::handleActivePollFDs() else throw std::runtime_error("Unknown file descriptor"); } + logger.log(DEBUG, "HTTPServer::handleActivePollFDs -- after for loop"); } void HTTPServer::handleNewConnection(int fd) { + Logger &logger = Logger::getInstance(); + logger.log(DEBUG, "HTTPServer::handleNewConnection"); + std::shared_ptr client = std::make_shared(fd); _active_clients.emplace(client->getFD(), client); _poll.addPollFD(client->getFD(), POLLIN); @@ -109,17 +128,32 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd) Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); + Client &client = *(findClientByFd(poll_fd.fd)); + logger.log(DEBUG, "client.getFD: %", client.getFD()); + logger.log(DEBUG, "poll_fd %", poll_fd.fd); + switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events)) { case ClientState::Receiving: case ClientState::CGI_Read: + // if (!fd_to_client.contains(poll_fd.fd)) + // { + // poll_fd.fd refers to a Server instead of a Client, so error! + // } + // Client &client = *(findClientByFd(poll_fd.fd)); + // logger.log(DEBUG, "client.getFD: %", client.getFD()); + // logger.log(DEBUG, "poll_fd": poll_fd); + // _poll.setEvents(client._externalProgramToServer[READ_END], POLLIN); _poll.setEvents(poll_fd.fd, POLLIN); + break; case ClientState::Loading: case ClientState::Sending: case ClientState::Error: case ClientState::CGI_Write: case ClientState::CGI_Start: + case ClientState::CGI_Load: + // does it make sense to set POLLOUT here? _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::Done: diff --git a/src/Poll.cpp b/src/Poll.cpp index 2262a66..5cb7240 100644 --- a/src/Poll.cpp +++ b/src/Poll.cpp @@ -42,7 +42,9 @@ void Poll::pollFDs(void) Logger &logger = Logger::getInstance(); logger.log(INFO, "Polling " + std::to_string(_poll_fds.size()) + " file descriptors"); + int poll_count = poll(_poll_fds.data(), _poll_fds.size(), NO_TIMEOUT); + logger.log(DEBUG, "foo"); if (poll_count == SYSTEM_ERROR || poll_count == 0) throw SystemException("poll"); // TODO change poll_count 0 handler } From bdd718a7a330b0ee653652851b7536f04bb8f0c7 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 1 Dec 2023 10:46:44 +0100 Subject: [PATCH 18/33] added getters for serverToCgiFd and cgiToServerFd and now giving Client class to the CGI class --- include/CGI.hpp | 4 +- include/Client.hpp | 9 +++-- include/HTTPServer.hpp | 4 +- src/CGI.cpp | 3 +- src/Client.cpp | 13 ++++++- src/HTTPServer.cpp | 83 ++++++++++++++++++------------------------ tests/get_request.sh | 2 +- 7 files changed, 61 insertions(+), 57 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 55ebf7c..c8fc44d 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -8,6 +8,8 @@ #define READ_END 0 #define WRITE_END 1 +class Client; + // Common gateway interface class CGI @@ -27,7 +29,7 @@ class CGI ~CGI(); // execute is probably redundant - ClientState start(size_t body_length); + ClientState start(size_t body_length, Client &client); ClientState parseURIForCGI(std::string requestTarget); void execute(std::string executable, char **env); bool fileExists(const std::string& filePath); diff --git a/include/Client.hpp b/include/Client.hpp index 651d0d5..62ee502 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -16,8 +16,11 @@ class Client const Client &operator=(const Client &other) = delete; ~Client(); - ClientState handleConnection(short events); + ClientState handleConnection(short events, Client &client); int getFD(void) const; + int const *getCgiToServerFd(void) const; + int const *getServerToCgiFd(void) const; + private: HTTPRequest _request; @@ -26,8 +29,8 @@ class Client CGI _cgi; Socket _socket; ClientState _state; - // int _serverToExternalProgram[2]; - // int _externalProgramToServer[2]; + int _serverToCgiFd[2]; + int _cgiToServerFd[2]; }; #endif diff --git a/include/HTTPServer.hpp b/include/HTTPServer.hpp index a51b987..a81cbbd 100644 --- a/include/HTTPServer.hpp +++ b/include/HTTPServer.hpp @@ -29,8 +29,8 @@ class HTTPServer void setupServers(void); void handleActivePollFDs(); void handleNewConnection(int fd); - void handleExistingConnection(const pollfd &poll_fd); - std::shared_ptr findClientByFd(int targetFd); + void handleExistingConnection(const pollfd &poll_fd, Client &client); + Client &findClientByFd(int targetFd); }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index e195083..14b6c1b 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -137,8 +137,9 @@ void CGI::execute(std::string executable, char **env) if (execve(path, (char *const *)argv, NULL) == SYSTEM_ERROR) throw SystemException("Execve"); } -ClientState CGI::start(size_t bodyLength) +ClientState CGI::start(size_t bodyLength, Client &client) { + (void)client; Logger &logger = Logger::getInstance(); logger.log(DEBUG, "CGI::start called"); diff --git a/src/Client.cpp b/src/Client.cpp index f694f5d..ede05a8 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -20,7 +21,15 @@ int Client::getFD(void) const return (_socket.getFD()); } -ClientState Client::handleConnection(short events) +int const *Client::getCgiToServerFd(void) const { + return (_cgiToServerFd); +} + +int const *Client::getServerToCgiFd(void) const { + return (_serverToCgiFd); +} + +ClientState Client::handleConnection(short events, Client &client) { Logger &logger = Logger::getInstance(); logger.log(INFO, "Handling client connection on fd: " + @@ -42,7 +51,7 @@ ClientState Client::handleConnection(short events) else if (events & POLLOUT && _state == ClientState::CGI_Start) { logger.log(ERROR, "ClientState::CGI_Start"); - _state = _cgi.start(_request.getBodyLength()); + _state = _cgi.start(_request.getBodyLength(), client); // _state = ClientState::Done; return (_state); } diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 5c51ea3..fd66904 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -17,17 +17,13 @@ HTTPServer::~HTTPServer() { } -std::shared_ptr HTTPServer::findClientByFd(int targetFd) +Client &HTTPServer::findClientByFd(int targetFd) { auto it = _active_clients.find(targetFd); - if (it != _active_clients.end()) { - // Found the Client with the specified fd - return it->second; - } else { - // Client with the specified fd not found - return nullptr; - } + if (it != _active_clients.end()) + return *(it->second); + throw std::runtime_error("Error: poll_fd.fd refers to a Server instead of a Client"); } int HTTPServer::run() @@ -105,8 +101,10 @@ void HTTPServer::handleActivePollFDs() _poll.pollEventsToString(poll_fd.revents)); if (_active_servers.find(poll_fd.fd) != _active_servers.end()) handleNewConnection(poll_fd.fd); - else if (_active_clients.find(poll_fd.fd) != _active_clients.end()) - handleExistingConnection(poll_fd); + else if (_active_clients.find(poll_fd.fd) != _active_clients.end()) { + Client &client = findClientByFd(poll_fd.fd); + handleExistingConnection(poll_fd, client); + } else throw std::runtime_error("Unknown file descriptor"); } @@ -123,45 +121,36 @@ void HTTPServer::handleNewConnection(int fd) _poll.addPollFD(client->getFD(), POLLIN); } -void HTTPServer::handleExistingConnection(const pollfd &poll_fd) +void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Client &client) { Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); - - Client &client = *(findClientByFd(poll_fd.fd)); - logger.log(DEBUG, "client.getFD: %", client.getFD()); - logger.log(DEBUG, "poll_fd %", poll_fd.fd); - - switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events)) + + switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events, client)) { - case ClientState::Receiving: - case ClientState::CGI_Read: - // if (!fd_to_client.contains(poll_fd.fd)) - // { - // poll_fd.fd refers to a Server instead of a Client, so error! - // } - // Client &client = *(findClientByFd(poll_fd.fd)); - // logger.log(DEBUG, "client.getFD: %", client.getFD()); - // logger.log(DEBUG, "poll_fd": poll_fd); - // _poll.setEvents(client._externalProgramToServer[READ_END], POLLIN); - _poll.setEvents(poll_fd.fd, POLLIN); - - break; - case ClientState::Loading: - case ClientState::Sending: - case ClientState::Error: - case ClientState::CGI_Write: - case ClientState::CGI_Start: - case ClientState::CGI_Load: - // does it make sense to set POLLOUT here? - _poll.setEvents(poll_fd.fd, POLLOUT); - break; - case ClientState::Done: - _poll.removeFD(poll_fd.fd); - _active_clients.erase(poll_fd.fd); - break; - default: - throw std::runtime_error( - "Unknown client state"); // TODO custom exception - } + case ClientState::Receiving: + _poll.setEvents(poll_fd.fd, POLLIN); + break; + case ClientState::CGI_Read: + logger.log(DEBUG, "client.getFD: %", client.getFD()); + logger.log(DEBUG, "poll_fd %", poll_fd.fd); + _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); + break; + case ClientState::Loading: + case ClientState::Sending: + case ClientState::Error: + case ClientState::CGI_Write: + case ClientState::CGI_Start: + case ClientState::CGI_Load: + // does it make sense to set POLLOUT here? + _poll.setEvents(poll_fd.fd, POLLOUT); + break; + case ClientState::Done: + _poll.removeFD(poll_fd.fd); + _active_clients.erase(poll_fd.fd); + break; + default: + throw std::runtime_error( + "Unknown client state"); // TODO custom exception + } } diff --git a/tests/get_request.sh b/tests/get_request.sh index 242eb53..f6fa2c1 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/get_cgi.txt" +REQUEST_FILE="tests/request/get.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From 513717aadcd3b2caa797b0b62fcad928f6910ce4 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 1 Dec 2023 11:08:10 +0100 Subject: [PATCH 19/33] now using the pipes from the client in the cgi, it compiles so commit --- include/CGI.hpp | 17 ++++++-------- include/Client.hpp | 4 ++-- src/CGI.cpp | 58 +++++++++++++++++++++++----------------------- src/Client.cpp | 19 ++++++++------- src/HTTPServer.cpp | 4 ++-- 5 files changed, 50 insertions(+), 52 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index c8fc44d..e165438 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -16,8 +16,8 @@ class CGI { private: pid_t _pid; - int _serverToExternalProgram[2]; - int _externalProgramToServer[2]; + // int _serverToExternalProgram[2]; + // int _externalProgramToServer[2]; bool _bodyIsSent; size_t _bodyBytesWritten; std::string _executable; @@ -31,18 +31,15 @@ class CGI // execute is probably redundant ClientState start(size_t body_length, Client &client); ClientState parseURIForCGI(std::string requestTarget); - void execute(std::string executable, char **env); - bool fileExists(const std::string& filePath); - bool isExecutable(const std::string& filePath); + void execute(std::string executable, char **env); + bool fileExists(const std::string& filePath); + bool isExecutable(const std::string& filePath); const std::string& getExecutable(void) const; const pid_t& getPid(void) const; - ClientState send(std::string body, size_t bodyLength); - - // ClientState receive(std::string body); - ClientState receive(void); - // ClientState send(int fd, HTTPMethod methodType, std::string requestBody); + ClientState send(Client &client ,std::string body, size_t bodyLength); + ClientState receive(Client &client); std::string body; int pipe_fd[2]; diff --git a/include/Client.hpp b/include/Client.hpp index 62ee502..2380e6f 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -18,8 +18,8 @@ class Client ClientState handleConnection(short events, Client &client); int getFD(void) const; - int const *getCgiToServerFd(void) const; - int const *getServerToCgiFd(void) const; + int *getCgiToServerFd(void); + int *getServerToCgiFd(void); private: diff --git a/src/CGI.cpp b/src/CGI.cpp index 14b6c1b..19219ab 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -1,15 +1,15 @@ -#include -#include -#include -#include +#include "Client.hpp" +#include "CGI.hpp" +#include "Logger.hpp" +#include "SystemException.hpp" +#include #include #include #include #include #include - // TO DO // - get execute functional // - make sure that the test.py is closing the STDIN if that is not already been done @@ -30,40 +30,40 @@ const pid_t &CGI::getPid(void) const { return (_pid); } -ClientState CGI::send(std::string body, size_t bodyLength) +ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) { Logger &logger = Logger::getInstance(); ssize_t bytesWritten = 0; logger.log(INFO, "GCI::send is called"); if (!_bodyIsSent) - bytesWritten = write(_serverToExternalProgram[WRITE_END], body.c_str(), BUFFER_SIZE); + bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), BUFFER_SIZE); if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); _bodyBytesWritten += bytesWritten; if (_bodyBytesWritten == bodyLength) { - close(_serverToExternalProgram[WRITE_END]); + close(client.getServerToCgiFd()[WRITE_END]); return (ClientState::CGI_Read); } logger.log(INFO, "CGI body: " + std::to_string(bytesWritten)); return (ClientState::CGI_Write); } -ClientState CGI::receive(void) +ClientState CGI::receive(Client &client) { Logger &logger = Logger::getInstance(); ssize_t bytesRead = 0; char buffer[1024]; - // if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { + // if (fcntl(client.getCgiToServerFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { // perror("fcntl"); // throw SystemException("fcntl"); // } bzero(buffer, sizeof(buffer)); logger.log(INFO, "CGI::receive is called"); - bytesRead = read(_externalProgramToServer[READ_END], buffer, sizeof(buffer)); + bytesRead = read(client.getCgiToServerFd()[READ_END], buffer, sizeof(buffer)); // if (bytesRead == -1) { // if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -87,7 +87,7 @@ ClientState CGI::receive(void) // if (bytesRead == SYSTEM_ERROR) // return (ClientState::Error); logger.log(DEBUG, "body in GCI::receive:\n" + body); - close(_externalProgramToServer[READ_END]); + close(client.getCgiToServerFd()[READ_END]); // int status; // waitpid(_pid, &status, 0); // if (WEXITSTATUS(status) == -1) @@ -146,41 +146,41 @@ ClientState CGI::start(size_t bodyLength, Client &client) logger.log(DEBUG, "Executable: %", _executable); - if (pipe(_serverToExternalProgram) == SYSTEM_ERROR) throw SystemException("Pipe"); - if (pipe(_externalProgramToServer) == SYSTEM_ERROR) throw SystemException("Pipe"); + if (pipe(client.getServerToCgiFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); + if (pipe(client.getCgiToServerFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); - // if (fcntl(_serverToExternalProgram[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(_serverToExternalProgram[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(_externalProgramToServer[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(_externalProgramToServer[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(client.getServerToCgiFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(client.getServerToCgiFd()[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(client.getCgiToServerFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} + // if (fcntl(client.getCgiToServerFd()[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); if (_pid == 0) { logger.log(ERROR, "_pid == 0"); - if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_serverToExternalProgram[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_externalProgramToServer[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(client.getServerToCgiFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(client.getCgiToServerFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); execute(_executable, _env); } logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); - if (close(_serverToExternalProgram[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(_externalProgramToServer[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); logger.log(DEBUG, "CGI::start after closing"); if (bodyLength != 0) return (ClientState::CGI_Write); logger.log(DEBUG, "GCI::start after writing"); - if (close(_serverToExternalProgram[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(_externalProgramToServer[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); - // if (close(_externalProgramToServer[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); + // if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); // int status; // waitpid() // return (receive()); diff --git a/src/Client.cpp b/src/Client.cpp index ede05a8..e248cd1 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -21,19 +21,24 @@ int Client::getFD(void) const return (_socket.getFD()); } -int const *Client::getCgiToServerFd(void) const { +int *Client::getCgiToServerFd(void) { return (_cgiToServerFd); } -int const *Client::getServerToCgiFd(void) const { +int *Client::getServerToCgiFd(void) { return (_serverToCgiFd); } +// implement method Martijn for verifying that we are dealing with a CGI +// also get fileExtension from Martijn and save in HTTPRequest class ClientState Client::handleConnection(short events, Client &client) { Logger &logger = Logger::getInstance(); logger.log(INFO, "Handling client connection on fd: " + std::to_string(_socket.getFD())); + + + try { if (events & POLLIN && _state == ClientState::Receiving) @@ -41,11 +46,7 @@ ClientState Client::handleConnection(short events, Client &client) logger.log(ERROR, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); logger.log(DEBUG, "_request_target: " + _request.getRequestTarget()); - - // implement method Martijn for verifying that we are dealing with a CGI - // also get fileExtension from Martijn and save in HTTPRequest class - - _request.setCGIToTrue(); // settting CGI to true for testing purposes + _request.setCGIToTrue(); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) @@ -58,13 +59,13 @@ ClientState Client::handleConnection(short events, Client &client) else if (events & POLLOUT && _state == ClientState::CGI_Write) { logger.log(ERROR, "ClientState::CGI_Write"); - _state = _cgi.send(_request.getBody(), _request.getBodyLength()); + _state = _cgi.send(client, _request.getBody(), _request.getBodyLength()); return (_state); } else if (events & POLLIN && _state == ClientState::CGI_Read) { logger.log(ERROR, "ClientState::CGI_Read"); - _state = _cgi.receive(); + _state = _cgi.receive(client); logger.log(INFO, "_cgi.body: %", _cgi.body); // int status; // waitpid(_cgi.getPid(), &status, 0); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index fd66904..7de6fd7 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -125,7 +125,7 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Client &client) { Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); - + switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events, client)) { case ClientState::Receiving: @@ -142,7 +142,7 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Client &client) case ClientState::CGI_Write: case ClientState::CGI_Start: case ClientState::CGI_Load: - // does it make sense to set POLLOUT here? + // does it make sense to set GCI_Load POLLOUT here? _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::Done: From c82815106b3cc0b5558ab9ba4d78b5b3d6800eb7 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 1 Dec 2023 17:45:14 +0100 Subject: [PATCH 20/33] it seems to work, need to look into the filemanager because there is something going wrong --- include/CGI.hpp | 9 ++++--- include/Client.hpp | 6 ++++- include/HTTPServer.hpp | 6 ++++- src/CGI.cpp | 61 ++++++++++++++++++------------------------ src/Client.cpp | 45 ++++++++++++++++++------------- src/HTTPServer.cpp | 51 ++++++++++++++++++++++++++++++----- tests/get_request.sh | 2 +- 7 files changed, 113 insertions(+), 67 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index e165438..ef37a7c 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -4,11 +4,14 @@ #include #include #include +#include +#include #define READ_END 0 #define WRITE_END 1 class Client; +class Poll; // Common gateway interface @@ -16,8 +19,6 @@ class CGI { private: pid_t _pid; - // int _serverToExternalProgram[2]; - // int _externalProgramToServer[2]; bool _bodyIsSent; size_t _bodyBytesWritten; std::string _executable; @@ -28,8 +29,8 @@ class CGI CGI &operator=(const CGI &rhs) = delete; ~CGI(); - // execute is probably redundant - ClientState start(size_t body_length, Client &client); + ClientState start(Poll &poll, Client &client, size_t body_length, + std::unordered_map> &active_pipes); ClientState parseURIForCGI(std::string requestTarget); void execute(std::string executable, char **env); bool fileExists(const std::string& filePath); diff --git a/include/Client.hpp b/include/Client.hpp index 2380e6f..28654fd 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include class Client { @@ -16,11 +18,13 @@ class Client const Client &operator=(const Client &other) = delete; ~Client(); - ClientState handleConnection(short events, Client &client); + ClientState handleConnection(short events, Poll &poll, Client &client, + std::unordered_map> &active_pipes); int getFD(void) const; int *getCgiToServerFd(void); int *getServerToCgiFd(void); + bool cgiHasBeenRead; private: HTTPRequest _request; diff --git a/include/HTTPServer.hpp b/include/HTTPServer.hpp index a81cbbd..7e690a9 100644 --- a/include/HTTPServer.hpp +++ b/include/HTTPServer.hpp @@ -25,12 +25,16 @@ class HTTPServer Poll _poll; std::unordered_map> _active_servers; std::unordered_map> _active_clients; + std::unordered_map> _active_pipes; void setupServers(void); void handleActivePollFDs(); void handleNewConnection(int fd); - void handleExistingConnection(const pollfd &poll_fd, Client &client); + void handleExistingConnection(const pollfd &poll_fd, Poll &poll, Client &client, + std::unordered_map> &active_pipes); Client &findClientByFd(int targetFd); + Client &getClientByPipeFd(int pipe_fd); + // void handleNewPipe(int fd, int pipeState); }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index 19219ab..a5a9c22 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -1,4 +1,5 @@ #include "Client.hpp" +#include "Poll.hpp" #include "CGI.hpp" #include "Logger.hpp" #include "SystemException.hpp" @@ -56,44 +57,29 @@ ClientState CGI::receive(Client &client) ssize_t bytesRead = 0; char buffer[1024]; - // if (fcntl(client.getCgiToServerFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { - // perror("fcntl"); - // throw SystemException("fcntl"); - // } - bzero(buffer, sizeof(buffer)); logger.log(INFO, "CGI::receive is called"); bytesRead = read(client.getCgiToServerFd()[READ_END], buffer, sizeof(buffer)); - // if (bytesRead == -1) { - // if (errno == EAGAIN || errno == EWOULDBLOCK) { - // logger.log(DEBUG, "No data available to read.\n"); - // return (ClientState::CGI_Read); - - // } else { - // // Handle other errors - // perror("read"); - // throw SystemException("errno"); - // } - // } - // else { - // bytesRead = read(STDIN_FILENO, buffer, sizeof(buffer)); - // if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); - logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); - logger.log(DEBUG, "buffer:\n" + std::string(buffer)); - body += buffer; - if (bytesRead != 0) - return (ClientState::CGI_Read); - // if (bytesRead == SYSTEM_ERROR) - // return (ClientState::Error); - logger.log(DEBUG, "body in GCI::receive:\n" + body); - close(client.getCgiToServerFd()[READ_END]); - // int status; - // waitpid(_pid, &status, 0); - // if (WEXITSTATUS(status) == -1) - // return (ClientState::Error); - // } - return (ClientState::CGI_Load); + if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); + logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); + logger.log(DEBUG, "buffer:\n" + std::string(buffer)); + body += buffer; + // if (bytesRead >= 1024) + // return (ClientState::CGI_Read); + client.cgiHasBeenRead = true; + // if (bytesRead == SYSTEM_ERROR) + // return (ClientState::Error); + logger.log(DEBUG, "body in GCI::receive:\n" + body); + close(client.getCgiToServerFd()[READ_END]); + // _poll.removeFD(poll_fd.fd); // optional + + // int status; + // waitpid(_pid, &status, 0); + // if (WEXITSTATUS(status) == -1) + // return (ClientState::Error); +// } + return (ClientState::Sending); } bool CGI::fileExists(const std::string& filePath) { @@ -137,7 +123,8 @@ void CGI::execute(std::string executable, char **env) if (execve(path, (char *const *)argv, NULL) == SYSTEM_ERROR) throw SystemException("Execve"); } -ClientState CGI::start(size_t bodyLength, Client &client) +ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, + std::unordered_map> &active_pipes) { (void)client; Logger &logger = Logger::getInstance(); @@ -178,6 +165,10 @@ ClientState CGI::start(size_t bodyLength, Client &client) logger.log(DEBUG, "GCI::start after writing"); if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + + std::shared_ptr pipe = std::make_shared(client.getCgiToServerFd()[READ_END]); + active_pipes.emplace(client.getCgiToServerFd()[READ_END], pipe); + poll.addPollFD(client.getCgiToServerFd()[READ_END], POLLIN); logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); // if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); diff --git a/src/Client.cpp b/src/Client.cpp index e248cd1..334a44a 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,7 +1,9 @@ -#include -#include -#include -#include +#include "CGI.hpp" +#include "Poll.hpp" +#include "Client.hpp" +#include "ClientException.hpp" +#include "Logger.hpp" + #include #include #include @@ -10,6 +12,7 @@ Client::Client(const int &server_fd) : _socket(server_fd) { _socket.setupClient(); _state = ClientState::Receiving; + cgiHasBeenRead = false; } Client::~Client() @@ -31,14 +34,12 @@ int *Client::getServerToCgiFd(void) { // implement method Martijn for verifying that we are dealing with a CGI // also get fileExtension from Martijn and save in HTTPRequest class -ClientState Client::handleConnection(short events, Client &client) +ClientState Client::handleConnection(short events, Poll &poll, Client &client, + std::unordered_map> &active_pipes) { Logger &logger = Logger::getInstance(); logger.log(INFO, "Handling client connection on fd: " + std::to_string(_socket.getFD())); - - - try { if (events & POLLIN && _state == ClientState::Receiving) @@ -52,7 +53,7 @@ ClientState Client::handleConnection(short events, Client &client) else if (events & POLLOUT && _state == ClientState::CGI_Start) { logger.log(ERROR, "ClientState::CGI_Start"); - _state = _cgi.start(_request.getBodyLength(), client); + _state = _cgi.start(poll, client, _request.getBodyLength(), active_pipes); // _state = ClientState::Done; return (_state); } @@ -67,6 +68,13 @@ ClientState Client::handleConnection(short events, Client &client) logger.log(ERROR, "ClientState::CGI_Read"); _state = _cgi.receive(client); logger.log(INFO, "_cgi.body: %", _cgi.body); + + // maybe the CGI should have it's own fileManager + logger.log(DEBUG, "ClientState::CGI_Load inside CGI_Read"); + _state = _file_manager.manage( + _request.getMethodType(), + "./data/www" + _request.getRequestTarget(), + _cgi.body); // int status; // waitpid(_cgi.getPid(), &status, 0); // WEXITSTATUS(status); @@ -90,15 +98,15 @@ ClientState Client::handleConnection(short events, Client &client) _request.getBody()); // TODO: resolve location return (_state); } - else if (events & POLLOUT && _state == ClientState::CGI_Load) - { - logger.log(DEBUG, "ClientState::CGI_Load"); - _state = _file_manager.manage( - _request.getMethodType(), - "./data/www" + _request.getRequestTarget(), - _cgi.body); - return (_state); - } + // else if (events & POLLOUT && _state == ClientState::CGI_Load) + // { + // logger.log(DEBUG, "ClientState::CGI_Load"); + // _state = _file_manager.manage( + // _request.getMethodType(), + // "./data/www" + _request.getRequestTarget(), + // _cgi.body); + // return (_state); + // } else if (events & POLLOUT && _state == ClientState::Error) { _state = _file_manager.loadErrorPage(); @@ -106,6 +114,7 @@ ClientState Client::handleConnection(short events, Client &client) } else if (events & POLLOUT && _state == ClientState::Sending) { + logger.log(DEBUG, "ClientState::Sending"); // if CGI // _state = // _response.send(_socket.getFD(), _cgi.getResponse()); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 7de6fd7..4e21b9d 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -3,7 +3,7 @@ #include HTTPServer::HTTPServer(const std::string &config_file_path) -try : _parser(config_file_path), _poll(), _active_servers(), _active_clients() +try : _parser(config_file_path), _poll(), _active_servers(), _active_clients(), _active_pipes() { } catch (const std::runtime_error &e) @@ -23,7 +23,17 @@ Client &HTTPServer::findClientByFd(int targetFd) if (it != _active_clients.end()) return *(it->second); - throw std::runtime_error("Error: poll_fd.fd refers to a Server instead of a Client"); + throw std::runtime_error("Error: findClientByFd"); +} + +Client &HTTPServer::getClientByPipeFd(int pipe_fd) { + for (const auto& entry : _active_clients) { + const auto& client = entry.second; + if (client->getCgiToServerFd()[READ_END] == pipe_fd) { + return *client; + } + } + throw std::runtime_error("Error: getClientByPipeFd"); } int HTTPServer::run() @@ -102,8 +112,17 @@ void HTTPServer::handleActivePollFDs() if (_active_servers.find(poll_fd.fd) != _active_servers.end()) handleNewConnection(poll_fd.fd); else if (_active_clients.find(poll_fd.fd) != _active_clients.end()) { + logger.log(DEBUG, "_active_clients.find(poll_fd.fd) != _active_clients.end()"); Client &client = findClientByFd(poll_fd.fd); - handleExistingConnection(poll_fd, client); + handleExistingConnection(poll_fd, _poll, client, _active_pipes); + } + else if (_active_pipes.find(poll_fd.fd) != _active_pipes.end()) { + logger.log(DEBUG, "_active_pipes.find(poll_fd.fd) != _active_pipes.end()"); + Client &client = getClientByPipeFd(poll_fd.fd); + logger.log(DEBUG, "Client found with pipe"); + (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); + // if (client.cgiHasBeenRead) + _poll.removeFD(poll_fd.fd); } else throw std::runtime_error("Unknown file descriptor"); @@ -111,6 +130,16 @@ void HTTPServer::handleActivePollFDs() logger.log(DEBUG, "HTTPServer::handleActivePollFDs -- after for loop"); } +// void HTTPServer::handleNewPipe(int fd, int pipeState) +// { +// Logger &logger = Logger::getInstance(); +// logger.log(DEBUG, "HTTPServer::handleNewPipe"); + +// std::shared_ptr pipe = std::make_shared(fd); +// _active_pipes.emplace(fd, pipe); +// _poll.addPollFD(fd, pipeState); +// } + void HTTPServer::handleNewConnection(int fd) { Logger &logger = Logger::getInstance(); @@ -121,12 +150,17 @@ void HTTPServer::handleNewConnection(int fd) _poll.addPollFD(client->getFD(), POLLIN); } -void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Client &client) +void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Client &client, + std::unordered_map> &active_pipes) { Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); - switch (_active_clients.at(poll_fd.fd)->handleConnection(poll_fd.events, client)) + logger.log(DEBUG, "_active_clients.at(poll_fd.fd): %", _active_clients.at(poll_fd.fd)); + logger.log(DEBUG, "client: %", &client); + + + switch ((&client)->handleConnection(poll_fd.events, poll, client, active_pipes)) { case ClientState::Receiving: _poll.setEvents(poll_fd.fd, POLLIN); @@ -134,8 +168,11 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Client &client) case ClientState::CGI_Read: logger.log(DEBUG, "client.getFD: %", client.getFD()); logger.log(DEBUG, "poll_fd %", poll_fd.fd); - _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); - break; + logger.log(DEBUG, "client.getCgiToServerFd()[READ_END] %", client.getCgiToServerFd()[READ_END]); + if (poll_fd.fd != client.getCgiToServerFd()[READ_END]) { + // _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); + _poll.setEvents(poll_fd.fd, POLLIN); + break; } case ClientState::Loading: case ClientState::Sending: case ClientState::Error: diff --git a/tests/get_request.sh b/tests/get_request.sh index f6fa2c1..242eb53 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/get.txt" +REQUEST_FILE="tests/request/get_cgi.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From 45e89f7a78e12485d75c6917713f528047793423 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 6 Dec 2023 11:57:03 +0100 Subject: [PATCH 21/33] added FileManager::manageCgi and improved CGI::receive --- data/www/test.py | 6 ------ include/FileManager.hpp | 1 + src/CGI.cpp | 30 ++++++++++++++---------------- src/Client.cpp | 16 ++++++++-------- src/FileManager.cpp | 6 ++++++ src/HTTPServer.cpp | 9 ++++++--- 6 files changed, 35 insertions(+), 33 deletions(-) diff --git a/data/www/test.py b/data/www/test.py index 7d937e6..049c6e1 100755 --- a/data/www/test.py +++ b/data/www/test.py @@ -1,16 +1,10 @@ import sys def main(): - # while 1: - # data = sys.stdin.read() - # print("Received data from stdin:", data) print('Error message 1', file=sys.stderr) print("Hello World!", file=sys.stdout) - sys.stdout.write('Hello world!') - - # sys.stdout.close() print('Error message 2', file=sys.stderr) diff --git a/include/FileManager.hpp b/include/FileManager.hpp index 55b81cb..c07bb5a 100644 --- a/include/FileManager.hpp +++ b/include/FileManager.hpp @@ -28,6 +28,7 @@ class FileManager ClientState loadErrorPage(void); ClientState manage(HTTPMethod method, const std::string &filename, const std::string &body); + ClientState manageCgi(std::string http_version, const std::string &body); ClientState manageGet(void); ClientState managePost(const std::string &body); ClientState manageDelete(const std::string &reqest_target_path); diff --git a/src/CGI.cpp b/src/CGI.cpp index a5a9c22..0683235 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -55,7 +55,7 @@ ClientState CGI::receive(Client &client) { Logger &logger = Logger::getInstance(); ssize_t bytesRead = 0; - char buffer[1024]; + char buffer[5]; bzero(buffer, sizeof(buffer)); logger.log(INFO, "CGI::receive is called"); @@ -65,21 +65,19 @@ ClientState CGI::receive(Client &client) logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); logger.log(DEBUG, "buffer:\n" + std::string(buffer)); body += buffer; - // if (bytesRead >= 1024) - // return (ClientState::CGI_Read); - client.cgiHasBeenRead = true; - // if (bytesRead == SYSTEM_ERROR) - // return (ClientState::Error); - logger.log(DEBUG, "body in GCI::receive:\n" + body); - close(client.getCgiToServerFd()[READ_END]); - // _poll.removeFD(poll_fd.fd); // optional - - // int status; - // waitpid(_pid, &status, 0); - // if (WEXITSTATUS(status) == -1) - // return (ClientState::Error); -// } - return (ClientState::Sending); + logger.log(DEBUG, "sizeof(buffer): %", sizeof(buffer)); + if (bytesRead < (ssize_t) sizeof(buffer)) + { + client.cgiHasBeenRead = true; + logger.log(DEBUG, "body in GCI::receive:\n" + body); + close(client.getCgiToServerFd()[READ_END]); + // int status; + // waitpid(_pid, &status, 0); + // if (WEXITSTATUS(status) == -1) + // return (ClientState::Error); + return (ClientState::Sending); + } + return (ClientState::CGI_Read); } bool CGI::fileExists(const std::string& filePath) { diff --git a/src/Client.cpp b/src/Client.cpp index 334a44a..abb9c91 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -54,7 +54,6 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { logger.log(ERROR, "ClientState::CGI_Start"); _state = _cgi.start(poll, client, _request.getBodyLength(), active_pipes); - // _state = ClientState::Done; return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) @@ -67,14 +66,15 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { logger.log(ERROR, "ClientState::CGI_Read"); _state = _cgi.receive(client); - logger.log(INFO, "_cgi.body: %", _cgi.body); + if (client.cgiHasBeenRead == true) { + _state = _file_manager.manageCgi(_request.getHTTPVersion(), _cgi.body); + logger.log(DEBUG, "response:\n\n" + _file_manager.getResponse()); - // maybe the CGI should have it's own fileManager - logger.log(DEBUG, "ClientState::CGI_Load inside CGI_Read"); - _state = _file_manager.manage( - _request.getMethodType(), - "./data/www" + _request.getRequestTarget(), - _cgi.body); + } + + // logger.log(DEBUG, "cgiBody: " + _cgi.body); + + // _state = ClientState::Sending; // int status; // waitpid(_cgi.getPid(), &status, 0); // WEXITSTATUS(status); diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 94e0b60..1655bd8 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -149,6 +149,12 @@ ClientState FileManager::manage(HTTPMethod method, return (ClientState::Unkown); } +ClientState FileManager::manageCgi(std::string http_version, const std::string &body) +{ + _response = http_version + " 200 OK\t\n\t\n" + body; + return (ClientState::Sending); +} + const std::string &FileManager::getResponse(void) const { return (_response); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 4e21b9d..a4803fb 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -119,10 +119,15 @@ void HTTPServer::handleActivePollFDs() else if (_active_pipes.find(poll_fd.fd) != _active_pipes.end()) { logger.log(DEBUG, "_active_pipes.find(poll_fd.fd) != _active_pipes.end()"); Client &client = getClientByPipeFd(poll_fd.fd); + logger.log(DEBUG, "Client: %", &client); + logger.log(DEBUG, "poll_fd.fd: %", poll_fd.fd); logger.log(DEBUG, "Client found with pipe"); (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); - // if (client.cgiHasBeenRead) + if (client.cgiHasBeenRead) + { + logger.log(DEBUG, "cgiHasBeenRead == true"); _poll.removeFD(poll_fd.fd); + } } else throw std::runtime_error("Unknown file descriptor"); @@ -178,8 +183,6 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Cli case ClientState::Error: case ClientState::CGI_Write: case ClientState::CGI_Start: - case ClientState::CGI_Load: - // does it make sense to set GCI_Load POLLOUT here? _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::Done: From 6c771c7069f411fa1b0baa9ecdcb23802c970c01 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 6 Dec 2023 13:24:44 +0100 Subject: [PATCH 22/33] cgi works! --- data/www/test.py | 2 +- src/CGI.cpp | 2 +- src/HTTPServer.cpp | 27 ++++++++++++++------------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/data/www/test.py b/data/www/test.py index 049c6e1..9613e0b 100755 --- a/data/www/test.py +++ b/data/www/test.py @@ -1,7 +1,7 @@ import sys def main(): - + print('Error message 1', file=sys.stderr) print("Hello World!", file=sys.stdout) diff --git a/src/CGI.cpp b/src/CGI.cpp index 0683235..c96e2ce 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -55,7 +55,7 @@ ClientState CGI::receive(Client &client) { Logger &logger = Logger::getInstance(); ssize_t bytesRead = 0; - char buffer[5]; + char buffer[1024]; bzero(buffer, sizeof(buffer)); logger.log(INFO, "CGI::receive is called"); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index a4803fb..71ce24f 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -119,14 +119,14 @@ void HTTPServer::handleActivePollFDs() else if (_active_pipes.find(poll_fd.fd) != _active_pipes.end()) { logger.log(DEBUG, "_active_pipes.find(poll_fd.fd) != _active_pipes.end()"); Client &client = getClientByPipeFd(poll_fd.fd); - logger.log(DEBUG, "Client: %", &client); - logger.log(DEBUG, "poll_fd.fd: %", poll_fd.fd); - logger.log(DEBUG, "Client found with pipe"); + logger.log(DEBUG, "Client % found on poll_fd.fd (pipe): %", &client, poll_fd.fd); (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); if (client.cgiHasBeenRead) { - logger.log(DEBUG, "cgiHasBeenRead == true"); + logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); _poll.removeFD(poll_fd.fd); + _active_pipes.erase(poll_fd.fd); + _poll.setEvents(client.getFD(), POLLOUT); } } else @@ -161,23 +161,24 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Cli Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); - logger.log(DEBUG, "_active_clients.at(poll_fd.fd): %", _active_clients.at(poll_fd.fd)); + // logger.log(DEBUG, "_active_clients.at(poll_fd.fd): %", _active_clients.at(poll_fd.fd)); logger.log(DEBUG, "client: %", &client); switch ((&client)->handleConnection(poll_fd.events, poll, client, active_pipes)) { case ClientState::Receiving: + case ClientState::CGI_Read: _poll.setEvents(poll_fd.fd, POLLIN); break; - case ClientState::CGI_Read: - logger.log(DEBUG, "client.getFD: %", client.getFD()); - logger.log(DEBUG, "poll_fd %", poll_fd.fd); - logger.log(DEBUG, "client.getCgiToServerFd()[READ_END] %", client.getCgiToServerFd()[READ_END]); - if (poll_fd.fd != client.getCgiToServerFd()[READ_END]) { - // _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); - _poll.setEvents(poll_fd.fd, POLLIN); - break; } + // case ClientState::CGI_Read: + // logger.log(DEBUG, "client.getFD: %", client.getFD()); + // logger.log(DEBUG, "poll_fd %", poll_fd.fd); + // logger.log(DEBUG, "client.getCgiToServerFd()[READ_END] %", client.getCgiToServerFd()[READ_END]); + // if (poll_fd.fd != client.getCgiToServerFd()[READ_END]) { + // // _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); + // _poll.setEvents(poll_fd.fd, POLLIN); + // break; } case ClientState::Loading: case ClientState::Sending: case ClientState::Error: From 3c627bea901240f1f2c08d519ce97bad4bfb1410 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 6 Dec 2023 17:02:07 +0100 Subject: [PATCH 23/33] now at CGI::write --- data/www/test.py | 22 +++++++++++++++++++--- include/CGI.hpp | 3 ++- include/Client.hpp | 1 + src/CGI.cpp | 33 ++++++++++++++++----------------- src/Client.cpp | 15 +++++++++++---- src/HTTPRequest.cpp | 1 + tests/request/get_cgi.txt | 5 +++-- tests/request/post_cgi.txt | 8 ++++++++ 8 files changed, 61 insertions(+), 27 deletions(-) create mode 100755 tests/request/post_cgi.txt diff --git a/data/www/test.py b/data/www/test.py index 9613e0b..5310ca5 100755 --- a/data/www/test.py +++ b/data/www/test.py @@ -1,12 +1,28 @@ import sys +import select def main(): - + input_data = "" # Initialize the input variable + + # Create a list containing the stdin file descriptor + rlist = [sys.stdin] + + # Wait until there is data available on the stdin + ready, _, _ = select.select(rlist, [], []) + + if sys.stdin in ready: + # Read from stdin + for line in sys.stdin: + input_data += line + print('Error message 1', file=sys.stderr) - print("Hello World!", file=sys.stdout) + sys.stdout.write("Hello world!") + + if input_data.strip(): # Check if input is not an empty string + sys.stdout.write(input_data) print('Error message 2', file=sys.stderr) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/include/CGI.hpp b/include/CGI.hpp index ef37a7c..a21ac43 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -19,7 +19,7 @@ class CGI { private: pid_t _pid; - bool _bodyIsSent; + // bool _bodyIsSent; size_t _bodyBytesWritten; std::string _executable; @@ -37,6 +37,7 @@ class CGI bool isExecutable(const std::string& filePath); const std::string& getExecutable(void) const; + void setExecutable(std::string executable); const pid_t& getPid(void) const; ClientState send(Client &client ,std::string body, size_t bodyLength); diff --git a/include/Client.hpp b/include/Client.hpp index 28654fd..52a70af 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -24,6 +24,7 @@ class Client int *getCgiToServerFd(void); int *getServerToCgiFd(void); + bool cgiBodyIsSent; bool cgiHasBeenRead; private: diff --git a/src/CGI.cpp b/src/CGI.cpp index c96e2ce..2ff45ef 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -19,7 +19,7 @@ // - create python program // * check out the wiki and github that Sander has sent -CGI::CGI() : _bodyIsSent(false), _bodyBytesWritten(0) {} +CGI::CGI() : _bodyBytesWritten(0) {} CGI::~CGI() {} @@ -27,6 +27,10 @@ const std::string &CGI::getExecutable(void) const { return (_executable); } +void CGI::setExecutable(std::string executable) { + _executable = executable; +} + const pid_t &CGI::getPid(void) const { return (_pid); } @@ -37,13 +41,14 @@ ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) ssize_t bytesWritten = 0; logger.log(INFO, "GCI::send is called"); - if (!_bodyIsSent) + if (!client.cgiBodyIsSent) bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), BUFFER_SIZE); if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); _bodyBytesWritten += bytesWritten; if (_bodyBytesWritten == bodyLength) { + client.cgiBodyIsSent = true; close(client.getServerToCgiFd()[WRITE_END]); return (ClientState::CGI_Read); } @@ -101,14 +106,6 @@ void CGI::execute(std::string executable, char **env) logger.log(ERROR, "CGI::execute is called"); logger.log(ERROR, "Executable: %", executable); - // if (!fileExists(std::string(executable))) { - // logger.log(ERROR, "Path to external program does not exist"); - // _exit(127); - // } - // if (!isExecutable(std::string(executable))) { - // logger.log(ERROR, "External program is not executable"); - // _exit(127); - // } std::string executableWithPath = "data/www" + executable; logger.log(ERROR, "executableWithPath: " + executableWithPath); @@ -130,15 +127,9 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, logger.log(DEBUG, "CGI::start called"); logger.log(DEBUG, "Executable: %", _executable); - if (pipe(client.getServerToCgiFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); if (pipe(client.getCgiToServerFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); - // if (fcntl(client.getServerToCgiFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(client.getServerToCgiFd()[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(client.getCgiToServerFd()[READ_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - // if (fcntl(client.getCgiToServerFd()[WRITE_END], F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) { perror("fcntl"); throw SystemException("fcntl");} - _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); if (_pid == 0) @@ -158,8 +149,16 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); logger.log(DEBUG, "CGI::start after closing"); - if (bodyLength != 0) + logger.log(DEBUG, "bodyLength: %", bodyLength); + logger.log(DEBUG, "cgiBodyIsSent: %", client.cgiBodyIsSent); + if (bodyLength != 0 && !client.cgiBodyIsSent) + { + if (dup2(client.getServerToCgiFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + std::shared_ptr pipe = std::make_shared(client.getServerToCgiFd()[WRITE_END]); + active_pipes.emplace(client.getServerToCgiFd()[WRITE_END], pipe); + poll.addPollFD(client.getServerToCgiFd()[WRITE_END], POLLOUT); return (ClientState::CGI_Write); + } logger.log(DEBUG, "GCI::start after writing"); if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); diff --git a/src/Client.cpp b/src/Client.cpp index abb9c91..1a519c4 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -12,6 +12,7 @@ Client::Client(const int &server_fd) : _socket(server_fd) { _socket.setupClient(); _state = ClientState::Receiving; + cgiBodyIsSent = false; cgiHasBeenRead = false; } @@ -47,7 +48,13 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, logger.log(ERROR, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); logger.log(DEBUG, "_request_target: " + _request.getRequestTarget()); + logger.log(DEBUG, "_request.getBodyLength(): %", _request.getBodyLength()); + logger.log(DEBUG, "_request.getBody(): %", _request.getBody()); _request.setCGIToTrue(); + if (_request.getMethodType() == HTTPMethod::POST) { + _cgi.setExecutable(_request.getRequestTarget()); + _state = ClientState::CGI_Start; + } return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) @@ -69,7 +76,6 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, if (client.cgiHasBeenRead == true) { _state = _file_manager.manageCgi(_request.getHTTPVersion(), _cgi.body); logger.log(DEBUG, "response:\n\n" + _file_manager.getResponse()); - } // logger.log(DEBUG, "cgiBody: " + _cgi.body); @@ -82,8 +88,8 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Loading) { - logger.log(DEBUG, "ClientState::Loading"); - if (_state == ClientState::Loading && _request.CGITrue() == true) { + logger.log(ERROR, "ClientState::Loading"); + if (_request.CGITrue() == true) { _state = _cgi.parseURIForCGI(_request.getRequestTarget()); logger.log(DEBUG, "executable: " + _cgi.getExecutable()); return (_state); @@ -114,11 +120,12 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Sending) { - logger.log(DEBUG, "ClientState::Sending"); + logger.log(ERROR, "ClientState::Sending"); // if CGI // _state = // _response.send(_socket.getFD(), _cgi.getResponse()); // else + logger.log(DEBUG, "Response: " + _file_manager.getResponse()); _state = _response.send(_socket.getFD(), _file_manager.getResponse()); logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 695a7d7..cfea361 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -134,6 +134,7 @@ ClientState HTTPRequest::receive(int client_fd) _bytes_read = read(client_fd, buffer, BUFFER_SIZE); if (_bytes_read == SYSTEM_ERROR) throw SystemException("Read failed on: " + std::to_string(client_fd)); + logger.log(DEBUG, "in receive _bytes_read is: %", _bytes_read); if (_content_length != 0) { _body += std::string(buffer, _bytes_read); diff --git a/tests/request/get_cgi.txt b/tests/request/get_cgi.txt index 0bb316d..2a82e2e 100755 --- a/tests/request/get_cgi.txt +++ b/tests/request/get_cgi.txt @@ -1,6 +1,7 @@ -GET /test.py HTTP/1.1 +GET /test.py/with/additional/path?and=a&query=string HTTP/1.1 User-Agent: custom-client -Host: weebserv +Host: webserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 Connection: Keep-Alive +/with/additional/path?and=a&query=string diff --git a/tests/request/post_cgi.txt b/tests/request/post_cgi.txt new file mode 100755 index 0000000..255903c --- /dev/null +++ b/tests/request/post_cgi.txt @@ -0,0 +1,8 @@ +POST /test.py HTTP/1.1 +User-Agent: custom-client +Host: webserv +Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 +Connection: Keep-Alive +Content-length: 42 + +/with/additional/path?and=a&query=string From 6f68d8d32e53213498fc4993335b2d09c777f998 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 11:53:17 +0100 Subject: [PATCH 24/33] small adjustments --- data/www/test.py | 21 +++++++++++++-------- include/Client.hpp | 1 + include/HTTPRequest.hpp | 6 +++--- src/CGI.cpp | 10 +++++++--- src/Client.cpp | 8 ++++---- src/HTTPServer.cpp | 9 ++++++++- tests/get_request.sh | 2 +- 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/data/www/test.py b/data/www/test.py index 5310ca5..44372a5 100755 --- a/data/www/test.py +++ b/data/www/test.py @@ -2,25 +2,30 @@ import select def main(): - input_data = "" # Initialize the input variable + input_data = "" + newline = False - # Create a list containing the stdin file descriptor rlist = [sys.stdin] - - # Wait until there is data available on the stdin ready, _, _ = select.select(rlist, [], []) if sys.stdin in ready: - # Read from stdin for line in sys.stdin: + if newline == True: + break + for char in line: + if char == '\n': + newline = True + break input_data += line + print('Error message 1', file=sys.stderr) - sys.stdout.write("Hello world!") - if input_data.strip(): # Check if input is not an empty string - sys.stdout.write(input_data) + # if input_data.strip(): + # sys.stdout.write(input_data) + if input_data.strip(): + sys.stderr.write(input_data) print('Error message 2', file=sys.stderr) diff --git a/include/Client.hpp b/include/Client.hpp index 52a70af..456885a 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -23,6 +23,7 @@ class Client int getFD(void) const; int *getCgiToServerFd(void); int *getServerToCgiFd(void); + HTTPRequest &getRequest(void); bool cgiBodyIsSent; bool cgiHasBeenRead; diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index c10fe46..51e9f0d 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -53,9 +53,9 @@ class HTTPRequest const size_t &getBodyLength(void) const; private: - ssize_t _bytes_read; - size_t _content_length; - HTTPMethod _methodType; + ssize_t _bytes_read; + size_t _content_length; + HTTPMethod _methodType; std::string _http_request; std::string _request_target; std::string _http_version; diff --git a/src/CGI.cpp b/src/CGI.cpp index 2ff45ef..0e400d8 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -37,6 +37,7 @@ const pid_t &CGI::getPid(void) const { ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) { + (void)bodyLength; Logger &logger = Logger::getInstance(); ssize_t bytesWritten = 0; @@ -46,9 +47,10 @@ ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); _bodyBytesWritten += bytesWritten; - if (_bodyBytesWritten == bodyLength) + if (_bodyBytesWritten < BUFFER_SIZE) { client.cgiBodyIsSent = true; + logger.log(DEBUG, "ServerToCgiFd()[WRITE_END] is now being closed"); close(client.getServerToCgiFd()[WRITE_END]); return (ClientState::CGI_Read); } @@ -153,16 +155,18 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, logger.log(DEBUG, "cgiBodyIsSent: %", client.cgiBodyIsSent); if (bodyLength != 0 && !client.cgiBodyIsSent) { - if (dup2(client.getServerToCgiFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + logger.log(DEBUG, "ClientState::CGI_Write is now being called"); + // if (dup2(client.getServerToCgiFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); std::shared_ptr pipe = std::make_shared(client.getServerToCgiFd()[WRITE_END]); active_pipes.emplace(client.getServerToCgiFd()[WRITE_END], pipe); poll.addPollFD(client.getServerToCgiFd()[WRITE_END], POLLOUT); + // poll.setEvents(client.getServerToCgiFd()[WRITE_END], POLLOUT); return (ClientState::CGI_Write); } logger.log(DEBUG, "GCI::start after writing"); if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - std::shared_ptr pipe = std::make_shared(client.getCgiToServerFd()[READ_END]); active_pipes.emplace(client.getCgiToServerFd()[READ_END], pipe); poll.addPollFD(client.getCgiToServerFd()[READ_END], POLLIN); diff --git a/src/Client.cpp b/src/Client.cpp index 1a519c4..12e72c7 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -33,6 +33,10 @@ int *Client::getServerToCgiFd(void) { return (_serverToCgiFd); } +HTTPRequest &Client::getRequest(void) { + return (_request); +} + // implement method Martijn for verifying that we are dealing with a CGI // also get fileExtension from Martijn and save in HTTPRequest class ClientState Client::handleConnection(short events, Poll &poll, Client &client, @@ -51,10 +55,6 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, logger.log(DEBUG, "_request.getBodyLength(): %", _request.getBodyLength()); logger.log(DEBUG, "_request.getBody(): %", _request.getBody()); _request.setCGIToTrue(); - if (_request.getMethodType() == HTTPMethod::POST) { - _cgi.setExecutable(_request.getRequestTarget()); - _state = ClientState::CGI_Start; - } return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 71ce24f..93a03d7 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -118,8 +118,13 @@ void HTTPServer::handleActivePollFDs() } else if (_active_pipes.find(poll_fd.fd) != _active_pipes.end()) { logger.log(DEBUG, "_active_pipes.find(poll_fd.fd) != _active_pipes.end()"); + logger.log(DEBUG, "poll_fd.fd: %", poll_fd.fd); Client &client = getClientByPipeFd(poll_fd.fd); logger.log(DEBUG, "Client % found on poll_fd.fd (pipe): %", &client, poll_fd.fd); + + // if (client.getRequest().getMethodType() == HTTPMethod::POST) + // handleExistingConnection(poll_fd, _poll, client, _active_pipes); + (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); if (client.cgiHasBeenRead) { @@ -179,10 +184,12 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Cli // // _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); // _poll.setEvents(poll_fd.fd, POLLIN); // break; } + case ClientState::CGI_Write: + // _poll.setEvents(client.getServerToCgiFd()[WRITE_END], POLLOUT); + // break; case ClientState::Loading: case ClientState::Sending: case ClientState::Error: - case ClientState::CGI_Write: case ClientState::CGI_Start: _poll.setEvents(poll_fd.fd, POLLOUT); break; diff --git a/tests/get_request.sh b/tests/get_request.sh index 242eb53..42c2e86 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/get_cgi.txt" +REQUEST_FILE="tests/request/post_cgi.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From 0b43658094040aba6fc5e50ac6440b93756d86ba Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 13:32:58 +0100 Subject: [PATCH 25/33] the cgi is f*cking working! --- data/www/test_get.py | 13 +++++++++++++ data/www/{test.py => test_post.py} | 6 +++--- src/CGI.cpp | 22 +++++++++++++--------- src/HTTPServer.cpp | 13 +++++++++---- tests/request/get_cgi.txt | 3 +-- tests/request/post_cgi.txt | 2 +- 6 files changed, 40 insertions(+), 19 deletions(-) create mode 100755 data/www/test_get.py rename data/www/{test.py => test_post.py} (85%) diff --git a/data/www/test_get.py b/data/www/test_get.py new file mode 100755 index 0000000..9a2aa42 --- /dev/null +++ b/data/www/test_get.py @@ -0,0 +1,13 @@ +import sys +import select + +def main(): + + print('Error message 1', file=sys.stderr) + + sys.stdout.write("Hello world!") + + print('Error message 2', file=sys.stderr) + +if __name__ == "__main__": + main() diff --git a/data/www/test.py b/data/www/test_post.py similarity index 85% rename from data/www/test.py rename to data/www/test_post.py index 44372a5..4411049 100755 --- a/data/www/test.py +++ b/data/www/test_post.py @@ -20,10 +20,10 @@ def main(): print('Error message 1', file=sys.stderr) - sys.stdout.write("Hello world!") + # sys.stdout.write("Hello world!") - # if input_data.strip(): - # sys.stdout.write(input_data) + if input_data.strip(): + sys.stdout.write(input_data) if input_data.strip(): sys.stderr.write(input_data) diff --git a/src/CGI.cpp b/src/CGI.cpp index 0e400d8..13031be 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -42,12 +42,17 @@ ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) ssize_t bytesWritten = 0; logger.log(INFO, "GCI::send is called"); + logger.log(DEBUG, "body: " + body); if (!client.cgiBodyIsSent) bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), BUFFER_SIZE); if (bytesWritten == SYSTEM_ERROR) throw SystemException("write to Python"); + logger.log(DEBUG, "bytesWritten: %", bytesWritten); + logger.log(DEBUG, "_bodyBytesWritten before adding bytesWritten: %", _bodyBytesWritten); _bodyBytesWritten += bytesWritten; - if (_bodyBytesWritten < BUFFER_SIZE) + logger.log(DEBUG, "_bodyBytesWritten after adding bytesWritten: %", _bodyBytesWritten); + logger.log(DEBUG, "bodyLength: %", bodyLength); + if (_bodyBytesWritten >= bodyLength) { client.cgiBodyIsSent = true; logger.log(DEBUG, "ServerToCgiFd()[WRITE_END] is now being closed"); @@ -153,6 +158,13 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, logger.log(DEBUG, "bodyLength: %", bodyLength); logger.log(DEBUG, "cgiBodyIsSent: %", client.cgiBodyIsSent); + + if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); + std::shared_ptr pipe = std::make_shared(client.getCgiToServerFd()[READ_END]); + active_pipes.emplace(client.getCgiToServerFd()[READ_END], pipe); + poll.addPollFD(client.getCgiToServerFd()[READ_END], POLLIN); + logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); + if (bodyLength != 0 && !client.cgiBodyIsSent) { logger.log(DEBUG, "ClientState::CGI_Write is now being called"); @@ -163,15 +175,7 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, // poll.setEvents(client.getServerToCgiFd()[WRITE_END], POLLOUT); return (ClientState::CGI_Write); } - logger.log(DEBUG, "GCI::start after writing"); if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - - if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - std::shared_ptr pipe = std::make_shared(client.getCgiToServerFd()[READ_END]); - active_pipes.emplace(client.getCgiToServerFd()[READ_END], pipe); - poll.addPollFD(client.getCgiToServerFd()[READ_END], POLLIN); - logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); - // if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); // int status; // waitpid() diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 93a03d7..41e7efd 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -29,7 +29,8 @@ Client &HTTPServer::findClientByFd(int targetFd) Client &HTTPServer::getClientByPipeFd(int pipe_fd) { for (const auto& entry : _active_clients) { const auto& client = entry.second; - if (client->getCgiToServerFd()[READ_END] == pipe_fd) { + if (client->getCgiToServerFd()[READ_END] == pipe_fd || + client->getServerToCgiFd()[WRITE_END] == pipe_fd) { return *client; } } @@ -122,10 +123,14 @@ void HTTPServer::handleActivePollFDs() Client &client = getClientByPipeFd(poll_fd.fd); logger.log(DEBUG, "Client % found on poll_fd.fd (pipe): %", &client, poll_fd.fd); - // if (client.getRequest().getMethodType() == HTTPMethod::POST) - // handleExistingConnection(poll_fd, _poll, client, _active_pipes); - (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); + if (client.cgiBodyIsSent) + { + logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); + _poll.removeFD(poll_fd.fd); + _active_pipes.erase(poll_fd.fd); + _poll.setEvents(client.getFD(), POLLIN); + } if (client.cgiHasBeenRead) { logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); diff --git a/tests/request/get_cgi.txt b/tests/request/get_cgi.txt index 2a82e2e..d08a2b0 100755 --- a/tests/request/get_cgi.txt +++ b/tests/request/get_cgi.txt @@ -1,7 +1,6 @@ -GET /test.py/with/additional/path?and=a&query=string HTTP/1.1 +GET /test_get.py/with/additional/path?and=a&query=string HTTP/1.1 User-Agent: custom-client Host: webserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 Connection: Keep-Alive -/with/additional/path?and=a&query=string diff --git a/tests/request/post_cgi.txt b/tests/request/post_cgi.txt index 255903c..768d567 100755 --- a/tests/request/post_cgi.txt +++ b/tests/request/post_cgi.txt @@ -1,4 +1,4 @@ -POST /test.py HTTP/1.1 +POST /test_post.py HTTP/1.1 User-Agent: custom-client Host: webserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 From cad25023fed5201a3aa4d03e1407664a714f63aa Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 15:12:52 +0100 Subject: [PATCH 26/33] a lot of cleaning up in CGI.cpp --- data/www/test_get.py | 4 +- data/www/test_post.py | 12 ++--- include/CGI.hpp | 5 +- src/CGI.cpp | 111 +++++++++--------------------------------- src/Client.cpp | 7 --- 5 files changed, 34 insertions(+), 105 deletions(-) diff --git a/data/www/test_get.py b/data/www/test_get.py index 9a2aa42..4371cd8 100755 --- a/data/www/test_get.py +++ b/data/www/test_get.py @@ -3,11 +3,11 @@ def main(): - print('Error message 1', file=sys.stderr) + print('From cgi: before writing \"Hello world!\"', file=sys.stderr) sys.stdout.write("Hello world!") - print('Error message 2', file=sys.stderr) + print('From cgi: after writing \"Hello world!\"', file=sys.stderr) if __name__ == "__main__": main() diff --git a/data/www/test_post.py b/data/www/test_post.py index 4411049..706df83 100755 --- a/data/www/test_post.py +++ b/data/www/test_post.py @@ -5,9 +5,13 @@ def main(): input_data = "" newline = False + print('From cgi: waiting on the servers message', file=sys.stderr) + rlist = [sys.stdin] ready, _, _ = select.select(rlist, [], []) + print('From cgi: processing the servers message', file=sys.stderr) + if sys.stdin in ready: for line in sys.stdin: if newline == True: @@ -17,17 +21,13 @@ def main(): newline = True break input_data += line - - print('Error message 1', file=sys.stderr) - # sys.stdout.write("Hello world!") + print('From cgi: before writing to the server', file=sys.stderr) if input_data.strip(): sys.stdout.write(input_data) - if input_data.strip(): - sys.stderr.write(input_data) - print('Error message 2', file=sys.stderr) + print('From cgi: after writing to the server', file=sys.stderr) if __name__ == "__main__": main() diff --git a/include/CGI.hpp b/include/CGI.hpp index a21ac43..4f87c9e 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -22,6 +22,8 @@ class CGI // bool _bodyIsSent; size_t _bodyBytesWritten; std::string _executable; + std::string _pathInfo; + std::string _queryString; public: CGI(); @@ -32,7 +34,7 @@ class CGI ClientState start(Poll &poll, Client &client, size_t body_length, std::unordered_map> &active_pipes); ClientState parseURIForCGI(std::string requestTarget); - void execute(std::string executable, char **env); + void execute(std::string executable); bool fileExists(const std::string& filePath); bool isExecutable(const std::string& filePath); @@ -45,7 +47,6 @@ class CGI std::string body; int pipe_fd[2]; - char **_env; }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index 13031be..b59b20b 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -11,15 +11,10 @@ #include #include -// TO DO -// - get execute functional -// - make sure that the test.py is closing the STDIN if that is not already been done -// - insert in handleConnection CGI_WRITE and CGI_READ -// - create some kind of a CGI_LOAD that serves as replacement for the file manager -// - create python program -// * check out the wiki and github that Sander has sent - -CGI::CGI() : _bodyBytesWritten(0) {} +CGI::CGI() : _bodyBytesWritten(0) { + _pathInfo = ""; + _queryString = ""; +} CGI::~CGI() {} @@ -37,7 +32,6 @@ const pid_t &CGI::getPid(void) const { ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) { - (void)bodyLength; Logger &logger = Logger::getInstance(); ssize_t bytesWritten = 0; @@ -46,12 +40,9 @@ ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) if (!client.cgiBodyIsSent) bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), BUFFER_SIZE); if (bytesWritten == SYSTEM_ERROR) - throw SystemException("write to Python"); + throw SystemException("write to cgi"); logger.log(DEBUG, "bytesWritten: %", bytesWritten); - logger.log(DEBUG, "_bodyBytesWritten before adding bytesWritten: %", _bodyBytesWritten); _bodyBytesWritten += bytesWritten; - logger.log(DEBUG, "_bodyBytesWritten after adding bytesWritten: %", _bodyBytesWritten); - logger.log(DEBUG, "bodyLength: %", bodyLength); if (_bodyBytesWritten >= bodyLength) { client.cgiBodyIsSent = true; @@ -71,9 +62,11 @@ ClientState CGI::receive(Client &client) bzero(buffer, sizeof(buffer)); logger.log(INFO, "CGI::receive is called"); + int status; + waitpid(_pid, &status, 0); + if (status == SYSTEM_ERROR) throw SystemException("waitpid"); bytesRead = read(client.getCgiToServerFd()[READ_END], buffer, sizeof(buffer)); - - if (bytesRead == SYSTEM_ERROR) throw SystemException("Read"); + if (bytesRead == SYSTEM_ERROR) throw SystemException("read"); logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); logger.log(DEBUG, "buffer:\n" + std::string(buffer)); body += buffer; @@ -83,10 +76,6 @@ ClientState CGI::receive(Client &client) client.cgiHasBeenRead = true; logger.log(DEBUG, "body in GCI::receive:\n" + body); close(client.getCgiToServerFd()[READ_END]); - // int status; - // waitpid(_pid, &status, 0); - // if (WEXITSTATUS(status) == -1) - // return (ClientState::Error); return (ClientState::Sending); } return (ClientState::CGI_Read); @@ -104,25 +93,21 @@ bool CGI::isExecutable(const std::string& filePath) std::filesystem::perms::owner_exec) != std::filesystem::perms::none; } -void CGI::execute(std::string executable, char **env) +void CGI::execute(std::string executable) { - (void)env; + const char *env[] = {_pathInfo.c_str(), _queryString.c_str(), NULL}; Logger &logger = Logger::getInstance(); std::string bin = "python3"; - + if (!fileExists("data/www" + executable) || !isExecutable("data/www" + executable)) + throw SystemException("File does not exist or is not executable."); logger.log(ERROR, "CGI::execute is called"); logger.log(ERROR, "Executable: %", executable); - std::string executableWithPath = "data/www" + executable; logger.log(ERROR, "executableWithPath: " + executableWithPath); - - // const char *const argv[] = {bin.c_str(), executable.c_str(), NULL}; const char *const argv[] = {bin.c_str(), executableWithPath.c_str(), NULL}; - // const char *path = "/data/www"; const char *path = "/usr/bin/python3"; - // execve(path, (char *const *)argv, env); - if (execve(path, (char *const *)argv, NULL) == SYSTEM_ERROR) throw SystemException("Execve"); + if (execve(path, (char *const *)argv, (char *const*) env) == SYSTEM_ERROR) throw SystemException("Execve"); } ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, @@ -133,118 +118,68 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, logger.log(DEBUG, "CGI::start called"); logger.log(DEBUG, "Executable: %", _executable); - if (pipe(client.getServerToCgiFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); if (pipe(client.getCgiToServerFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); - _pid = fork(); if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); if (_pid == 0) { - logger.log(ERROR, "_pid == 0"); if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(client.getServerToCgiFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); if (dup2(client.getCgiToServerFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - - execute(_executable, _env); + + execute(_executable); } logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); logger.log(DEBUG, "CGI::start after closing"); - logger.log(DEBUG, "bodyLength: %", bodyLength); logger.log(DEBUG, "cgiBodyIsSent: %", client.cgiBodyIsSent); - - if (dup2(client.getCgiToServerFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); std::shared_ptr pipe = std::make_shared(client.getCgiToServerFd()[READ_END]); active_pipes.emplace(client.getCgiToServerFd()[READ_END], pipe); poll.addPollFD(client.getCgiToServerFd()[READ_END], POLLIN); logger.log(DEBUG, "CGI::start after closing WRITE_END and start reading"); - if (bodyLength != 0 && !client.cgiBodyIsSent) { logger.log(DEBUG, "ClientState::CGI_Write is now being called"); - // if (dup2(client.getServerToCgiFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); std::shared_ptr pipe = std::make_shared(client.getServerToCgiFd()[WRITE_END]); active_pipes.emplace(client.getServerToCgiFd()[WRITE_END], pipe); poll.addPollFD(client.getServerToCgiFd()[WRITE_END], POLLOUT); - // poll.setEvents(client.getServerToCgiFd()[WRITE_END], POLLOUT); return (ClientState::CGI_Write); } if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - // if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - // int status; - // waitpid() - // return (receive()); return (ClientState::CGI_Read); - // return (ClientState::Done); } -// !! need to free _env and it's arguments somewhere !! ClientState CGI::parseURIForCGI(std::string requestTarget) { Logger &logger = Logger::getInstance(); logger.log(DEBUG, "parseURIForCGI is called"); logger.log(DEBUG, "requestTarget: %", requestTarget); - // return (ClientState::Loading); - - std::string filenameExtension = ".py"; // or something like: "data/www/python/test.py" to specify it better. OR give it as input. Discuss with the team! + std::string filenameExtension = ".py"; size_t lengthFilenameExtension = std::strlen(filenameExtension.c_str()); size_t filenameExtensionPos = requestTarget.find(filenameExtension); + logger.log(DEBUG, "Length of filenameExtension: %", lengthFilenameExtension); if (filenameExtensionPos == std::string::npos) return (ClientState::Error); - // return (ClientState::Done); - _executable = requestTarget.substr(0, filenameExtensionPos + lengthFilenameExtension); bool skip = false; - size_t env_num = 1; - size_t i = 0; - logger.log(DEBUG, "Executable is: " + _executable); if (filenameExtensionPos + lengthFilenameExtension >= std::strlen(requestTarget.c_str()) - 1) { - // logger.log(DEBUG, "filenameExtensionPos + lengthFilenameExtension >= std::strlen(requestTarget.c_str()) - 1"); return (ClientState::CGI_Start); - // return (ClientState::Done); } - - logger.log(DEBUG, "filenameExtensionPos + lengthfilenameExtension: %", filenameExtensionPos + lengthFilenameExtension); - std::string remaining = requestTarget.substr(filenameExtensionPos + lengthFilenameExtension, std::string::npos); size_t questionMarkPos = remaining.find('?'); - if (remaining.at(0) == '/') - { - env_num++; - if (questionMarkPos != std::string::npos) - env_num++; - } - else if (remaining.at(0) == '?') - env_num++; - _env = new char *[env_num]; if (remaining.at(0) == '/' && !skip) - { - std::string pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); - // vector string instead of new char shit - _env[i] = new char[pathInfo.length() + 1]; - std::strcpy(_env[i], pathInfo.c_str()); - if (env_num == 2) - { _env[i + 1] = nullptr; skip = true;} - i++; - } - if (!skip) { - std::string queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); - _env[i] = new char[queryString.length() + 1]; - std::strcpy(_env[i], queryString.c_str()); - _env[i + 1] = nullptr; } - - for (size_t i = 0; _env[i]; i++) { - logger.log(DEBUG, _env[i]); - } - logger.log(INFO, "Do we reach the end of parseURIForCGI"); + _pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); + if (!skip) + _queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); + logger.log(DEBUG, _pathInfo); + logger.log(DEBUG, _queryString); return (ClientState::CGI_Start); - // return (ClientState::CGI_Start); } \ No newline at end of file diff --git a/src/Client.cpp b/src/Client.cpp index 12e72c7..4170a64 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -77,13 +77,6 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, _state = _file_manager.manageCgi(_request.getHTTPVersion(), _cgi.body); logger.log(DEBUG, "response:\n\n" + _file_manager.getResponse()); } - - // logger.log(DEBUG, "cgiBody: " + _cgi.body); - - // _state = ClientState::Sending; - // int status; - // waitpid(_cgi.getPid(), &status, 0); - // WEXITSTATUS(status); return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) From 1e1a3e231692a5b49c27ab1956b95e9e3d8d4624 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 15:21:00 +0100 Subject: [PATCH 27/33] did some cleaning up for the client.cpp --- include/CGI.hpp | 3 --- src/Client.cpp | 36 ++++++------------------------------ 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 4f87c9e..cba2816 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -13,13 +13,10 @@ class Client; class Poll; -// Common gateway interface - class CGI { private: pid_t _pid; - // bool _bodyIsSent; size_t _bodyBytesWritten; std::string _executable; std::string _pathInfo; diff --git a/src/Client.cpp b/src/Client.cpp index 4170a64..456dc1c 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -37,8 +37,6 @@ HTTPRequest &Client::getRequest(void) { return (_request); } -// implement method Martijn for verifying that we are dealing with a CGI -// also get fileExtension from Martijn and save in HTTPRequest class ClientState Client::handleConnection(short events, Poll &poll, Client &client, std::unordered_map> &active_pipes) { @@ -49,29 +47,26 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { if (events & POLLIN && _state == ClientState::Receiving) { - logger.log(ERROR, "ClientState::Receiving"); + logger.log(INFO, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); - logger.log(DEBUG, "_request_target: " + _request.getRequestTarget()); - logger.log(DEBUG, "_request.getBodyLength(): %", _request.getBodyLength()); - logger.log(DEBUG, "_request.getBody(): %", _request.getBody()); _request.setCGIToTrue(); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) { - logger.log(ERROR, "ClientState::CGI_Start"); + logger.log(INFO, "ClientState::CGI_Start"); _state = _cgi.start(poll, client, _request.getBodyLength(), active_pipes); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) { - logger.log(ERROR, "ClientState::CGI_Write"); + logger.log(INFO, "ClientState::CGI_Write"); _state = _cgi.send(client, _request.getBody(), _request.getBodyLength()); return (_state); } else if (events & POLLIN && _state == ClientState::CGI_Read) { - logger.log(ERROR, "ClientState::CGI_Read"); + logger.log(INFO, "ClientState::CGI_Read"); _state = _cgi.receive(client); if (client.cgiHasBeenRead == true) { _state = _file_manager.manageCgi(_request.getHTTPVersion(), _cgi.body); @@ -81,31 +76,18 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Loading) { - logger.log(ERROR, "ClientState::Loading"); + logger.log(INFO, "ClientState::Loading"); if (_request.CGITrue() == true) { _state = _cgi.parseURIForCGI(_request.getRequestTarget()); logger.log(DEBUG, "executable: " + _cgi.getExecutable()); return (_state); } - // if (_request._cgi == true) - // _state = _cgi.createResponse(); - // else - // logger.log(INFO, "request target from loading else if: %", _request.getRequestTarget()); _state = _file_manager.manage( _request.getMethodType(), "./data/www" + _request.getRequestTarget(), _request.getBody()); // TODO: resolve location return (_state); } - // else if (events & POLLOUT && _state == ClientState::CGI_Load) - // { - // logger.log(DEBUG, "ClientState::CGI_Load"); - // _state = _file_manager.manage( - // _request.getMethodType(), - // "./data/www" + _request.getRequestTarget(), - // _cgi.body); - // return (_state); - // } else if (events & POLLOUT && _state == ClientState::Error) { _state = _file_manager.loadErrorPage(); @@ -113,15 +95,9 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Sending) { - logger.log(ERROR, "ClientState::Sending"); - // if CGI - // _state = - // _response.send(_socket.getFD(), _cgi.getResponse()); - // else - logger.log(DEBUG, "Response: " + _file_manager.getResponse()); + logger.log(INFO, "ClientState::Sending"); _state = _response.send(_socket.getFD(), _file_manager.getResponse()); - logger.log(DEBUG, "request target from handleConnection: %", _request.getRequestTarget()); return (_state); } } From d325322e997d428fdaffe115049a312e1ddd0176 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 15:52:10 +0100 Subject: [PATCH 28/33] did some cleaning up on HTTPServer.cpp --- include/ClientState.hpp | 1 - include/HTTPServer.hpp | 3 +- src/Client.cpp | 12 ++++---- src/HTTPServer.cpp | 67 ++++++++++++++--------------------------- 4 files changed, 31 insertions(+), 52 deletions(-) diff --git a/include/ClientState.hpp b/include/ClientState.hpp index a432d34..5f4bbc5 100644 --- a/include/ClientState.hpp +++ b/include/ClientState.hpp @@ -7,7 +7,6 @@ enum class ClientState CGI_Start, CGI_Write, CGI_Read, - CGI_Load, Loading, Sending, Done, diff --git a/include/HTTPServer.hpp b/include/HTTPServer.hpp index 7e690a9..111ca4c 100644 --- a/include/HTTPServer.hpp +++ b/include/HTTPServer.hpp @@ -34,7 +34,8 @@ class HTTPServer std::unordered_map> &active_pipes); Client &findClientByFd(int targetFd); Client &getClientByPipeFd(int pipe_fd); - // void handleNewPipe(int fd, int pipeState); + void handlePipeConnection(const pollfd &poll_fd, Poll &poll, Client &client, + std::unordered_map> &active_pipes); }; #endif diff --git a/src/Client.cpp b/src/Client.cpp index 456dc1c..5090a84 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -47,26 +47,26 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { if (events & POLLIN && _state == ClientState::Receiving) { - logger.log(INFO, "ClientState::Receiving"); + logger.log(DEBUG, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); _request.setCGIToTrue(); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) { - logger.log(INFO, "ClientState::CGI_Start"); + logger.log(DEBUG, "ClientState::CGI_Start"); _state = _cgi.start(poll, client, _request.getBodyLength(), active_pipes); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Write) { - logger.log(INFO, "ClientState::CGI_Write"); + logger.log(DEBUG, "ClientState::CGI_Write"); _state = _cgi.send(client, _request.getBody(), _request.getBodyLength()); return (_state); } else if (events & POLLIN && _state == ClientState::CGI_Read) { - logger.log(INFO, "ClientState::CGI_Read"); + logger.log(DEBUG, "ClientState::CGI_Read"); _state = _cgi.receive(client); if (client.cgiHasBeenRead == true) { _state = _file_manager.manageCgi(_request.getHTTPVersion(), _cgi.body); @@ -76,7 +76,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Loading) { - logger.log(INFO, "ClientState::Loading"); + logger.log(DEBUG, "ClientState::Loading"); if (_request.CGITrue() == true) { _state = _cgi.parseURIForCGI(_request.getRequestTarget()); logger.log(DEBUG, "executable: " + _cgi.getExecutable()); @@ -95,7 +95,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, } else if (events & POLLOUT && _state == ClientState::Sending) { - logger.log(INFO, "ClientState::Sending"); + logger.log(DEBUG, "ClientState::Sending"); _state = _response.send(_socket.getFD(), _file_manager.getResponse()); return (_state); diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 41e7efd..b57d22c 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -93,7 +93,6 @@ void HTTPServer::handleActivePollFDs() _poll.pollFDs(); for (const auto &poll_fd : _poll.getPollFDs()) { - logger.log(DEBUG, "HTTPServer::handleActivePollFDs -- in for loop"); if (poll_fd.revents == 0) continue; try @@ -113,31 +112,12 @@ void HTTPServer::handleActivePollFDs() if (_active_servers.find(poll_fd.fd) != _active_servers.end()) handleNewConnection(poll_fd.fd); else if (_active_clients.find(poll_fd.fd) != _active_clients.end()) { - logger.log(DEBUG, "_active_clients.find(poll_fd.fd) != _active_clients.end()"); Client &client = findClientByFd(poll_fd.fd); handleExistingConnection(poll_fd, _poll, client, _active_pipes); } else if (_active_pipes.find(poll_fd.fd) != _active_pipes.end()) { - logger.log(DEBUG, "_active_pipes.find(poll_fd.fd) != _active_pipes.end()"); - logger.log(DEBUG, "poll_fd.fd: %", poll_fd.fd); Client &client = getClientByPipeFd(poll_fd.fd); - logger.log(DEBUG, "Client % found on poll_fd.fd (pipe): %", &client, poll_fd.fd); - - (&client)->handleConnection(poll_fd.events, _poll, client, _active_pipes); - if (client.cgiBodyIsSent) - { - logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); - _poll.removeFD(poll_fd.fd); - _active_pipes.erase(poll_fd.fd); - _poll.setEvents(client.getFD(), POLLIN); - } - if (client.cgiHasBeenRead) - { - logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); - _poll.removeFD(poll_fd.fd); - _active_pipes.erase(poll_fd.fd); - _poll.setEvents(client.getFD(), POLLOUT); - } + handlePipeConnection(poll_fd, _poll, client, _active_pipes); } else throw std::runtime_error("Unknown file descriptor"); @@ -145,15 +125,28 @@ void HTTPServer::handleActivePollFDs() logger.log(DEBUG, "HTTPServer::handleActivePollFDs -- after for loop"); } -// void HTTPServer::handleNewPipe(int fd, int pipeState) -// { -// Logger &logger = Logger::getInstance(); -// logger.log(DEBUG, "HTTPServer::handleNewPipe"); +void HTTPServer::handlePipeConnection(const pollfd &poll_fd, Poll &poll, Client &client, + std::unordered_map> &active_pipes) +{ + Logger &logger = Logger::getInstance(); + logger.log(DEBUG, "Client % found on poll_fd.fd (pipe): %", &client, poll_fd.fd); -// std::shared_ptr pipe = std::make_shared(fd); -// _active_pipes.emplace(fd, pipe); -// _poll.addPollFD(fd, pipeState); -// } + (&client)->handleConnection(poll_fd.events, poll, client, active_pipes); + if (client.cgiBodyIsSent) + { + logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); + _poll.removeFD(poll_fd.fd); + active_pipes.erase(poll_fd.fd); + _poll.setEvents(client.getFD(), POLLIN); + } + if (client.cgiHasBeenRead) + { + logger.log(DEBUG, "remove pipe fd: %", poll_fd.fd); + _poll.removeFD(poll_fd.fd); + active_pipes.erase(poll_fd.fd); + _poll.setEvents(client.getFD(), POLLOUT); + } +} void HTTPServer::handleNewConnection(int fd) { @@ -171,31 +164,17 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Cli Logger &logger = Logger::getInstance(); logger.log(DEBUG, "HTTPServer::handleExistingConnection"); - // logger.log(DEBUG, "_active_clients.at(poll_fd.fd): %", _active_clients.at(poll_fd.fd)); - logger.log(DEBUG, "client: %", &client); - - switch ((&client)->handleConnection(poll_fd.events, poll, client, active_pipes)) { case ClientState::Receiving: case ClientState::CGI_Read: _poll.setEvents(poll_fd.fd, POLLIN); break; - // case ClientState::CGI_Read: - // logger.log(DEBUG, "client.getFD: %", client.getFD()); - // logger.log(DEBUG, "poll_fd %", poll_fd.fd); - // logger.log(DEBUG, "client.getCgiToServerFd()[READ_END] %", client.getCgiToServerFd()[READ_END]); - // if (poll_fd.fd != client.getCgiToServerFd()[READ_END]) { - // // _poll.setEvents(client.getCgiToServerFd()[READ_END], POLLIN); - // _poll.setEvents(poll_fd.fd, POLLIN); - // break; } - case ClientState::CGI_Write: - // _poll.setEvents(client.getServerToCgiFd()[WRITE_END], POLLOUT); - // break; case ClientState::Loading: case ClientState::Sending: case ClientState::Error: case ClientState::CGI_Start: + case ClientState::CGI_Write: _poll.setEvents(poll_fd.fd, POLLOUT); break; case ClientState::Done: From 516444968a48ea88aed2858b07d8bffc23300a4e Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Thu, 7 Dec 2023 17:04:59 +0100 Subject: [PATCH 29/33] some additions and cleaning up --- data/www/test_get.py | 13 ------------- data/www/test_post.py | 24 ++++-------------------- include/CGI.hpp | 1 + src/CGI.cpp | 10 +++++++--- src/Client.cpp | 6 ++++-- src/FileManager.cpp | 2 +- tests/request/get_cgi.txt | 2 +- tests/request/post_cgi.txt | 2 +- 8 files changed, 19 insertions(+), 41 deletions(-) delete mode 100755 data/www/test_get.py diff --git a/data/www/test_get.py b/data/www/test_get.py deleted file mode 100755 index 4371cd8..0000000 --- a/data/www/test_get.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys -import select - -def main(): - - print('From cgi: before writing \"Hello world!\"', file=sys.stderr) - - sys.stdout.write("Hello world!") - - print('From cgi: after writing \"Hello world!\"', file=sys.stderr) - -if __name__ == "__main__": - main() diff --git a/data/www/test_post.py b/data/www/test_post.py index 706df83..6071987 100755 --- a/data/www/test_post.py +++ b/data/www/test_post.py @@ -2,30 +2,14 @@ import select def main(): - input_data = "" - newline = False - print('From cgi: waiting on the servers message', file=sys.stderr) - - rlist = [sys.stdin] - ready, _, _ = select.select(rlist, [], []) - print('From cgi: processing the servers message', file=sys.stderr) - - if sys.stdin in ready: - for line in sys.stdin: - if newline == True: - break - for char in line: - if char == '\n': - newline = True - break - input_data += line + print('From cgi: waiting on the servers message', file=sys.stderr) - print('From cgi: before writing to the server', file=sys.stderr) + print(sys.stdin.readlines()) - if input_data.strip(): - sys.stdout.write(input_data) + # if input_data.strip(): + # sys.stdout.write(input_data) print('From cgi: after writing to the server', file=sys.stderr) diff --git a/include/CGI.hpp b/include/CGI.hpp index cba2816..1e7de06 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -20,6 +20,7 @@ class CGI size_t _bodyBytesWritten; std::string _executable; std::string _pathInfo; + std::string _subPathInfo; std::string _queryString; public: diff --git a/src/CGI.cpp b/src/CGI.cpp index b59b20b..4ae7739 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -13,6 +13,7 @@ CGI::CGI() : _bodyBytesWritten(0) { _pathInfo = ""; + _subPathInfo = ""; _queryString = ""; } @@ -95,6 +96,7 @@ bool CGI::isExecutable(const std::string& filePath) void CGI::execute(std::string executable) { + // "PATH_INFO=/with/additional/path" const char *env[] = {_pathInfo.c_str(), _queryString.c_str(), NULL}; Logger &logger = Logger::getInstance(); std::string bin = "python3"; @@ -105,7 +107,7 @@ void CGI::execute(std::string executable) logger.log(ERROR, "Executable: %", executable); std::string executableWithPath = "data/www" + executable; logger.log(ERROR, "executableWithPath: " + executableWithPath); - const char *const argv[] = {bin.c_str(), executableWithPath.c_str(), NULL}; + const char *const argv[] = {bin.c_str(), executableWithPath.c_str(), _subPathInfo.c_str(), NULL}; const char *path = "/usr/bin/python3"; if (execve(path, (char *const *)argv, (char *const*) env) == SYSTEM_ERROR) throw SystemException("Execve"); } @@ -175,8 +177,10 @@ ClientState CGI::parseURIForCGI(std::string requestTarget) } std::string remaining = requestTarget.substr(filenameExtensionPos + lengthFilenameExtension, std::string::npos); size_t questionMarkPos = remaining.find('?'); - if (remaining.at(0) == '/' && !skip) - _pathInfo = "PATH_INFO=" + remaining.substr(0, questionMarkPos); + if (remaining.at(0) == '/' && !skip) { + _subPathInfo = remaining.substr(0, questionMarkPos); + _pathInfo = "PATH_INFO=" + _subPathInfo; + } if (!skip) _queryString = "QUERY_STRING=" + remaining.substr(questionMarkPos, std::string::npos); logger.log(DEBUG, _pathInfo); diff --git a/src/Client.cpp b/src/Client.cpp index 5090a84..59896e7 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -50,6 +50,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, logger.log(DEBUG, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); _request.setCGIToTrue(); + return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) @@ -78,6 +79,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { logger.log(DEBUG, "ClientState::Loading"); if (_request.CGITrue() == true) { + _state = _cgi.parseURIForCGI(_request.getRequestTarget()); logger.log(DEBUG, "executable: " + _cgi.getExecutable()); return (_state); @@ -85,7 +87,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, _state = _file_manager.manage( _request.getMethodType(), "./data/www" + _request.getRequestTarget(), - _request.getBody()); // TODO: resolve location + _request.getBody()); return (_state); } else if (events & POLLOUT && _state == ClientState::Error) @@ -107,7 +109,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, _response.clear(); _response.append(e.what()); _state = _file_manager.openErrorPage( - "./data/errors", e.getStatusCode()); // TODO: resolve location + "./data/errors", e.getStatusCode()); return (_state); } return (ClientState::Unkown); diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 1655bd8..9a3de89 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -140,7 +140,7 @@ ClientState FileManager::manage(HTTPMethod method, openGetFile(request_target_path); return (manageGet()); } - else if (method == HTTPMethod::POST) // && CGI == false + else if (method == HTTPMethod::POST) { if (!_request_target.is_open()) openPostFile(request_target_path); diff --git a/tests/request/get_cgi.txt b/tests/request/get_cgi.txt index d08a2b0..aeb2933 100755 --- a/tests/request/get_cgi.txt +++ b/tests/request/get_cgi.txt @@ -1,4 +1,4 @@ -GET /test_get.py/with/additional/path?and=a&query=string HTTP/1.1 +GET /cgi.py/with/additional/path?and=a&query=string HTTP/1.1 User-Agent: custom-client Host: webserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 diff --git a/tests/request/post_cgi.txt b/tests/request/post_cgi.txt index 768d567..6575eb5 100755 --- a/tests/request/post_cgi.txt +++ b/tests/request/post_cgi.txt @@ -1,4 +1,4 @@ -POST /test_post.py HTTP/1.1 +POST /cgi.py/with/additional/path?and=a&query=string HTTP/1.1 User-Agent: custom-client Host: webserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5 From 5070531050c4e8cdaae92ee028dc2b19ab05a1d3 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 8 Dec 2023 10:01:06 +0100 Subject: [PATCH 30/33] post_cgi also working with additional path and query_string, and got rid of garbage when sending in the cgi, also replaced some normal exeptions for ClientExceptions --- include/CGI.hpp | 1 + src/CGI.cpp | 45 +++++++++++++++++++++++++++++--------------- src/Client.cpp | 3 +-- src/HTTPRequest.cpp | 6 +++++- tests/get_request.sh | 2 +- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/CGI.hpp b/include/CGI.hpp index 1e7de06..9796654 100644 --- a/include/CGI.hpp +++ b/include/CGI.hpp @@ -41,6 +41,7 @@ class CGI const pid_t& getPid(void) const; ClientState send(Client &client ,std::string body, size_t bodyLength); + size_t getBufferSize(size_t bodyLength); ClientState receive(Client &client); std::string body; diff --git a/src/CGI.cpp b/src/CGI.cpp index 4ae7739..ee91ff0 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -1,3 +1,4 @@ +#include "ClientException.hpp" #include "Client.hpp" #include "Poll.hpp" #include "CGI.hpp" @@ -31,17 +32,31 @@ const pid_t &CGI::getPid(void) const { return (_pid); } +size_t CGI::getBufferSize(size_t bodyLength) +{ + size_t bufferSize = 0; + + if (BUFFER_SIZE > bodyLength) + bufferSize = bodyLength; + else if (BUFFER_SIZE > (bodyLength - _bodyBytesWritten)) + bufferSize = bodyLength - _bodyBytesWritten; + else + bufferSize = BUFFER_SIZE; + return (bufferSize); +} + ClientState CGI::send(Client &client ,std::string body, size_t bodyLength) { Logger &logger = Logger::getInstance(); ssize_t bytesWritten = 0; - + logger.log(INFO, "GCI::send is called"); logger.log(DEBUG, "body: " + body); + if (!client.cgiBodyIsSent) - bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), BUFFER_SIZE); + bytesWritten = write(client.getServerToCgiFd()[WRITE_END], body.c_str(), getBufferSize(bodyLength)); if (bytesWritten == SYSTEM_ERROR) - throw SystemException("write to cgi"); + throw ClientException(StatusCode::InternalServerError); logger.log(DEBUG, "bytesWritten: %", bytesWritten); _bodyBytesWritten += bytesWritten; if (_bodyBytesWritten >= bodyLength) @@ -65,9 +80,9 @@ ClientState CGI::receive(Client &client) logger.log(INFO, "CGI::receive is called"); int status; waitpid(_pid, &status, 0); - if (status == SYSTEM_ERROR) throw SystemException("waitpid"); + if (status == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); bytesRead = read(client.getCgiToServerFd()[READ_END], buffer, sizeof(buffer)); - if (bytesRead == SYSTEM_ERROR) throw SystemException("read"); + if (bytesRead == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); logger.log(DEBUG, "Bytes read: " + std::to_string(bytesRead)); logger.log(DEBUG, "buffer:\n" + std::string(buffer)); body += buffer; @@ -96,22 +111,20 @@ bool CGI::isExecutable(const std::string& filePath) void CGI::execute(std::string executable) { - // "PATH_INFO=/with/additional/path" const char *env[] = {_pathInfo.c_str(), _queryString.c_str(), NULL}; Logger &logger = Logger::getInstance(); std::string bin = "python3"; - if (!fileExists("data/www" + executable) || !isExecutable("data/www" + executable)) - throw SystemException("File does not exist or is not executable."); logger.log(ERROR, "CGI::execute is called"); logger.log(ERROR, "Executable: %", executable); std::string executableWithPath = "data/www" + executable; logger.log(ERROR, "executableWithPath: " + executableWithPath); const char *const argv[] = {bin.c_str(), executableWithPath.c_str(), _subPathInfo.c_str(), NULL}; const char *path = "/usr/bin/python3"; - if (execve(path, (char *const *)argv, (char *const*) env) == SYSTEM_ERROR) throw SystemException("Execve"); + if (execve(path, (char *const *)argv, (char *const*) env) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); } +// Can I throw a ClientExeception inside a child process or do I need to throw normal exception? ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, std::unordered_map> &active_pipes) { @@ -120,10 +133,10 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, logger.log(DEBUG, "CGI::start called"); logger.log(DEBUG, "Executable: %", _executable); - if (pipe(client.getServerToCgiFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); - if (pipe(client.getCgiToServerFd()) == SYSTEM_ERROR) throw SystemException("Pipe"); + if (pipe(client.getServerToCgiFd()) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (pipe(client.getCgiToServerFd()) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); _pid = fork(); - if (_pid == SYSTEM_ERROR) throw SystemException("Fork"); + if (_pid == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); if (_pid == 0) { if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); @@ -136,8 +149,8 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, execute(_executable); } logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); - if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); logger.log(DEBUG, "CGI::start after closing"); logger.log(DEBUG, "bodyLength: %", bodyLength); logger.log(DEBUG, "cgiBodyIsSent: %", client.cgiBodyIsSent); @@ -153,7 +166,7 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, poll.addPollFD(client.getServerToCgiFd()[WRITE_END], POLLOUT); return (ClientState::CGI_Write); } - if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); + if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); return (ClientState::CGI_Read); } @@ -172,6 +185,8 @@ ClientState CGI::parseURIForCGI(std::string requestTarget) _executable = requestTarget.substr(0, filenameExtensionPos + lengthFilenameExtension); bool skip = false; logger.log(DEBUG, "Executable is: " + _executable); + if (!fileExists("data/www" + _executable) || !isExecutable("data/www" + _executable)) + throw ClientException(StatusCode::NotFound); if (filenameExtensionPos + lengthFilenameExtension >= std::strlen(requestTarget.c_str()) - 1) { return (ClientState::CGI_Start); } diff --git a/src/Client.cpp b/src/Client.cpp index 59896e7..473c63d 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -49,8 +49,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { logger.log(DEBUG, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); - _request.setCGIToTrue(); - + // _request.setCGIToTrue(); return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index cfea361..cb3dcad 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -1,3 +1,4 @@ +#include "ClientException.hpp" #include #include #include @@ -133,7 +134,7 @@ ClientState HTTPRequest::receive(int client_fd) _bytes_read = read(client_fd, buffer, BUFFER_SIZE); if (_bytes_read == SYSTEM_ERROR) - throw SystemException("Read failed on: " + std::to_string(client_fd)); + throw ClientException(StatusCode::InternalServerError); logger.log(DEBUG, "in receive _bytes_read is: %", _bytes_read); if (_content_length != 0) { @@ -160,7 +161,10 @@ ClientState HTTPRequest::receive(int client_fd) if (_content_length == 0) return (ClientState::Loading); _body += _http_request.substr(pos + 2); + if (std::strlen(_body.c_str()) == _content_length) + return (ClientState::Loading); return (ClientState::Receiving); + } else return (ClientState::Loading); diff --git a/tests/get_request.sh b/tests/get_request.sh index 42c2e86..f6fa2c1 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/post_cgi.txt" +REQUEST_FILE="tests/request/get.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From a8c03577afbf7a89cddb582b859910fc125c3e13 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Fri, 8 Dec 2023 13:05:17 +0100 Subject: [PATCH 31/33] added a comment for mr Marty --- src/Client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.cpp b/src/Client.cpp index 473c63d..166ace5 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -49,7 +49,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, { logger.log(DEBUG, "ClientState::Receiving"); _state = _request.receive(_socket.getFD()); - // _request.setCGIToTrue(); + _request.setCGIToTrue(); // TODO: method returns if current request is a CGI return (_state); } else if (events & POLLOUT && _state == ClientState::CGI_Start) From f66ddb2faea10d5bd7afd6a754daa7fd583307d7 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 13 Dec 2023 14:56:08 +0100 Subject: [PATCH 32/33] throwing client exceptions in the cgi's child process is now being handled, cgi DELETE is also covered --- include/Client.hpp | 2 ++ include/ClientState.hpp | 2 +- src/CGI.cpp | 14 ++++++-------- src/Client.cpp | 16 ++++++++++++++-- src/FileManager.cpp | 2 +- src/HTTPServer.cpp | 17 +++++++++++++++++ tests/get_request.sh | 2 +- 7 files changed, 42 insertions(+), 13 deletions(-) diff --git a/include/Client.hpp b/include/Client.hpp index 456885a..4f65623 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -24,9 +24,11 @@ class Client int *getCgiToServerFd(void); int *getServerToCgiFd(void); HTTPRequest &getRequest(void); + void setState(ClientState state); bool cgiBodyIsSent; bool cgiHasBeenRead; + bool KO; private: HTTPRequest _request; diff --git a/include/ClientState.hpp b/include/ClientState.hpp index 5f4bbc5..bdd3223 100644 --- a/include/ClientState.hpp +++ b/include/ClientState.hpp @@ -11,7 +11,7 @@ enum class ClientState Sending, Done, Error, - Unkown, + Unknown, }; #endif diff --git a/src/CGI.cpp b/src/CGI.cpp index ee91ff0..741d8e9 100644 --- a/src/CGI.cpp +++ b/src/CGI.cpp @@ -124,7 +124,6 @@ void CGI::execute(std::string executable) if (execve(path, (char *const *)argv, (char *const*) env) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); } -// Can I throw a ClientExeception inside a child process or do I need to throw normal exception? ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, std::unordered_map> &active_pipes) { @@ -139,13 +138,12 @@ ClientState CGI::start(Poll &poll, Client &client, size_t bodyLength, if (_pid == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); if (_pid == 0) { - if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(client.getServerToCgiFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw SystemException("close"); - if (dup2(client.getCgiToServerFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw SystemException("dup2"); - if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw SystemException("close"); - + if (close(client.getServerToCgiFd()[WRITE_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (close(client.getCgiToServerFd()[READ_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (dup2(client.getServerToCgiFd()[READ_END], STDIN_FILENO) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (close(client.getServerToCgiFd()[READ_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (dup2(client.getCgiToServerFd()[WRITE_END], STDOUT_FILENO) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); + if (close(client.getCgiToServerFd()[WRITE_END]) == SYSTEM_ERROR) throw ClientException(StatusCode::InternalServerError); execute(_executable); } logger.log(DEBUG, "CGI::start after else if (_pid == 0)"); diff --git a/src/Client.cpp b/src/Client.cpp index 166ace5..c658007 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -14,6 +14,7 @@ Client::Client(const int &server_fd) : _socket(server_fd) _state = ClientState::Receiving; cgiBodyIsSent = false; cgiHasBeenRead = false; + KO = false; } Client::~Client() @@ -37,6 +38,10 @@ HTTPRequest &Client::getRequest(void) { return (_request); } +void Client::setState(ClientState state) { + _state = state; +} + ClientState Client::handleConnection(short events, Poll &poll, Client &client, std::unordered_map> &active_pipes) { @@ -77,7 +82,7 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, else if (events & POLLOUT && _state == ClientState::Loading) { logger.log(DEBUG, "ClientState::Loading"); - if (_request.CGITrue() == true) { + if (_request.CGITrue() == true && _request.getMethodType() != HTTPMethod::DELETE) { _state = _cgi.parseURIForCGI(_request.getRequestTarget()); logger.log(DEBUG, "executable: " + _cgi.getExecutable()); @@ -97,6 +102,11 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, else if (events & POLLOUT && _state == ClientState::Sending) { logger.log(DEBUG, "ClientState::Sending"); + if (KO == true) + { + _state =_response.send(_socket.getFD(), "HTTP/1.1 500 KO\t\n\t\n"); + return (_state); + } _state = _response.send(_socket.getFD(), _file_manager.getResponse()); return (_state); @@ -105,11 +115,13 @@ ClientState Client::handleConnection(short events, Poll &poll, Client &client, catch (ClientException &e) { logger.log(ERROR, "Client exception: " + std::string(e.what())); + if (_request.CGITrue() == true && _request.getMethodType() != HTTPMethod::DELETE) + _exit(1); _response.clear(); _response.append(e.what()); _state = _file_manager.openErrorPage( "./data/errors", e.getStatusCode()); return (_state); } - return (ClientState::Unkown); + return (ClientState::Unknown); } diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 9a3de89..8cbc1d5 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -146,7 +146,7 @@ ClientState FileManager::manage(HTTPMethod method, openPostFile(request_target_path); return (managePost(body)); } - return (ClientState::Unkown); + return (ClientState::Unknown); } ClientState FileManager::manageCgi(std::string http_version, const std::string &body) diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index b57d22c..dc908bf 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -1,6 +1,8 @@ #include #include #include +#include "HTTPRequest.hpp" +#include "ClientException.hpp" HTTPServer::HTTPServer(const std::string &config_file_path) try : _parser(config_file_path), _poll(), _active_servers(), _active_clients(), _active_pipes() @@ -97,6 +99,20 @@ void HTTPServer::handleActivePollFDs() continue; try { + if (poll_fd.revents & POLLHUP) // this code block is solving a thrown exception from the cgi's child process + { + Client &client = getClientByPipeFd(poll_fd.fd); + if (client.getRequest().CGITrue() && _request.getMethodType() != HTTPMethod::DELETE) + { + _poll.removeFD(poll_fd.fd); + _active_pipes.erase(poll_fd.fd); + _poll.setEvents(client.getFD(), POLLOUT); + client.setState(ClientState::Sending); + client.KO = true; + handleExistingConnection(poll_fd, _poll, client, _active_pipes); + continue; + } + } _poll.checkREvents(poll_fd.revents); } catch (const Poll::PollException &e) @@ -177,6 +193,7 @@ void HTTPServer::handleExistingConnection(const pollfd &poll_fd, Poll &poll, Cli case ClientState::CGI_Write: _poll.setEvents(poll_fd.fd, POLLOUT); break; + case ClientState::Unknown: case ClientState::Done: _poll.removeFD(poll_fd.fd); _active_clients.erase(poll_fd.fd); diff --git a/tests/get_request.sh b/tests/get_request.sh index f6fa2c1..242eb53 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -5,7 +5,7 @@ SERVER_HOST="localhost" SERVER_PORT="9696" # Request file -REQUEST_FILE="tests/request/get.txt" +REQUEST_FILE="tests/request/get_cgi.txt" # Use netcat to send the contents of the request file to the server nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" From 7af6bbfc41dfe51ea2272875f5a9721729367762 Mon Sep 17 00:00:00 2001 From: Lucien Van Bussel Date: Wed, 13 Dec 2023 15:00:49 +0100 Subject: [PATCH 33/33] fixed a stuped copy pasta mistake --- src/HTTPServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index dc908bf..b18096c 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -102,7 +102,7 @@ void HTTPServer::handleActivePollFDs() if (poll_fd.revents & POLLHUP) // this code block is solving a thrown exception from the cgi's child process { Client &client = getClientByPipeFd(poll_fd.fd); - if (client.getRequest().CGITrue() && _request.getMethodType() != HTTPMethod::DELETE) + if (client.getRequest().CGITrue() && client.getRequest().getMethodType() != HTTPMethod::DELETE) { _poll.removeFD(poll_fd.fd); _active_pipes.erase(poll_fd.fd);