diff --git a/Makefile b/Makefile index 417d8e6..afc17fd 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ SRCS = main.cpp \ base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \ accept.cpp request.cpp response.cpp \ method_get.cpp method_post.cpp method_delete.cpp \ - run_loop.cpp \ + run_loop.cpp timeout.cpp \ parser.cpp \ extraConfig.cpp \ postProcessing.cpp \ diff --git a/memo.txt b/memo.txt index 2860c19..b6cd444 100644 --- a/memo.txt +++ b/memo.txt @@ -1,5 +1,4 @@ IN 42 SUBJECT AND/OR PRIORITY : -- Fix "href" in autoindex - CGI - chunked request (response not mandatory it seems) - Ecrire des tests ! @@ -9,7 +8,6 @@ IN 42 SUBJECT AND/OR PRIORITY : - replace atoi() with a better function to avoid overflow like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/ ----------------------------- -- 408 Request Timeout - gerer le champ "Accept" du client - gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root") - do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 4068f38..0b30006 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -8,6 +8,7 @@ Client::Client() : status(0), header_complete(false), + request_complete(false), read_body_size(0), assigned_server(NULL), assigned_location(NULL), @@ -22,6 +23,7 @@ Client::Client() Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip) : status(0), header_complete(false), + request_complete(false), read_body_size(0), assigned_server(NULL), assigned_location(NULL), @@ -130,6 +132,7 @@ void Client::clear() { clear_request(); header_complete = false; + request_complete = false; read_body_size = 0; assigned_server = NULL; assigned_location = NULL; diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 43a0913..325abc3 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -46,6 +46,7 @@ class Client std::string response; unsigned int status; bool header_complete; + bool request_complete; size_t read_body_size; ServerConfig *assigned_server; // cant be const cause of error_pages.operator[] const LocationConfig *assigned_location; diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 35c082d..71b8e21 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -76,14 +76,14 @@ class Webserv void _request(Client *client); int _read_request(Client *client); // response.cpp - void _response(Client *client); - int _send_response(Client *client); - void _append_base_headers(Client *client); - void _construct_response(Client *client); - void _process_method(Client *client); - void _insert_status_line(Client *client); - void _error_html_response(Client *client); - void _append_body(Client *client, const std::string &body, const std::string &file_extension = ""); + void _response(Client *client); + int _send_response(Client *client); + void _append_base_headers(Client *client); + void _construct_response(Client *client); + void _process_method(Client *client); + void _insert_status_line(Client *client); + void _error_html_response(Client *client); + void _append_body(Client *client, const std::string &body, const std::string &file_extension = ""); // ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[] // const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const; std::string _determine_file_extension(const std::string &path) const; @@ -116,12 +116,16 @@ class Webserv void _close_client(int fd); void _close_all_clients(); void _close_all_listen_sockets(); + void _reopen_lsocket(std::vector::iterator it); + void _handle_epoll_error_lsocket(uint32_t events, std::vector::iterator it); + void _handle_epoll_error_client(uint32_t events, int fd); // init.cpp void _bind(int socket_fd, in_port_t port, std::string host); void _listen(int socket_fd, unsigned int max_connections); void _init_http_status_map(); void _init_mime_types_map(); - + // timeout.cpp + void _timeout(); }; #endif diff --git a/srcs/webserv/close.cpp b/srcs/webserv/close.cpp index aa236f3..d021245 100644 --- a/srcs/webserv/close.cpp +++ b/srcs/webserv/close.cpp @@ -42,3 +42,71 @@ void Webserv::_close_all_listen_sockets() _listen_sockets.pop_back(); } } + +void Webserv::_reopen_lsocket(std::vector::iterator it) +{ +/* +** Many common code with init_virtual_servers(). Could refactor it. +*/ + int ret; + + std::cerr << "close lsocket " << it->fd << "\n"; + if (::close(it->fd) == -1) + std::perror("err close()"); + std::cerr << "try to reopen lsocket\n"; + ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ? + if (ret == -1) + { + std::perror("err socket()"); + _listen_sockets.erase(it); + return; + } + it->fd = ret; + + // HUGO ADD + // allow socket descriptor to be reuseable + // I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select + int on = 1; + if (setsockopt(it->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + { + ::perror("err setsockopt()"); + _listen_sockets.erase(it); + return; + } + // HUGO ADD END + + try { + _bind(it->fd, std::atoi(it->port.c_str()), it->host); + _listen(it->fd, 42); // 42 arbitrary + } + catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + _listen_sockets.erase(it); + return; + } + + if (_epoll_update(it->fd, EPOLLIN, EPOLL_CTL_ADD) == -1) + { + _listen_sockets.erase(it); + return; + } + std::cerr << "reopen success\n"; +} + +void Webserv::_handle_epoll_error_lsocket(uint32_t events, std::vector::iterator it) +{ + if (events & EPOLLERR) + std::cerr << "EPOLLERR on lsocket fd " << it->fd << "\n"; // DEBUG + if (events & EPOLLHUP) + std::cerr << "EPOLLHUP on lsocket fd " << it->fd << "\n"; // DEBUG + _reopen_lsocket(it); +} + +void Webserv::_handle_epoll_error_client(uint32_t events, int fd) +{ + if (events & EPOLLERR) + std::cerr << "EPOLLERR on client fd " << fd << "\n"; // DEBUG + if (events & EPOLLHUP) + std::cerr << "EPOLLHUP on client fd " << fd << "\n"; // DEBUG + _close_client(fd); +} diff --git a/srcs/webserv/http_status.hpp b/srcs/webserv/http_status.hpp index beba594..b002129 100644 --- a/srcs/webserv/http_status.hpp +++ b/srcs/webserv/http_status.hpp @@ -41,6 +41,7 @@ # define S403 "403 Forbidden" # define S404 "404 Not Found" # define S405 "405 Method Not Allowed" +# define S408 "408 Request Timeout" # define S413 "413 Content Too Large" # define S500 "500 Internal Server Error" diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 4dc88d8..4f66cfd 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -49,7 +49,7 @@ void Webserv::init_virtual_servers(std::vector* servers) // HUGO ADD END _bind(new_socket.fd, std::atoi(it->port.c_str()), it->host); - _listen(new_socket.fd, 512); // 512 arbitrary + _listen(new_socket.fd, 42); // 42 arbitrary if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1) throw std::runtime_error("Socket init"); @@ -89,7 +89,7 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections) void Webserv::_init_http_status_map() { -/* "map.insert()" over "map.operator[]" : +/* "map.insert()" more appropriate than "map.operator[]" : ** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93 */ typedef std::map::value_type status_pair; @@ -110,6 +110,7 @@ void Webserv::_init_http_status_map() _http_status.insert(status_pair(403, S403)); _http_status.insert(status_pair(404, S404)); _http_status.insert(status_pair(405, S405)); + _http_status.insert(status_pair(408, S408)); _http_status.insert(status_pair(413, S413)); _http_status.insert(status_pair(500, S500)); diff --git a/srcs/webserv/method_get.cpp b/srcs/webserv/method_get.cpp index 72683f3..12b5751 100644 --- a/srcs/webserv/method_get.cpp +++ b/srcs/webserv/method_get.cpp @@ -7,7 +7,10 @@ void Webserv::_get(Client *client) std::string path = client->get_rq_abs_path(); std::cerr << "path before = " << path << "\n"; // DEBUG - path.replace(0, client->assigned_location->path.size(), client->assigned_location->root); + if (client->assigned_location->path == "/") + path.insert(0, client->assigned_location->root); + else + path.replace(0, client->assigned_location->path.size(), client->assigned_location->root); std::cerr << "path after = " << path << "\n"; // DEBUG // TMP HUGO ( We move this in process_switch() ) diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index d3d075a..b70056c 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -26,6 +26,7 @@ void Webserv::_request(Client *client) else if (ret == READ_COMPLETE) { _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD); + client->request_complete = true; } } diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index fc5988b..7540cdf 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -21,7 +21,7 @@ void Webserv::_response(Client *client) } else if (ret == SEND_COMPLETE) { - if (client->get_rq_headers("Connection") == "close") + if (client->get_rq_headers("Connection") == "close" || client->status == 408) _close_client(client->get_cl_fd()); else { diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index cb91672..108a226 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -21,35 +21,39 @@ void Webserv::run() if (nfds == -1) { std::perror("err epoll_wait()"); - throw std::runtime_error("Epoll wait"); - } - else if (nfds == 0) - { - if (!_clients.empty()) - { - std::cerr << "Timeout " << TIMEOUT << "ms\n"; - _close_all_clients(); - } + if (errno == EINTR) + g_run = false; + else + throw std::runtime_error("Epoll wait"); } + else if (nfds == 0 && !_clients.empty()) + _timeout(); i = 0; while (i < nfds) { - try - { - // TODO : handle EPOLLERR and EPOLLHUP + try { it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd); - if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN) - _accept_connection(*it_socket); - else if (events[i].events & EPOLLIN) - _request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); - else if (events[i].events & EPOLLOUT) - _response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); + if (it_socket != _listen_sockets.end()) + { + if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) + _handle_epoll_error_lsocket(events[i].events, it_socket); + else if (events[i].events & EPOLLIN) + _accept_connection(*it_socket); + } + else + { + if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) + _handle_epoll_error_client(events[i].events, events[i].data.fd); + else if (events[i].events & EPOLLIN) + _request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); + else if (events[i].events & EPOLLOUT) + _response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); + } ++i; if (!g_run) break; } - catch (const std::bad_alloc& e) - { + catch (const std::bad_alloc& e) { std::cerr << e.what() << '\n'; _close_all_clients(); /* Swap to free the memory @@ -57,8 +61,7 @@ void Webserv::run() std::vector().swap(_clients); break; } - catch (const std::exception& e) - { + catch (const std::exception& e) { std::cerr << e.what() << '\n'; ++i; } diff --git a/srcs/webserv/timeout.cpp b/srcs/webserv/timeout.cpp new file mode 100644 index 0000000..9bd3e8c --- /dev/null +++ b/srcs/webserv/timeout.cpp @@ -0,0 +1,18 @@ + +#include "Webserv.hpp" + +void Webserv::_timeout() +{ + std::cerr << "_timeout()\n"; + std::vector::iterator it = _clients.begin(); + while (it != _clients.end()) + { + if (!it->request_complete) + { + std::cerr << "timeout request fd " << it->get_cl_fd() << "\n"; + it->status = 408; + _epoll_update(it->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD); + } + ++it; + } +}