From b34e49311bb147d0d91ea71040d1b38f8d8e39e9 Mon Sep 17 00:00:00 2001 From: lperrey Date: Wed, 17 Aug 2022 23:42:12 +0200 Subject: [PATCH] body write to CGI now monitored by epoll + kermit.jpg :) --- Makefile | 9 +-- memo.txt | 2 +- srcs/Client.cpp | 33 +++++++--- srcs/Client.hpp | 20 +++++- srcs/webserv/Webserv.hpp | 21 +++--- srcs/webserv/cgi.cpp | 92 +++++++++++++++----------- srcs/webserv/cgi_epoll.cpp | 129 +++++++++++++++++++++++++++++++++++++ srcs/webserv/close.cpp | 28 +++++--- srcs/webserv/response.cpp | 40 +++++++----- srcs/webserv/run_loop.cpp | 113 +++++--------------------------- www/Kermit_the_Frog.jpg | Bin 0 -> 34092 bytes www/form_cgi.html | 8 +-- www/kermit_tea.jpg | Bin 0 -> 112622 bytes 13 files changed, 302 insertions(+), 193 deletions(-) create mode 100644 srcs/webserv/cgi_epoll.cpp create mode 100644 www/Kermit_the_Frog.jpg create mode 100644 www/kermit_tea.jpg diff --git a/Makefile b/Makefile index 65e7816..626bcaf 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,12 @@ NAME = webserv CXX = c++ -CXXFLAGS = -Wall -Wextra #-Werror +CXXFLAGS = -Wall -Wextra -Werror CXXFLAGS += $(HEADERS_D:%=-I%) CXXFLAGS += -std=c++98 CXXFLAGS += -g #CXXFLAGS += -fno-limit-debug-info CXXFLAGS += -MMD -MP #header dependencie -#CXXFLAGS += -O3 VPATH = $(SRCS_D) @@ -25,11 +24,9 @@ SRCS = main.cpp \ accept.cpp request.cpp response.cpp \ method_get.cpp method_post.cpp method_delete.cpp \ run_loop.cpp timeout.cpp \ - parser.cpp \ - extraConfig.cpp \ - postProcessing.cpp \ + parser.cpp extraConfig.cpp postProcessing.cpp \ utils.cpp \ - cgi.cpp \ + cgi.cpp cgi_epoll.cpp \ Client.cpp Client_multipart_body.cpp \ OBJS_D = builds diff --git a/memo.txt b/memo.txt index 92ca789..99f9ffe 100644 --- a/memo.txt +++ b/memo.txt @@ -41,7 +41,7 @@ Valgrind error RESOLVED ! : ==847174== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==847174== by 0x49AA35D: std::__cxx11::basic_string, std::allocator >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28) ==847174== by 0x49ABB52: std::__cxx11::basic_string, std::allocator >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28) -==847174== by 0x426BC5: Webserv::_read_cgi_output(cgi_pipe_rfd&) (run_loop.cpp:39) // (LUKE: its an string.append() call) +==847174== by 0x426BC5: Webserv::_read_cgi_output(cgi_pipe_r_from_child&) (run_loop.cpp:39) // (LUKE: its an string.append() call) ==847174== by 0x427962: Webserv::run() (run_loop.cpp:153) ==847174== by 0x4052E9: main (main.cpp:38) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 25596c1..8012801 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -12,7 +12,11 @@ Client::Client() request_complete(false), assigned_server(NULL), assigned_location(NULL), - cgi_pipe_rfd(0), + cgi_state(0), + cgi_pipe_w_to_child(0), + cgi_pipe_r_from_child(0), + cgi_pipe_w_to_parent(0), + cgi_pipe_r_from_parent(0), cgi_pid(0), _fd(0), _port(""), @@ -29,7 +33,11 @@ Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string a request_complete(false), assigned_server(NULL), assigned_location(NULL), - cgi_pipe_rfd(0), + cgi_state(0), + cgi_pipe_w_to_child(0), + cgi_pipe_r_from_child(0), + cgi_pipe_w_to_parent(0), + cgi_pipe_r_from_parent(0), cgi_pid(0), _fd(afd), _port(aport), @@ -87,7 +95,7 @@ void Client::parse_request_headers(std::vector &servers) if (raw_request.find(CRLF CRLF) == NPOS) return ; header_complete = true; - clear_request(); // not mandatory + clear_request_vars(); // not mandatory _parse_request_line(); if (status) @@ -229,7 +237,7 @@ void Client::fill_script_path(std::string &path, size_t pos) void Client::clear() { - clear_request(); + clear_request_vars(); raw_request.clear(); response.clear(); status = 0; @@ -238,12 +246,10 @@ void Client::clear() request_complete = false; assigned_server = NULL; assigned_location = NULL; - cgi_pipe_rfd = 0; - cgi_pid = 0; - cgi_output.clear(); + clear_cgi_vars(); } -void Client::clear_request() +void Client::clear_request_vars() { clear_script(); _request.method = UNKNOWN; @@ -264,6 +270,17 @@ void Client::clear_script() _request.script.info.clear(); } +void Client::clear_cgi_vars() +{ + cgi_state = false; + cgi_pipe_w_to_child = 0; + cgi_pipe_r_from_child = 0; + cgi_pipe_w_to_parent = 0; + cgi_pipe_r_from_parent = 0; + cgi_pid = 0; + cgi_output.clear(); +} + // debug void Client::print_client(std::string message) { diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 2d6e879..0fa8a5d 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -40,6 +40,15 @@ struct Request struct Script script; }; +enum cgi_states +{ + CGI_NO_CGI = 0, + CGI_WAIT_TO_EXEC, + CGI_READY_TO_EXEC, + CGI_WAIT_FOR_OUTPUT, + CGI_OUTPUT_READY +}; + class Client { public: @@ -58,7 +67,13 @@ class Client // size_t read_body_size; // unused for now ServerConfig *assigned_server; // cant be const cause of error_pages.operator[] const LocationConfig *assigned_location; - int cgi_pipe_rfd; + + // CGI variables + int cgi_state; + int cgi_pipe_w_to_child; + int cgi_pipe_r_from_child; + int cgi_pipe_w_to_parent; + int cgi_pipe_r_from_parent; pid_t cgi_pid; std::string cgi_output; @@ -88,7 +103,8 @@ class Client void parse_request_headers(std::vector &servers); void parse_request_body(); void clear(); - void clear_request(); + void clear_request_vars(); + void clear_cgi_vars(); void clear_script(); void fill_script_path(std::string &path, size_t pos); // DEBUG diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 24ce1fc..d865989 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -105,11 +105,12 @@ class Webserv void _handle_last_signal(); // close.cpp void _close_client(int fd); + void _close_client_cgi_pipes(Client *client); void _close_all_clients(); void _close_all_clients_fd(); - void _close_all_clients_cgi_fd(); - void _close_cgi_pipe_rfd(int fd); - void _close_all_cgi_pipe_rfd(); + void _close_all_clients_cgi_pipes(); + void _close_cgi_pipe_r_from_child(int fd); + void _close_all_cgi_pipe_r_from_child(); void _close_all_listen_sockets(); void _reopen_lsocket(std::vector::iterator it); void _handle_epoll_error_lsocket(uint32_t events, std::vector::iterator it); @@ -124,6 +125,7 @@ class Webserv // cgi.cpp bool _is_cgi(Client *client, std::string path); size_t _cgi_pos(Client *client, std::string &path, size_t pos); + void _cgi_open_pipes(Client *client); void _exec_cgi(Client *client); void _set_env_vector(Client *client, std::vector &env_vector); void _set_env_cstr(char *env_cstr[], std::vector &env_vector); @@ -136,11 +138,14 @@ class Webserv void _check_fields_duplicates(Client *client, std::string & output); void _add_script_body_length_header(std::string & output); void _remove_body_leading_empty_lines(std::string & output); - - Client *_find_cgi_fd(int cgi_fd); - void _read_cgi_output(Client *client); - void _handle_epoll_error_cgi_fd(uint32_t events, Client *client); - void _cgi_epollhup(uint32_t events, Client *client); + // cgi_epoll.cpp + void _read_cgi_output(Client *client); + void _handle_epollerr_cgi_output(uint32_t events, Client *client); + void _handle_epollhup_cgi_output(uint32_t events, Client *client); + void _cgi_input_ready(Client *client); + void _handle_epollerr_cgi_input(uint32_t events, Client *client); + Client *_find_cgi_output_fd(int fd); + Client *_find_cgi_input_fd(int fd); /////////////////////// diff --git a/srcs/webserv/cgi.cpp b/srcs/webserv/cgi.cpp index a16c3f1..99ffdb3 100644 --- a/srcs/webserv/cgi.cpp +++ b/srcs/webserv/cgi.cpp @@ -66,6 +66,28 @@ size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos) return NPOS; } +void Webserv::_cgi_open_pipes(Client *client) +{ + #define R 0 + #define W 1 + + int fd_in[2]; + int fd_out[2]; + ::pipe(fd_in); + ::pipe(fd_out); + + client->cgi_pipe_r_from_parent = fd_in[R]; + client->cgi_pipe_w_to_child = fd_in[W]; + client->cgi_pipe_r_from_child = fd_out[R]; + client->cgi_pipe_w_to_parent = fd_out[W]; + + // epoll add for writing body to child + _epoll_update(client->cgi_pipe_w_to_child, EPOLLOUT, EPOLL_CTL_ADD); + // stop monitoring client->fd until we can write body + _epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL); + client->cgi_state = CGI_WAIT_TO_EXEC; +} + void Webserv::_exec_cgi(Client *client) { char* env_cstr[19] = {NULL}; @@ -152,24 +174,11 @@ void Webserv::_set_env_cstr(char *env_cstr[], std::vector &env_vect void Webserv::_exec_script(Client *client, char *env[]) { - #define RD 0 - #define WR 1 - #define FD_WR_TO_CHLD fd_in[WR] - #define FD_WR_TO_PRNT fd_out[WR] - #define FD_RD_FR_CHLD fd_out[RD] - #define FD_RD_FR_PRNT fd_in[RD] - pid_t pid; char * const nll[1] = {NULL}; std::string script_output; - std::string body = client->get_rq_body(); - int fd_in[2]; - int fd_out[2]; std::string path; - ::pipe(fd_in); - ::pipe(fd_out); - pid = fork(); if (pid == -1) std::perror("err fork()"); @@ -178,52 +187,57 @@ void Webserv::_exec_script(Client *client, char *env[]) std::signal(SIGPIPE, SIG_DFL); std::signal(SIGINT, SIG_DFL); - _close_all_clients_fd(); - ::close(_epfd); - ::close(FD_WR_TO_CHLD); - ::close(FD_RD_FR_CHLD); - if (dup2(FD_RD_FR_PRNT, STDIN_FILENO) == -1) + if (dup2(client->cgi_pipe_r_from_parent, STDIN_FILENO) == -1) { std::perror("err dup2()"); - ::close(FD_RD_FR_PRNT); // Valgind debug, not essential - ::close(FD_WR_TO_PRNT); // Valgind debug, not essential + if (::close(client->cgi_pipe_r_from_parent) == -1) // Valgind debug, not essential + std::perror("err close"); + if (::close(client->cgi_pipe_w_to_parent) == -1) // Valgind debug, not essential + std::perror("err close"); throw ExecFail(); } - if (dup2(FD_WR_TO_PRNT, STDOUT_FILENO) == -1) + if (dup2(client->cgi_pipe_w_to_parent, STDOUT_FILENO) == -1) { std::perror("err dup2()"); - ::close(FD_RD_FR_PRNT); // Valgind debug, not essential - ::close(FD_WR_TO_PRNT); // Valgind debug, not essential + if (::close(client->cgi_pipe_r_from_parent) == -1) // Valgind debug, not essential + std::perror("err close"); + if (::close(client->cgi_pipe_w_to_parent) == -1) // Valgind debug, not essential + std::perror("err close"); throw ExecFail(); } - ::close(FD_RD_FR_PRNT); - ::close(FD_WR_TO_PRNT); + _close_all_clients_fd(); + if (::close(_epfd) == -1) + std::perror("err close"); path = client->get_rq_script_path(); // Wut ? Only relative path ? /*DEBUG*/std::cerr << "execve:[" << path << "]\n"; if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing { std::perror("err execve()"); - ::close(STDIN_FILENO); // Valgind debug, not essential - ::close(STDOUT_FILENO); // Valgind debug, not essential + if (::close(STDIN_FILENO) == -1) // Valgind debug, not essential + std::perror("err close"); + if (::close(STDOUT_FILENO) == -1) // Valgind debug, not essential + std::perror("err close"); throw ExecFail(); } } else //parent { - ::close(FD_RD_FR_PRNT); - ::close(FD_WR_TO_PRNT); - ::write(FD_WR_TO_CHLD, body.c_str(), body.size()); // move this before the fork ? - ::close(FD_WR_TO_CHLD); + if (::close(client->cgi_pipe_r_from_parent) == -1) + std::perror("err close"); + if (::close(client->cgi_pipe_w_to_parent) == -1) + std::perror("err close"); + if (::close(client->cgi_pipe_w_to_child) == -1) + std::perror("err close"); - // add FD_RD_FR_CHLD to epoll, - _epoll_update(FD_RD_FR_CHLD, EPOLLIN, EPOLL_CTL_ADD); + // add client->cgi_pipe_r_from_child to epoll, + _epoll_update(client->cgi_pipe_r_from_child, EPOLLIN, EPOLL_CTL_ADD); // stop monitoring client->fd until the cgi-script as done is job _epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL); - client->cgi_pipe_rfd = FD_RD_FR_CHLD; client->cgi_pid = pid; + client->cgi_state = CGI_WAIT_FOR_OUTPUT; } } @@ -241,15 +255,15 @@ void Webserv::_check_script_output(Client *client, std::string & output) _check_script_status(client, output); if (client->status >= 400 && client->status < 600) return; -/*DEBUG*/ std::cout << "\n" B_PURPLE "[script status]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[script status]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; client->status = _check_script_fields(output, client->status); -/*DEBUG*/ std::cout << "\n" B_PURPLE "[script fields]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[script fields]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; _check_fields_duplicates(client, output); -/*DEBUG*/ std::cout << "\n" B_PURPLE "[fields duplicates]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[fields duplicates]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; _remove_body_leading_empty_lines(output); -/*DEBUG*/ std::cout << "\n" B_PURPLE "[script empty lines]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[script empty lines]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; _add_script_body_length_header(output); -/*DEBUG*/ std::cout << "\n" B_PURPLE "[script content length]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[script content length]:\n" << "client->status:[" << client->status << "]" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n"; } void Webserv::_check_script_status(Client *client, std::string & output) diff --git a/srcs/webserv/cgi_epoll.cpp b/srcs/webserv/cgi_epoll.cpp new file mode 100644 index 0000000..a7d93d9 --- /dev/null +++ b/srcs/webserv/cgi_epoll.cpp @@ -0,0 +1,129 @@ + +#include "Webserv.hpp" + + +#define BUFSIZE 8192 // (8Ko) +#define STATUS_500 std::string("Status: 500" CRLF CRLF); +void Webserv::_read_cgi_output(Client *client) +{ + char buf[BUFSIZE]; + ssize_t ret; + std::cerr << "_read_cgi_output()" << "\n"; + std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; + std::cerr << "client fd = " << client->get_cl_fd() << "\n"; + std::cerr << "cgi fd = " << client->cgi_pipe_r_from_child << "\n"; + + ret = ::read(client->cgi_pipe_r_from_child, buf, BUFSIZE); + std::cerr << "cgi read ret = " << ret << "\n"; + if (ret == -1) + { + std::perror("err read(cgi_fd)"); + client->cgi_output = STATUS_500; + } + else if (ret == 0) + std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n"; + else + { + std::cerr << "NORMAL BEHAVIOR I THINK!\n"; // debug + client->cgi_output.append(buf, ret); + } +} + +void Webserv::_handle_epollerr_cgi_output(uint32_t events, Client *client) +{ + (void)events; + std::cerr << "cgi EPOLLERR" << "\n"; + std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; + std::cerr << "client fd = " << client->get_cl_fd() << "\n"; + std::cerr << "cgi fd = " << client->cgi_pipe_r_from_child << "\n"; + + client->cgi_output = STATUS_500; + + // Common with EPOLLHUP + pid_t wait_ret; + wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); + std::cerr << "cgi EPOLLERR waitpid ret = " << wait_ret << "\n"; + if (wait_ret == client->cgi_pid) + { + if (client->cgi_pipe_r_from_child) + { + if (::close(client->cgi_pipe_r_from_child) == -1) + std::perror("err close()"); + } + client->cgi_pipe_r_from_child = 0; + _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); + client->cgi_state = CGI_OUTPUT_READY; + } + +} + +void Webserv::_handle_epollhup_cgi_output(uint32_t events, Client *client) +{ + (void)events; + (void)client; + +/* std::cerr << "cgi EPOLLHUP" << "\n"; + std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; + std::cerr << "client fd = " << client->get_cl_fd() << "\n"; + std::cerr << "cgi fd = " << client->cgi_pipe_r_from_child << "\n"; */ + + // Common with EPOLLERR + pid_t wait_ret; + wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); + // std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n"; + if (wait_ret == client->cgi_pid) + { + if (client->cgi_pipe_r_from_child) + { + if (::close(client->cgi_pipe_r_from_child) == -1) + std::perror("err close()"); + } + client->cgi_pipe_r_from_child = 0; + _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); + client->cgi_state = CGI_OUTPUT_READY; + } +} + +void Webserv::_cgi_input_ready(Client *client) +{ + client->cgi_state = CGI_READY_TO_EXEC; + _epoll_update(client->cgi_pipe_w_to_child, 0, EPOLL_CTL_DEL); + _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); +} + +void Webserv::_handle_epollerr_cgi_input(uint32_t events, Client *client) +{ + (void)events; + + client->cgi_output = STATUS_500; + + client->cgi_state = CGI_OUTPUT_READY; + _close_client_cgi_pipes(client); + _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); +} + +Client *Webserv::_find_cgi_output_fd(int fd) +{ + std::vector::iterator it = _clients.begin(); + std::vector::iterator it_end = _clients.end(); + while (it != it_end) + { + if (it->cgi_pipe_r_from_child == fd) + return (&(*it)); + ++it; + } + return (NULL); +} + +Client *Webserv::_find_cgi_input_fd(int fd) +{ + std::vector::iterator it = _clients.begin(); + std::vector::iterator it_end = _clients.end(); + while (it != it_end) + { + if (it->cgi_pipe_w_to_child == fd) + return (&(*it)); + ++it; + } + return (NULL); +} diff --git a/srcs/webserv/close.cpp b/srcs/webserv/close.cpp index 138be7e..18fdd74 100644 --- a/srcs/webserv/close.cpp +++ b/srcs/webserv/close.cpp @@ -13,6 +13,7 @@ void Webserv::_close_client(int fd) std::cerr << "close fd " << fd << "\n"; if (::close(fd) == -1) std::perror("err close()"); + _close_client_cgi_pipes(&(*it)); _clients.erase(it); break; } @@ -20,6 +21,22 @@ void Webserv::_close_client(int fd) } } +void Webserv::_close_client_cgi_pipes(Client *client) +{ + if (client->cgi_state) + { + std::cerr << "close cgi-pipes" << "\n"; + if (::close(client->cgi_pipe_w_to_child) == -1) + std::perror("err close()"); + if (::close(client->cgi_pipe_r_from_child) == -1) + std::perror("err close()"); + if (::close(client->cgi_pipe_w_to_parent) == -1) + std::perror("err close()"); + if (::close(client->cgi_pipe_r_from_parent) == -1) + std::perror("err close()"); + } +} + void Webserv::_close_all_clients() { _close_all_clients_fd(); @@ -28,7 +45,7 @@ void Webserv::_close_all_clients() void Webserv::_close_all_clients_fd() { - _close_all_clients_cgi_fd(); + _close_all_clients_cgi_pipes(); std::vector::iterator it = _clients.begin(); std::vector::iterator it_end = _clients.end(); while (it != it_end) @@ -41,19 +58,14 @@ void Webserv::_close_all_clients_fd() } } -void Webserv::_close_all_clients_cgi_fd() +void Webserv::_close_all_clients_cgi_pipes() { std::vector::iterator it = _clients.begin(); std::vector::iterator it_end = _clients.end(); while (it != it_end) { // _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG - if (it->cgi_pipe_rfd) - { - std::cerr << "close cgi-fd " << it->cgi_pipe_rfd << "\n"; - if (::close(it->cgi_pipe_rfd) == -1) - std::perror("err close()"); - } + _close_client_cgi_pipes(&(*it)); ++it; } } diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index 3f574b4..d78c60f 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -43,28 +43,21 @@ int Webserv::_send_response(Client *client) if (client->response.empty()) { _append_base_headers(client); - if (!client->status) - { - _construct_response(client); - if (client->cgi_pipe_rfd) - return SEND_IN_PROGRESS; - } } - else if (!client->cgi_output.empty()) + + if (!client->status) { - /*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; - /*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(client->cgi_output); std::cout << B_PURPLE "-----------" RESET "\n\n"; - _check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output; - if (client->status < 400) - client->response += client->cgi_output; - /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; + _construct_response(client); + if (client->cgi_state == CGI_WAIT_FOR_OUTPUT + || client->cgi_state == CGI_WAIT_TO_EXEC) + return SEND_IN_PROGRESS; } _insert_status_line(client); if (client->status >= 400) _error_html_response(client); -/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n"; +// /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n"; // /* Debug */ std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0); @@ -103,12 +96,25 @@ void Webserv::_construct_response(Client *client) std::string script_output; path = _replace_url_root(client, client->get_rq_abs_path()); - if (_is_cgi(client, path)) + if (client->cgi_state == CGI_READY_TO_EXEC) { + std::string body = client->get_rq_body(); + ::write(client->cgi_pipe_w_to_child, body.c_str(), body.size()); _exec_cgi(client); - return; } - _process_method(client, path); + else if (client->cgi_state == CGI_OUTPUT_READY) + { + // /*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; + // /*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(client->cgi_output); std::cout << B_PURPLE "-----------" RESET "\n\n"; + _check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output; + if (client->status < 400) + client->response += client->cgi_output; + // /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; + } + else if (_is_cgi(client, path)) + _cgi_open_pipes(client); + else + _process_method(client, path); } void Webserv::_process_method(Client *client, std::string &path) diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index dea6a8e..4c5d752 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -4,98 +4,6 @@ #define MAX_EVENTS 42 // arbitrary #define TIMEOUT 3000 - -#define BUFSIZE 8192 // (8Ko) -#define STATUS_500 std::string("Status: 500" CRLF CRLF); -void Webserv::_read_cgi_output(Client *client) -{ - char buf[BUFSIZE]; - ssize_t ret; - std::cerr << "_read_cgi_output()" << "\n"; - std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; - std::cerr << "client fd = " << client->get_cl_fd() << "\n"; - std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; - - ret = ::read(client->cgi_pipe_rfd, buf, BUFSIZE); - std::cerr << "cgi read ret = " << ret << "\n"; - if (ret == -1) - { - std::perror("err read(cgi_fd)"); - client->cgi_output = STATUS_500; - } - else if (ret == 0) - std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n"; - else - { - std::cerr << "NORMAL BEHAVIOR I THINK!\n"; // debug - client->cgi_output.append(buf, ret); - } -} - -void Webserv::_handle_epoll_error_cgi_fd(uint32_t events, Client *client) -{ - (void)events; - std::cerr << "cgi EPOLLERR" << "\n"; - std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; - std::cerr << "client fd = " << client->get_cl_fd() << "\n"; - std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; - - client->cgi_output = STATUS_500; - - pid_t wait_ret; - wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); - std::cerr << "cgi EPOLLERR waitpid ret = " << wait_ret << "\n"; - if (wait_ret == client->cgi_pid) - { - if (client->cgi_pipe_rfd) - { - if (::close(client->cgi_pipe_rfd) == -1) - std::perror("err close()"); - } - client->cgi_pipe_rfd = 0; - _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); - } - -} - -void Webserv::_cgi_epollhup(uint32_t events, Client *client) -{ - (void)events; - (void)client; - -/* std::cerr << "cgi EPOLLHUP" << "\n"; - std::cerr << "cgi_pid = " << client->cgi_pid << "\n"; - std::cerr << "client fd = " << client->get_cl_fd() << "\n"; - std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; */ - - pid_t wait_ret; - wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG); - // std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n"; - if (wait_ret == client->cgi_pid) - { - if (client->cgi_pipe_rfd) - { - if (::close(client->cgi_pipe_rfd) == -1) - std::perror("err close()"); - } - client->cgi_pipe_rfd = 0; - _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); - } -} - -Client *Webserv::_find_cgi_fd(int cgi_fd) -{ - std::vector::iterator it = _clients.begin(); - std::vector::iterator it_end = _clients.end(); - while (it != it_end) - { - if (it->cgi_pipe_rfd == cgi_fd) - return (&(*it)); - ++it; - } - return (NULL); -} - void Webserv::run() { std::cerr << "Server started\n"; @@ -104,7 +12,8 @@ void Webserv::run() int i; int count_loop = 0; std::vector::iterator it_lsocket; - Client *client_cgi = NULL; + Client *client_cgi_output = NULL; + Client *client_cgi_input = NULL; g_run = true; while (g_run) @@ -128,7 +37,8 @@ void Webserv::run() { try { it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd); - client_cgi = _find_cgi_fd(events[i].data.fd); + client_cgi_output = _find_cgi_output_fd(events[i].data.fd); + client_cgi_input = _find_cgi_input_fd(events[i].data.fd); if (it_lsocket != _listen_sockets.end()) { if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) @@ -136,14 +46,21 @@ void Webserv::run() else if (events[i].events & EPOLLIN) _accept_connection(*it_lsocket); } - else if (client_cgi) + else if (client_cgi_input) + { + if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) + _handle_epollerr_cgi_input(events[i].events, client_cgi_input); + else if (events[i].events & EPOLLOUT) + _cgi_input_ready(client_cgi_input); + } + else if (client_cgi_output) { if (events[i].events & EPOLLERR) - _handle_epoll_error_cgi_fd(events[i].events, client_cgi); + _handle_epollerr_cgi_output(events[i].events, client_cgi_output); else if (events[i].events & EPOLLIN) - _read_cgi_output(client_cgi); + _read_cgi_output(client_cgi_output); else if ( (events[i].events & EPOLLHUP) && !(events[i].events & EPOLLIN) ) - _cgi_epollhup(events[i].events, client_cgi); + _handle_epollhup_cgi_output(events[i].events, client_cgi_output); } else if (std::find(_clients.begin(), _clients.end(), events[i].data.fd) != _clients.end()) // TODO: save the it in var to avoid multiples find() { diff --git a/www/Kermit_the_Frog.jpg b/www/Kermit_the_Frog.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4dcb88cec45c402621ec13b52691c17425eb3d3f GIT binary patch literal 34092 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

AV#B1oW8?F1zoO)J; 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 literal 0 HcmV?d00001 diff --git a/www/form_cgi.html b/www/form_cgi.html index 2e73f05..388629a 100644 --- a/www/form_cgi.html +++ b/www/form_cgi.html @@ -285,12 +285,8 @@