diff --git a/memo.txt b/memo.txt index dd03cec..5b32a3e 100644 --- a/memo.txt +++ b/memo.txt @@ -1,13 +1,23 @@ - -- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement) +IN 42 SUBJECT, PRIORITY : +- chunked request (response not mandatory it seems) +- upload files with confif "upload_dir" +- 505 HTTP Version Not Supported +- CGI - 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 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)} - add headers "Date" and "Last-Modified" to response - change "std::string" to reference "std::string &" in most functions and add "const" if apropriate. -- http_method en mode binary flags. "std::vector allow_methods" -> "unsigned int allow_methods;" - 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. - 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. diff --git a/srcs/Client.cpp b/srcs/Client.cpp index f994d57..33e8a8d 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -1,13 +1,19 @@ #include "Client.hpp" -char Client::buf[MAX_FILESIZE+1]; - /********************************************* * 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; } @@ -54,9 +60,12 @@ void Client::parse_request() void Client::clear() { clear_request(); + header_complete = false; + read_body_size = 0; + assigned_server = NULL; + assigned_location = NULL; raw_request.clear(); response.clear(); - body_size = 0; status = 0; } @@ -128,6 +137,8 @@ void Client::_parse_request_headers( std::vector list ) 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]; _request.body = body; diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 5a67601..9a255e1 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -7,6 +7,7 @@ # include # include # include "utils.hpp" +# include "ServerConfig.hpp" struct Request { @@ -17,7 +18,6 @@ struct Request std::string body; }; -# define MAX_FILESIZE 1000000 // (1Mo) class Client { public: @@ -27,12 +27,16 @@ class Client //Client &operator=(Client const &rhs); int fd; + const listen_socket *lsocket; + std::string raw_request; std::string response; - static char buf[MAX_FILESIZE+1]; - size_t body_size; 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 ? http_method get_method(); diff --git a/srcs/main.cpp b/srcs/main.cpp index fbe96e3..94b1fc4 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -44,7 +44,7 @@ int main(int ac, char **av) } catch (std::exception& e) { - std::cout << e.what() << '\n'; + std::cerr << e.what() << '\n'; } return (0); diff --git a/srcs/utils.cpp b/srcs/utils.cpp index b53f4a7..47ab461 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -1,6 +1,14 @@ #include "utils.hpp" +void throw_test() +{ + static int i = 0; + ++i; + if (i % 8 == 0) + throw std::bad_alloc(); +} + std::vector split(std::string input, char delimiter) { std::vector answer; diff --git a/srcs/utils.hpp b/srcs/utils.hpp index ad08e95..bd48de3 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -50,5 +50,6 @@ http_method str_to_http_method(std::string &str); std::string http_methods_to_str(unsigned int methods); 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 throw_test(); #endif diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 246a818..fd7abbf 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -40,6 +40,8 @@ void signal_handler(int signum); # define FAILURE -1 # define SUCCESS 1 +# define MIME_TYPE_DEFAULT "application/octet-stream" + class Webserv { public: @@ -73,16 +75,17 @@ class Webserv void _read_request(Client *client); // response.cpp void _response(Client *client); - void _send_response(Client *client, ServerConfig &server); + void _send_response(Client *client); void _append_base_headers(Client *client); - void _construct_response(Client *client, ServerConfig &server); - void _process_method(Client *client, ServerConfig &server, LocationConfig &location); + void _construct_response(Client *client); + void _process_method(Client *client); void _insert_status_line(Client *client); - void _error_html_response(Client *client, ServerConfig &server); - void _append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension = ""); + void _error_html_response(Client *client); + 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 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 _post(Client *client, ServerConfig &server, LocationConfig &location); + void _post(Client *client); 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); - ServerConfig &_determine_process_server(Client *client); - LocationConfig &_determine_location(ServerConfig &server, std::string &path); + 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; // cgi_script.cpp bool _is_cgi(Client *client); void _exec_cgi(Client *client); diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 3a33ed1..cfe877b 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -89,132 +89,144 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections) void Webserv::_init_http_status_map() { - _http_status[200] = S200; - _http_status[201] = S201; - _http_status[204] = S204; +/* "map.insert()" over "map.operator[]" : +** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93 +*/ + typedef std::map::value_type status_pair; - _http_status[400] = S400; - _http_status[403] = S403; - _http_status[404] = S404; - _http_status[405] = S405; - _http_status[413] = S413; + // _http_status.insert(std::make_pair(200, S200)); // equivalent + _http_status.insert(status_pair(200, S200)); + _http_status.insert(status_pair(201, S201)); + _http_status.insert(status_pair(204, S204)); - _http_status[500] = S500; - _http_status[501] = S501; + _http_status.insert(status_pair(400, S400)); + _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() { - _mime_types[""] = "application/octet-stream"; +/* From : +** http://nginx.org/en/docs/http/ngx_http_core_module.html#types +*/ + typedef std::map::value_type mime_pair; - _mime_types["html"] = "text/html"; - _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.insert(mime_pair("", MIME_TYPE_DEFAULT)); - _mime_types["mml"] = "text/mathml"; - _mime_types["txt"] = "text/plain"; - _mime_types["jad"] = "text/vnd.sun.j2me.app-descriptor"; - _mime_types["wml"] = "text/vnd.wap.wml"; - _mime_types["htc"] = "text/x-component"; + _mime_types.insert(mime_pair("html", "text/html")); + _mime_types.insert(mime_pair("html", "text/html")); + _mime_types.insert(mime_pair("htm", "text/html")); + _mime_types.insert(mime_pair("shtml", "text/html")); + _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["tif"] = "image/tiff"; - _mime_types["tiff"] = "image/tiff"; - _mime_types["wbmp"] = "image/vnd.wap.wbmp"; - _mime_types["ico"] = "image/x-icon"; - _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.insert(mime_pair("mml", "text/mathml")); + _mime_types.insert(mime_pair("txt", "text/plain")); + _mime_types.insert(mime_pair("jad", "text/vnd.sun.j2me.app-descriptor")); + _mime_types.insert(mime_pair("wml", "text/vnd.wap.wml")); + _mime_types.insert(mime_pair("htc", "text/x-component")); - _mime_types["woff"] = "application/font-woff"; - _mime_types["jar"] = "application/java-archive"; - _mime_types["war"] = "application/java-archive"; - _mime_types["ear"] = "application/java-archive"; - _mime_types["json"] = "application/json"; - _mime_types["hqx"] = "application/mac-binhex40"; - _mime_types["doc"] = "application/msword"; - _mime_types["pdf"] = "application/pdf"; - _mime_types["ps"] = "application/postscript"; - _mime_types["eps"] = "application/postscript"; - _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.insert(mime_pair("png", "image/png")); + _mime_types.insert(mime_pair("tif", "image/tiff")); + _mime_types.insert(mime_pair("tiff", "image/tiff")); + _mime_types.insert(mime_pair("wbmp", "image/vnd.wap.wbmp")); + _mime_types.insert(mime_pair("ico", "image/x-icon")); + _mime_types.insert(mime_pair("jng", "image/x-jng")); + _mime_types.insert(mime_pair("bmp", "image/x-ms-bmp")); + _mime_types.insert(mime_pair("svg", "image/svg+xml")); + _mime_types.insert(mime_pair("svgz", "image/svg+xml")); + _mime_types.insert(mime_pair("webp", "image/webp")); - _mime_types["bin"] = "application/octet-stream"; - _mime_types["exe"] = "application/octet-stream"; - _mime_types["dll"] = "application/octet-stream"; - _mime_types["deb"] = "application/octet-stream"; - _mime_types["dmg"] = "application/octet-stream"; - _mime_types["iso"] = "application/octet-stream"; - _mime_types["img"] = "application/octet-stream"; - _mime_types["msi"] = "application/octet-stream"; - _mime_types["msp"] = "application/octet-stream"; - _mime_types["msm"] = "application/octet-stream"; + _mime_types.insert(mime_pair("woff", "application/font-woff")); + _mime_types.insert(mime_pair("jar", "application/java-archive")); + _mime_types.insert(mime_pair("war", "application/java-archive")); + _mime_types.insert(mime_pair("ear", "application/java-archive")); + _mime_types.insert(mime_pair("json", "application/json")); + _mime_types.insert(mime_pair("hqx", "application/mac-binhex40")); + _mime_types.insert(mime_pair("doc", "application/msword")); + _mime_types.insert(mime_pair("pdf", "application/pdf")); + _mime_types.insert(mime_pair("ps", "application/postscript")); + _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["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - _mime_types["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + _mime_types.insert(mime_pair("bin", "application/octet-stream")); + _mime_types.insert(mime_pair("exe", "application/octet-stream")); + _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["midi"] = "audio/midi"; - _mime_types["kar"] = "audio/midi"; - _mime_types["mp3"] = "audio/mpeg"; - _mime_types["ogg"] = "audio/ogg"; - _mime_types["m4a"] = "audio/x-m4a"; - _mime_types["ra"] = "audio/x-realaudio"; + _mime_types.insert(mime_pair("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")); + _mime_types.insert(mime_pair("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")); + _mime_types.insert(mime_pair("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")); - _mime_types["3gpp"] = "video/3gpp"; - _mime_types["3gp"] = "video/3gpp"; - _mime_types["ts"] = "video/mp2t"; - _mime_types["mp4"] = "video/mp4"; - _mime_types["mpeg"] = "video/mpeg"; - _mime_types["mpg"] = "video/mpeg"; - _mime_types["mov"] = "video/quicktime"; - _mime_types["webm"] = "video/webm"; - _mime_types["flv"] = "video/x-flv"; - _mime_types["m4v"] = "video/x-m4v"; - _mime_types["mng"] = "video/x-mng"; - _mime_types["asx"] = "video/x-ms-asf"; - _mime_types["asf"] = "video/x-ms-asf"; - _mime_types["wmv"] = "video/x-ms-wmv"; - _mime_types["avi"] = "video/x-msvideo"; + _mime_types.insert(mime_pair("mid", "audio/midi")); + _mime_types.insert(mime_pair("midi", "audio/midi")); + _mime_types.insert(mime_pair("kar", "audio/midi")); + _mime_types.insert(mime_pair("mp3", "audio/mpeg")); + _mime_types.insert(mime_pair("ogg", "audio/ogg")); + _mime_types.insert(mime_pair("m4a", "audio/x-m4a")); + _mime_types.insert(mime_pair("ra", "audio/x-realaudio")); + + _mime_types.insert(mime_pair("3gpp", "video/3gpp")); + _mime_types.insert(mime_pair("3gp", "video/3gpp")); + _mime_types.insert(mime_pair("ts", "video/mp2t")); + _mime_types.insert(mime_pair("mp4", "video/mp4")); + _mime_types.insert(mime_pair("mpeg", "video/mpeg")); + _mime_types.insert(mime_pair("mpg", "video/mpeg")); + _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")); } diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index 34338fb..b492a63 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -2,6 +2,7 @@ #include "Webserv.hpp" #define BUFSIZE 8192 +#define MAX_HEADER_SIZE 42000 // arbitrary void Webserv::_request(Client *client) { @@ -10,11 +11,12 @@ void Webserv::_request(Client *client) _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; + std::cerr << "call recv()" << "\n" ; ret = ::recv(client->fd, buf, BUFSIZE, 0); std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ; if (ret == -1) @@ -25,20 +27,58 @@ void Webserv::_read_request(Client *client) _close_client(client->fd); return ; } - if (ret == 0) // Not sure what to do in case of 0. Just close ? + if (ret == 0) { _close_client(client->fd); return ; } - /* - if (ret == BUFSIZE) - // send error like "request too long" to client - */ - buf[ret] = '\0'; - client->raw_request.append(buf); - client->parse_request(); + client->raw_request.append(buf, ret); + if (!client->header_complete) + { + 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; + } } diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index 70d0aaa..7083008 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -3,26 +3,25 @@ void Webserv::_response(Client *client) { - client->status = 200; // default value - - ServerConfig &server = _determine_process_server(client); - _send_response(client, server); + _send_response(client); if (g_last_signal) _handle_last_signal(); } -void Webserv::_send_response(Client *client, ServerConfig &server) +void Webserv::_send_response(Client *client) { ssize_t ret; std::cerr << "send()\n"; _append_base_headers(client); - _construct_response(client, server); + if (!client->status) + _construct_response(client); _insert_status_line(client); 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); if (ret == -1) { @@ -31,19 +30,7 @@ void Webserv::_send_response(Client *client, ServerConfig &server) _close_client(client->fd); return ; } - - // 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 ; - } - } + std::cerr << "ret send() = " << ret << "\n"; // DEBUG if (client->get_headers("Connection") == "close") _close_client(client->fd); @@ -64,19 +51,18 @@ void Webserv::_append_base_headers(Client *client) 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 - if (client->get_body().size() > server.client_body_limit) + if (client->get_body().size() > client->assigned_server->client_body_limit) { client->status = 413; return; } - LocationConfig &location = _determine_location(server, client->get_path()); - _process_method(client, server, location); + _process_method(client); } -void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location) +void Webserv::_process_method(Client *client) { unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE // 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()) { case (GET): - _get(client, server, location); break; + _get(client); break; case (POST): - _post(client, server, location); break; + _post(client); break; case (DELETE): - _delete(client, server, location); break; + _delete(client); break; default: break; } @@ -119,20 +105,20 @@ void Webserv::_insert_status_line(Client *client) 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; ::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 - _get_file(client, server.error_pages[client->status]); + _get_file(client, client->assigned_server->error_pages[client->status]); } -#define INDEX "index.html" // temp wip -void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location) +//#define INDEX "index.html" // temp wip +void Webserv::_get(Client *client) { /* RULES ** @@ -150,11 +136,13 @@ THIS NEEDS WORK... /* if (path == "/") // TODO : index and autoindex path.append(INDEX); +<<<<<<< HEAD path.insert(0, location.root); */ + path.insert(0, client->assigned_location->root); // that was actually a horrible idea... - path.insert(0, location.root); +// path.insert(0, location.root); std::cerr << "path = " << path << "\n"; // 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) { - 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"; @@ -287,7 +281,7 @@ void Webserv::_get_file(Client *client, const std::string &path) 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) { std::cerr << path << ": ifd.open fail" << '\n'; @@ -303,13 +297,12 @@ void Webserv::_get_file(Client *client, const std::string &path) // Then chunk client->status = 500; // WIP temp std::cerr << "File too large for non chunk body\n"; - ifd.close(); return ; } ifd.seekg(0, std::ios::beg); - ifd.read(client->buf, size); - if (!ifd) + buf << ifd.rdbuf(); + if (!ifd || !buf) { std::cerr << path << ": ifd.read fail" << '\n'; client->status = 500; @@ -317,59 +310,46 @@ void Webserv::_get_file(Client *client, const std::string &path) else { client->status = 200; - client->buf[ifd.gcount()] = '\0'; - - 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); + std::string file_ext = _determine_file_extension(path); + _append_body(client, buf.str(), 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) { -/* - TODO : determine Content-Type - how ? read the body ? - or before in other way (like based and file extension) and pass here as argument ? - http://nginx.org/en/docs/http/ngx_http_core_module.html#types - Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that. -*/ - const std::string &mime_type = _mime_types[file_extension]; - client->response.append("Content-Type: "); + const std::string &mime_type = _mime_types[file_extension]; + + client->response.append("Content-Type: "); + if (mime_type.empty()) + client->response.append(MIME_TYPE_DEFAULT); + else + { client->response.append(mime_type); if (mime_type.find("text/") != std::string::npos) client->response.append("; charset=UTF-8"); - client->response.append(CRLF); + } + client->response.append(CRLF); - client->response.append("Content-Length: "); - std::string tmp = ::itos(body_size); - client->response.append(tmp); - client->response.append(CRLF); + client->response.append("Content-Length: "); + std::string tmp = ::itos(body.size()); + client->response.append(tmp); + client->response.append(CRLF); - client->response.append(CRLF); - client->response.append(body); + client->response.append(CRLF); + 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 https://www.rfc-editor.org/rfc/rfc9110.html#name-post */ std::string path = client->get_path(); - path.insert(0, location.root); + path.insert(0, client->assigned_location->root); /* CGI Here ? */ @@ -394,7 +374,7 @@ void Webserv::_post_file(Client *client, const std::string &path) return ; } - ofd.open(path.c_str(), std::ios::binary | std::ios::trunc); + ofd.open(path.c_str(), std::ios::trunc); if (!ofd) { std::cerr << path << ": ofd.open fail" << '\n'; @@ -402,11 +382,11 @@ void Webserv::_post_file(Client *client, const std::string &path) } 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. // Need to _determine_process_server() as soon as possible, // 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) { std::cerr << path << ": ofd.write fail" << '\n'; @@ -422,20 +402,17 @@ void Webserv::_post_file(Client *client, const std::string &path) client->status = 201; // 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 https://www.rfc-editor.org/rfc/rfc9110.html#name-delete */ std::string path = client->get_path(); - path.insert(0, location.root); + path.insert(0, client->assigned_location->root); /* 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 @@ -491,16 +468,16 @@ ServerConfig &Webserv::_determine_process_server(Client *client) ++it; } if (it != _servers.end()) - return (*it); + return (&(*it)); 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::vector::iterator it = server.locations.begin(); + std::vector::const_iterator it = server.locations.begin(); while (it != server.locations.end()) { // std::cout << it->path << " -- "; @@ -514,7 +491,15 @@ LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string & ++it; } if (it != server.locations.end()) - return (*it); + return (&(*it)); 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("")); } diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index ec9165d..04c1e03 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -34,17 +34,29 @@ void Webserv::run() i = 0; while (i < nfds) { - // TODO : handle EPOLLERR and EPOLLHUP - 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)) ); - ++i; - if (!g_run) + try + { + // TODO : handle EPOLLERR and EPOLLHUP + 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)) ); + ++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().swap(_clients); break; + } } } }