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:
2
Makefile
2
Makefile
@@ -23,7 +23,7 @@ SRCS = main.cpp \
|
|||||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
base.cpp init.cpp close.cpp epoll_update.cpp signal.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 \
|
run_loop.cpp timeout.cpp \
|
||||||
parser.cpp \
|
parser.cpp \
|
||||||
extraConfig.cpp \
|
extraConfig.cpp \
|
||||||
postProcessing.cpp \
|
postProcessing.cpp \
|
||||||
|
|||||||
2
memo.txt
2
memo.txt
@@ -1,5 +1,4 @@
|
|||||||
IN 42 SUBJECT AND/OR PRIORITY :
|
IN 42 SUBJECT AND/OR PRIORITY :
|
||||||
- Fix "href" in autoindex
|
|
||||||
- CGI
|
- CGI
|
||||||
- chunked request (response not mandatory it seems)
|
- chunked request (response not mandatory it seems)
|
||||||
- Ecrire des tests !
|
- Ecrire des tests !
|
||||||
@@ -9,7 +8,6 @@ IN 42 SUBJECT AND/OR PRIORITY :
|
|||||||
- replace atoi() with a better function to avoid overflow
|
- replace atoi() with a better function to avoid overflow
|
||||||
like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/
|
like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/
|
||||||
-----------------------------
|
-----------------------------
|
||||||
- 408 Request Timeout
|
|
||||||
- gerer le champ "Accept" du client
|
- gerer le champ "Accept" du client
|
||||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
- 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)
|
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
Client::Client()
|
Client::Client()
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
|
request_complete(false),
|
||||||
read_body_size(0),
|
read_body_size(0),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
@@ -22,6 +23,7 @@ Client::Client()
|
|||||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
|
request_complete(false),
|
||||||
read_body_size(0),
|
read_body_size(0),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
@@ -130,6 +132,7 @@ void Client::clear()
|
|||||||
{
|
{
|
||||||
clear_request();
|
clear_request();
|
||||||
header_complete = false;
|
header_complete = false;
|
||||||
|
request_complete = false;
|
||||||
read_body_size = 0;
|
read_body_size = 0;
|
||||||
assigned_server = NULL;
|
assigned_server = NULL;
|
||||||
assigned_location = NULL;
|
assigned_location = NULL;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class Client
|
|||||||
std::string response;
|
std::string response;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
bool header_complete;
|
bool header_complete;
|
||||||
|
bool request_complete;
|
||||||
size_t read_body_size;
|
size_t read_body_size;
|
||||||
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;
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ class Webserv
|
|||||||
void _request(Client *client);
|
void _request(Client *client);
|
||||||
int _read_request(Client *client);
|
int _read_request(Client *client);
|
||||||
// response.cpp
|
// response.cpp
|
||||||
void _response(Client *client);
|
void _response(Client *client);
|
||||||
int _send_response(Client *client);
|
int _send_response(Client *client);
|
||||||
void _append_base_headers(Client *client);
|
void _append_base_headers(Client *client);
|
||||||
void _construct_response(Client *client);
|
void _construct_response(Client *client);
|
||||||
void _process_method(Client *client);
|
void _process_method(Client *client);
|
||||||
void _insert_status_line(Client *client);
|
void _insert_status_line(Client *client);
|
||||||
void _error_html_response(Client *client);
|
void _error_html_response(Client *client);
|
||||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
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[]
|
// 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;
|
// const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||||
std::string _determine_file_extension(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_client(int fd);
|
||||||
void _close_all_clients();
|
void _close_all_clients();
|
||||||
void _close_all_listen_sockets();
|
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
|
// init.cpp
|
||||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||||
void _listen(int socket_fd, unsigned int max_connections);
|
void _listen(int socket_fd, unsigned int max_connections);
|
||||||
void _init_http_status_map();
|
void _init_http_status_map();
|
||||||
void _init_mime_types_map();
|
void _init_mime_types_map();
|
||||||
|
// timeout.cpp
|
||||||
|
void _timeout();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -42,3 +42,71 @@ void Webserv::_close_all_listen_sockets()
|
|||||||
_listen_sockets.pop_back();
|
_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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
# define S403 "403 Forbidden"
|
# define S403 "403 Forbidden"
|
||||||
# define S404 "404 Not Found"
|
# define S404 "404 Not Found"
|
||||||
# define S405 "405 Method Not Allowed"
|
# define S405 "405 Method Not Allowed"
|
||||||
|
# define S408 "408 Request Timeout"
|
||||||
# define S413 "413 Content Too Large"
|
# define S413 "413 Content Too Large"
|
||||||
|
|
||||||
# define S500 "500 Internal Server Error"
|
# define S500 "500 Internal Server Error"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
|||||||
// HUGO ADD END
|
// HUGO ADD END
|
||||||
|
|
||||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
_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)
|
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||||
throw std::runtime_error("Socket init");
|
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()
|
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
|
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||||
*/
|
*/
|
||||||
typedef std::map<int, std::string>::value_type status_pair;
|
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(403, S403));
|
||||||
_http_status.insert(status_pair(404, S404));
|
_http_status.insert(status_pair(404, S404));
|
||||||
_http_status.insert(status_pair(405, S405));
|
_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(413, S413));
|
||||||
|
|
||||||
_http_status.insert(status_pair(500, S500));
|
_http_status.insert(status_pair(500, S500));
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ void Webserv::_get(Client *client)
|
|||||||
std::string path = client->get_rq_abs_path();
|
std::string path = client->get_rq_abs_path();
|
||||||
|
|
||||||
std::cerr << "path before = " << path << "\n"; // DEBUG
|
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
|
std::cerr << "path after = " << path << "\n"; // DEBUG
|
||||||
|
|
||||||
// TMP HUGO ( We move this in process_switch() )
|
// TMP HUGO ( We move this in process_switch() )
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ void Webserv::_request(Client *client)
|
|||||||
else if (ret == READ_COMPLETE)
|
else if (ret == READ_COMPLETE)
|
||||||
{
|
{
|
||||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||||
|
client->request_complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void Webserv::_response(Client *client)
|
|||||||
}
|
}
|
||||||
else if (ret == SEND_COMPLETE)
|
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());
|
_close_client(client->get_cl_fd());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,35 +21,39 @@ void Webserv::run()
|
|||||||
if (nfds == -1)
|
if (nfds == -1)
|
||||||
{
|
{
|
||||||
std::perror("err epoll_wait()");
|
std::perror("err epoll_wait()");
|
||||||
throw std::runtime_error("Epoll wait");
|
if (errno == EINTR)
|
||||||
}
|
g_run = false;
|
||||||
else if (nfds == 0)
|
else
|
||||||
{
|
throw std::runtime_error("Epoll wait");
|
||||||
if (!_clients.empty())
|
|
||||||
{
|
|
||||||
std::cerr << "Timeout " << TIMEOUT << "ms\n";
|
|
||||||
_close_all_clients();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (nfds == 0 && !_clients.empty())
|
||||||
|
_timeout();
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < nfds)
|
while (i < nfds)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
// TODO : handle EPOLLERR and EPOLLHUP
|
|
||||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
if (it_socket != _listen_sockets.end())
|
||||||
_accept_connection(*it_socket);
|
{
|
||||||
else if (events[i].events & EPOLLIN)
|
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
_handle_epoll_error_lsocket(events[i].events, it_socket);
|
||||||
else if (events[i].events & EPOLLOUT)
|
else if (events[i].events & EPOLLIN)
|
||||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
_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;
|
++i;
|
||||||
if (!g_run)
|
if (!g_run)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (const std::bad_alloc& e)
|
catch (const std::bad_alloc& e) {
|
||||||
{
|
|
||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
_close_all_clients();
|
_close_all_clients();
|
||||||
/* Swap to free the memory
|
/* Swap to free the memory
|
||||||
@@ -57,8 +61,7 @@ void Webserv::run()
|
|||||||
std::vector<Client>().swap(_clients);
|
std::vector<Client>().swap(_clients);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e) {
|
||||||
{
|
|
||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|||||||
18
srcs/webserv/timeout.cpp
Normal file
18
srcs/webserv/timeout.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user