successful merge?

This commit is contained in:
Me
2022-08-08 15:25:16 +02:00
11 changed files with 334 additions and 247 deletions

View File

@@ -1,13 +1,23 @@
IN 42 SUBJECT, PRIORITY :
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement) - chunked request (response not mandatory it seems)
- upload files with confif "upload_dir"
- 505 HTTP Version Not Supported
- CGI
- handle redirection - handle redirection
- index and autoindex
- Ecrire des tests !
-----------------------------
- replace atoi() with a better function
- 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)
- maybe add a "last_action_time" in Client for timeout handling - maybe add a "last_action_time" in Client for timeout handling
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 - change "std::string" to reference "std::string &" in most functions
and add "const" if apropriate. and add "const" if apropriate.
- http_method en mode binary flags. "std::vector<http_method> allow_methods" -> "unsigned int allow_methods;"
- Dans le parsing, trier les "locations" par ordre de precision. - Dans le parsing, trier les "locations" par ordre de precision.
Compter les "/" dans le chemin, les locations avec le plus de "/" seront en premier dans le vector. Compter les "/" dans le chemin, les locations avec le plus de "/" seront en premier dans le vector.
- 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.

View File

@@ -1,13 +1,19 @@
#include "Client.hpp" #include "Client.hpp"
char Client::buf[MAX_FILESIZE+1];
/********************************************* /*********************************************
* COPLIENS * COPLIENS
*********************************************/ *********************************************/
Client::Client() : fd(0), body_size(0), status(0) { Client::Client()
: fd(0),
lsocket(NULL),
status(0),
header_complete(false),
read_body_size(0),
assigned_server(NULL),
assigned_location(NULL)
{
return; return;
} }
@@ -54,9 +60,12 @@ void Client::parse_request()
void Client::clear() void Client::clear()
{ {
clear_request(); clear_request();
header_complete = false;
read_body_size = 0;
assigned_server = NULL;
assigned_location = NULL;
raw_request.clear(); raw_request.clear();
response.clear(); response.clear();
body_size = 0;
status = 0; status = 0;
} }
@@ -128,6 +137,8 @@ void Client::_parse_request_headers( std::vector<std::string> list )
void Client::_parse_request_body( size_t pos ) void Client::_parse_request_body( size_t pos )
{ {
// TODO : a revoir avec une std::string,
// pour ne pas avoir le probleme d'un '0' qui marque la fin des données
std::string body = &raw_request[pos]; std::string body = &raw_request[pos];
_request.body = body; _request.body = body;

View File

@@ -7,6 +7,7 @@
# include <map> # include <map>
# include <vector> # include <vector>
# include "utils.hpp" # include "utils.hpp"
# include "ServerConfig.hpp"
struct Request struct Request
{ {
@@ -17,7 +18,6 @@ struct Request
std::string body; std::string body;
}; };
# define MAX_FILESIZE 1000000 // (1Mo)
class Client class Client
{ {
public: public:
@@ -27,12 +27,16 @@ class Client
//Client &operator=(Client const &rhs); //Client &operator=(Client const &rhs);
int fd; int fd;
const listen_socket *lsocket;
std::string raw_request; std::string raw_request;
std::string response; std::string response;
static char buf[MAX_FILESIZE+1];
size_t body_size;
unsigned int status; unsigned int status;
listen_socket *lsocket;
bool header_complete;
size_t read_body_size;
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
const LocationConfig *assigned_location;
// const functions ? // const functions ?
http_method get_method(); http_method get_method();

View File

@@ -44,7 +44,7 @@ int main(int ac, char **av)
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cout << e.what() << '\n'; std::cerr << e.what() << '\n';
} }
return (0); return (0);

View File

@@ -1,6 +1,14 @@
#include "utils.hpp" #include "utils.hpp"
void throw_test()
{
static int i = 0;
++i;
if (i % 8 == 0)
throw std::bad_alloc();
}
std::vector<std::string> split(std::string input, char delimiter) std::vector<std::string> split(std::string input, char delimiter)
{ {
std::vector<std::string> answer; std::vector<std::string> answer;

View File

@@ -50,5 +50,6 @@ http_method str_to_http_method(std::string &str);
std::string http_methods_to_str(unsigned int methods); std::string http_methods_to_str(unsigned int methods);
int path_is_valid(std::string path); int path_is_valid(std::string path);
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr); void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
void throw_test();
#endif #endif

View File

@@ -40,6 +40,8 @@ void signal_handler(int signum);
# define FAILURE -1 # define FAILURE -1
# define SUCCESS 1 # define SUCCESS 1
# define MIME_TYPE_DEFAULT "application/octet-stream"
class Webserv class Webserv
{ {
public: public:
@@ -73,16 +75,17 @@ class Webserv
void _read_request(Client *client); void _read_request(Client *client);
// response.cpp // response.cpp
void _response(Client *client); void _response(Client *client);
void _send_response(Client *client, ServerConfig &server); void _send_response(Client *client);
void _append_base_headers(Client *client); void _append_base_headers(Client *client);
void _construct_response(Client *client, ServerConfig &server); void _construct_response(Client *client);
void _process_method(Client *client, ServerConfig &server, LocationConfig &location); void _process_method(Client *client);
void _insert_status_line(Client *client); void _insert_status_line(Client *client);
void _error_html_response(Client *client, ServerConfig &server); void _error_html_response(Client *client);
void _append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension = ""); void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
void _get(Client *client, ServerConfig &server, LocationConfig &location); void _get(Client *client);
// void _get(Client *client, ServerConfig &server, LocationConfig &location);
// in progress // in progress
void _autoindex(Client *client, LocationConfig &location, std::string &path); void _autoindex(Client *client, LocationConfig &location, std::string &path);
@@ -90,14 +93,15 @@ class Webserv
void _get_file(Client *client, const std::string &path); void _get_file(Client *client, const std::string &path);
void _post(Client *client, ServerConfig &server, LocationConfig &location); void _post(Client *client);
void _post_file(Client *client, const std::string &path); void _post_file(Client *client, const std::string &path);
void _delete(Client *client, ServerConfig &server, LocationConfig &location); void _delete(Client *client);
void _delete_file(Client *client, const std::string &path); void _delete_file(Client *client, const std::string &path);
ServerConfig &_determine_process_server(Client *client); ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
LocationConfig &_determine_location(ServerConfig &server, std::string &path); const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
std::string _determine_file_extension(const std::string &path) const;
// cgi_script.cpp // cgi_script.cpp
bool _is_cgi(Client *client); bool _is_cgi(Client *client);
void _exec_cgi(Client *client); void _exec_cgi(Client *client);

View File

@@ -89,132 +89,144 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
void Webserv::_init_http_status_map() void Webserv::_init_http_status_map()
{ {
_http_status[200] = S200; /* "map.insert()" over "map.operator[]" :
_http_status[201] = S201; ** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
_http_status[204] = S204; */
typedef std::map<int, std::string>::value_type status_pair;
_http_status[400] = S400; // _http_status.insert(std::make_pair(200, S200)); // equivalent
_http_status[403] = S403; _http_status.insert(status_pair(200, S200));
_http_status[404] = S404; _http_status.insert(status_pair(201, S201));
_http_status[405] = S405; _http_status.insert(status_pair(204, S204));
_http_status[413] = S413;
_http_status[500] = S500; _http_status.insert(status_pair(400, S400));
_http_status[501] = S501; _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(413, S413));
_http_status.insert(status_pair(500, S500));
_http_status.insert(status_pair(501, S501));
} }
void Webserv::_init_mime_types_map() void Webserv::_init_mime_types_map()
{ {
_mime_types[""] = "application/octet-stream"; /* From :
** http://nginx.org/en/docs/http/ngx_http_core_module.html#types
*/
typedef std::map<std::string, std::string>::value_type mime_pair;
_mime_types["html"] = "text/html"; _mime_types.insert(mime_pair("", MIME_TYPE_DEFAULT));
_mime_types["htm"] = "text/html";
_mime_types["shtml"] = "text/html";
_mime_types["css"] = "text/css";
_mime_types["xml"] = "text/xml";
_mime_types["gif"] = "image/gif";
_mime_types["jpeg"] = "image/jpeg";
_mime_types["jpg"] = "image/jpeg";
_mime_types["js"] = "application/javascript";
_mime_types["atom"] = "application/atom+xml";
_mime_types["rss"] = "application/rss+xml";
_mime_types["mml"] = "text/mathml"; _mime_types.insert(mime_pair("html", "text/html"));
_mime_types["txt"] = "text/plain"; _mime_types.insert(mime_pair("html", "text/html"));
_mime_types["jad"] = "text/vnd.sun.j2me.app-descriptor"; _mime_types.insert(mime_pair("htm", "text/html"));
_mime_types["wml"] = "text/vnd.wap.wml"; _mime_types.insert(mime_pair("shtml", "text/html"));
_mime_types["htc"] = "text/x-component"; _mime_types.insert(mime_pair("css", "text/css"));
_mime_types.insert(mime_pair("xml", "text/xml"));
_mime_types.insert(mime_pair("gif", "image/gif"));
_mime_types.insert(mime_pair("jpeg", "image/jpeg"));
_mime_types.insert(mime_pair("jpg", "image/jpeg"));
_mime_types.insert(mime_pair("js", "application/javascript"));
_mime_types.insert(mime_pair("atom", "application/atom+xml"));
_mime_types.insert(mime_pair("rss", "application/rss+xml"));
_mime_types["png"] = "image/png"; _mime_types.insert(mime_pair("mml", "text/mathml"));
_mime_types["tif"] = "image/tiff"; _mime_types.insert(mime_pair("txt", "text/plain"));
_mime_types["tiff"] = "image/tiff"; _mime_types.insert(mime_pair("jad", "text/vnd.sun.j2me.app-descriptor"));
_mime_types["wbmp"] = "image/vnd.wap.wbmp"; _mime_types.insert(mime_pair("wml", "text/vnd.wap.wml"));
_mime_types["ico"] = "image/x-icon"; _mime_types.insert(mime_pair("htc", "text/x-component"));
_mime_types["jng"] = "image/x-jng";
_mime_types["bmp"] = "image/x-ms-bmp";
_mime_types["svg"] = "image/svg+xml";
_mime_types["svgz"] = "image/svg+xml";
_mime_types["webp"] = "image/webp";
_mime_types["woff"] = "application/font-woff"; _mime_types.insert(mime_pair("png", "image/png"));
_mime_types["jar"] = "application/java-archive"; _mime_types.insert(mime_pair("tif", "image/tiff"));
_mime_types["war"] = "application/java-archive"; _mime_types.insert(mime_pair("tiff", "image/tiff"));
_mime_types["ear"] = "application/java-archive"; _mime_types.insert(mime_pair("wbmp", "image/vnd.wap.wbmp"));
_mime_types["json"] = "application/json"; _mime_types.insert(mime_pair("ico", "image/x-icon"));
_mime_types["hqx"] = "application/mac-binhex40"; _mime_types.insert(mime_pair("jng", "image/x-jng"));
_mime_types["doc"] = "application/msword"; _mime_types.insert(mime_pair("bmp", "image/x-ms-bmp"));
_mime_types["pdf"] = "application/pdf"; _mime_types.insert(mime_pair("svg", "image/svg+xml"));
_mime_types["ps"] = "application/postscript"; _mime_types.insert(mime_pair("svgz", "image/svg+xml"));
_mime_types["eps"] = "application/postscript"; _mime_types.insert(mime_pair("webp", "image/webp"));
_mime_types["ai"] = "application/postscript";
_mime_types["rtf"] = "application/rtf";
_mime_types["m3u8"] = "application/vnd.apple.mpegurl";
_mime_types["xls"] = "application/vnd.ms-excel";
_mime_types["eot"] = "application/vnd.ms-fontobject";
_mime_types["ppt"] = "application/vnd.ms-powerpoint";
_mime_types["wmlc"] = "application/vnd.wap.wmlc";
_mime_types["kml"] = "application/vnd.google-earth.kml+xml";
_mime_types["kmz"] = "application/vnd.google-earth.kmz";
_mime_types["7z"] = "application/x-7z-compressed";
_mime_types["cco"] = "application/x-cocoa";
_mime_types["jardiff"] = "application/x-java-archive-diff";
_mime_types["jnlp"] = "application/x-java-jnlp-file";
_mime_types["run"] = "application/x-makeself";
_mime_types["pl"] = "application/x-perl";
_mime_types["pm"] = "application/x-perl";
_mime_types["prc"] = "application/x-pilot";
_mime_types["pdb"] = "application/x-pilot";
_mime_types["rar"] = "application/x-rar-compressed";
_mime_types["rpm"] = "application/x-redhat-package-manager";
_mime_types["sea"] = "application/x-sea";
_mime_types["swf"] = "application/x-shockwave-flash";
_mime_types["sit"] = "application/x-stuffit";
_mime_types["tcl"] = "application/x-tcl";
_mime_types["tk"] = "application/x-tcl";
_mime_types["der"] = "application/x-x509-ca-cert";
_mime_types["pem"] = "application/x-x509-ca-cert";
_mime_types["crt"] = "application/x-x509-ca-cert";
_mime_types["xpi"] = "application/x-xpinstall";
_mime_types["xhtml"] = "application/xhtml+xml";
_mime_types["xspf"] = "application/xspf+xml";
_mime_types["zip"] = "application/zip";
_mime_types["bin"] = "application/octet-stream"; _mime_types.insert(mime_pair("woff", "application/font-woff"));
_mime_types["exe"] = "application/octet-stream"; _mime_types.insert(mime_pair("jar", "application/java-archive"));
_mime_types["dll"] = "application/octet-stream"; _mime_types.insert(mime_pair("war", "application/java-archive"));
_mime_types["deb"] = "application/octet-stream"; _mime_types.insert(mime_pair("ear", "application/java-archive"));
_mime_types["dmg"] = "application/octet-stream"; _mime_types.insert(mime_pair("json", "application/json"));
_mime_types["iso"] = "application/octet-stream"; _mime_types.insert(mime_pair("hqx", "application/mac-binhex40"));
_mime_types["img"] = "application/octet-stream"; _mime_types.insert(mime_pair("doc", "application/msword"));
_mime_types["msi"] = "application/octet-stream"; _mime_types.insert(mime_pair("pdf", "application/pdf"));
_mime_types["msp"] = "application/octet-stream"; _mime_types.insert(mime_pair("ps", "application/postscript"));
_mime_types["msm"] = "application/octet-stream"; _mime_types.insert(mime_pair("eps", "application/postscript"));
_mime_types.insert(mime_pair("ai", "application/postscript"));
_mime_types.insert(mime_pair("rtf", "application/rtf"));
_mime_types.insert(mime_pair("m3u8", "application/vnd.apple.mpegurl"));
_mime_types.insert(mime_pair("xls", "application/vnd.ms-excel"));
_mime_types.insert(mime_pair("eot", "application/vnd.ms-fontobject"));
_mime_types.insert(mime_pair("ppt", "application/vnd.ms-powerpoint"));
_mime_types.insert(mime_pair("wmlc", "application/vnd.wap.wmlc"));
_mime_types.insert(mime_pair("kml", "application/vnd.google-earth.kml+xml"));
_mime_types.insert(mime_pair("kmz", "application/vnd.google-earth.kmz"));
_mime_types.insert(mime_pair("7z", "application/x-7z-compressed"));
_mime_types.insert(mime_pair("cco", "application/x-cocoa"));
_mime_types.insert(mime_pair("jardiff", "application/x-java-archive-diff"));
_mime_types.insert(mime_pair("jnlp", "application/x-java-jnlp-file"));
_mime_types.insert(mime_pair("run", "application/x-makeself"));
_mime_types.insert(mime_pair("pl", "application/x-perl"));
_mime_types.insert(mime_pair("pm", "application/x-perl"));
_mime_types.insert(mime_pair("prc", "application/x-pilot"));
_mime_types.insert(mime_pair("pdb", "application/x-pilot"));
_mime_types.insert(mime_pair("rar", "application/x-rar-compressed"));
_mime_types.insert(mime_pair("rpm", "application/x-redhat-package-manager"));
_mime_types.insert(mime_pair("sea", "application/x-sea"));
_mime_types.insert(mime_pair("swf", "application/x-shockwave-flash"));
_mime_types.insert(mime_pair("sit", "application/x-stuffit"));
_mime_types.insert(mime_pair("tcl", "application/x-tcl"));
_mime_types.insert(mime_pair("tk", "application/x-tcl"));
_mime_types.insert(mime_pair("der", "application/x-x509-ca-cert"));
_mime_types.insert(mime_pair("pem", "application/x-x509-ca-cert"));
_mime_types.insert(mime_pair("crt", "application/x-x509-ca-cert"));
_mime_types.insert(mime_pair("xpi", "application/x-xpinstall"));
_mime_types.insert(mime_pair("xhtml", "application/xhtml+xml"));
_mime_types.insert(mime_pair("xspf", "application/xspf+xml"));
_mime_types.insert(mime_pair("zip", "application/zip"));
_mime_types["docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; _mime_types.insert(mime_pair("bin", "application/octet-stream"));
_mime_types["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; _mime_types.insert(mime_pair("exe", "application/octet-stream"));
_mime_types["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; _mime_types.insert(mime_pair("dll", "application/octet-stream"));
_mime_types.insert(mime_pair("deb", "application/octet-stream"));
_mime_types.insert(mime_pair("dmg", "application/octet-stream"));
_mime_types.insert(mime_pair("iso", "application/octet-stream"));
_mime_types.insert(mime_pair("img", "application/octet-stream"));
_mime_types.insert(mime_pair("msi", "application/octet-stream"));
_mime_types.insert(mime_pair("msp", "application/octet-stream"));
_mime_types.insert(mime_pair("msm", "application/octet-stream"));
_mime_types["mid"] = "audio/midi"; _mime_types.insert(mime_pair("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
_mime_types["midi"] = "audio/midi"; _mime_types.insert(mime_pair("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
_mime_types["kar"] = "audio/midi"; _mime_types.insert(mime_pair("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"));
_mime_types["mp3"] = "audio/mpeg";
_mime_types["ogg"] = "audio/ogg";
_mime_types["m4a"] = "audio/x-m4a";
_mime_types["ra"] = "audio/x-realaudio";
_mime_types["3gpp"] = "video/3gpp"; _mime_types.insert(mime_pair("mid", "audio/midi"));
_mime_types["3gp"] = "video/3gpp"; _mime_types.insert(mime_pair("midi", "audio/midi"));
_mime_types["ts"] = "video/mp2t"; _mime_types.insert(mime_pair("kar", "audio/midi"));
_mime_types["mp4"] = "video/mp4"; _mime_types.insert(mime_pair("mp3", "audio/mpeg"));
_mime_types["mpeg"] = "video/mpeg"; _mime_types.insert(mime_pair("ogg", "audio/ogg"));
_mime_types["mpg"] = "video/mpeg"; _mime_types.insert(mime_pair("m4a", "audio/x-m4a"));
_mime_types["mov"] = "video/quicktime"; _mime_types.insert(mime_pair("ra", "audio/x-realaudio"));
_mime_types["webm"] = "video/webm";
_mime_types["flv"] = "video/x-flv"; _mime_types.insert(mime_pair("3gpp", "video/3gpp"));
_mime_types["m4v"] = "video/x-m4v"; _mime_types.insert(mime_pair("3gp", "video/3gpp"));
_mime_types["mng"] = "video/x-mng"; _mime_types.insert(mime_pair("ts", "video/mp2t"));
_mime_types["asx"] = "video/x-ms-asf"; _mime_types.insert(mime_pair("mp4", "video/mp4"));
_mime_types["asf"] = "video/x-ms-asf"; _mime_types.insert(mime_pair("mpeg", "video/mpeg"));
_mime_types["wmv"] = "video/x-ms-wmv"; _mime_types.insert(mime_pair("mpg", "video/mpeg"));
_mime_types["avi"] = "video/x-msvideo"; _mime_types.insert(mime_pair("mov", "video/quicktime"));
_mime_types.insert(mime_pair("webm", "video/webm"));
_mime_types.insert(mime_pair("flv", "video/x-flv"));
_mime_types.insert(mime_pair("m4v", "video/x-m4v"));
_mime_types.insert(mime_pair("mng", "video/x-mng"));
_mime_types.insert(mime_pair("asx", "video/x-ms-asf"));
_mime_types.insert(mime_pair("asf", "video/x-ms-asf"));
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
} }

View File

@@ -2,6 +2,7 @@
#include "Webserv.hpp" #include "Webserv.hpp"
#define BUFSIZE 8192 #define BUFSIZE 8192
#define MAX_HEADER_SIZE 42000 // arbitrary
void Webserv::_request(Client *client) void Webserv::_request(Client *client)
{ {
@@ -10,11 +11,12 @@ void Webserv::_request(Client *client)
_handle_last_signal(); _handle_last_signal();
} }
void Webserv::_read_request(Client *client) void Webserv::_read_request(Client *client) // Messy, Need refactoring
{ {
char buf[BUFSIZE+1]; char buf[BUFSIZE];
ssize_t ret; ssize_t ret;
std::cerr << "call recv()" << "\n" ;
ret = ::recv(client->fd, buf, BUFSIZE, 0); ret = ::recv(client->fd, buf, BUFSIZE, 0);
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ; std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
if (ret == -1) if (ret == -1)
@@ -25,20 +27,58 @@ void Webserv::_read_request(Client *client)
_close_client(client->fd); _close_client(client->fd);
return ; return ;
} }
if (ret == 0) // Not sure what to do in case of 0. Just close ? if (ret == 0)
{ {
_close_client(client->fd); _close_client(client->fd);
return ; return ;
} }
/*
if (ret == BUFSIZE)
// send error like "request too long" to client
*/
buf[ret] = '\0'; client->raw_request.append(buf, ret);
client->raw_request.append(buf); if (!client->header_complete)
client->parse_request(); {
if (client->raw_request.find(CRLF CRLF) != std::string::npos)
{
client->header_complete = true;
client->parse_request(); // TODO : split function to avoid useless parsing ?
client->assigned_server = _determine_process_server(client);
client->assigned_location = _determine_location(*client->assigned_server, client->get_path());
if (!client->get_headers("Content-Length").empty()
&& ::atoi(client->get_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
{
client->status = 413;
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
return;
}
}
else if (client->raw_request.size() > MAX_HEADER_SIZE)
{
client->status = 400;
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
return;
}
}
else if (client->header_complete)
{
client->read_body_size += ret;
if (client->read_body_size > client->assigned_server->client_body_limit)
{
client->status = 413;
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
return;
}
if ((int)client->read_body_size > ::atoi(client->get_headers("Content-Length").c_str()))
{
client->parse_request(); // reparse for the body
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
return;
}
}
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
if (client->header_complete && client->get_headers("Content-Type").empty() && client->get_headers("Content-Length").empty() )
{
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
return;
}
} }

View File

@@ -3,26 +3,25 @@
void Webserv::_response(Client *client) void Webserv::_response(Client *client)
{ {
client->status = 200; // default value _send_response(client);
ServerConfig &server = _determine_process_server(client);
_send_response(client, server);
if (g_last_signal) if (g_last_signal)
_handle_last_signal(); _handle_last_signal();
} }
void Webserv::_send_response(Client *client, ServerConfig &server) void Webserv::_send_response(Client *client)
{ {
ssize_t ret; ssize_t ret;
std::cerr << "send()\n"; std::cerr << "send()\n";
_append_base_headers(client); _append_base_headers(client);
_construct_response(client, server); if (!client->status)
_construct_response(client);
_insert_status_line(client); _insert_status_line(client);
if (client->status >= 400) if (client->status >= 400)
_error_html_response(client, server); _error_html_response(client);
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
ret = ::send(client->fd, client->response.c_str(), client->response.size(), 0); ret = ::send(client->fd, client->response.c_str(), client->response.size(), 0);
if (ret == -1) if (ret == -1)
{ {
@@ -31,19 +30,7 @@ void Webserv::_send_response(Client *client, ServerConfig &server)
_close_client(client->fd); _close_client(client->fd);
return ; return ;
} }
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
// Body send (WIP for working binary files)
if (client->body_size)
{
ret = ::send(client->fd, client->buf, client->body_size, 0);
if (ret == -1)
{
std::perror("err send()");
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
_close_client(client->fd);
return ;
}
}
if (client->get_headers("Connection") == "close") if (client->get_headers("Connection") == "close")
_close_client(client->fd); _close_client(client->fd);
@@ -64,19 +51,18 @@ void Webserv::_append_base_headers(Client *client)
client->response.append("Connection: keep-alive" CRLF); client->response.append("Connection: keep-alive" CRLF);
} }
void Webserv::_construct_response(Client *client, ServerConfig &server) void Webserv::_construct_response(Client *client)
{ {
// TODO : Move this in read(), stop read if content too large // TODO : Move this in read(), stop read if content too large
if (client->get_body().size() > server.client_body_limit) if (client->get_body().size() > client->assigned_server->client_body_limit)
{ {
client->status = 413; client->status = 413;
return; return;
} }
LocationConfig &location = _determine_location(server, client->get_path()); _process_method(client);
_process_method(client, server, location);
} }
void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location) void Webserv::_process_method(Client *client)
{ {
unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE
// after update in ConfigParser, use the "allow_methods" of location. // after update in ConfigParser, use the "allow_methods" of location.
@@ -91,11 +77,11 @@ void Webserv::_process_method(Client *client, ServerConfig &server, LocationConf
switch (client->get_method()) switch (client->get_method())
{ {
case (GET): case (GET):
_get(client, server, location); break; _get(client); break;
case (POST): case (POST):
_post(client, server, location); break; _post(client); break;
case (DELETE): case (DELETE):
_delete(client, server, location); break; _delete(client); break;
default: default:
break; break;
} }
@@ -119,20 +105,20 @@ void Webserv::_insert_status_line(Client *client)
client->response.insert(0, status_line); client->response.insert(0, status_line);
} }
void Webserv::_error_html_response(Client *client, ServerConfig &server) void Webserv::_error_html_response(Client *client)
{ {
if (server.error_pages[client->status].empty()) if (client->assigned_server->error_pages[client->status].empty())
{ {
std::string html_page = HTML_ERROR; std::string html_page = HTML_ERROR;
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]); ::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
_append_body(client, html_page.c_str(), html_page.size(), "html"); _append_body(client, html_page, "html");
} }
else else
_get_file(client, server.error_pages[client->status]); _get_file(client, client->assigned_server->error_pages[client->status]);
} }
#define INDEX "index.html" // temp wip //#define INDEX "index.html" // temp wip
void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location) void Webserv::_get(Client *client)
{ {
/* RULES ** /* RULES **
@@ -150,11 +136,13 @@ THIS NEEDS WORK...
/* if (path == "/") // TODO : index and autoindex /* if (path == "/") // TODO : index and autoindex
path.append(INDEX); path.append(INDEX);
<<<<<<< HEAD
path.insert(0, location.root); path.insert(0, location.root);
*/ */
path.insert(0, client->assigned_location->root);
// that was actually a horrible idea... // that was actually a horrible idea...
path.insert(0, location.root); // path.insert(0, location.root);
std::cerr << "path = " << path << "\n"; std::cerr << "path = " << path << "\n";
// path = root + location.path // path = root + location.path
@@ -266,10 +254,16 @@ void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &
# define MAX_FILESIZE 1000000 // (1Mo)
void Webserv::_get_file(Client *client, const std::string &path) void Webserv::_get_file(Client *client, const std::string &path)
{ {
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ? /*
// char buf[MAX_FILESIZE+1]; std::ios::binary
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.binary
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
*/
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
std::stringstream buf;
std::cout << "made it to get_file\n"; std::cout << "made it to get_file\n";
@@ -287,7 +281,7 @@ void Webserv::_get_file(Client *client, const std::string &path)
return ; return ;
} }
ifd.open(path.c_str(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?) ifd.open(path.c_str(), std::ios::ate);
if (!ifd) if (!ifd)
{ {
std::cerr << path << ": ifd.open fail" << '\n'; std::cerr << path << ": ifd.open fail" << '\n';
@@ -303,13 +297,12 @@ void Webserv::_get_file(Client *client, const std::string &path)
// Then chunk // Then chunk
client->status = 500; // WIP temp client->status = 500; // WIP temp
std::cerr << "File too large for non chunk body\n"; std::cerr << "File too large for non chunk body\n";
ifd.close();
return ; return ;
} }
ifd.seekg(0, std::ios::beg); ifd.seekg(0, std::ios::beg);
ifd.read(client->buf, size); buf << ifd.rdbuf();
if (!ifd) if (!ifd || !buf)
{ {
std::cerr << path << ": ifd.read fail" << '\n'; std::cerr << path << ": ifd.read fail" << '\n';
client->status = 500; client->status = 500;
@@ -317,59 +310,46 @@ void Webserv::_get_file(Client *client, const std::string &path)
else else
{ {
client->status = 200; client->status = 200;
client->buf[ifd.gcount()] = '\0'; std::string file_ext = _determine_file_extension(path);
_append_body(client, buf.str(), file_ext);
std::string file_ext = "";
size_t dot_pos = path.rfind(".");
std::cerr << "dot_pos = " << dot_pos << "\n";
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
file_ext = path.substr(dot_pos + 1);
std::cerr << "file_ext = " << file_ext << "\n";
client->body_size = ifd.gcount();
// WIP, pass empty body argument because append to string mess up binary file
_append_body(client, "", client->body_size, file_ext);
} }
ifd.close();
} }
} }
void Webserv::_append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension) //void Webserv::_append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension)
void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
{ {
/* const std::string &mime_type = _mime_types[file_extension];
TODO : determine Content-Type
how ? read the body ? client->response.append("Content-Type: ");
or before in other way (like based and file extension) and pass here as argument ? if (mime_type.empty())
http://nginx.org/en/docs/http/ngx_http_core_module.html#types client->response.append(MIME_TYPE_DEFAULT);
Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that. else
*/ {
const std::string &mime_type = _mime_types[file_extension];
client->response.append("Content-Type: ");
client->response.append(mime_type); client->response.append(mime_type);
if (mime_type.find("text/") != std::string::npos) if (mime_type.find("text/") != std::string::npos)
client->response.append("; charset=UTF-8"); client->response.append("; charset=UTF-8");
client->response.append(CRLF); }
client->response.append(CRLF);
client->response.append("Content-Length: "); client->response.append("Content-Length: ");
std::string tmp = ::itos(body_size); std::string tmp = ::itos(body.size());
client->response.append(tmp); client->response.append(tmp);
client->response.append(CRLF); client->response.append(CRLF);
client->response.append(CRLF); client->response.append(CRLF);
client->response.append(body); client->response.append(body);
} }
void Webserv::_post(Client *client, ServerConfig &server, LocationConfig &location) void Webserv::_post(Client *client)
{ {
(void)server; // To remove from arg if we determine its useless
/* /*
WIP WIP
https://www.rfc-editor.org/rfc/rfc9110.html#name-post https://www.rfc-editor.org/rfc/rfc9110.html#name-post
*/ */
std::string path = client->get_path(); std::string path = client->get_path();
path.insert(0, location.root); path.insert(0, client->assigned_location->root);
/* CGI Here ? */ /* CGI Here ? */
@@ -394,7 +374,7 @@ void Webserv::_post_file(Client *client, const std::string &path)
return ; return ;
} }
ofd.open(path.c_str(), std::ios::binary | std::ios::trunc); ofd.open(path.c_str(), std::ios::trunc);
if (!ofd) if (!ofd)
{ {
std::cerr << path << ": ofd.open fail" << '\n'; std::cerr << path << ": ofd.open fail" << '\n';
@@ -402,11 +382,11 @@ void Webserv::_post_file(Client *client, const std::string &path)
} }
else else
{ {
// Used body.size() so Content-Length useless at this point ? // Content-Length useless at this point ?
// Maybe usefull in _read_request() for rejecting too big content. // Maybe usefull in _read_request() for rejecting too big content.
// Need to _determine_process_server() as soon as possible, // Need to _determine_process_server() as soon as possible,
// like in _read_request() for stopping read if body is too big ? // like in _read_request() for stopping read if body is too big ?
ofd.write(client->get_body().c_str(), client->get_body().size()); ofd << client->get_body();
if (!ofd) if (!ofd)
{ {
std::cerr << path << ": ofd.write fail" << '\n'; std::cerr << path << ": ofd.write fail" << '\n';
@@ -422,20 +402,17 @@ void Webserv::_post_file(Client *client, const std::string &path)
client->status = 201; client->status = 201;
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4 // WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
} }
ofd.close();
} }
} }
void Webserv::_delete(Client *client, ServerConfig &server, LocationConfig &location) void Webserv::_delete(Client *client)
{ {
(void)server; // To remove from arg if we determine its useless
/* /*
WIP WIP
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
*/ */
std::string path = client->get_path(); std::string path = client->get_path();
path.insert(0, location.root); path.insert(0, client->assigned_location->root);
/* CGI Here ? */ /* CGI Here ? */
@@ -466,7 +443,7 @@ void Webserv::_delete_file(Client *client, const std::string &path)
} }
} }
ServerConfig &Webserv::_determine_process_server(Client *client) ServerConfig *Webserv::_determine_process_server(Client *client)
{ {
/* /*
http://nginx.org/en/docs/http/request_processing.html http://nginx.org/en/docs/http/request_processing.html
@@ -491,16 +468,16 @@ ServerConfig &Webserv::_determine_process_server(Client *client)
++it; ++it;
} }
if (it != _servers.end()) if (it != _servers.end())
return (*it); return (&(*it));
else else
return (*default_server); return (&(*default_server));
} }
LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string &path) const LocationConfig *Webserv::_determine_location(const ServerConfig &server, const std::string &path) const
{ {
// std::cout << "determin location path sent: " << path << '\n'; // std::cout << "determin location path sent: " << path << '\n';
std::vector<LocationConfig>::iterator it = server.locations.begin(); std::vector<LocationConfig>::const_iterator it = server.locations.begin();
while (it != server.locations.end()) while (it != server.locations.end())
{ {
// std::cout << it->path << " -- "; // std::cout << it->path << " -- ";
@@ -514,7 +491,15 @@ LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string &
++it; ++it;
} }
if (it != server.locations.end()) if (it != server.locations.end())
return (*it); return (&(*it));
else else
return (server.locations.front()); return (&(server.locations.front()));
}
std::string Webserv::_determine_file_extension(const std::string &path) const
{
size_t dot_pos = path.rfind(".");
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
return ( path.substr(dot_pos + 1) );
return (std::string(""));
} }

View File

@@ -34,17 +34,29 @@ void Webserv::run()
i = 0; i = 0;
while (i < nfds) while (i < nfds)
{ {
// 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) // TODO : handle EPOLLERR and EPOLLHUP
_accept_connection(*it_socket); it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
else if (events[i].events & EPOLLIN) if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); _accept_connection(*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)) ); _request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
++i; else if (events[i].events & EPOLLOUT)
if (!g_run) _response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
++i;
if (!g_run)
break;
}
catch (const std::bad_alloc& e)
{
std::cerr << e.what() << '\n';
_close_all_clients();
/* Swap to free the memory
From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */
std::vector<Client>().swap(_clients);
break; break;
}
} }
} }
} }