added timeout response (status 408)

+ added EPOLLERR and EPOLLHUP handling
+ fix root substitution for default "/" location (temp or permanent ?)
+ tested siege a little, seems good
This commit is contained in:
LuckyLaszlo
2022-08-11 07:12:13 +02:00
parent 08f6929db9
commit ab0bc2c4c0
13 changed files with 139 additions and 38 deletions

View File

@@ -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 \

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<listen_socket>::iterator it);
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::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

View File

@@ -42,3 +42,71 @@ void Webserv::_close_all_listen_sockets()
_listen_sockets.pop_back();
}
}
void Webserv::_reopen_lsocket(std::vector<listen_socket>::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<listen_socket>::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);
}

View File

@@ -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"

View File

@@ -49,7 +49,7 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* 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<int, std::string>::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));

View File

@@ -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() )

View File

@@ -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;
}
}

View File

@@ -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
{

View File

@@ -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<Client>().swap(_clients);
break;
}
catch (const std::exception& e)
{
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
++i;
}

18
srcs/webserv/timeout.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include "Webserv.hpp"
void Webserv::_timeout()
{
std::cerr << "_timeout()\n";
std::vector<Client>::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;
}
}