diff --git a/Makefile b/Makefile index 2f15769..a67a053 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NAME = webserv -CXX = clang++ +CXX = c++ CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS += $(HEADERS_D:%=-I%) diff --git a/default.config b/default.config index 5f7bdb6..8a9fa18 100644 --- a/default.config +++ b/default.config @@ -1,29 +1,35 @@ + server { # this is a comment - server_name our_server; + server_name server1; listen 0.0.0.0:4040; # client_body_limit asdfa; - client_body_limit 3000000; + client_body_limit 5000; + # Max == 18446744073709551615 / 1024 == 18014398509481984 index index.html; # this is another comment + #index mdr.html; # this is another comment root ./www/; error_page 404 ./www/error_pages/error_404.html; -# something to do with /upload + location / { + allow_methods GET; + root ./www/; + } -# location /srcs/cgi-bin/ { -# root ./srcs/cgi-bin/; -# allow_methods POST; -# cgi_ext php; -# } + location /srcs/cgi-bin/ { + root ./srcs/cgi-bin/; + allow_methods POST; + cgi_ext php; + } location /list { autoindex on; @@ -31,18 +37,18 @@ server { location /cgi-bin { root ./srcs/cgi-bin/; - cgi_ext cpp php sh; + cgi_ext out php sh; } location /upload { allow_methods POST; autoindex on; - upload_dir ./www/user_files/; # TODO: append a '/' if there is none ? + upload_dir ./www/user_files/; # root doesn’t matter if used only with POST and no CGI } location /the_dump { - allow_methods GET; + allow_methods GET DELETE; root ./www/user_files; autoindex on; } @@ -89,5 +95,13 @@ server { # allow_methods DELETE; # } - +} + +server { + server_name server2; + + listen 0.0.0.0:4040; + + index index.html; + root ./www2/; } diff --git a/main_test.sh b/main_test.sh index bbe0551..aa797b6 100755 --- a/main_test.sh +++ b/main_test.sh @@ -4,8 +4,8 @@ # import and run all tests #test_files=("test_template.sh" "test_header.sh" "test_body.sh") -#test_files=("test_template.sh" "test_header.sh" "test_path.sh") -test_files=("test_path.sh") +test_files=("test_template.sh" "test_header.sh" "test_path.sh") +#test_files=("test_path.sh") test_all() diff --git a/memo.txt b/memo.txt index 1e57ae9..7ded14e 100644 --- a/memo.txt +++ b/memo.txt @@ -1,25 +1,21 @@ IN 42 SUBJECT AND/OR PRIORITY : - CGI (TODO HUGO) -- chunked request (need testing) -- Need to test normal body parsing -- upload files testing and adjustements -- https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size -Config en Ko (value * 2^10) serait plus commode que en octet -Et 0 valeur special pour desactiver +- Need to test normal body parsing (Verif avec HUGO) - Ecrire des tests ! - handle redirection (Work, but weird behavior need deeper test) -- check return 0 de send() - curl --resolve, for testing hostname + curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040 - test limit de connexions sur listen() ----------------------------- Si ce n'est pas deja fait : -- dans config, check erreur si port > 16bits -(peut-être check si ip > 32bits) +peut-être check si ip > 32bits ----------------------------- +- chunked request (need testing) +- client_body_limit 0 valeur special pour desactiver dans config - 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) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index f3247c2..6b04f61 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -183,8 +183,11 @@ void Client::_parse_chunked_body(size_t pos) /* endptr_copy = endptr; */ (void)endptr_copy; chunk_size = std::strtoul(&_request.body[pos], &endptr, 16); -/* if (chunk_size == LONG_MAX && errno == ERANGE) - status = 413; */ + if (errno == ERANGE) + { + status = 413; + return ; + } /* if (endptr == endptr_copy) { std::cerr << "parse_request_body(), no conversion possible\n"; @@ -215,18 +218,13 @@ void Client::fill_script_path(std::string &path, size_t pos) { std::string tmp; - /*DEBUG*/ std::cout << "\n" << B_PURPLE << "debug path dot" << RESET << "\npath:[" << path << "]\n" << "&path[pos]:[" << &path[pos] << "]\n"; if (path[0] == '.') { path.erase(0, 1); pos--; } - /*DEBUG*/ std::cout << "path:[" << path << "]\n" << "&path[pos]:[" << &path[pos] << "]\n"; - _request.script.path = path.substr(0, pos); - /*DEBUG*/ std::cout << "script_path:[" << _request.script.path << "]\n"; _request.script.info = path.substr(pos); - /*DEBUG*/ std::cout << "script_info:[" << _request.script.info << "]\n" << B_PURPLE << "end debug path dot" << RESET << "\n"; } void Client::clear() @@ -412,7 +410,7 @@ void Client::_parse_port_hostname(std::string host) void Client::_check_request_errors() { - std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n"; + std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n"; std::cerr << "strtoul=" << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n"; std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n"; /////////////////////// diff --git a/srcs/cgi-bin/cgi.cpp b/srcs/cgi-bin/cgi.cpp deleted file mode 100755 index 95013cb..0000000 Binary files a/srcs/cgi-bin/cgi.cpp and /dev/null differ diff --git a/srcs/cgi-bin/cgi_cpp.cpp b/srcs/cgi-bin/cgi_cpp.cpp new file mode 100644 index 0000000..8cabc71 --- /dev/null +++ b/srcs/cgi-bin/cgi_cpp.cpp @@ -0,0 +1,114 @@ +# include +# include +# include +# include +# include // getenv + +# define CR "\r" +# define LF "\n" +# define CRLF CR LF +# define NPOS std::string::npos + +std::string trim(std::string str, char del) +{ + size_t pos; + + // delete leadings del + pos = str.find_first_not_of(del); + if (pos == NPOS) + pos = str.size(); + str = str.substr(pos); + + // delete trailing del + pos = str.find_last_not_of(del); + if (pos != NPOS) + str = str.substr(0, pos + 1); + + return str; +} + +std::vector + split(const std::string & input, std::string delim, char ctrim = '\0') +{ + std::vector split_str; + std::string tmp; + size_t start = 0; + size_t end = 0; + size_t len = 0; + + while (end != NPOS) + { + end = input.find(delim, start); + len = end - start; + if (end == NPOS) + len = end; + tmp = input.substr(start, len); + if (ctrim != '\0') + tmp = trim(tmp, ctrim); + if (tmp.size() != 0) + split_str.push_back( tmp ); + start = end + delim.size(); + } + return split_str; +} + +int main (int ac, char **av, char **en) +{ + std::vector split_str; + std::vector sub_split_str; + std::vector::const_iterator it; + char * tmp; + std::string input; + std::string http_header; + std::string http_body; + std::ostringstream strs; + size_t pos; + + std::cin >> input; + + http_header = "Content-Type: text/html; charset=UTF-8" CRLF; + http_header += "Content-Length: "; + + http_body = "\ + \ + \ + \ + CGI\ + \ + \ +

cgi

\ + "; + + http_body += "

"; + tmp = getenv("REQUEST_METHOD"); + if (tmp != NULL) + http_body += tmp; + else + http_body = "method not foud"; + http_body += "

"; + + split_str = split(input, "&"); + for (it = split_str.begin(); it != split_str.end(); ++it) + { + sub_split_str = split(*it, "="); + http_body += "

"; + http_body += sub_split_str[0]; + http_body += "

"; + http_body += "

"; + http_body += sub_split_str[1]; + http_body += "

"; + } + + http_body += "\ + \ + \ + "; + + strs << http_body.size(); + http_header += strs.str(); + http_header += CRLF CRLF; + + std::cout << http_header << CRLF CRLF << http_body; + return 0; +} + diff --git a/srcs/cgi-bin/cgi_cpp_content_length.cpp b/srcs/cgi-bin/cgi_cpp_content_length.cpp new file mode 100644 index 0000000..9bb5089 --- /dev/null +++ b/srcs/cgi-bin/cgi_cpp_content_length.cpp @@ -0,0 +1,133 @@ +# include +# include +# include +# include +# include // getenv + +# define CR "\r" +# define LF "\n" +# define CRLF CR LF +# define NPOS std::string::npos + +std::string trim(std::string str, char del) +{ + size_t pos; + + // delete leadings del + pos = str.find_first_not_of(del); + if (pos == NPOS) + pos = str.size(); + str = str.substr(pos); + + // delete trailing del + pos = str.find_last_not_of(del); + if (pos != NPOS) + str = str.substr(0, pos + 1); + + return str; +} + +std::vector + split(const std::string & input, std::string delim, char ctrim = '\0') +{ + std::vector split_str; + std::string tmp; + size_t start = 0; + size_t end = 0; + size_t len = 0; + + while (end != NPOS) + { + end = input.find(delim, start); + len = end - start; + if (end == NPOS) + len = end; + tmp = input.substr(start, len); + if (ctrim != '\0') + tmp = trim(tmp, ctrim); + if (tmp.size() != 0) + split_str.push_back( tmp ); + start = end + delim.size(); + } + return split_str; +} + +int main (int ac, char **av, char **en) { + std::vector split_str; + std::vector sub_split_str; + std::vector::const_iterator it; + char * tmp; + std::string output; + std::ostringstream strs; + size_t pos; + + std::cout << "Content-Type: text/html; charset=UTF-8" << CRLF CRLF; + + std::cout + << "" + << "" + << "" + << " CGI" + << "" + << "" + << "

cgi

" + << "

"; + + tmp = getenv("REQUEST_METHOD"); + if (tmp != NULL) + output = tmp; + else + output = "method not foud"; + + std::cout + << output + << "

" + << "

http-request-body-message content :

"; + + + std::cin >> output; + split_str = split(output, "&"); + output.clear(); + for (it = split_str.begin(); it != split_str.end(); ++it) + { + sub_split_str = split(*it, "="); + + std::cout + << "

" + << sub_split_str[0] + << " : " + << sub_split_str[1] + << "

"; + } + + tmp = getenv("QUERY_STRING"); + if (tmp == NULL) + std::cout << "query not foud"; + + std::cout + << "

http-uri-query content :

"; + + output = tmp; + split_str = split(output, "&"); + output.clear(); + for (it = split_str.begin(); it != split_str.end(); ++it) + { + sub_split_str = split(*it, "="); + + std::cout + << "

" + << sub_split_str[0] + << "

" + << "

" + << sub_split_str[1] + << "

"; + } + + + std::cout + << "" + << ""; + + return 0; +} + diff --git a/srcs/config/ConfigParser.hpp b/srcs/config/ConfigParser.hpp index 727c3f0..661a17d 100644 --- a/srcs/config/ConfigParser.hpp +++ b/srcs/config/ConfigParser.hpp @@ -14,8 +14,6 @@ # include // strtol, stroul # include // cout, cin # include // ifstream -//# include // access() -# include // opendir(), doesn't work... # include // stat(), replaces opendir() don't bother with ERRNO ? # include // sort() in Post @@ -23,43 +21,37 @@ class ConfigParser { public: - ConfigParser(const char* path); // a string? - -// might not need this either, ask Luke + ConfigParser(); ~ConfigParser(); + ConfigParser(const std::string &config_file); + + void read_config(const std::string &config_file); std::vector * parse(); // const? // i thought if it were an instance of this class you could call -// private member functions from anywhere... +// private member functions from anywhere... // QUESTION : Wut ? void print_content() const; - private: std::string _content; - // not sure i even need this... - ConfigParser(); - ServerConfig _parse_server(size_t *start); LocationConfig _parse_location(size_t *start); - void _set_server_values(ServerConfig *server, const std::string key, std::string value); void _set_location_values(LocationConfig *location, const std::string key, std::string value); - /* Extra */ - std::string _pre_set_val_check(const std::string key, \ + std::string _pre_set_val_check(const std::string key, const std::string value); std::string _get_first_word(size_t *curr); // const? std::string _get_rest_of_line(size_t *curr); // const? - /* Post Processing */ void _post_processing(std::vector *servers); bool _find_root_path_location(std::vector locations) const; diff --git a/srcs/config/LocationConfig.hpp b/srcs/config/LocationConfig.hpp index bb6efc6..b5085d2 100644 --- a/srcs/config/LocationConfig.hpp +++ b/srcs/config/LocationConfig.hpp @@ -1,31 +1,14 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* LocationConfig.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: lperrey +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2022/07/23 16:08:00 by me #+# #+# */ -/* Updated: 2022/08/15 19:37:34 by erlazo ### ########.fr */ -/* */ -/* ************************************************************************** */ #ifndef LOCATIONCONFIG_HPP # define LOCATIONCONFIG_HPP -# include # include # include -# include -# include // stat() # include "utils.hpp" -// again, struct instead? -class LocationConfig +struct LocationConfig { -public: - std::string path; // /path and /path/ are fine // i remove trailing / systematically std::string root; @@ -91,13 +74,4 @@ public: }; - #endif - - - - - - - - diff --git a/srcs/config/ServerConfig.hpp b/srcs/config/ServerConfig.hpp index fcab029..ea823ef 100644 --- a/srcs/config/ServerConfig.hpp +++ b/srcs/config/ServerConfig.hpp @@ -10,13 +10,9 @@ # include // string # include // cout, cin -// a class that's all public? just so we have options? -class ServerConfig +struct ServerConfig { -public: - std::vector server_name; - // we could shove default in here if we wanted to... std::string host; std::string port; @@ -24,9 +20,7 @@ public: std::string root; // ./www/ or www work www/ and www work // i do remove trailing / tho - size_t client_body_limit; // set to default max if none set - // 413 (Request Entity Too Large) if exceeded - // default is 1m 1 000 000 ? + size_t client_body_limit; std::vector index; std::map error_pages; diff --git a/srcs/config/extraConfig.cpp b/srcs/config/extraConfig.cpp index 3d87dc1..1e2278a 100644 --- a/srcs/config/extraConfig.cpp +++ b/srcs/config/extraConfig.cpp @@ -1,6 +1,4 @@ - - #include "ConfigParser.hpp" // should i be sending & references? diff --git a/srcs/config/parser.cpp b/srcs/config/parser.cpp index 6da9bc7..f9c450a 100644 --- a/srcs/config/parser.cpp +++ b/srcs/config/parser.cpp @@ -1,23 +1,26 @@ #include "ConfigParser.hpp" -// Default -// possibly remove... -ConfigParser::ConfigParser() +ConfigParser::ConfigParser() {}; +ConfigParser::~ConfigParser() {}; + +ConfigParser::ConfigParser(const std::string &config_file) { - std::cout << "Default Constructor\n"; + read_config(config_file); } -ConfigParser::ConfigParser(const char* path) +void ConfigParser::read_config(const std::string &config_file) { std::ifstream file; std::string buf; size_t comment; - _content.clear(); - file.open(path); - if (file.is_open()) + file.open(config_file.c_str()); + if (!file) + throw std::invalid_argument("failed to open config"); + else { + _content.clear(); while (!file.eof()) { getline(file, buf); @@ -35,19 +38,9 @@ ConfigParser::ConfigParser(const char* path) _content.append(tmp + '\n'); } } - file.close(); } - else - throw std::invalid_argument("failed to open config"); } -// might not need this... -ConfigParser::~ConfigParser() -{ - // do i need to destroy anything, won't it handle itself? -} - - // const? std::vector * ConfigParser::parse() { @@ -156,7 +149,7 @@ LocationConfig ConfigParser::_parse_location(size_t *start) // should i be sending pointers or references? -void ConfigParser::_set_server_values(ServerConfig *server, \ +void ConfigParser::_set_server_values(ServerConfig *server, const std::string key, std::string value) { // should i be sending pointers or references? @@ -181,7 +174,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ } } else if (key == "listen" && size == 1 && server->host == "" - && server->port == "") + && server->port == "") // QUESTION LUKE : C'est quoi cette condition ? Si listen est vide ? Je comprends pas trop. { if (tmp_val[0].find_first_of(":") == NPOS) { @@ -222,6 +215,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ if (!::isNumeric(tmp_val[0])) throw std::invalid_argument("client_body_limit not a number"); server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10); + if (errno == ERANGE || server->client_body_limit > (ULONG_MAX / KB) ) + throw std::invalid_argument("client_body_limit too big"); + server->client_body_limit = server->client_body_limit * KB; } else if (key == "index") { @@ -231,7 +227,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ else if (key == "error_page") { std::string path = tmp_val[size - 1]; - for (size_t i = 0; i < size() - 1; i++) + for (size_t i = 0; i < size - 1; i++) { if (!(isNumeric_btw(400, 599, tmp_val[i]))) throw std::invalid_argument("invalid error code"); @@ -247,7 +243,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ // should i be sending pointers or references? -void ConfigParser::_set_location_values(LocationConfig *location, \ +void ConfigParser::_set_location_values(LocationConfig *location, const std::string key, std::string value) { // should i be sending pointers or references? diff --git a/srcs/config/postProcessing.cpp b/srcs/config/postProcessing.cpp index 806d6f3..8a4c7c0 100644 --- a/srcs/config/postProcessing.cpp +++ b/srcs/config/postProcessing.cpp @@ -22,7 +22,7 @@ void ConfigParser::_post_processing(std::vector *servers) 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? + it->client_body_limit = 1 * MB; // if error_pages is left empty, we'll use the defaults which diff --git a/srcs/main.cpp b/srcs/main.cpp index b5848b3..08c85c0 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -11,15 +11,9 @@ int main(int ac, char **av) { std::string config = (ac == 2 ? av[1] : "./default.config"); - // like this just looks kinda gross, why bother creating an instance - // and not immediately parsing? like it servers no other purpose... - // what if i call parse directly in the constructor? - // oh because the constructor has no return, but still - // is there a better way? - ConfigParser configParser(config.c_str()); - - configParser.print_content(); + ConfigParser configParser(config); + // configParser.print_content(); // i don't love that servers has to be a pointer... std::vector* servers = configParser.parse(); diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 90562ce..d85d5d5 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -171,18 +171,19 @@ file_type eval_file_type(const std::string &path) return (IS_OTHER); } -size_t eval_file_mode(std::string path, int mode) +// rename in "eval_file_access" ? +size_t eval_file_mode(const std::string &path, int mode) { - if (access(path.c_str(), F_OK) == -1) + if (::access(path.c_str(), F_OK) == -1) { std::perror("err access()"); return 404; // NOT_FOUND, file doesn't exist } - if (access(path.c_str(), mode) == -1) + if (::access(path.c_str(), mode) == -1) { std::perror("err access()"); - return 403; // FORBIDDEN, file doesn't have execution permission + return 403; // FORBIDDEN, file doesn't have access permission } return 0; } @@ -226,7 +227,7 @@ std::string begin = str.rfind(delim, pos); if (begin == NPOS) begin = 0; - else + else if (begin < pos) begin += delim.size(); end = str.find(delim, pos); diff --git a/srcs/utils.hpp b/srcs/utils.hpp index ede2808..44f5347 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -22,6 +22,8 @@ # define CRLF CR LF # define CRLF_SIZE 2 # define NPOS std::string::npos +# define KB 1024 +# define MB 1048576 /* Equivalent for end of http header size : ** std::string(CRLF CRLF).size(); @@ -65,15 +67,15 @@ std::string trim(std::string str, char del); http_method str_to_http_method(std::string &str); std::string http_methods_to_str(unsigned int methods); file_type eval_file_type(const std::string &path); -size_t eval_file_mode(std::string path, int mode); +size_t eval_file_mode(const std::string &path, int mode); void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr); std::string str_tolower(std::string str); std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n"); std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n"); size_t parse_http_headers (std::string headers, std::map & fields ); void str_map_key_tolower(std::map & mp); -void throw_test(); // debug +void throw_test(); void print_special(std::string str); diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 7fe0b2a..1c9ace1 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -114,6 +114,8 @@ class Webserv void _check_script_output(Client *client, std::string & output); void _check_script_status(Client *client, std::string & output); void _check_script_fields(Client *client, std::string & output); + void _add_script_body_length_header(std::string & output); + void _remove_body_leading_empty_lines(std::string & output); // epoll_update.cpp int _epoll_update(int fd, uint32_t events, int op); int _epoll_update(int fd, uint32_t events, int op, void *ptr); diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index e1b4d52..e572749 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -5,13 +5,12 @@ bool Webserv::_is_cgi(Client *client, std::string path) { std::string script_path; size_t file_type; - size_t file_mode; + size_t file_mode = client->status; size_t pos = 0; while (pos != NPOS) { pos = _cgi_pos(client, path, pos); - /*DEBUG*/ std::cout << "pos:" << pos << "\n&path[pos]:" << &path[pos] << "\n" << B_YELLOW << "fin debug _cgi_pos()\n\n" << RESET; if (pos == NPOS) break; client->fill_script_path(path, pos); @@ -27,7 +26,6 @@ bool Webserv::_is_cgi(Client *client, std::string path) } } client->clear_script(); - client->clear_script(); client->status = file_mode; // 404 not_found OR 403 forbidden return false; } @@ -40,25 +38,20 @@ size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos) size_t len; std::locale loc; // for isalpha() - /*DEBUG*/ it = client->assigned_location->cgi_ext.begin(); std::cout << B_YELLOW << "\nDEBUG _cgi_pos()\n" << RESET << "vector_ext.size():[" << client->assigned_location->cgi_ext.size() << "]\n\n"; v_ext = client->assigned_location->cgi_ext; if (v_ext.empty()) return NPOS; - /*DEBUG*/ std::cout << "ext:[" << *it << "]\n" << "path:[" << path << "]\n\n"; it_end = client->assigned_location->cgi_ext.end(); while (pos < path.size()) { - /*DEBUG*/ std::cout << "\nwhile\n"; if (path.compare(pos, 2, "./") == 0) pos += 2; - /*DEBUG*/ std::cout << "&path[pos]:[" << &path[pos] << "]\n"; pos = path.find('.', pos); if (pos == NPOS) return pos; it = client->assigned_location->cgi_ext.begin(); for ( ; it != it_end; ++it) { - /*DEBUG*/ std::cout << " for\n"; std::cout << " &path[pos]:[" << &path[pos] << "]\n" << " *it:[" << *it << "]\n" << " (*it).size():[" << (*it).size() << "]\n" << " path.substr(pos, (*it).size()):[" << path.substr(pos + 1, (*it).size()) << "]\n\n"; len = (*it).size(); if (path.compare(pos + 1, len, *it) == 0) if ( !std::isalpha(path[pos + 1 + len], loc) ) @@ -147,8 +140,6 @@ std::string Webserv::_exec_script(Client *client, char **env) std::string body = client->get_rq_body(); int fd_in[2]; int fd_out[2]; - int save_in = dup(STDIN_FILENO); - int save_out = dup(STDOUT_FILENO); std::string path; pipe(fd_in); @@ -157,21 +148,21 @@ std::string Webserv::_exec_script(Client *client, char **env) pid = fork(); if (pid == -1) std::cerr << "fork crashed" << std::endl; - else if (pid == 0) + else if (pid == 0) // child { close(FD_WR_TO_CHLD); close(FD_RD_FR_CHLD); dup2(FD_RD_FR_PRNT, STDIN_FILENO); dup2(FD_WR_TO_PRNT, STDOUT_FILENO); path = "." + client->get_rq_script_path(); - /*DEBUG*/std::cerr << "execve\n" << "path:[" << path << "]\n"; +/*DEBUG*/std::cerr << "execve:[" << path << "]\n"; execve(path.c_str(), nll, env); // for tests execve crash : //execve("wrong", nll, env); std::cerr << "execve crashed.\n"; // TODO HUGO : check errno } - else + else //parent { close(FD_RD_FR_PRNT); close(FD_WR_TO_PRNT); @@ -189,9 +180,7 @@ std::string Webserv::_exec_script(Client *client, char **env) } if (script_output.empty()) script_output = "Status: 500\r\n\r\n"; - - dup2(save_in, STDIN_FILENO); - dup2(save_out, STDOUT_FILENO); + return script_output; } @@ -199,6 +188,8 @@ void Webserv::_check_script_output(Client *client, std::string & output) { _check_script_status(client, output); _check_script_fields(client, output); + _remove_body_leading_empty_lines(output); + _add_script_body_length_header(output); // _check_script_empty_lines(client, output); // _check_script_space_colons(client, output); // _check_script_new_lines(client, output); @@ -255,3 +246,58 @@ void Webserv::_check_script_fields(Client *client, std::string & output) } } +void Webserv::_remove_body_leading_empty_lines(std::string & output) +{ + size_t pos; + size_t pos_empty; + + pos = output.find(CRLF CRLF); + if (pos == NPOS) + return; + pos += CRLF_SIZE * 2; + pos_empty = pos; + while (pos_empty == pos) + { + pos = output.find(CRLF, pos); + if (pos == pos_empty) + extract_line(output, pos, CRLF); + } +} + +void Webserv::_add_script_body_length_header(std::string & output) +{ + std::map field; + std::map::iterator it; + std::stringstream str_len; + std::string tmp; + size_t pos; + size_t len; + + pos = output.find(CRLF CRLF); + if (pos != NPOS) + tmp = output.substr(pos + (CRLF_SIZE * 2)); + len = tmp.size(); + str_len << len; + + // put script headers in map + tmp = output; + pos = tmp.find(CRLF CRLF); + if (pos != NPOS) + tmp.erase(pos); + ::parse_http_headers(tmp, field); + // case insensitive search in map for "Content-Length" + tmp = "Content-Length"; + for (it = field.begin(); it != field.end(); ++it) + { + if (str_tolower(it->first) == str_tolower(tmp)) + { + pos = output.find(it->first); + ::extract_line(output, pos, CRLF); + } + } + tmp += ": "; + tmp += str_len.str(); + tmp += CRLF; + output.insert(0, tmp); +} + diff --git a/srcs/webserv/method_delete.cpp b/srcs/webserv/method_delete.cpp index 40e0e3b..16c7d94 100644 --- a/srcs/webserv/method_delete.cpp +++ b/srcs/webserv/method_delete.cpp @@ -4,7 +4,6 @@ void Webserv::_delete(Client *client, const std::string &path) { /* - WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-delete */ _delete_file(client, path); @@ -12,24 +11,21 @@ void Webserv::_delete(Client *client, const std::string &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 ; - } + std::cout << "_delete_file()\n"; + client->status = ::eval_file_mode(path, W_OK); + if (client->status) + return; - if (access(path.c_str(), W_OK) == -1) - { - std::perror("err access()"); - client->status = 403; - return ; - } - - if (remove(path.c_str()) == -1) + if (std::remove(path.c_str()) == -1) { std::perror("err remove()"); - client->status = 500; + if (errno == ENOTEMPTY || errno == EEXIST) + client->status = 403; + else + client->status = 500; return ; } + + client->status = 204; + client->response.append(CRLF); } diff --git a/srcs/webserv/method_get.cpp b/srcs/webserv/method_get.cpp index 310e27a..4649dd2 100644 --- a/srcs/webserv/method_get.cpp +++ b/srcs/webserv/method_get.cpp @@ -16,9 +16,10 @@ std::string Webserv::_replace_url_root(Client *client, std::string path) // const? void Webserv::_get(Client *client, std::string &path) { - - -// Index/Autoindex block +/* + https://www.rfc-editor.org/rfc/rfc9110.html#name-get +*/ + std::cout << "_get()\n"; if (eval_file_type(path) == IS_DIR) { if (path[path.size() - 1] != '/') @@ -34,12 +35,14 @@ void Webserv::_get(Client *client, std::string &path) } if (client->assigned_location->autoindex == true) _autoindex(client, path); + else + client->status = 404; } else _get_file(client, path); } -# define MAX_FILESIZE 1000000 // (1Mo) +# define MAX_FILESIZE 1 * MB // unused void Webserv::_get_file(Client *client, const std::string &path) { /* @@ -52,21 +55,11 @@ void Webserv::_get_file(Client *client, const std::string &path) std::cout << "_get_file()\n"; - if (access(path.c_str(), F_OK) == -1) - { - std::perror("err access()"); - client->status = 404; - return ; - } + client->status = ::eval_file_mode(path, R_OK); + if (client->status) + 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); + ifd.open(path.c_str()); if (!ifd) { std::cerr << path << ": ifd.open fail" << '\n'; @@ -74,17 +67,6 @@ void Webserv::_get_file(Client *client, const std::string &path) } else { - /* std::streampos size = ifd.tellg(); - // WIP Low priority : 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) { @@ -100,6 +82,19 @@ void Webserv::_get_file(Client *client, const std::string &path) } } +/* +// WIP Low priority : Chunk or not chunk (if filesize > MAX_FILESIZE) +// WITH flag "std::ios::ate" for open() +std::streampos size = ifd.tellg(); +ifd.seekg(0, std::ios::beg); +if (size > MAX_FILESIZE) +{ + // Chunked GET here + return ; +} +*/ + + // const? void Webserv::_autoindex(Client *client, const std::string &path) { diff --git a/srcs/webserv/method_post.cpp b/srcs/webserv/method_post.cpp index 9ee0aaa..ce7309b 100644 --- a/srcs/webserv/method_post.cpp +++ b/srcs/webserv/method_post.cpp @@ -5,10 +5,9 @@ void Webserv::_post(Client *client, const std::string &path) { /* - WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-post */ - (void)path; + (void)path; // unused, we use "assigned_location->upload_dir" instead std::cout << "_post()\n"; std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n"; @@ -31,12 +30,17 @@ void Webserv::_post(Client *client, const std::string &path) // TODO : Loop for multi body void Webserv::_upload_files(Client *client) { + std::cout << "_upload_files()\n"; std::ofstream ofd; std::vector::const_iterator body_it = client->get_rq_multi_bodys().begin(); std::string path; std::string filename; size_t pos; - bool file_existed; + bool file_existed = false; + + client->status = ::eval_file_mode(client->assigned_location->upload_dir, W_OK); + if (client->status) + return; while (body_it != client->get_rq_multi_bodys().end()) { @@ -72,13 +76,13 @@ void Webserv::_upload_files(Client *client) path.append(filename); - if (access(path.c_str(), F_OK) == -1) + 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 ? access() on the upload_dir ? - if (file_existed && access(path.c_str(), W_OK) == -1) + if (file_existed && ::access(path.c_str(), W_OK) == -1) { std::perror("err access()"); client->status = 403; @@ -103,10 +107,15 @@ void Webserv::_upload_files(Client *client) ofd.close(); } + client->status = 204; + client->response.append(CRLF); + // https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content +} + +/* if (file_existed) // with multi body it doesn't make much sense { - // client->status = 200; - client->status = 204; // DEBUG 204 + client->status = 200; client->response.append("Location: "); client->response.append("/index.html"); // WIP client->response.append(CRLF); @@ -115,12 +124,11 @@ void Webserv::_upload_files(Client *client) } else { - // client->status = 201; - client->status = 204; // DEBUG 204 + client->status = 201; client->response.append("Location: "); client->response.append("/index.html"); // WIP client->response.append(CRLF); client->response.append(CRLF); // WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4 } -} +*/ diff --git a/srcs/webserv/parsing_message_http.cpp b/srcs/webserv/parsing_message_http.cpp deleted file mode 100644 index 2712f20..0000000 --- a/srcs/webserv/parsing_message_http.cpp +++ /dev/null @@ -1,17 +0,0 @@ - -#include "parsing_message_http.hpp" - -std::string - parse_http_body(std::string message) -{ - std::string body; - size_t pos; - - pos = message.find(CRLF CRLF); - pos += std::string(CRLF CRLF).size(); - // TODO: copying just like that might fail in case of binary or images - body = message.substr(pos); - - return body; -} - diff --git a/srcs/webserv/parsing_message_http.hpp b/srcs/webserv/parsing_message_http.hpp deleted file mode 100644 index d1e77ed..0000000 --- a/srcs/webserv/parsing_message_http.hpp +++ /dev/null @@ -1,32 +0,0 @@ - -#ifndef PARSING_MESSAGE_HTTP_HPP -# define PARSING_MESSAGE_HTTP_HPP - -# include -# include -# include -# include -# include "utils.hpp" - -std::map - parse_http_headers ( - std::string headers, - std::map fields ) - -std::string - parse_http_body(std::string message); - -// http message structure : -// -// start-line -// request-line -// method SP target SP version -// response-line -// version SP status SP reason -// header-fields -// name ":" SP value -// CRLF -// body - -#endif - diff --git a/srcs/webserv/parsing_request.cpp b/srcs/webserv/parsing_request.cpp deleted file mode 100644 index d83d9e3..0000000 --- a/srcs/webserv/parsing_request.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "parsing_request.hpp" - diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index 6cce4c0..c3a7776 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -47,6 +47,8 @@ int Webserv::_send_response(Client *client) if (client->status >= 400) _error_html_response(client); +/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n"; + std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0); if (ret == -1) @@ -55,6 +57,11 @@ int Webserv::_send_response(Client *client) std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG return SEND_CLOSE; } + if (ret == 0) // actually never happen ? + { + std::cerr << "SEND RET 0 for client.fd =" << client->get_cl_fd() << "\n"; // DEBUG + return SEND_CLOSE; + } std::cerr << "ret send() = " << ret << "\n"; // DEBUG return SEND_COMPLETE; @@ -83,8 +90,16 @@ void Webserv::_construct_response(Client *client) if (_is_cgi(client, path)) { script_output = _exec_cgi(client); + +///*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(script_output); std::cout << B_PURPLE "-----------" RESET "\n\n"; +///*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; + _check_script_output(client, script_output); client->response += script_output; + +/*DEBUG*/ std::cout << B_YELLOW "inside cgi" RESET "\n"; +///*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; + return; } _process_method(client, path); @@ -157,12 +172,17 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s ServerConfig *_determine_process_server(Client *client, std::vector &servers) { /* + Behavior like this : http://nginx.org/en/docs/http/request_processing.html - _determine_process_server() should be complete. - TODO : test it */ - std::string const &server_name = client->get_rq_headers("Host"); + std::string server_name = client->get_rq_headers("Host"); + std::cerr << "server_name = " << server_name << "\n"; + size_t pos = server_name.rfind(':'); + if (pos != NPOS) + server_name.erase(pos); + std::cerr << "server_name = " << server_name << "\n"; + std::vector::iterator it = servers.begin(); std::vector::iterator default_server = servers.end(); diff --git a/www/form_get.html b/www/form_get.html index 626478d..8379b2d 100644 --- a/www/form_get.html +++ b/www/form_get.html @@ -3,11 +3,82 @@ + -
+ + + +

get form

+

to /cgi-bin/cgi_cpp.out

+
+
+
+

+
+
+ +
+ +

post form

+

to /cgi-bin/cgi_cpp.out

+
+
+
+

+ + +
+
+ +
+ +

get form

+

to /cgi-bin/cgi_cpp_content_length.out

+
+
+
+

+ + +
+
+ +
+ +

post form

+

to /cgi-bin/cgi_cpp_content_length.out

+
+
+
+

+ + +
+
+ diff --git a/www/form_post.html b/www/form_post.html deleted file mode 100644 index ae36576..0000000 --- a/www/form_post.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -
- -
- - - diff --git a/www2/index.html b/www2/index.html new file mode 100644 index 0000000..3cf91ae --- /dev/null +++ b/www2/index.html @@ -0,0 +1,11 @@ + + + + Le Webserv + + +

( ͡◉ ͜ ʖ ͡◉) Le index du serveur 2 !

+
+

˚ಎ˚

+ +