diff --git a/Makefile b/Makefile index ad045bd..1761476 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,11 @@ SRCS_D = srcs \ 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 \ - ConfigParser.cpp \ - ConfigParserUtils.cpp \ - ConfigParserPost.cpp \ + parser.cpp \ + extraConfig.cpp \ + postProcessing.cpp \ utils.cpp \ cgi_script.cpp \ Client.cpp \ diff --git a/default.config b/default.config index 024d45c..17399b2 100644 --- a/default.config +++ b/default.config @@ -10,14 +10,44 @@ server { # client_body_limit 400; index index.html; # this is another comment + root ./www/; -# If not explicitly set, ConfigParser need to genererate a location block -# like this for path "/" (based on field "root" and "index" of the server) - location / { - root ./www/; - index index.html; + location /test { + index index1.html; } - allow_methods GET; + # /stylesheet/ alone doesn't work, i mean we don't have wildcards... + location /stylesheet/style.css { +# root ./www/../; + root ./; + } + + location /test/something.html { + # allow_methods DELETE; + } + +# location /something/long/here { +# } + + location /test/test_deeper/ { +# allow_methods + autoindex on; + } + + location /test/test_deeper/super_deep { + autoindex on; + } + +# location /test/test_deeper/something.html { +# allow_methods DELETE; +# } + + +# ok in theory if one were to go to /test they would get the index in www +# as opposed to the one in /website... +# location /test { +# root /www; +# } + } diff --git a/memo.txt b/memo.txt index dd03cec..c076cb7 100644 --- a/memo.txt +++ b/memo.txt @@ -1,13 +1,25 @@ +IN 42 SUBJECT, PRIORITY : +- chunked request (response not mandatory it seems) +- CGI +- index (default file directory) +- Ecrire des tests ! +- handle redirection (weird behavior, to fix) +- upload files with config "upload_repo" +----------------------------- +- autoindex (done, need test) +-------------- +- 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) -- handle redirection - 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/config/ConfigParser.hpp b/srcs/config/ConfigParser.hpp index c5e29f3..32197eb 100644 --- a/srcs/config/ConfigParser.hpp +++ b/srcs/config/ConfigParser.hpp @@ -26,8 +26,9 @@ # include // cout, cin # include // ifstream //# include // access() -# include // opendir() - +# include // opendir(), doesn't work... +# include // stat(), replaces opendir() don't bother with ERRNO ? +# include // sort() in Post class ConfigParser { @@ -51,6 +52,13 @@ public: // private member functions from anywhere... void _print_content() const; + +// I don't love that this is here but... +// doesn't work use the operator overload +// bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b); + + + private: std::string _content; @@ -82,11 +90,20 @@ private: void _post_processing(std::vector *servers); + bool _find_root_path_location(std::vector locations); // const? }; +// no idea if it should go here... +//bool compareLocationConfigs(const LocationConfig &a, +// const LocationConfig &b); + + + + + // def needs work line a better name an do i even need this? // should it be in Utils instead? class MyException : public std::invalid_argument diff --git a/srcs/config/ConfigParserPost.cpp b/srcs/config/ConfigParserPost.cpp deleted file mode 100644 index 3968214..0000000 --- a/srcs/config/ConfigParserPost.cpp +++ /dev/null @@ -1,85 +0,0 @@ - - - -#include "ConfigParser.hpp" - - - -void ConfigParser::_post_processing(std::vector *servers) -{ - - // make certain servers default - // fill out empty settings - // if special settings are empty throw - - std::vector::iterator it = servers->begin(); - - while (it != servers->end()) - { - // host and port should already be set - if (it->host == "") - throw std::invalid_argument("Config file needs a host and port"); - - // is that a good default? - if (it->root == "") - it->root = "/"; - if (it->client_body_limit == 0) - it->client_body_limit = 5000; // what is the recomended size? - - // autoindex should already be false by default right? - - // what do we do if Allow methods is left empty? - // all ? - if (it->allow_methods.empty()) - throw std::invalid_argument("No methods specified"); - - - // what to do if index is left empty? index.html? - // ok but i still need to check index, no idea how... - - // if error_pages is left empty, we'll use the defaults which - // i believe are set elsewhere... - - std::vector::iterator it_l = it->locations.begin(); - while (it_l != it->locations.end()) - { - // check that path is feasible... - // opendir? - DIR* dir = opendir(it_l->path.c_str()); - if (dir) - closedir(dir); - else - throw std::invalid_argument("location dir could not be opened"); - - if (it_l->client_body_limit == 0) - it_l->client_body_limit = 5000; // what is the recomended size? - if (it_l->root == "") - it_l->root = it->root; - - // fill out allow methods from server? - if (it_l->allow_methods.empty()) - it_l->allow_methods = it->allow_methods; - - // fill out index from Server? - // or do a bunch of checks on what is in there... - - // same for redirect status i think - - // maybe do something for Cgi_info? - - ++it_l; - } - - - ++it; - } - - // do the defaults at the end? - - -} - - - - - diff --git a/srcs/config/LocationConfig.hpp b/srcs/config/LocationConfig.hpp index 6db5d58..66de1c5 100644 --- a/srcs/config/LocationConfig.hpp +++ b/srcs/config/LocationConfig.hpp @@ -6,7 +6,7 @@ /* By: lperrey +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2022/07/23 16:08:00 by me #+# #+# */ -/* Updated: 2022/08/02 14:06:07 by lperrey ### ########.fr */ +/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */ /* */ /* ************************************************************************** */ @@ -16,7 +16,13 @@ # include # include # include +# include +# include // stat() +# include // printf(), gotta go + + +# include "utils.hpp" // again, struct instead? class LocationConfig @@ -24,22 +30,64 @@ class LocationConfig public: // canonic stuff? - std::string path; - - int client_body_limit; - std::string root; + std::string path; // /path and /path/ are fine + // i add trailing / if a dir + std::string root; std::vector index; - std::vector allow_methods; - std::map cgi_info; + unsigned int allow_methods; + std::vector cgi_ext; // php not .php + bool autoindex; - // wait if i can call several times, shouldn't it be a map? - // wait no there can only be 1 and i think it might have to be in - // location only... - int redirect_status; - std::string redirect_uri; + std::string upload_repo; // might change name... + + int redirect_status; + std::string redirect_uri; // au pire you do location / { return 301 http://location; } // and that's how you get the redirect from the root. + void print_all() + { + std::cout << "\nPRINTING A LOCATION\n"; + + std::cout << "Path: " << path << '\n'; + std::cout << "root: " << root << '\n'; + std::cout << "autoindex: " << autoindex << '\n'; + + std::cout << "Skipping index...\n"; + + std::cout << "Location allow_methods: "; + std::cout << ::http_methods_to_str(allow_methods) << "\n"; + + std::cout << "Skipping redirect status etc...\n"; + + std::cout << "------\n"; + } + +// works a lot better than using a compare function... + bool operator<(const LocationConfig& rhs) const + { + int comp_lhs = 0; + int comp_rhs = 0; + size_t tmp = 0; + + while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos) + { + ++tmp; + ++comp_lhs; + } + if (path[path.find_last_of("/") + 1] != '\0') + ++comp_lhs; + tmp = 0; + while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos) + { + ++tmp; + ++comp_rhs; + } + if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0') + ++comp_rhs; + + return (comp_lhs < comp_rhs); // right comparison ? not <= ? + }; }; diff --git a/srcs/config/ServerConfig.hpp b/srcs/config/ServerConfig.hpp index 1875592..c0feecf 100644 --- a/srcs/config/ServerConfig.hpp +++ b/srcs/config/ServerConfig.hpp @@ -14,48 +14,24 @@ class ServerConfig { public: - // do i need some canonic stuff? - - // there can be several std::vector server_name; // we could shove default in here if we wanted to... - // there can only be 1 per server... std::string host; std::string port; // port needs to be something else... not quite an int - // should a Server be able to handle several? - // there can only be one. - std::string root; + std::string root; // ./www/ or www work www/ and www work + // i do remove trailing / tho unsigned int client_body_limit; // set to default max if none set - // might be the only one we let slide if bad input... - bool autoindex; - - // we will check the index in the post processing with access() ? std::vector index; std::map error_pages; - // i'm tempted to do something diff for storing method types... - // fuck it, you can only call allow_methods once in Server - // once more in each location. - std::vector allow_methods; - std::vector locations; - // not convinced we need these... -// struct timeval send_timeout; -// struct timeval recv_timeout; - - -// fuck maybe i do need return here... - // wait if i can call several times, shouldn't it be a map? -// i think actually there can only be 1 and it can only be in a location? -// int redirect_status; -// std::string redirect_uri; void print_all() { @@ -73,15 +49,12 @@ public: std::cout << it->first << "--" << it->second << " "; // for (size_t i = 0; i < error_pages.size(); i++) // std::cout << error_pages->first << "--" << error_pages->second << " "; - std::cout << "\nallow_methods: "; - for (size_t i = 0; i < allow_methods.size(); i++) - std::cout << allow_methods[i] << " "; - std::cout << "\nskiping Locations for now...\n"; - std::cout << "also skiping send_timeout and recv\n"; - std::cout << "autoindex: " << autoindex << '\n'; + +// std::cout << "skiping Locations for now...\n"; + for (std::vector::iterator it = locations.begin(); it < locations.end(); it++) + it->print_all(); + std::cout << "client_body_limit: " << client_body_limit << '\n'; - // std::cout << "redirect_status: " << redirect_status << '\n'; - // std::cout << "redirect_uri: " << redirect_uri << '\n'; std::cout << "host: " << host << '\n'; std::cout << "port: " << port << '\n'; diff --git a/srcs/config/compareLocationConfigs.cpp b/srcs/config/compareLocationConfigs.cpp new file mode 100644 index 0000000..8851968 --- /dev/null +++ b/srcs/config/compareLocationConfigs.cpp @@ -0,0 +1,27 @@ + + +// prolly get rid of this file... + + +#include "LocationConfig.hpp" + +#include +#include + +// Ok so maybe it can't be a member functions? +bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b) +{ + int len_a; + int len_b; + size_t tmp = 0; + + // consider adding 1 to path that ends in a file not folder. + + + while ((tmp = a.path.find_first_of("/", tmp)) != std::string::npos) + ++len_a; + tmp = 0; + while ((tmp = b.path.find_first_of("/", tmp)) != std::string::npos) + ++len_b; + return (len_a < len_b); // right comparison ? not <= ? +} diff --git a/srcs/config/ConfigParserUtils.cpp b/srcs/config/extraConfig.cpp similarity index 99% rename from srcs/config/ConfigParserUtils.cpp rename to srcs/config/extraConfig.cpp index ee5ef4e..dfd770b 100644 --- a/srcs/config/ConfigParserUtils.cpp +++ b/srcs/config/extraConfig.cpp @@ -82,6 +82,9 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr) return (values); } + + + void ConfigParser::_print_content() const { std::cout << _content; diff --git a/srcs/config/ConfigParser.cpp b/srcs/config/parser.cpp similarity index 70% rename from srcs/config/ConfigParser.cpp rename to srcs/config/parser.cpp index d754ec6..c804b86 100644 --- a/srcs/config/ConfigParser.cpp +++ b/srcs/config/parser.cpp @@ -31,8 +31,6 @@ ConfigParser::ConfigParser(const char* path) file.open(path); if (file.is_open()) { - // are there more throws i need to add in case of errors, what would - // those errors be? while (!file.eof()) { getline(file, buf); @@ -45,7 +43,7 @@ ConfigParser::ConfigParser(const char* path) } else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment) { - // is there a comment at the end of the line + // check for comment at the end of the line std::string tmp = buf.substr(0, comment - 1); _content.append(tmp + '\n'); } @@ -53,7 +51,7 @@ ConfigParser::ConfigParser(const char* path) file.close(); } else - throw std::invalid_argument("open config"); + throw std::invalid_argument("failed to open config"); } ConfigParser::~ConfigParser() @@ -85,10 +83,11 @@ std::vector * ConfigParser::parse() throw std::invalid_argument("empty config file"); while (curr != std::string::npos) { -// why no checks here -// if not here do i need them elsewhere? - start = _content.find_first_not_of(" \t\n", curr); - curr = _content.find_first_of(" \t\n", start); + if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos) + throw std::invalid_argument("empty config file"); + + if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos) + throw std::invalid_argument("empty config file"); std::string key = _content.substr(start, curr - start); if (key != "server") throw std::invalid_argument("bad config file arguments 1"); @@ -104,13 +103,12 @@ ServerConfig ConfigParser::_parse_server(size_t *start) size_t curr = _content.find_first_not_of(" \t\n", *start); ret.client_body_limit = 0; - ret.autoindex = false; if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 1"); - curr = _content.find_first_of(" \t\n", curr + 1); -// if (curr == std::string::npos) // are there other things to check for? -// throw std::invalid_argument("bad config file syntax"); + if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos) + throw std::invalid_argument("bad config file syntax"); + // are there other things to check for? while (curr != std::string::npos) // here curr == { + 1 { // so this moves curr to past the word... @@ -142,9 +140,15 @@ LocationConfig ConfigParser::_parse_location(size_t *start) size_t curr = *start; // start is after the 1st word aka "location" - ret.client_body_limit = 0; + ret.autoindex = false; ret.redirect_status = 0; + ret.allow_methods = 0; + ret.path = _get_first_word(&curr); +// are you sure about this? + if (ret.path[0] != '/') + ret.path.insert(0, "/"); +// throw std::invalid_argument("Location path require a leading /"); // in theory now curr should be right after the "path" curr = _content.find_first_not_of(" \t\n", curr); @@ -152,9 +156,9 @@ LocationConfig ConfigParser::_parse_location(size_t *start) if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 2"); - curr = _content.find_first_of(" \t\n", curr + 1); -// if (curr == std::string::npos) // are there other things to check for? -// throw std::invalid_argument("bad config file syntax"); + if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos) + throw std::invalid_argument("bad config file syntax"); + // are there other things to check for? while (curr != std::string::npos) { // so this moves curr to past the word... @@ -177,8 +181,6 @@ LocationConfig ConfigParser::_parse_location(size_t *start) - - void ConfigParser::_set_server_values(ServerConfig *server, \ const std::string key, std::string value) { @@ -189,11 +191,12 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ if (size < 1) throw std::invalid_argument("missing value"); - else if (key == "server_name" && size == 1) + else if (key == "server_name" && server->server_name.empty()) { - for (size_t i = 0; i < server->server_name.size(); i++) + for (std::vector::iterator it = server->server_name.begin(); \ + it < server->server_name.end(); it++) { - if (server->server_name[i].compare(tmp_val[0]) == 0) + if (it->compare(tmp_val[0]) == 0) throw std::invalid_argument("server_name already exists"); } server->server_name.push_back(tmp_val[0]); @@ -229,19 +232,11 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ } else if (key == "root" && size == 1 && server->root == "") { - DIR* dir = opendir(tmp_val[0].c_str()); - if (dir) - closedir(dir); - else - throw std::invalid_argument("root dir could not be opened"); + // remove trailing / + if (tmp_val[0][tmp_val[0].size() - 1] == '/') + tmp_val[0].erase(tmp_val[0].size() - 1, 1); server->root = tmp_val[0]; } - else if (key == "autoindex" && size == 1) - { - // autoindex is a bool, there's no good way for me to see if it has - // bet set already - server->autoindex = (tmp_val[0] == "on" ? true : false); - } else if (key == "client_body_limit" && size == 1 \ && server->client_body_limit == 0) { @@ -252,22 +247,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ else if (key == "index") { // i think you can call index several times... - // should i be doing an access? - // since index is at the root, but root might not yet be defined - // will check index later in post for (unsigned long i = 0; i != tmp_val.size(); i++) server->index.push_back(tmp_val[i]); } - else if (key == "allow_methods" && server->allow_methods.empty()) - { - for (unsigned long i = 0; i != tmp_val.size(); i++) - { - http_method m = ::str_to_http_method(tmp_val[i]); - if (m == UNKNOWN) - throw std::invalid_argument("not a valid method"); - server->allow_methods.push_back(m); - } - } else if (key == "error_page") { @@ -292,22 +274,8 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ server->error_pages[status_code] = path; } } -/* else if (key == "recv_timeout" && size == 1 && server->server_name == "") - { - // what is tv_sec and do i need it? -// ok so i don't fully understand this part but ok, keep for now... - server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str()); - } - else if (key == "send_timeout" && size == 1 && server->server_name == "") - { - server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); - } -*/ else - { - // means either you didn't write the right key, or the value is - // missing, or the value has already been filled. + else throw std::invalid_argument("bad key value pair"); - } } @@ -323,48 +291,43 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ throw std::invalid_argument("missing value"); else if (key == "root" && size == 1 && location->root == "") { - DIR* dir = opendir(tmp_val[0].c_str()); - if (dir) - closedir(dir); - else - throw std::invalid_argument("root dir could not be opened"); + // remove trailing / + if (tmp_val[0][tmp_val[0].size() - 1] == '/') + tmp_val[0].erase(tmp_val[0].size() - 1, 1); location->root = tmp_val[0]; } - else if (key == "client_body_limit" && size == 1 \ - && location->client_body_limit == 0) + else if (key == "autoindex" && size == 1) { - if (!::isNumeric(tmp_val[0])) - throw std::invalid_argument("client_body_limit not a number"); - location->client_body_limit = atoi(tmp_val[0].c_str()); + location->autoindex = (tmp_val[0] == "on" ? true : false); + std::cout << "in parser " << location->path << " autoindex: " << location->autoindex << '\n'; } else if (key == "index") { + // what about index /index.html; aka at the root? not handle? // you can definitely call Index several times, i think for (unsigned long i = 0; i != tmp_val.size(); i++) location->index.push_back(tmp_val[i]); } - else if (key == "allow_methods" && location->allow_methods.empty()) + else if (key == "allow_methods" && location->allow_methods == 0) { for (unsigned long i = 0; i != tmp_val.size(); i++) { http_method m = ::str_to_http_method(tmp_val[i]); if (m == UNKNOWN) throw std::invalid_argument("not a valid method"); - location->allow_methods.push_back(m); + location->allow_methods |= m; } } - else if (key == "cgi_info") + else if (key == "cgi_ext") { - // you can call cgi_info several times i think. -// ok wtf is all this even doing, figure that out - unsigned long i = value.find_first_of(" "); - if (i == std::string::npos) - throw std::invalid_argument("bad config file arguments 8"); - // ok why an int now, we gotta be more consistent! - int j = value.find_first_not_of(" ", i); - location->cgi_info[value.substr(0, i)] = value.substr(j, value.length()); + for (size_t i = 0; i < tmp_val.size(); i++) + { + if (tmp_val[i][0] == '.') + throw std::invalid_argument("cgi_ext should not have a leading '.'"); + location->cgi_ext.push_back(tmp_val[i]); + } } - else if (key == "return" && location->redirect_status == 0 \ + else if (key == "redirect" && location->redirect_status == 0 \ && location->redirect_uri == "") { // actually i think there can only be one per location... @@ -372,20 +335,26 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ if (tmp_val.size() != 2) throw std::invalid_argument("wrong number of values"); // and tmp_val[0] should be a number and tmp_val[1] a string? - if (!(::isNumeric(tmp_val[0]))) - throw std::invalid_argument("value not a number"); + if (tmp_val[0] != "301" && tmp_val[0] != "302" + && tmp_val[0] != "303" && tmp_val[0] != "307" + && tmp_val[0] != "308") + throw std::invalid_argument("bad redirect status"); +// double check this + // it means we aren't allowing internal redirects. + if (tmp_val[1].compare(0, 7, "http://") + || tmp_val[1].compare(0, 8, "https://")) + throw std::invalid_argument("bad redirect uri"); - // somehow check that tmp_val[1] is a string? or valid? how? - // something about using access() to see if location->redirect_status = atoi(tmp_val[0].c_str()); location->redirect_uri = tmp_val[1]; } - else + else if (key == "upload_repo" && size == 1 && location->upload_repo == "") { - // means either you didn't write the right key, or the value is - // missing, or the value has already been filled. - throw std::invalid_argument("bad key value pair"); + // what checks to do? + location->upload_repo = tmp_val[0]; } + else + throw std::invalid_argument("bad key value pair"); } diff --git a/srcs/config/postProcessing.cpp b/srcs/config/postProcessing.cpp new file mode 100644 index 0000000..e193549 --- /dev/null +++ b/srcs/config/postProcessing.cpp @@ -0,0 +1,98 @@ + + + +#include "ConfigParser.hpp" + +void ConfigParser::_post_processing(std::vector *servers) +{ + std::vector::iterator it = servers->begin(); + + while (it != servers->end()) + { + // host and port are Mandatory + if (it->host == "") + throw std::invalid_argument("Config file needs a host and port"); + + // root is mandatory + if (it->root == "") + throw std::invalid_argument("Config file needs a root"); + + // index is mandatory in Server + if (it->index.empty()) + throw std::invalid_argument("Config file needs an Index"); + + if (it->client_body_limit == 0) + it->client_body_limit = 5000; // what is the recomended size? + + + // if error_pages is left empty, we'll use the defaults which + // i believe are set elsewhere... + + + if (!_find_root_path_location(it->locations)) + { + LocationConfig tmp; + + tmp.path = "/"; + tmp.root = it->root; + tmp.index = it->index; + tmp.allow_methods = ANY_METHODS; + tmp.autoindex = false; + tmp.redirect_status = 0; + it->locations.push_back(tmp); + } + + std::vector::iterator it_l = it->locations.begin(); + + while (it_l != it->locations.end()) + { + if (it_l->root == "") + it_l->root = it->root; + + if (it_l->allow_methods == UNKNOWN) + it_l->allow_methods = ANY_METHODS; + + if (it_l->index.empty()) + it_l->index = it->index; + + // same for redirect status i think + + // maybe do something for Cgi_ext? + +// std::cout << "In Post, Root + Path: " << it_l->root + it_l->path << '\n'; +/* if (path_is_valid(it_l->root + it_l->path) == 0) + { + //either we throw or we erase + throw std::invalid_argument("location path is invalid"); + } +*/ + if (path_is_valid(it_l->root + it_l->path) == 1 \ + && it_l->path[it_l->path.size() - 1] != '/') + it_l->path.push_back('/'); + + + ++it_l; + } + std::sort(it->locations.begin(), it->locations.end()); +// std::reverse(it->locations.begin(), it->locations.end()); + + ++it; + } +} + +bool ConfigParser::_find_root_path_location(std::vector locations) +{ + std::vector::iterator it = locations.begin(); + + while (it != locations.end()) + { + if (it->path.compare("/") == 0) + { + // std::cout << "in compare: " << it->path << " -- "; + return true; + } + ++it; + } + return false; +} + 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 2144a51..4df5eab 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; @@ -60,6 +68,9 @@ http_method str_to_http_method(std::string &str) return POST; else if (str == "DELETE") return DELETE; + else if (str == "ALL_METHODS") // for Eric: why this test ? can we delete it? + return ANY_METHODS; + // would prefere ALL_METHODS return UNKNOWN; } @@ -85,6 +96,32 @@ std::string http_methods_to_str(unsigned int methods) return (str); } +# include + +// you could make this &path... +int path_is_valid(std::string path) +{ + const char *tmp_path = path.c_str(); + struct stat s; + + if (stat(tmp_path, &s) == 0) + { + if (S_ISREG(s.st_mode)) + { +// std::cout << "is a file\n"; + return (2); + } + else if (S_ISDIR(s.st_mode)) + { +// std::cout << "is a Dir\n"; + return (1); + } + } +// std::cout << "path is neither dir nor file\n"; + return (0); +} + + void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr) { if (ori_substr.empty()) diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 6ea657c..faa73c8 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -6,20 +6,12 @@ # include # include # include // atoi +# include // stat() # define CR "\r" # define LF "\n" # define CRLF CR LF -// enum http_method -// { -// UNKNOWN = 0b00000000, -// GET = 0b00000001, -// POST = 0b00000010, -// DELETE = 0b00000100, -// ANY_METHODS = 0b11111111, -// }; - enum http_method { UNKNOWN = 0b0, @@ -27,6 +19,8 @@ enum http_method POST = 1 << 1, DELETE = 1 << 2, ANY_METHODS = 0b11111111, +// ALL_METHODS = 0b11111111, + // i would prefer this... }; struct listen_socket @@ -45,6 +39,8 @@ std::string itos(int n); std::string trim(std::string str, char c); 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 c5d6409..518cd24 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -24,11 +24,13 @@ # include // string # include // perror, remove # include // atoi (athough it's already cover by ) +# include // opendir() # include "Client.hpp" # include "ServerConfig.hpp" # include "utils.hpp" # include "http_status.hpp" +# include "autoindex.hpp" extern bool g_run; extern int g_last_signal; @@ -38,6 +40,8 @@ void signal_handler(int signum); # define FAILURE -1 # define SUCCESS 1 +# define MIME_TYPE_DEFAULT "application/octet-stream" + class Webserv { public: @@ -68,29 +72,29 @@ class Webserv void _accept_connection(listen_socket &lsocket); // request.cpp void _request(Client *client); - void _read_request(Client *client); + int _read_request(Client *client); // response.cpp void _response(Client *client); - void _send_response(Client *client, ServerConfig &server); - + int _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 _get(Client *client, ServerConfig &server, LocationConfig &location); + 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; + // method_get.cpp + void _get(Client *client); void _get_file(Client *client, const std::string &path); - - void _post(Client *client, ServerConfig &server, LocationConfig &location); + void _autoindex(Client *client, std::string &path); + // method_post.cpp + void _post(Client *client); void _post_file(Client *client, const std::string &path); - - void _delete(Client *client, ServerConfig &server, LocationConfig &location); + // method_delete.cpp + 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); // cgi_script.cpp bool _is_cgi(Client *client); void _exec_cgi(Client *client); diff --git a/srcs/webserv/autoindex.hpp b/srcs/webserv/autoindex.hpp new file mode 100644 index 0000000..1fc9f9e --- /dev/null +++ b/srcs/webserv/autoindex.hpp @@ -0,0 +1,32 @@ + +#ifndef AUTOINDEX_HPP +# define AUTOINDEX_HPP + +// # define HTML_ERROR(STATUS) "\r\n"STATUS"

"STATUS"


Le Webserv/0.1

" + +# define AUTOINDEX_START \ +""\ +""\ +""\ + "Index of " + +# define AUTOINDEX_MID1 \ +""\ +""\ +"" \ + "

Index of " + +# define AUTOINDEX_MID2 \ +"

"\ +"
"\ +"
"
+
+# define AUTOINDEX_END \
+"
"\ +"
"\ +""\ +"" + + + +#endif diff --git a/srcs/webserv/http_status.hpp b/srcs/webserv/http_status.hpp index d6017f0..beba594 100644 --- a/srcs/webserv/http_status.hpp +++ b/srcs/webserv/http_status.hpp @@ -30,6 +30,13 @@ # define S201 "201 Created" # define S204 "204 No Content" +# define S301 "301 Moved Permanently" +# define S302 "302 Found" +# define S303 "303 See Other" +# define S304 "304 Not Modified" // unused +# define S307 "307 Temporary Redirect" +# define S308 "308 Permanent Redirect" + # define S400 "400 Bad Request" # define S403 "403 Forbidden" # define S404 "404 Not Found" @@ -38,5 +45,6 @@ # define S500 "500 Internal Server Error" # define S501 "501 Not Implemented" +# define S505 "505 HTTP Version Not Supported" #endif diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 3a33ed1..4b034ce 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -89,132 +89,151 @@ 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(301, S301)); + _http_status.insert(status_pair(302, S302)); + _http_status.insert(status_pair(303, S303)); + _http_status.insert(status_pair(304, S304)); + _http_status.insert(status_pair(307, S307)); + _http_status.insert(status_pair(308, S308)); + + _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/method_delete.cpp b/srcs/webserv/method_delete.cpp new file mode 100644 index 0000000..f707e3a --- /dev/null +++ b/srcs/webserv/method_delete.cpp @@ -0,0 +1,40 @@ + +#include "Webserv.hpp" + +void Webserv::_delete(Client *client) +{ +/* + WIP + https://www.rfc-editor.org/rfc/rfc9110.html#name-delete +*/ + std::string path = client->get_path(); + path.insert(0, client->assigned_location->root); + + /* CGI Here ? */ + + _delete_file(client, path); +} + +void Webserv::_delete_file(Client *client, const std::string &path) +{ + if (access(path.c_str(), F_OK) == -1) + { + std::perror("err access()"); + client->status = 404; + return ; + } + + if (access(path.c_str(), W_OK) == -1) + { + std::perror("err access()"); + client->status = 403; + return ; + } + + if (remove(path.c_str()) == -1) + { + std::perror("err remove()"); + client->status = 500; + return ; + } +} diff --git a/srcs/webserv/method_get.cpp b/srcs/webserv/method_get.cpp new file mode 100644 index 0000000..c8f0506 --- /dev/null +++ b/srcs/webserv/method_get.cpp @@ -0,0 +1,189 @@ + +#include "Webserv.hpp" + +void Webserv::_get(Client *client) +{ +/* RULES ** + +if path is a valid dir check if index is specified and serve that +if no index and autoindex, server that +if file, server that! + +Where does cgi fit in in all this ??? + + +*/ + std::string path = client->get_path(); + + // this might not be the best thing, a voir + path.insert(0, client->assigned_location->root); + + std::cerr << "path = " << path << "\n"; + + // path = root + location.path + // we will tack on an index if there is a valid one + // or autoindex if allowed + // or let _get_file sort out the error otherwise. + + if (path_is_valid(path) == 1) + { + // std::cout << "path is valid\n"; + if (path[path.size() - 1] != '/') + path.push_back('/'); + for (size_t i = 0; i < client->assigned_location->index.size(); i++) + { +// std::cout << "location path: " << client->assigned_location->path << '\n'; +// std::cout << "location index: " << client->assigned_location->index[i] << '\n'; +// std::cout << "path with index: " << path + assigned_location->index[i] << '\n'; + if (path_is_valid(path + client->assigned_location->index[i]) == 2) + { + // std::cout << "found a valid index\n"; + path.append(client->assigned_location->index[i]); + _get_file(client, path); + return ; + } + } + if (client->assigned_location->autoindex == true) + { + _autoindex(client, path); + return ; + } + } +// else +// _get_file(client, path); + // what about cgi ??? + + + + // TMP HUGO + // + if (_is_cgi(client)) + { + _exec_cgi(client); + return; + } + // + // END TMP HUGO + + _get_file(client, path); +} + +# define MAX_FILESIZE 1000000 // (1Mo) +void Webserv::_get_file(Client *client, const std::string &path) +{ +/* + 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"; + + if (access(path.c_str(), F_OK) == -1) + { + std::perror("err access()"); + client->status = 404; + return ; + } + + if (access(path.c_str(), R_OK) == -1) + { + std::perror("err access()"); + client->status = 403; + return ; + } + + ifd.open(path.c_str(), std::ios::ate); + if (!ifd) + { + std::cerr << path << ": ifd.open fail" << '\n'; + client->status = 500; + } + else + { + std::streampos size = ifd.tellg(); + + // WIP : Chunk or not chunk (if filesize too big) + if (size > MAX_FILESIZE) + { + // Then chunk + client->status = 500; // WIP temp + std::cerr << "File too large for non chunk body\n"; + return ; + } + + ifd.seekg(0, std::ios::beg); + buf << ifd.rdbuf(); + if (!ifd || !buf) + { + std::cerr << path << ": ifd.read fail" << '\n'; + client->status = 500; + } + else + { + client->status = 200; + std::string file_ext = _determine_file_extension(path); + _append_body(client, buf.str(), file_ext); + } + } +} + +// i only sort of need &path... +// def can improve but works for now... +//void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &path) +void Webserv::_autoindex(Client *client, std::string &path) +{ +// std::cout << "made it to _autoindex\n"; + + (void)path; + std::string dir_list; + DIR *dir; + struct dirent *ent; + +// std::cout << "location root: " << client->assigned_location->root << " location path: " +// << client->assigned_location->path << '\n'; + +// if ((dir = opendir (path.c_str())) != NULL) + if ((dir = opendir ((client->assigned_location->root + client->assigned_location->path).c_str())) != NULL) + { + /* print all the files and directories within directory */ + dir_list.append(AUTOINDEX_START); + dir_list.append(client->assigned_location->path); + dir_list.append(AUTOINDEX_MID1); + dir_list.append(client->assigned_location->path); + dir_list.append(AUTOINDEX_MID2); + while ((ent = readdir (dir)) != NULL) + { + if (strcmp(".", ent->d_name) == 0) + continue ; + dir_list.append("assigned_location->path.c_str()); + dir_list.append(ent->d_name); + dir_list.append("\">"); + dir_list.append(ent->d_name); + dir_list.append(""); + dir_list.append("\r\n"); // is this right? + } + +// nginx.org.
+// index1.html + +// apparently this is more than good enough! +// .. + + dir_list.append(AUTOINDEX_END); +// std::cout << "\n\n" << dir_list << '\n'; + closedir (dir); + _append_body(client, dir_list, "html"); + } + else + { + // in theory not possible cuz we already checked... + /* could not open directory */ +// perror (""); + std::cout << "could not open dir\n"; + return ; + } +} diff --git a/srcs/webserv/method_post.cpp b/srcs/webserv/method_post.cpp new file mode 100644 index 0000000..2c011b4 --- /dev/null +++ b/srcs/webserv/method_post.cpp @@ -0,0 +1,66 @@ + +#include "Webserv.hpp" + + +void Webserv::_post(Client *client) +{ +/* + WIP + https://www.rfc-editor.org/rfc/rfc9110.html#name-post +*/ + std::string path = client->get_path(); + path.insert(0, client->assigned_location->root); + + /* CGI Here ? */ + + _post_file(client, path); +} + +void Webserv::_post_file(Client *client, const std::string &path) +{ + std::ofstream ofd; + + bool file_existed; + if (access(path.c_str(), F_OK) == -1) + file_existed = false; + else + file_existed = true; + + // How to determine status 403 for file that dont already exist ? + if (file_existed && access(path.c_str(), W_OK) == -1) + { + std::perror("err access()"); + client->status = 403; + return ; + } + + ofd.open(path.c_str(), std::ios::trunc); + if (!ofd) + { + std::cerr << path << ": ofd.open fail" << '\n'; + client->status = 500; + } + else + { + // 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 << client->get_body(); + if (!ofd) + { + std::cerr << path << ": ofd.write fail" << '\n'; + client->status = 500; + } + else if (file_existed) + { + client->status = 200; + // WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok + } + else + { + client->status = 201; + // WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4 + } + } +} diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index 34338fb..707e24a 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -2,19 +2,38 @@ #include "Webserv.hpp" #define BUFSIZE 8192 +#define MAX_HEADER_SIZE 42000 // arbitrary + +enum read_return +{ + READ_IN_PROGRESS, + READ_COMPLETE, + READ_CLOSE, +}; void Webserv::_request(Client *client) { - _read_request(client); + int ret = _read_request(client); + if (g_last_signal) - _handle_last_signal(); + _handle_last_signal(); + + if (ret == READ_CLOSE) + { + _close_client(client->fd); + } + else if (ret == READ_COMPLETE) + { + _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD); + } } -void Webserv::_read_request(Client *client) +int 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) @@ -22,23 +41,65 @@ void Webserv::_read_request(Client *client) std::perror("err recv()"); std::cerr << "client ptr =" << client << "\n"; // DEBUG std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG - _close_client(client->fd); - return ; + return READ_CLOSE; } - if (ret == 0) // Not sure what to do in case of 0. Just close ? + if (ret == 0) { - _close_client(client->fd); - return ; + std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG + return READ_CLOSE; } - /* - 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) + { + // std::cerr << "Raw_request :\n|||||||||||||||||||||||||||||\n " << client->raw_request << "\n|||||||||||||||||||||||||||||\n"; // DEBUG + client->header_complete = true; + client->parse_request(); // TODO : split function to avoid useless parsing ? + if (client->status) // WIP, need to change client->parse_request() for status update + return READ_COMPLETE; + client->assigned_server = _determine_process_server(client); + client->assigned_location = _determine_location(*client->assigned_server, client->get_path()); + if (client->get_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0) + { // TODO : move in Client parsing ? + client->status = 505; + return READ_COMPLETE; + } + 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; + return READ_COMPLETE; + } + } + else if (client->raw_request.size() > MAX_HEADER_SIZE) + { + client->status = 400; + return READ_COMPLETE; + } + } + else if (client->header_complete) + { + client->read_body_size += ret; + if (client->read_body_size > client->assigned_server->client_body_limit) + { + client->status = 413; + return READ_COMPLETE; + } + if ((int)client->read_body_size >= ::atoi(client->get_headers("Content-Length").c_str())) + { + client->parse_request(); // reparse for the body + return READ_COMPLETE; + } + } - _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD); + + if (client->header_complete && client->get_headers("Content-Type").empty() && client->get_headers("Content-Length").empty() ) + { + return READ_COMPLETE; + } + + return READ_IN_PROGRESS; } diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index 134dcff..179842b 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -1,57 +1,60 @@ #include "Webserv.hpp" +enum send_return +{ + SEND_IN_PROGRESS, // unused + SEND_COMPLETE, + SEND_CLOSE, +}; + void Webserv::_response(Client *client) { - client->status = 200; // default value + int ret = _send_response(client); - ServerConfig &server = _determine_process_server(client); - _send_response(client, server); if (g_last_signal) _handle_last_signal(); + + if (ret == SEND_CLOSE) + { + _close_client(client->fd); + } + else if (ret == SEND_COMPLETE) + { + if (client->get_headers("Connection") == "close") + _close_client(client->fd); + else + { + _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD); + client->clear(); + } + } } -void Webserv::_send_response(Client *client, ServerConfig &server) +int 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) { std::perror("err send()"); std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG - _close_client(client->fd); - return ; + return SEND_CLOSE; } + 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") - _close_client(client->fd); - else - { - _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD); - client->clear(); - } + return SEND_COMPLETE; } void Webserv::_append_base_headers(Client *client) @@ -64,38 +67,53 @@ 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); +/* if (client->assigned_location->redirect_status) + { + // (for codes 301, 302, 303, 307, and 308) + client->status = client->assigned_location->redirect_status; + client->response.append("Location: "); + client->response.append(client->assigned_location->redirect_uri); + client->response.append(CRLF); + } */ + if (client->get_path().find("redirect_test") != std::string::npos) // Test block + { // Weird behavior. The web browser seems to wait for a complete response until timeout. + // (for codes 301, 302, 303, 307, and 308) + client->status = 307; + client->response.append("Location: "); + client->response.append("https://www.rfc-editor.org/rfc/rfc3875#section-3.3"); + client->response.append(CRLF); + client->response.append(CRLF); + return ; + } + _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. - // TODO in ConfigParser : by default if no field in config file, "allow_methods" must be set to ANY_METHODS - + std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug + std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug if (client->get_method() == UNKNOWN) { client->status = 501; } - else if (allow_methods & client->get_method()) + else if (client->assigned_location->allow_methods & client->get_method()) { 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; } @@ -104,7 +122,7 @@ void Webserv::_process_method(Client *client, ServerConfig &server, LocationConf { client->status = 405; client->response.append("Allow: "); - client->response.append(::http_methods_to_str(allow_methods)); + client->response.append(::http_methods_to_str(client->assigned_location->allow_methods)); client->response.append(CRLF); } } @@ -119,241 +137,43 @@ 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 || 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) +void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension) { - (void)server; // To remove from arg if we determine its useless - std::string path = client->get_path(); + const std::string &mime_type = _mime_types[file_extension]; - if (path == "/") // TODO : index and autoindex - path.append(INDEX); - path.insert(0, location.root); - - std::cerr << "path = " << path << "\n"; - - // TMP HUGO - // - if (_is_cgi(client)) - { - _exec_cgi(client); - return; - } - // - // END TMP HUGO - - _get_file(client, 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]; - - if (access(path.c_str(), F_OK) == -1) - { - std::perror("err access()"); - client->status = 404; - return ; - } - - if (access(path.c_str(), R_OK) == -1) - { - std::perror("err access()"); - client->status = 403; - return ; - } - - ifd.open(path.c_str(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?) - if (!ifd) - { - std::cerr << path << ": ifd.open fail" << '\n'; - client->status = 500; - } + client->response.append("Content-Type: "); + if (mime_type.empty()) + client->response.append(MIME_TYPE_DEFAULT); else { - std::streampos size = ifd.tellg(); - - // WIP : Chunk or not chunk (if filesize too big) - if (size > MAX_FILESIZE) - { - // 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) - { - std::cerr << path << ": ifd.read fail" << '\n'; - client->status = 500; - } - 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); - } - - ifd.close(); - } -} - -void Webserv::_append_body(Client *client, const char *body, size_t body_size, 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: "); 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)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); - - /* CGI Here ? */ - - _post_file(client, path); -} - -void Webserv::_post_file(Client *client, const std::string &path) -{ - std::ofstream ofd; - - bool file_existed; - if (access(path.c_str(), F_OK) == -1) - file_existed = false; - else - file_existed = true; - - // How to determine status 403 for file that dont already exist ? - if (file_existed && access(path.c_str(), W_OK) == -1) - { - std::perror("err access()"); - client->status = 403; - return ; - } - - ofd.open(path.c_str(), std::ios::binary | std::ios::trunc); - if (!ofd) - { - std::cerr << path << ": ofd.open fail" << '\n'; - client->status = 500; - } - else - { - // Used body.size() so 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()); - if (!ofd) - { - std::cerr << path << ": ofd.write fail" << '\n'; - client->status = 500; - } - else if (file_existed) - { - client->status = 200; - // WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok - } - else - { - 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)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); - - /* CGI Here ? */ - - _delete_file(client, path); -} - -void Webserv::_delete_file(Client *client, const std::string &path) -{ - if (access(path.c_str(), F_OK) == -1) - { - std::perror("err access()"); - client->status = 404; - return ; - } - - if (access(path.c_str(), W_OK) == -1) - { - std::perror("err access()"); - client->status = 403; - return ; - } - - if (remove(path.c_str()) == -1) - { - std::perror("err remove()"); - client->status = 500; - return ; - } -} - -ServerConfig &Webserv::_determine_process_server(Client *client) +ServerConfig *Webserv::_determine_process_server(Client *client) { /* http://nginx.org/en/docs/http/request_processing.html @@ -378,29 +198,71 @@ 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 { -/* - Assume there is at least one location in vector for path "/" - TODO in ConfigParser : - If no location block in config file, one need to be generated - for path "/", and filled with fields "root" and "index" based on parent server block + std::cout << "determin location path sent: " << path << '\n'; + + +/// NO FUCKING IDEA WHY BUT... +// basically if 2 strings are identical to a point, compare from +// longer one or it'll freak out cuz of \0 or something idk +//// Basically: str.compare() from the larger string... + +/* RULES *** + +If a path coresponds exactly to a location, use that one +if no path coresponds then use the most correct one + most correct means the most precise branch that is still above + the point we are aiming for + */ - std::vector::iterator it = server.locations.begin(); - while (it != server.locations.end()) + std::vector::const_iterator best; + std::cout << "\nMade it to weird location picker case.\n"; + + for (std::vector::const_iterator it = server.locations.begin(); it != server.locations.end(); it++) { - if (it->path.compare(0, path.size(), path)) - break; - ++it; + std::cout << it->path << " -- "; + // if (rit->path.size() > path.size()) +/* if ((rit->path[rit->path.size() - 1] == '/' ? rit->path.size() : rit->path.size() - 1) > path.size()) + { + std::cout << "skipping this one\n"; + continue ; + } +*/ +// OK I REALLY DON"T LOVE THIS PART, BUT IT DOES WORK FOR NOW... + // if (it->path[it->path.size() - 1] == '/' + // && it->path.compare(0, it->path.size(), path + "/") == 0) +// HOLD ON THIS MIGHT BE GOOD, BUT I COULD USE SOME HELP... + if (it->path[it->path.size() - 1] == '/' + && it->path.compare(0, it->path.size() - 1, path) == 0) + { + best = it; + std::cout << "Picked a best! 1\n"; + } +// int comp = path.compare(0, rit->path.size(), rit->path); + //int comp = rit->path.compare(0, rit->path.size() - 1, path); +// std::cout << "rit path size: " << rit->path.size() << " comp: " << comp << '\n'; + // if (rit->path.compare(0, rit->path.size(), path) == 0) + // if (comp == 0) + if (path.compare(0, it->path.size(), it->path) == 0) + { + best = it; + std::cout << "Picked a best! 2\n"; + } } - if (it != server.locations.end()) - return (*it); - else - return (server.locations.front()); + return (&(*best)); +} + +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..cb91672 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -34,17 +34,34 @@ 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; + } + catch (const std::exception& e) + { + std::cerr << e.what() << '\n'; + ++i; + } } } } diff --git a/stylesheet/style.css b/stylesheet/style.css new file mode 100644 index 0000000..2035bb4 --- /dev/null +++ b/stylesheet/style.css @@ -0,0 +1,4 @@ +h1 { + color: red; + text-align: center; +} diff --git a/www/index.html b/www/index.html index bc65643..9e2c22a 100644 --- a/www/index.html +++ b/www/index.html @@ -8,4 +8,4 @@

(˚3˚)

- \ No newline at end of file + diff --git a/www/test/index1.html b/www/test/index1.html new file mode 100644 index 0000000..cf2702d --- /dev/null +++ b/www/test/index1.html @@ -0,0 +1,14 @@ + + + + Webserv test index + + + + + +

Webserv Test Index

+
+

(˚3˚)

+ + diff --git a/www/test/something.html b/www/test/something.html new file mode 100644 index 0000000..5eb0f8b --- /dev/null +++ b/www/test/something.html @@ -0,0 +1,11 @@ + + + + Webserv test Something + + +

Webserv Test Something

+
+

(˚3˚)

+ + diff --git a/www/test/test_deeper/index1.html b/www/test/test_deeper/index1.html new file mode 100644 index 0000000..c5c0b87 --- /dev/null +++ b/www/test/test_deeper/index1.html @@ -0,0 +1,11 @@ + + + + Webserv Test Deeper Index + + +

Webserv Test Deeper Index

+
+

(˚3˚)

+ + diff --git a/www/test/test_deeper/something.html b/www/test/test_deeper/something.html new file mode 100644 index 0000000..c864180 --- /dev/null +++ b/www/test/test_deeper/something.html @@ -0,0 +1,11 @@ + + + + Webserv test deeper Something + + +

Webserv Test Deeper Something

+
+

(˚3˚)

+ + diff --git a/www/test/test_deeper/super_deep/something.html b/www/test/test_deeper/super_deep/something.html new file mode 100644 index 0000000..94fb38c --- /dev/null +++ b/www/test/test_deeper/super_deep/something.html @@ -0,0 +1,11 @@ + + + + Webserv test deeper Something + + +

Webserv Test Super Deep Something

+
+

(˚3˚)

+ +