Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv

This commit is contained in:
Eric LAZO
2022-08-18 00:19:31 +02:00
15 changed files with 320 additions and 227 deletions

View File

@@ -2,13 +2,12 @@
NAME = webserv NAME = webserv
CXX = c++ CXX = c++
CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS = -Wall -Wextra -Werror
CXXFLAGS += $(HEADERS_D:%=-I%) CXXFLAGS += $(HEADERS_D:%=-I%)
CXXFLAGS += -std=c++98 CXXFLAGS += -std=c++98
CXXFLAGS += -g CXXFLAGS += -g
#CXXFLAGS += -fno-limit-debug-info #CXXFLAGS += -fno-limit-debug-info
CXXFLAGS += -MMD -MP #header dependencie CXXFLAGS += -MMD -MP #header dependencie
#CXXFLAGS += -O3
VPATH = $(SRCS_D) VPATH = $(SRCS_D)
@@ -25,11 +24,9 @@ SRCS = main.cpp \
accept.cpp request.cpp response.cpp \ accept.cpp request.cpp response.cpp \
method_get.cpp method_post.cpp method_delete.cpp \ method_get.cpp method_post.cpp method_delete.cpp \
run_loop.cpp timeout.cpp \ run_loop.cpp timeout.cpp \
parser.cpp \ parser.cpp extraConfig.cpp postProcessing.cpp \
extraConfig.cpp \
postProcessing.cpp \
utils.cpp \ utils.cpp \
cgi.cpp \ cgi.cpp cgi_epoll.cpp \
Client.cpp Client_multipart_body.cpp \ Client.cpp Client_multipart_body.cpp \
OBJS_D = builds OBJS_D = builds

View File

@@ -1,7 +1,4 @@
----Priorité élevée------------------------ ----Priorité élevée------------------------
- CGI (TODO HUGO)
- Need to test normal body parsing (Verif avec HUGO)
- curl --resolve, for testing hostname - curl --resolve, for testing hostname
curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040 curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040
@@ -12,20 +9,10 @@
- cgi_cpp_status.cpp with POST dont show error page. Normal or not ? - cgi_cpp_status.cpp with POST dont show error page. Normal or not ?
For non blocking CGI :
// We could maybe,
// add FD_RD_FR_CHLD to epoll,
// return to the main loop,
// read FD_RD_FR_CHLD each time epoll say its ready,
// then try waitpid() with WNOHANG after each read.
// when waitpid() tell us its finish (or maybe when epoll return EPOLLHUP)
// then actually parse the script_output and send it to the client.
- check status in autoindex - check status in autoindex
- merge changes from hugo5 to master (attention a pas casse svp :clown:) - merge changes from hugo5 to master (attention a pas casse svp :clown:)
----Priorité modérée------------------------ ----Priorité modérée------------------------
- namespace utils ? - namespace utils ?
- change "std::string" to reference "std::string &" in most functions - change "std::string" to reference "std::string &" in most functions
@@ -36,8 +23,8 @@ and add "const" if apropriate.
----Priorité faible------------------------ ----Priorité faible------------------------
- idealy, we should not clear() raw_request after a response, - idealy, we should not clear() raw_request after a response,
but just the part we actually parsed for this response. but just the part we actually parsed for this response.
I think the client could send multiples request on the same connection one after the other without waiting for response. I think in HTTP/1 the client could not send multiples request on the same connection one after the other without waiting for response.
So raw_request could contain more than the first request we handle. So only for HTTP/2 adn HTTP/3 raw_request could contain more than the first request we handle.
- chunked request (need testing) - chunked request (need testing)
- client_body_limit 0 valeur special pour desactiver dans config - client_body_limit 0 valeur special pour desactiver dans config
- gerer le champ "Accept" du client - gerer le champ "Accept" du client
@@ -47,8 +34,6 @@ and add "const" if apropriate.
little global timeout on epoll, like 100ms, then find client that actualy need to timeout little global timeout on epoll, like 100ms, then find client that actualy need to timeout
if (actual_time - client.last_action_time > 10000ms){timeout(client)} if (actual_time - client.last_action_time > 10000ms){timeout(client)}
- add headers "Date" and "Last-Modified" to response - add headers "Date" and "Last-Modified" to response
- change "std::string" to reference "std::string &" in most functions
and add "const" if apropriate.
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI. - Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
Valgrind error RESOLVED ! : Valgrind error RESOLVED ! :
@@ -56,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== 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<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28) ==847174== by 0x49AA35D: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_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<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28) ==847174== by 0x49ABB52: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_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 0x427962: Webserv::run() (run_loop.cpp:153)
==847174== by 0x4052E9: main (main.cpp:38) ==847174== by 0x4052E9: main (main.cpp:38)

View File

@@ -12,7 +12,11 @@ Client::Client()
request_complete(false), request_complete(false),
assigned_server(NULL), assigned_server(NULL),
assigned_location(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), cgi_pid(0),
_fd(0), _fd(0),
_port(""), _port(""),
@@ -29,7 +33,11 @@ Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string a
request_complete(false), request_complete(false),
assigned_server(NULL), assigned_server(NULL),
assigned_location(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), cgi_pid(0),
_fd(afd), _fd(afd),
_port(aport), _port(aport),
@@ -87,7 +95,7 @@ void Client::parse_request_headers(std::vector<ServerConfig> &servers)
if (raw_request.find(CRLF CRLF) == NPOS) if (raw_request.find(CRLF CRLF) == NPOS)
return ; return ;
header_complete = true; header_complete = true;
clear_request(); // not mandatory clear_request_vars(); // not mandatory
_parse_request_line(); _parse_request_line();
if (status) if (status)
@@ -229,7 +237,7 @@ void Client::fill_script_path(std::string &path, size_t pos)
void Client::clear() void Client::clear()
{ {
clear_request(); clear_request_vars();
raw_request.clear(); raw_request.clear();
response.clear(); response.clear();
status = 0; status = 0;
@@ -238,12 +246,10 @@ void Client::clear()
request_complete = false; request_complete = false;
assigned_server = NULL; assigned_server = NULL;
assigned_location = NULL; assigned_location = NULL;
cgi_pipe_rfd = 0; clear_cgi_vars();
cgi_pid = 0;
cgi_output.clear();
} }
void Client::clear_request() void Client::clear_request_vars()
{ {
clear_script(); clear_script();
_request.method = UNKNOWN; _request.method = UNKNOWN;
@@ -264,6 +270,17 @@ void Client::clear_script()
_request.script.info.clear(); _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 // debug
void Client::print_client(std::string message) void Client::print_client(std::string message)
{ {

View File

@@ -40,6 +40,15 @@ struct Request
struct Script script; 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 class Client
{ {
public: public:
@@ -58,7 +67,13 @@ class Client
// size_t read_body_size; // unused for now // size_t read_body_size; // unused for now
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[] ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
const LocationConfig *assigned_location; 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; pid_t cgi_pid;
std::string cgi_output; std::string cgi_output;
@@ -88,7 +103,8 @@ class Client
void parse_request_headers(std::vector<ServerConfig> &servers); void parse_request_headers(std::vector<ServerConfig> &servers);
void parse_request_body(); void parse_request_body();
void clear(); void clear();
void clear_request(); void clear_request_vars();
void clear_cgi_vars();
void clear_script(); void clear_script();
void fill_script_path(std::string &path, size_t pos); void fill_script_path(std::string &path, size_t pos);
// DEBUG // DEBUG

View File

@@ -7,19 +7,19 @@
int main(int ac, char **av) int main(int ac, char **av)
{ {
std::vector<ServerConfig>* servers_config = NULL;
try try
{ {
std::string config = (ac == 2 ? av[1] : "./default.config"); std::string config = (ac == 2 ? av[1] : "./default.config");
ConfigParser configParser(config); ConfigParser configParser(config);
// configParser.print_content(); // configParser.print_content();
// i don't love that servers has to be a pointer... // i don't love that servers_config has to be a pointer...
std::vector<ServerConfig>* servers = configParser.parse(); servers_config = configParser.parse();
// use an iterator you moron // use an iterator you moron
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++) for (std::vector<ServerConfig>::iterator it = servers_config->begin(); it < servers_config->end(); it++)
{ {
(void)0; (void)0;
// std::cout << it->server_name << " "; // std::cout << it->server_name << " ";
@@ -33,14 +33,16 @@ int main(int ac, char **av)
Webserv serv; Webserv serv;
// serv.init_virtual_servers(); // serv.init_virtual_servers();
serv.init_virtual_servers(servers); serv.init_virtual_servers(servers_config);
delete servers; delete servers_config;
servers_config = NULL;
serv.run(); serv.run();
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << e.what() << '\n'; std::cerr << e.what() << '\n';
delete servers_config;
} }
return (0); return (0);
} }

View File

@@ -105,11 +105,12 @@ class Webserv
void _handle_last_signal(); void _handle_last_signal();
// close.cpp // close.cpp
void _close_client(int fd); void _close_client(int fd);
void _close_client_cgi_pipes(Client *client);
void _close_all_clients(); void _close_all_clients();
void _close_all_clients_fd(); void _close_all_clients_fd();
void _close_all_clients_cgi_fd(); void _close_all_clients_cgi_pipes();
void _close_cgi_pipe_rfd(int fd); void _close_cgi_pipe_r_from_child(int fd);
void _close_all_cgi_pipe_rfd(); void _close_all_cgi_pipe_r_from_child();
void _close_all_listen_sockets(); void _close_all_listen_sockets();
void _reopen_lsocket(std::vector<listen_socket>::iterator it); void _reopen_lsocket(std::vector<listen_socket>::iterator it);
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it); void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
@@ -124,6 +125,7 @@ class Webserv
// cgi.cpp // cgi.cpp
bool _is_cgi(Client *client, std::string path); bool _is_cgi(Client *client, std::string path);
size_t _cgi_pos(Client *client, std::string &path, size_t pos); size_t _cgi_pos(Client *client, std::string &path, size_t pos);
void _cgi_open_pipes(Client *client);
void _exec_cgi(Client *client); void _exec_cgi(Client *client);
void _set_env_vector(Client *client, std::vector<std::string> &env_vector); void _set_env_vector(Client *client, std::vector<std::string> &env_vector);
void _set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector); void _set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector);
@@ -136,11 +138,14 @@ class Webserv
void _check_fields_duplicates(Client *client, std::string & output); void _check_fields_duplicates(Client *client, std::string & output);
void _add_script_body_length_header(std::string & output); void _add_script_body_length_header(std::string & output);
void _remove_body_leading_empty_lines(std::string & output); void _remove_body_leading_empty_lines(std::string & output);
// cgi_epoll.cpp
Client *_find_cgi_fd(int cgi_fd); void _read_cgi_output(Client *client);
void _read_cgi_output(Client *client); void _handle_epollerr_cgi_output(uint32_t events, Client *client);
void _handle_epoll_error_cgi_fd(uint32_t events, Client *client); void _handle_epollhup_cgi_output(uint32_t events, Client *client);
void _cgi_epollhup(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);
/////////////////////// ///////////////////////

View File

@@ -66,6 +66,28 @@ size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
return NPOS; 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) void Webserv::_exec_cgi(Client *client)
{ {
char* env_cstr[19] = {NULL}; char* env_cstr[19] = {NULL};
@@ -152,24 +174,11 @@ void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vect
void Webserv::_exec_script(Client *client, char *env[]) 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; pid_t pid;
char * const nll[1] = {NULL}; char * const nll[1] = {NULL};
std::string script_output; std::string script_output;
std::string body = client->get_rq_body();
int fd_in[2];
int fd_out[2];
std::string path; std::string path;
::pipe(fd_in);
::pipe(fd_out);
pid = fork(); pid = fork();
if (pid == -1) if (pid == -1)
std::perror("err fork()"); std::perror("err fork()");
@@ -178,52 +187,57 @@ void Webserv::_exec_script(Client *client, char *env[])
std::signal(SIGPIPE, SIG_DFL); std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, SIG_DFL); std::signal(SIGINT, SIG_DFL);
_close_all_clients_fd(); if (dup2(client->cgi_pipe_r_from_parent, STDIN_FILENO) == -1)
::close(_epfd);
::close(FD_WR_TO_CHLD);
::close(FD_RD_FR_CHLD);
if (dup2(FD_RD_FR_PRNT, STDIN_FILENO) == -1)
{ {
std::perror("err dup2()"); std::perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential if (::close(client->cgi_pipe_r_from_parent) == -1) // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // 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(); 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()"); std::perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential if (::close(client->cgi_pipe_r_from_parent) == -1) // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // 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(); throw ExecFail();
} }
::close(FD_RD_FR_PRNT); _close_all_clients_fd();
::close(FD_WR_TO_PRNT); if (::close(_epfd) == -1)
std::perror("err close");
path = client->get_rq_script_path(); // Wut ? Only relative path ? path = client->get_rq_script_path(); // Wut ? Only relative path ?
/*DEBUG*/std::cerr << "execve:[" << path << "]\n"; /*DEBUG*/std::cerr << "execve:[" << path << "]\n";
if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing
{ {
std::perror("err execve()"); std::perror("err execve()");
::close(STDIN_FILENO); // Valgind debug, not essential if (::close(STDIN_FILENO) == -1) // Valgind debug, not essential
::close(STDOUT_FILENO); // Valgind debug, not essential std::perror("err close");
if (::close(STDOUT_FILENO) == -1) // Valgind debug, not essential
std::perror("err close");
throw ExecFail(); throw ExecFail();
} }
} }
else //parent else //parent
{ {
::close(FD_RD_FR_PRNT); if (::close(client->cgi_pipe_r_from_parent) == -1)
::close(FD_WR_TO_PRNT); std::perror("err close");
::write(FD_WR_TO_CHLD, body.c_str(), body.size()); // move this before the fork ? if (::close(client->cgi_pipe_w_to_parent) == -1)
::close(FD_WR_TO_CHLD); std::perror("err close");
if (::close(client->cgi_pipe_w_to_child) == -1)
std::perror("err close");
// add FD_RD_FR_CHLD to epoll, // add client->cgi_pipe_r_from_child to epoll,
_epoll_update(FD_RD_FR_CHLD, EPOLLIN, EPOLL_CTL_ADD); _epoll_update(client->cgi_pipe_r_from_child, EPOLLIN, EPOLL_CTL_ADD);
// stop monitoring client->fd until the cgi-script as done is job // stop monitoring client->fd until the cgi-script as done is job
_epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL); _epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL);
client->cgi_pipe_rfd = FD_RD_FR_CHLD;
client->cgi_pid = pid; 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); _check_script_status(client, output);
if (client->status >= 400 && client->status < 600) if (client->status >= 400 && client->status < 600)
return; 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); 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); _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); _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); _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) void Webserv::_check_script_status(Client *client, std::string & output)

129
srcs/webserv/cgi_epoll.cpp Normal file
View File

@@ -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<Client>::iterator it = _clients.begin();
std::vector<Client>::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<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_pipe_w_to_child == fd)
return (&(*it));
++it;
}
return (NULL);
}

View File

@@ -13,6 +13,7 @@ void Webserv::_close_client(int fd)
std::cerr << "close fd " << fd << "\n"; std::cerr << "close fd " << fd << "\n";
if (::close(fd) == -1) if (::close(fd) == -1)
std::perror("err close()"); std::perror("err close()");
_close_client_cgi_pipes(&(*it));
_clients.erase(it); _clients.erase(it);
break; 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() void Webserv::_close_all_clients()
{ {
_close_all_clients_fd(); _close_all_clients_fd();
@@ -28,7 +45,7 @@ void Webserv::_close_all_clients()
void Webserv::_close_all_clients_fd() void Webserv::_close_all_clients_fd()
{ {
_close_all_clients_cgi_fd(); _close_all_clients_cgi_pipes();
std::vector<Client>::iterator it = _clients.begin(); std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end(); std::vector<Client>::iterator it_end = _clients.end();
while (it != it_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<Client>::iterator it = _clients.begin(); std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end(); std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end) while (it != it_end)
{ {
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG // _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
if (it->cgi_pipe_rfd) _close_client_cgi_pipes(&(*it));
{
std::cerr << "close cgi-fd " << it->cgi_pipe_rfd << "\n";
if (::close(it->cgi_pipe_rfd) == -1)
std::perror("err close()");
}
++it; ++it;
} }
} }

View File

@@ -1,12 +1,11 @@
#include "Webserv.hpp" #include "Webserv.hpp"
void Webserv::_post(Client *client, const std::string &path)
{
/* /*
https://www.rfc-editor.org/rfc/rfc9110.html#name-post https://www.rfc-editor.org/rfc/rfc9110.html#name-post
*/ */
void Webserv::_post(Client *client, const std::string &path)
{
(void)path; // unused, we use "assigned_location->upload_dir" instead (void)path; // unused, we use "assigned_location->upload_dir" instead
std::cout << "_post()\n"; std::cout << "_post()\n";
std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n"; std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n";
@@ -26,8 +25,8 @@ void Webserv::_post(Client *client, const std::string &path)
_upload_files(client); _upload_files(client);
} }
// #include <ctime> // could add date to DEFAULT_NAME to avoid overwriting files
#define DEFAULT_NAME "unnamed_file" #define DEFAULT_NAME "unnamed_file"
// TODO : Loop for multi body
void Webserv::_upload_files(Client *client) void Webserv::_upload_files(Client *client)
{ {
std::cout << "_upload_files()\n"; std::cout << "_upload_files()\n";
@@ -37,6 +36,7 @@ void Webserv::_upload_files(Client *client)
std::string filename; std::string filename;
size_t pos; size_t pos;
bool file_existed = false; bool file_existed = false;
static int i_name = 0;
client->status = ::eval_file_access(client->assigned_location->upload_dir, W_OK); client->status = ::eval_file_access(client->assigned_location->upload_dir, W_OK);
if (client->status) if (client->status)
@@ -64,13 +64,10 @@ void Webserv::_upload_files(Client *client)
filename.erase(filename.size()-1, 1); filename.erase(filename.size()-1, 1);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
if (filename.empty())
filename = DEFAULT_NAME;
}
else
{
filename = DEFAULT_NAME;
} }
if (pos == NPOS || filename.empty())
filename = DEFAULT_NAME + ::itos(i_name++);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
path = client->assigned_location->upload_dir; // Assume there a final '/' path = client->assigned_location->upload_dir; // Assume there a final '/'
path.append(filename); path.append(filename);

View File

@@ -43,28 +43,21 @@ int Webserv::_send_response(Client *client)
if (client->response.empty()) if (client->response.empty())
{ {
_append_base_headers(client); _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"; _construct_response(client);
/*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(client->cgi_output); std::cout << B_PURPLE "-----------" RESET "\n\n"; if (client->cgi_state == CGI_WAIT_FOR_OUTPUT
_check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output; || client->cgi_state == CGI_WAIT_TO_EXEC)
if (client->status < 400) return SEND_IN_PROGRESS;
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";
} }
_insert_status_line(client); _insert_status_line(client);
if (client->status >= 400) if (client->status >= 400)
_error_html_response(client); _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 // /* 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); 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; std::string script_output;
path = _replace_url_root(client, client->get_rq_abs_path()); 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); _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) void Webserv::_process_method(Client *client, std::string &path)

View File

@@ -4,98 +4,6 @@
#define MAX_EVENTS 42 // arbitrary #define MAX_EVENTS 42 // arbitrary
#define TIMEOUT 3000 #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<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_pipe_rfd == cgi_fd)
return (&(*it));
++it;
}
return (NULL);
}
void Webserv::run() void Webserv::run()
{ {
std::cerr << "Server started\n"; std::cerr << "Server started\n";
@@ -104,7 +12,8 @@ void Webserv::run()
int i; int i;
int count_loop = 0; int count_loop = 0;
std::vector<listen_socket>::iterator it_lsocket; std::vector<listen_socket>::iterator it_lsocket;
Client *client_cgi = NULL; Client *client_cgi_output = NULL;
Client *client_cgi_input = NULL;
g_run = true; g_run = true;
while (g_run) while (g_run)
@@ -128,7 +37,8 @@ void Webserv::run()
{ {
try { try {
it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd); 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 (it_lsocket != _listen_sockets.end())
{ {
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
@@ -136,14 +46,21 @@ void Webserv::run()
else if (events[i].events & EPOLLIN) else if (events[i].events & EPOLLIN)
_accept_connection(*it_lsocket); _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) 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) 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) ) 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() else if (std::find(_clients.begin(), _clients.end(), events[i].data.fd) != _clients.end()) // TODO: save the it in var to avoid multiples find()
{ {

BIN
www/Kermit_the_Frog.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -285,12 +285,8 @@
<label for="fdownload">download file:</label><br> <label for="fdownload">download file:</label><br>
<input type="file" id="fupload" name="fupload"> <input type="file" id="fupload" name="fupload">
<select name="file"> <select name="file">
<option value="Cagneyc_intro.gif" >Cagneyc_intro.gif </option> <option value="Kermit_the_Frog.jpg" selected >Kermit_the_Frog.jpg</option>
<option value="file.md" >file.md </option> <option value="kermit_tea.jpg" >kermit_tea.jpg</option>
<option value="index.html" >index.html </option>
<option value="kermit.ico" >kermit.ico </option>
<option value="punpun.png" selected>punpun.png </option>
<option value="subject.pdf" >subject.pdf </option>
<option value="Van_Eyck_Portrait_Arnolfini.jpg" >Van_Eyck_Portrait_Arnolfini.jpg</option> <option value="Van_Eyck_Portrait_Arnolfini.jpg" >Van_Eyck_Portrait_Arnolfini.jpg</option>
<option value="directory" >directory </option> <option value="directory" >directory </option>
<option value="DOESNT_EXIST.BAD" >DOESNT_EXIST.BAD </option> <option value="DOESNT_EXIST.BAD" >DOESNT_EXIST.BAD </option>

BIN
www/kermit_tea.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB