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 \
|
||||
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 \
|
||||
|
||||
2
memo.txt
2
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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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() )
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
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