From 17230ccc4201d8ebf2bf777d6f21e3505fb3a641 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Wed, 10 Aug 2022 16:24:24 +0200 Subject: [PATCH 01/14] in cgi exec add save stdin and out --- srcs/webserv/cgi_script.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index be03cbe..1e47585 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -89,6 +89,8 @@ 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); pipe(fd_in); pipe(fd_out); @@ -104,9 +106,9 @@ std::string Webserv::_exec_script(Client *client, char **env) dup2(FD_WR_TO_PRNT, STDOUT_FILENO); // DEBUG std::cerr << "execve:\n"; - execve(client->get_rq_script_path().c_str(), nll, env); + //execve(client->get_rq_script_path().c_str(), nll, env); // for tests execve crash : - //execve("wrong", nll, env); + execve("wrong", nll, env); std::cerr << "execve crashed.\n"; } else @@ -127,6 +129,9 @@ 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; } From c7905ebd19e1576218c8204e23853b676a12cb08 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Wed, 10 Aug 2022 17:12:28 +0200 Subject: [PATCH 02/14] changed del_line_in_str to etract_line --- srcs/utils.cpp | 20 ++++++++++++++++---- srcs/utils.hpp | 2 +- srcs/webserv/cgi_script.cpp | 7 ++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 75c60a0..d8e0c11 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -151,10 +151,14 @@ std::string str_tolower(std::string str) return str; } -void del_line_in_str(std::string * str, size_t pos, std::string delim) +// identify a line in a string, by delim (ex. '\n') +// delete this line from the string +// and return the deleted line +std::string extract_line(std::string * str, size_t pos, std::string delim) { - size_t begin; - size_t end; + std::string del_str; + size_t begin; + size_t end; begin = (*str).rfind(delim, pos); if (begin == std::string::npos) @@ -168,10 +172,18 @@ void del_line_in_str(std::string * str, size_t pos, std::string delim) else end += delim.size(); + del_str = (*str).substr(begin, end - begin); (*str).erase(begin, end - begin); + return del_str; } - +// transform a str, like a http header, into a map +// with delim +// and perform an action on keys and values +// action receives address of keys and values, and return bool error : +// bool action(&keys, &values) +//std::map +// str_to_map(str, delim, action = NULL) bool operator==(const listen_socket& lhs, int fd) { return lhs.fd == fd; } diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 861dd3f..ea38236 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -44,7 +44,7 @@ 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); std::string str_tolower(std::string str); -void del_line_in_str(std::string * str, size_t pos, std::string delim); +std::string extract_line(std::string * str, size_t pos, std::string delim); void throw_test(); #endif diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index 1e47585..01c56a7 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -154,7 +154,8 @@ void Webserv::_check_script_status(Client *client, std::string output) client->status = atoi(output.c_str() + status_pos); ::del_line_in_str(&output, pos, CRLF); } - client->status = 200; + else + client->status = 200; } void Webserv::_check_script_fields(Client *client, std::string output) @@ -165,8 +166,8 @@ void Webserv::_check_script_fields(Client *client, std::string output) std::map::iterator it_scr; size_t pos; - srv_fld = parse_http_headers(client->response); - scr_fld = parse_http_headers(output); + srv_fld = ::parse_http_headers(client->response); + scr_fld = ::parse_http_headers(output); // wip: compare both map to supress duplicates for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++) { From 9a379c835d845d93bfe06aa195f1ed7098d5f0f8 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Wed, 10 Aug 2022 20:01:23 +0200 Subject: [PATCH 03/14] correction of trim in case of str only fill with delim char + added a new split, that also does trim, to split without counting newlines --- srcs/Client.cpp | 6 ++-- srcs/utils.cpp | 47 +++++++++++++++++---------- srcs/utils.hpp | 2 +- srcs/webserv/parsing_message_http.cpp | 37 ++++++++++++--------- srcs/webserv/parsing_message_http.hpp | 4 +-- 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 4068f38..3cdb737 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -200,10 +200,12 @@ std::string Client::get_rq_headers(const std::string & key) const void Client::_parse_request_line() { std::vector line; + std::string raw_line; int ret; - ret = ::parse_http_first_line(raw_request, line); - if (ret != 3) + raw_line = extract_line(); + line = ::parse_http_first_line(raw_line); + if (line.size() != 3) { std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n"; status = 400; // "bad request" diff --git a/srcs/utils.cpp b/srcs/utils.cpp index d8e0c11..4b8c903 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -21,21 +21,42 @@ std::vector split(std::string input, char delimiter) return answer; } -std::string trim(std::string str, char c) +std::vector + split_trim(std::string input, std::string delim = "\n", char ctrim = '') +{ + std::vector split_str; + std::string tmp; + size_t start = 0; + size_t end; + + end = input.find(delim); + while (end != -1) + { + tmp = input.substr(start, end - start); + tmp = trim(tmp, ctrim); + if (tmp.size() != 0) + split_str.push_back( tmp ); + start = end + delim.size(); + end = input.find(delim, start); + } + split_str.push_back( input.substr(start, end - start) ); + return split_str; +} + +std::string trim(std::string str, char del) { size_t pos; - // delete leadings c - pos = str.find_first_not_of(c); + // delete leadings del + pos = str.find_first_not_of(del); if (pos == std::string::npos) - return str; + pos = str.size(); str = str.substr(pos); - // delete endings c - pos = str.find_last_not_of(c); - if (pos == std::string::npos) - return str; - str = str.substr(0, pos + 1); + // delete trailing del + pos = str.find_last_not_of(del); + if (pos != std::string::npos) + str = str.substr(0, pos + 1); return str; } @@ -177,14 +198,6 @@ std::string extract_line(std::string * str, size_t pos, std::string delim) return del_str; } -// transform a str, like a http header, into a map -// with delim -// and perform an action on keys and values -// action receives address of keys and values, and return bool error : -// bool action(&keys, &values) -//std::map -// str_to_map(str, delim, action = NULL) - bool operator==(const listen_socket& lhs, int fd) { return lhs.fd == fd; } diff --git a/srcs/utils.hpp b/srcs/utils.hpp index ea38236..587b9f3 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -38,7 +38,7 @@ std::vector split(std::string input, char delimiter); bool isNumeric(std::string str); bool isNumeric_btw(int low, int high, std::string str); std::string itos(int n); -std::string trim(std::string str, char c); +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); int path_is_valid(std::string path); diff --git a/srcs/webserv/parsing_message_http.cpp b/srcs/webserv/parsing_message_http.cpp index 63e67cd..7430bba 100644 --- a/srcs/webserv/parsing_message_http.cpp +++ b/srcs/webserv/parsing_message_http.cpp @@ -1,29 +1,36 @@ #include "parsing_message_http.hpp" -size_t - parse_http_first_line(std::string message, std::vector &line) +/* + - parse_http_first_line() : + - copy first line into a vector of 3 strings + / copy line into a vector of 3 strings + - parse_http_headers(std::string message) + - + - parse_http_body(std::string message) +*/ + +std::vector + parse_http_first_line(std::string line) { std::vector sline; + std::vector line = ""; std::string sub; std::string tmp; size_t pos; - size_t ret; - // TODO: check for err in substr - pos = message.find(CRLF); - sub = message.substr(0, pos); - sline = ::split(sub, ' '); - ret = sline.size(); - if (ret != 3) - return ret; - for (int i = 0; i < 3; i++) + sline = ::split(line, ' '); + if (sline.size() == 3) { - tmp = ::trim(sline[i], ' '); - tmp = ::trim(tmp, '\r'); - line.push_back(tmp); + for (int i = 0; i < 3; i++) + { + tmp = sline[i]; + tmp = ::trim(tmp, '\r'); + tmp = ::trim(tmp, ' '); + line.push_back(tmp); + } } - return ret; + return line; } std::map diff --git a/srcs/webserv/parsing_message_http.hpp b/srcs/webserv/parsing_message_http.hpp index 67cd473..a512aa1 100644 --- a/srcs/webserv/parsing_message_http.hpp +++ b/srcs/webserv/parsing_message_http.hpp @@ -8,8 +8,8 @@ # include # include "utils.hpp" -size_t - parse_http_first_line(std::string message, std::vector &line); +std::vector + parse_http_first_line(std::string message); std::map parse_http_headers(std::string message); From 11f71ea74fa2cf8c6905ede2bef7c0ca6f6a86da Mon Sep 17 00:00:00 2001 From: hugogogo Date: Wed, 10 Aug 2022 20:27:48 +0200 Subject: [PATCH 04/14] wip parsing first line http message with new utils functions --- srcs/Client.cpp | 2 +- srcs/utils.cpp | 13 +++++++------ srcs/utils.hpp | 1 + srcs/webserv/parsing_message_http.cpp | 23 ----------------------- srcs/webserv/parsing_message_http.hpp | 3 --- 5 files changed, 9 insertions(+), 33 deletions(-) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 3cdb737..c817f15 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -204,7 +204,7 @@ void Client::_parse_request_line() int ret; raw_line = extract_line(); - line = ::parse_http_first_line(raw_line); + line = ::split_trim(raw_line, " ", ' '); if (line.size() != 3) { std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n"; diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 4b8c903..e0ca568 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -22,18 +22,19 @@ std::vector split(std::string input, char delimiter) } std::vector - split_trim(std::string input, std::string delim = "\n", char ctrim = '') + split_trim(std::string input, std::string delim = "\n", char ctrim = '\0') { - std::vector split_str; - std::string tmp; - size_t start = 0; - size_t end; + std::vector split_str; + std::string tmp; + int start = 0; + int end; end = input.find(delim); while (end != -1) { tmp = input.substr(start, end - start); - tmp = trim(tmp, ctrim); + if (ctrim != '\0') + tmp = trim(tmp, ctrim); if (tmp.size() != 0) split_str.push_back( tmp ); start = end + delim.size(); diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 587b9f3..2d402fc 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -35,6 +35,7 @@ bool operator==(const listen_socket& lhs, int fd); bool operator==(int fd, const listen_socket& rhs); std::vector split(std::string input, char delimiter); +std::vector split_trim(std::string s, std::string d, char c); bool isNumeric(std::string str); bool isNumeric_btw(int low, int high, std::string str); std::string itos(int n); diff --git a/srcs/webserv/parsing_message_http.cpp b/srcs/webserv/parsing_message_http.cpp index 7430bba..9b8adc8 100644 --- a/srcs/webserv/parsing_message_http.cpp +++ b/srcs/webserv/parsing_message_http.cpp @@ -10,29 +10,6 @@ - parse_http_body(std::string message) */ -std::vector - parse_http_first_line(std::string line) -{ - std::vector sline; - std::vector line = ""; - std::string sub; - std::string tmp; - size_t pos; - - sline = ::split(line, ' '); - if (sline.size() == 3) - { - for (int i = 0; i < 3; i++) - { - tmp = sline[i]; - tmp = ::trim(tmp, '\r'); - tmp = ::trim(tmp, ' '); - line.push_back(tmp); - } - } - return line; -} - std::map parse_http_headers(std::string message) { diff --git a/srcs/webserv/parsing_message_http.hpp b/srcs/webserv/parsing_message_http.hpp index a512aa1..5024d8d 100644 --- a/srcs/webserv/parsing_message_http.hpp +++ b/srcs/webserv/parsing_message_http.hpp @@ -8,9 +8,6 @@ # include # include "utils.hpp" -std::vector - parse_http_first_line(std::string message); - std::map parse_http_headers(std::string message); From 1d67e6988d9476b26e8671f110f9c694e6e95ddc Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 00:28:01 +0200 Subject: [PATCH 05/14] http message parsin wip but really better --- Makefile | 1 - srcs/Client.cpp | 32 +++++++--- srcs/Client.hpp | 1 - srcs/utils.cpp | 86 ++++++++++++++++++++++++--- srcs/utils.hpp | 5 +- srcs/webserv/cgi_script.cpp | 4 +- srcs/webserv/parsing_message_http.cpp | 57 ------------------ srcs/webserv/parsing_message_http.hpp | 7 +-- 8 files changed, 112 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index 417d8e6..b8b6734 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,6 @@ SRCS = main.cpp \ utils.cpp \ cgi_script.cpp \ Client.cpp \ - parsing_message_http.cpp \ OBJS_D = builds OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index c817f15..e10dca5 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -77,22 +77,26 @@ Client & Client::operator=( Client const & rhs ) // https://www.tutorialspoint.com/http/http_requests.htm void Client::parse_request(std::vector &servers) { - std::map headers; - std::string body; +// std::map headers; +// std::string body; + std::string copy_request; // DEBUG // std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n"; clear_request(); // not mandatory - _parse_request_line(); + _parse_request_line(copy_request); + if (status) + return; + _parse_request_headers(copy_request); if (status) return; - _parse_request_headers(); assigned_server = ::_determine_process_server(this, servers); assigned_location = ::_determine_location(*assigned_server, _request.abs_path); _check_request_errors(); if (status) return; + // use getter for headers because it works case insensitive _parse_port_hostname(this->get_rq_headers("Host")); /* dont clear raw_request, we need it for future reparsing of body @@ -203,7 +207,7 @@ void Client::_parse_request_line() std::string raw_line; int ret; - raw_line = extract_line(); + raw_line = ::get_line(raw_request, 0, CRLF); line = ::split_trim(raw_line, " ", ' '); if (line.size() != 3) { @@ -233,8 +237,22 @@ void Client::_parse_request_uri( std::string uri ) void Client::_parse_request_headers() { - // TODO: check error and adjust status - _request.headers = ::parse_http_headers(raw_request); + std::string headers; + size_t pos; + int ret; + + headers = raw_request; + // extract header part + ::extract_line(headers, 0, CRLF); + pos = headers.find(CRLF); + ::extract_line(headers, pos); // delete from pos to the end + // copy result of parser into headers + ret = ::parse_http_headers(raw_request, _request.headers); + if (ret > 0) + { + std::cerr << "err _parse_request_headers(): " << ret << " field are bad formated\n"; + status = 400; // "bad request" + } } void Client::_parse_port_hostname(std::string host) diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 43a0913..2b82f36 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -11,7 +11,6 @@ # include // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa # include "utils.hpp" # include "ServerConfig.hpp" -# include "parsing_message_http.hpp" struct Script { diff --git a/srcs/utils.cpp b/srcs/utils.cpp index e0ca568..b3d8173 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -9,6 +9,14 @@ void throw_test() throw std::bad_alloc(); } +// notice : the use of getline make it such as +// it doesn't identify multiple delim as one : +// " something \n else " -> 1 - something +// 2 - else +// is not the same as : +// " something \n\n else " -> 1 - something +// 2 - +// 3 - else std::vector split(std::string input, char delimiter) { std::vector answer; @@ -176,29 +184,91 @@ std::string str_tolower(std::string str) // identify a line in a string, by delim (ex. '\n') // delete this line from the string // and return the deleted line -std::string extract_line(std::string * str, size_t pos, std::string delim) +// if delim is empty, the extraction begin exactly at pos, and end at npos +std::string + extract_line(std::string & str, size_t pos = 0, std::string delim = "") { std::string del_str; size_t begin; size_t end; - begin = (*str).rfind(delim, pos); + begin = str.rfind(delim, pos); if (begin == std::string::npos) begin = 0; else begin += delim.size(); - end = (*str).find(delim, pos); - if (end == std::string::npos) - end = 0; - else + end = str.find(delim, pos); + if (end != std::string::npos) end += delim.size(); + if (delim.empty()) + end = std::string::npos; - del_str = (*str).substr(begin, end - begin); - (*str).erase(begin, end - begin); + del_str = str.substr(begin, end - begin); + str.erase(begin, end - begin); return del_str; } +// get a line in a string, by delim +// same as extract, except it doesn't delete it +std::string get_line(std::string str, size_t pos = 0, std::string delim = "") +{ + std::string ret_str; + size_t begin; + size_t end; + + begin = str.rfind(delim, pos); + if (begin == std::string::npos) + begin = 0; + else + begin += delim.size(); + + end = str.find(delim, pos); + if (end != std::string::npos) + end += delim.size(); + if (delim.empty()) + end = std::string::npos; + + ret_str = str.substr(begin, end - begin); + return ret_str; +} + +std::map + parse_http_headers ( + std::string headers, + std::map fields ) +{ + std::vector list; + std::vector::iterator it; + std::vector::iterator it_end; + size_t err = 0; + size_t pos; + + list = ::split_trim(sub, CRLF, ' '); + + it_end = list.end(); + for (it = list.begin(); it != it_end; it++) + { + pos = (*it).find(':'); + if (pos == std::string::npos) + { + err++; + continue; + } + key = (*it).substr(0, pos); + if ( key.find(' ') != std::string::npos ) + { + err++; + continue; + } + key = ::str_tolower(key); // to make "key" case_insensitive + val = (*it).substr(pos + 1); + val = ::trim(val, ' '); + fields.insert( std::pair(key, val) ); + } + return err; +} + bool operator==(const listen_socket& lhs, int fd) { return lhs.fd == fd; } diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 2d402fc..58bf2e8 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -45,7 +45,10 @@ 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); std::string str_tolower(std::string str); -std::string extract_line(std::string * str, size_t pos, std::string delim); +std::string extract_line(std::string & s, size_t p, std::string d); +std::string get_line(std::string str, size_t pos, std::string del); +std::map + parse_http_headers (std::string headers, std::map fields ); void throw_test(); #endif diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index 01c56a7..70a05ef 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -152,7 +152,7 @@ void Webserv::_check_script_status(Client *client, std::string output) { status_pos = pos + std::string("Status:").size(); client->status = atoi(output.c_str() + status_pos); - ::del_line_in_str(&output, pos, CRLF); + ::extract_line(output, pos, CRLF); } else client->status = 200; @@ -176,7 +176,7 @@ void Webserv::_check_script_fields(Client *client, std::string output) if (it_srv->first == it_scr->first) { pos = client->response.find(it_srv->first); - ::del_line_in_str(&client->response, pos, CRLF); + ::extract_line(client->response, pos, CRLF); } } } diff --git a/srcs/webserv/parsing_message_http.cpp b/srcs/webserv/parsing_message_http.cpp index 9b8adc8..2712f20 100644 --- a/srcs/webserv/parsing_message_http.cpp +++ b/srcs/webserv/parsing_message_http.cpp @@ -1,48 +1,6 @@ #include "parsing_message_http.hpp" -/* - - parse_http_first_line() : - - copy first line into a vector of 3 strings - / copy line into a vector of 3 strings - - parse_http_headers(std::string message) - - - - parse_http_body(std::string message) -*/ - -std::map - parse_http_headers(std::string message) -{ - std::map headers; - std::vector list; - std::vector::iterator it; - std::string sub; - std::string key; - std::string val; - size_t pos; - - pos = (message).find(CRLF CRLF); - sub = (message).substr(0, pos); - list = ::split(sub, '\n'); - if ( maybe_http_first_line( *list.begin() ) ) - list.erase(list.begin()); - - for (it = list.begin(); it != list.end(); it++) - { - // TODO: if pattern is not "NAME: value" return error - pos = (*it).find(':'); - key = (*it).substr( 0, pos ); - key = ::trim(key, ' '); - key = ::trim(key, '\r'); - key = ::str_tolower(key); - val = (*it).substr( pos + 1 ); - val = ::trim(val, ' '); - val = ::trim(val, '\r'); - headers.insert( std::pair(key, val) ); - } - return headers; -} - std::string parse_http_body(std::string message) { @@ -57,18 +15,3 @@ std::string return body; } -bool maybe_http_first_line(std::string str) -{ -// method SP target SP version https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.1 -// version SP status SP reason https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.2 - - std::vector sline; - - sline = ::split(str, ' '); - if (sline.size() != 3) - return false; - if (sline[0].find(':') != std::string::npos) - return false; - return true; -} - diff --git a/srcs/webserv/parsing_message_http.hpp b/srcs/webserv/parsing_message_http.hpp index 5024d8d..d1e77ed 100644 --- a/srcs/webserv/parsing_message_http.hpp +++ b/srcs/webserv/parsing_message_http.hpp @@ -9,14 +9,13 @@ # include "utils.hpp" std::map - parse_http_headers(std::string message); + parse_http_headers ( + std::string headers, + std::map fields ) std::string parse_http_body(std::string message); -bool - maybe_http_first_line(std::string); - // http message structure : // // start-line From 27b4f966180ebd16b7ca62d0f8c26c1c59ad68ba Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 00:41:17 +0200 Subject: [PATCH 06/14] parsing headers is allright now + adding error codes in readme and in comments in client --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ srcs/Client.cpp | 22 +++++++++++++--------- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2f66508..95ff686 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,45 @@ [7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7) +--- +## http errors + +#### HTTP Client Errors +- 400 Bad Request This error code indicates that the request cannot be processed because of wrong syntax usage by the client. +- 401 Unauthorized This error code indicates that the client is not authorized to receive the requested data, without authentication. A login name and password based authentication might be required to access the requested data. +- 403 Forbidden There is no way you can access the requested data. A 403 error announces that the data is off limits. +- 404 Not Found This error indicates that the resources requested by the client are currently unavailable. +- 405 Method Not Allowed This error indicates wrong usage of request method. Depending on the kind of data requested, the appropriate request method must be chosen. +- 406 Not Acceptable When the data provided by a web server does not match the specifications made in ‘Accept’ header of the client HTTP request, this error is the result. +- 407 Proxy Authentication Required This error clearly indicates that an authentication from the proxy server is required to gain access to requested resources. +- 408 Request Timeout This type of error indicates that the client was delayed in making a request, within the specified time allocated to it, by the server. +- 409 Conflict This error code is displayed when the server perceives a conflict between two requests made simultaneously by different clients, for the same resource. +- 410 Gone This error code indicates that the requested data is no longer hosted on the server and therefore further requests made for it, would be futile. +- 411 Length Required If the request made by the client does not include information about the length of the requested data or resource, this error code is displayed. +- 412 Precondition Failed Some requests made by clients come attached with conditions that need to be satisfied by the server, before data transaction may happen. If these conditions are not met, error 412 results. +- 413 Request Entity Too Large When a client makes a request which is too overwhelming for the server’s resources to handle, it presents this error code. +- 414 Requested URI Too Long A Uniform Resource Identifier (URI) is a character string used to describe a data stream or resource on a server. Error 414 occurs when the server is unable to process the URI, because of limited resources and long string length. +- 415 Unsupported Media Type A server may be designed to allow only certain formats for media files. When error 415 is displayed, it indicates that the format of file being uploaded through a client request, does not match the requisite format. +- 416 Request Range Not Satisfiable Sometimes, a client may request for only a small part of a file, instead of asking for the entire file. If this request is not specified properly and the part of the file requested does not exist, this error is displayed. +- 417 Expectation Failed This error code is displayed when the server cannot meet the specifications provided in the request. +- 422 Unprocessable Entity This error is displayed when the request made, cannot be processed due to an error in semantic structure. +- 423 Locked This error is displayed when a requested piece of data or resource has been locked, making it inaccessible for a server. +- 424 Failed Dependency A server may process a succession of requests from a client with the fulfillment of each, dependent on the one made before. This error is displayed when a request made before is not fulfilled, due to which the current request cannot be processed. +- 426 Upgrade Required This error signifies that the client may need to switch over to a secure protocol like TLS to get the request processed. +- 444 No Response This error signifies that the server has simply rejected the client request and terminated connection. +- 449 Retry With This is a request made by the server to the client, to make the request again after executing certain actions or making specific changes in request. This is an error code introduced by Microsoft. +- 499 Client Closed Request When client terminates a connection made with the server, while its processing the associated request, this error code is displayed. +- 450 Blocked By Windows Parental Controls Another error code introduced by Microsoft, this one is displayed when a URL is blocked by parental control settings on the web browsers. + +#### HTTP Server Errors +- 500 Internal Server Error A generic message displayed by the server, when the problem with the request cannot be specified by any other appropriate code. +- 501 Not Implemented This error indicates the inability of the server to process a request, as it hasn’t been configured to respond to the request method used. +- 502 Bad Gateway Sometimes, hosted pages on web servers are transmitted to client via proxy servers. If the proxy server (to which a client has sent a request), fails connecting with the web server (known as the upstream server), error 502 results. +- 503 Service Unavailable When the server is already overloaded with multiple requests, it will temporarily stop entertaining new requests, by displaying a 503 error code. +- 504 Gateway Timeout When the request made by a proxy server to the web server hosting a resource times out, error 504 is reported. +- 505 HTTP Version Not Supported An error code seen rarely, it is displayed when the web server does not support the protocol version of the client request. + + --- ## cgi env variables [cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index e10dca5..cb6382a 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -99,17 +99,21 @@ void Client::parse_request(std::vector &servers) // use getter for headers because it works case insensitive _parse_port_hostname(this->get_rq_headers("Host")); -/* dont clear raw_request, we need it for future reparsing of body - see call of parse_request() in _read_request() */ +// dont clear raw_request, we need it for future reparsing of body +// see call of parse_request() in _read_request() // raw_request.clear(); } void Client::parse_request_body() { - // TODO: check error and adjust status - _request.body = ::parse_http_body(raw_request); + size_t pos; + + pos = raw_request.find(CRLF CRLF); + pos += std::string(CRLF CRLF).size(); + _request.body = message.substr(pos); + if (_request.body.size() > assigned_server->client_body_limit) - status = 413; + status = 413; // HTTP Client Errors } bool Client::fill_script_path(std::string script) @@ -279,12 +283,12 @@ void Client::_check_request_errors() ////////////////////// // Request line checks if (_request.method == UNKNOWN) - status = 501; + status = 501; // HTTP Client Errors else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0) - status = 505; + status = 505; // HTTP Client Errors else if (!(assigned_location->allow_methods & _request.method)) { - status = 405; + status = 405; // HTTP Client Errors response.append("Allow: "); response.append(::http_methods_to_str(assigned_location->allow_methods)); response.append(CRLF); @@ -305,7 +309,7 @@ void Client::_check_request_errors() // Headers checks if (!this->get_rq_headers("Content-Length").empty() && ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit) - status = 413; + status = 413; // HTTP Client Errors return; } From ff77dfd298ca68cd217130dad36e7c1c3f6593e4 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 14:50:56 +0200 Subject: [PATCH 07/14] debug new request parsing --- srcs/Client.cpp | 56 ++++++++++++++++++++++++++----------- srcs/Client.hpp | 2 ++ srcs/cgi-bin/php-cgi | 3 +- srcs/utils.cpp | 6 ++-- srcs/utils.hpp | 4 +-- srcs/webserv/cgi_script.cpp | 11 ++++++-- 6 files changed, 58 insertions(+), 24 deletions(-) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index cb6382a..5c5e975 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -77,18 +77,16 @@ Client & Client::operator=( Client const & rhs ) // https://www.tutorialspoint.com/http/http_requests.htm void Client::parse_request(std::vector &servers) { -// std::map headers; -// std::string body; - std::string copy_request; - -// DEBUG -// std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n"; clear_request(); // not mandatory - - _parse_request_line(copy_request); + _parse_request_line(); +// debug +print_client("first line"); if (status) return; - _parse_request_headers(copy_request); + _parse_request_headers(); +// debug +print_client("headers"); + if (status) return; assigned_server = ::_determine_process_server(this, servers); @@ -106,11 +104,11 @@ void Client::parse_request(std::vector &servers) void Client::parse_request_body() { - size_t pos; + size_t pos; pos = raw_request.find(CRLF CRLF); pos += std::string(CRLF CRLF).size(); - _request.body = message.substr(pos); + _request.body = raw_request.substr(pos); if (_request.body.size() > assigned_server->client_body_limit) status = 413; // HTTP Client Errors @@ -166,6 +164,32 @@ void Client::clear_script() _request.script.info.clear(); } +// debug +void Client::print_client(std::string message) +{ + std::map::iterator it; + + std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; + std::cout << "\n" << message << ":\n----------\n" + << "raw_request:\n__\n" << raw_request << "\n__\n" + << "get_cl_fd() : " << get_cl_fd() << "\n" + << "get_cl_port() : " << get_cl_port() << "\n" + << "get_cl_ip() : " << get_cl_ip() << "\n" + << "get_rq_method_str() : " << get_rq_method_str() << "\n" + << "get_rq_uri() : " << get_rq_uri() << "\n" + << "get_rq_abs_path() : " << get_rq_abs_path() << "\n" + << "get_rq_query() : " << get_rq_query() << "\n" + << "get_rq_version() : " << get_rq_version() << "\n" + << "get_rq_body() : " << get_rq_body() << "\n" + << "get_rq_port() : " << get_rq_port() << "\n" + << "get_rq_hostname() : " << get_rq_hostname() << "\n" + << "get_rq_script_path() : " << get_rq_script_path() << "\n" + << "get_rq_script_info() : " << get_rq_script_info() << "\n" + << "headers : " << "\n"; + for (it = _request.headers.begin(); it != _request.headers.end(); it++) + std::cout << " " << it->first << ": " << it->second << "\n"; + std::cout << "\n=== END DEBUG ===\n\n"; +} /********************************************* * GETTERS @@ -209,13 +233,12 @@ void Client::_parse_request_line() { std::vector line; std::string raw_line; - int ret; raw_line = ::get_line(raw_request, 0, CRLF); line = ::split_trim(raw_line, " ", ' '); if (line.size() != 3) { - std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n"; + std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n"; status = 400; // "bad request" } else @@ -247,9 +270,10 @@ void Client::_parse_request_headers() headers = raw_request; // extract header part - ::extract_line(headers, 0, CRLF); - pos = headers.find(CRLF); - ::extract_line(headers, pos); // delete from pos to the end + ::extract_line(headers, 0, CRLF); // delete first line + pos = headers.find(CRLF CRLF); +// ::extract_line(headers, pos); // delete from empty line to the end + headers.erase(pos); //delete from empty line to the end // copy result of parser into headers ret = ::parse_http_headers(raw_request, _request.headers); if (ret > 0) diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 2b82f36..2e8300c 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -75,6 +75,8 @@ class Client void clear_request(); void clear_script(); bool fill_script_path(std::string script); + // DEBUG + void print_client(std::string message = ""); private: int _fd; diff --git a/srcs/cgi-bin/php-cgi b/srcs/cgi-bin/php-cgi index d3d3625..b8e85dc 100755 --- a/srcs/cgi-bin/php-cgi +++ b/srcs/cgi-bin/php-cgi @@ -1,7 +1,8 @@ #! /usr/bin/php +size_t parse_http_headers ( std::string headers, std::map fields ) @@ -243,8 +243,10 @@ std::map std::vector::iterator it_end; size_t err = 0; size_t pos; + std::string key; + std::string val; - list = ::split_trim(sub, CRLF, ' '); + list = ::split_trim(headers, CRLF, ' '); it_end = list.end(); for (it = list.begin(); it != it_end; it++) diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 58bf2e8..4e7e624 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -3,6 +3,7 @@ # define UTILS_HPP # include +# include # include # include # include // atoi @@ -47,8 +48,7 @@ void replace_all_substr(std::string &str, const std::string &ori_substr, co std::string str_tolower(std::string str); std::string extract_line(std::string & s, size_t p, std::string d); std::string get_line(std::string str, size_t pos, std::string del); -std::map - parse_http_headers (std::string headers, std::map fields ); +size_t parse_http_headers (std::string headers, std::map fields ); void throw_test(); #endif diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index 70a05ef..5f92af8 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -137,8 +137,13 @@ std::string Webserv::_exec_script(Client *client, char **env) void Webserv::_check_script_output(Client *client, std::string output) { - // TODO: it doesn't work with execve error, i don't know why yet ? +// DEBUG +std::cout << "outpu:__________\n" << output << "__________\n" + << "\nstatus:" << client->status << "__________\n"; _check_script_status(client, output); +// DEBUG +std::cout << "outpu:__________\n" << output << "__________\n" + << "\nstatus:" << client->status << "__________\n"; _check_script_fields(client, output); } @@ -166,8 +171,8 @@ void Webserv::_check_script_fields(Client *client, std::string output) std::map::iterator it_scr; size_t pos; - srv_fld = ::parse_http_headers(client->response); - scr_fld = ::parse_http_headers(output); + ::parse_http_headers(client->response, srv_fld); + ::parse_http_headers(output, scr_fld); // wip: compare both map to supress duplicates for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++) { From ad2b5a629ac4cf763c93bc3e7fb006704705984f Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 17:33:42 +0200 Subject: [PATCH 08/14] correct extract_line to avoid duplicate with erase or substr, and to resolve pbm of returning trailing delim --- srcs/Client.cpp | 47 ++++++++++++++++---------- srcs/utils.cpp | 90 ++++++++++++++++++++++++++++++------------------- srcs/utils.hpp | 9 +++-- 3 files changed, 91 insertions(+), 55 deletions(-) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 5c5e975..c04ce05 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -78,9 +78,13 @@ Client & Client::operator=( Client const & rhs ) void Client::parse_request(std::vector &servers) { clear_request(); // not mandatory +// debug +print_client("before"); + _parse_request_line(); // debug print_client("first line"); + if (status) return; _parse_request_headers(); @@ -167,25 +171,26 @@ void Client::clear_script() // debug void Client::print_client(std::string message) { - std::map::iterator it; + std::map::iterator it; std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; - std::cout << "\n" << message << ":\n----------\n" - << "raw_request:\n__\n" << raw_request << "\n__\n" - << "get_cl_fd() : " << get_cl_fd() << "\n" - << "get_cl_port() : " << get_cl_port() << "\n" - << "get_cl_ip() : " << get_cl_ip() << "\n" - << "get_rq_method_str() : " << get_rq_method_str() << "\n" - << "get_rq_uri() : " << get_rq_uri() << "\n" - << "get_rq_abs_path() : " << get_rq_abs_path() << "\n" - << "get_rq_query() : " << get_rq_query() << "\n" - << "get_rq_version() : " << get_rq_version() << "\n" - << "get_rq_body() : " << get_rq_body() << "\n" - << "get_rq_port() : " << get_rq_port() << "\n" - << "get_rq_hostname() : " << get_rq_hostname() << "\n" - << "get_rq_script_path() : " << get_rq_script_path() << "\n" - << "get_rq_script_info() : " << get_rq_script_info() << "\n" - << "headers : " << "\n"; + std::cout << "\n" << message << ":\n----------\n" << "raw_request:\n__\n"; + ::print_special(raw_request); + std::cout << "\n__\n" + << "get_cl_fd() : [" << get_cl_fd() << "]\n" + << "get_cl_port() : [" << get_cl_port() << "]\n" + << "get_cl_ip() : [" << get_cl_ip() << "]\n" + << "get_rq_method_str() : [" << get_rq_method_str() << "]\n" + << "get_rq_uri() : [" << get_rq_uri() << "]\n" + << "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n" + << "get_rq_query() : [" << get_rq_query() << "]\n" + << "get_rq_version() : [" << get_rq_version() << "]\n" + << "get_rq_body() : [" << get_rq_body() << "]\n" + << "get_rq_port() : [" << get_rq_port() << "]\n" + << "get_rq_hostname() : [" << get_rq_hostname() << "]\n" + << "get_rq_script_path() : [" << get_rq_script_path() << "]\n" + << "get_rq_script_info() : [" << get_rq_script_info() << "]\n" + << "headers :\n"; for (it = _request.headers.begin(); it != _request.headers.end(); it++) std::cout << " " << it->first << ": " << it->second << "\n"; std::cout << "\n=== END DEBUG ===\n\n"; @@ -235,7 +240,13 @@ void Client::_parse_request_line() std::string raw_line; raw_line = ::get_line(raw_request, 0, CRLF); - line = ::split_trim(raw_line, " ", ' '); + +// DEBUG +std::cout << "raw_line:["; +::print_special(raw_line); +std::cout << "]\n"; + + line = ::split_trim(raw_line, " "); if (line.size() != 3) { std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n"; diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 5644d15..fe1af34 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -30,25 +30,27 @@ std::vector split(std::string input, char delimiter) } std::vector - split_trim(std::string input, std::string delim = "\n", char ctrim = '\0') + split_trim(std::string input, std::string delim, char ctrim) { std::vector split_str; std::string tmp; - int start = 0; - int end; + size_t start = 0; + size_t end = 0; + size_t len = 0; - end = input.find(delim); - while (end != -1) + while (end != std::string::npos) { - tmp = input.substr(start, end - start); + end = input.find(delim, start); + len = end - start; + if (end == std::string::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(); - end = input.find(delim, start); } - split_str.push_back( input.substr(start, end - start) ); return split_str; } @@ -70,6 +72,19 @@ std::string trim(std::string str, char del) return str; } +//// trim a set of char +//std::string trim(std::string str, std::string del) +//{ +// std::string new_str; +// +// while (new_str.compare(str) != 0) +// { +// for (size_t i = 0; i < del.size(); i++) +// trim(str, del[i]); +// } +// return str; +//} + std::string itos(int n) { std::stringstream strs; @@ -160,7 +175,11 @@ int path_is_valid(std::string path) } -void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr) +void + replace_all_substr( + std::string &str, + const std::string &ori_substr, + const std::string &new_substr) { if (ori_substr.empty()) return; @@ -184,13 +203,13 @@ std::string str_tolower(std::string str) // identify a line in a string, by delim (ex. '\n') // delete this line from the string // and return the deleted line -// if delim is empty, the extraction begin exactly at pos, and end at npos std::string - extract_line(std::string & str, size_t pos = 0, std::string delim = "") + extract_line(std::string & str, size_t pos, std::string delim) { std::string del_str; size_t begin; size_t end; + size_t len; begin = str.rfind(delim, pos); if (begin == std::string::npos) @@ -199,38 +218,23 @@ std::string begin += delim.size(); end = str.find(delim, pos); + len = end; if (end != std::string::npos) - end += delim.size(); - if (delim.empty()) - end = std::string::npos; + len = end - begin; - del_str = str.substr(begin, end - begin); - str.erase(begin, end - begin); + del_str = str.substr(begin, len); + str.erase(begin, len); return del_str; } // get a line in a string, by delim // same as extract, except it doesn't delete it -std::string get_line(std::string str, size_t pos = 0, std::string delim = "") +std::string get_line(std::string str, size_t pos, std::string delim) { - std::string ret_str; - size_t begin; - size_t end; + std::string ret; - begin = str.rfind(delim, pos); - if (begin == std::string::npos) - begin = 0; - else - begin += delim.size(); - - end = str.find(delim, pos); - if (end != std::string::npos) - end += delim.size(); - if (delim.empty()) - end = std::string::npos; - - ret_str = str.substr(begin, end - begin); - return ret_str; + ret = ::extract_line(str, pos, delim); + return ret; } size_t @@ -271,6 +275,24 @@ size_t return err; } +// DEBUG +void print_special(std::string str) +{ + char c; + + for (size_t i = 0; i < str.size(); i++) + { + c = str[i]; + if (c == '\r') + std::cout << "\\r"; + else if (c == '\n') + std::cout << "\\n" << "\n"; + else + std::cout << c; + fflush(stdout); + } +} + bool operator==(const listen_socket& lhs, int fd) { return lhs.fd == fd; } diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 4e7e624..6e325cf 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -10,6 +10,7 @@ # include // stat() # include // tolower # include // transform +# include // fflush # define CR "\r" # define LF "\n" @@ -36,7 +37,7 @@ bool operator==(const listen_socket& lhs, int fd); bool operator==(int fd, const listen_socket& rhs); std::vector split(std::string input, char delimiter); -std::vector split_trim(std::string s, std::string d, char c); +std::vector split_trim(std::string input, std::string delim = "\n", char ctrim = '\0'); bool isNumeric(std::string str); bool isNumeric_btw(int low, int high, std::string str); std::string itos(int n); @@ -46,9 +47,11 @@ 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); std::string str_tolower(std::string str); -std::string extract_line(std::string & s, size_t p, std::string d); -std::string get_line(std::string str, size_t pos, std::string del); +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 throw_test(); +// debug +void print_special(std::string str); #endif From a1fff0f8c2d241f9673918a6ea5f447b94dd2b18 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 17:46:27 +0200 Subject: [PATCH 09/14] added colors, for print_special + renamed client's public function parse_request() into parse_request_headers() --- srcs/Client.cpp | 19 ++++--------------- srcs/Client.hpp | 4 ++-- srcs/colors.h | 25 +++++++++++++++++++++++++ srcs/utils.cpp | 4 ++-- srcs/utils.hpp | 1 + srcs/webserv/request.cpp | 2 +- 6 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 srcs/colors.h diff --git a/srcs/Client.cpp b/srcs/Client.cpp index c04ce05..a4d58b8 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -75,22 +75,17 @@ Client & Client::operator=( Client const & rhs ) // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers // https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests // https://www.tutorialspoint.com/http/http_requests.htm -void Client::parse_request(std::vector &servers) +void Client::parse_request_headers(std::vector &servers) { clear_request(); // not mandatory -// debug -print_client("before"); - _parse_request_line(); // debug print_client("first line"); - if (status) return; - _parse_request_headers(); + _parse_request_fields(); // debug print_client("headers"); - if (status) return; assigned_server = ::_determine_process_server(this, servers); @@ -174,7 +169,7 @@ void Client::print_client(std::string message) std::map::iterator it; std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; - std::cout << "\n" << message << ":\n----------\n" << "raw_request:\n__\n"; + std::cout << "\n" << message << ":----------\n\n" << "raw_request:\n__\n"; ::print_special(raw_request); std::cout << "\n__\n" << "get_cl_fd() : [" << get_cl_fd() << "]\n" @@ -240,12 +235,6 @@ void Client::_parse_request_line() std::string raw_line; raw_line = ::get_line(raw_request, 0, CRLF); - -// DEBUG -std::cout << "raw_line:["; -::print_special(raw_line); -std::cout << "]\n"; - line = ::split_trim(raw_line, " "); if (line.size() != 3) { @@ -273,7 +262,7 @@ void Client::_parse_request_uri( std::string uri ) _request.abs_path = uri.substr(0, pos); } -void Client::_parse_request_headers() +void Client::_parse_request_fields() { std::string headers; size_t pos; diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 2e8300c..a6cc82f 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -69,7 +69,7 @@ class Client std::string get_rq_script_info() const; std::string get_rq_headers(const std::string & key) const; - void parse_request(std::vector &servers); + void parse_request_headers(std::vector &servers); void parse_request_body(); void clear(); void clear_request(); @@ -86,7 +86,7 @@ class Client struct Request _request; void _parse_request_line(); - void _parse_request_headers(); + void _parse_request_fields(); void _parse_request_uri( std::string uri ); void _parse_port_hostname(std::string host); diff --git a/srcs/colors.h b/srcs/colors.h new file mode 100644 index 0000000..0374e42 --- /dev/null +++ b/srcs/colors.h @@ -0,0 +1,25 @@ +#ifndef COLORS_H +# define COLORS_H + +# define GRAY "\e[0;30m" +# define RED "\e[0;31m" +# define GREEN "\e[0;32m" +# define YELLOW "\e[0;33m" +# define BLUE "\e[0;34m" +# define PURPLE "\e[0;35m" +# define CYAN "\e[0;36m" +# define WHITE "\e[0;37m" + +# define B_GRAY "\e[1;30m" +# define B_RED "\e[1;31m" +# define B_GREEN "\e[1;32m" +# define B_YELLOW "\e[1;33m" +# define B_BLUE "\e[1;34m" +# define B_PURPLE "\e[1;35m" +# define B_CYAN "\e[1;36m" +# define B_WHITE "\e[1;37m" + +# define RESET "\e[0m" + +#endif + diff --git a/srcs/utils.cpp b/srcs/utils.cpp index fe1af34..80db088 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -284,9 +284,9 @@ void print_special(std::string str) { c = str[i]; if (c == '\r') - std::cout << "\\r"; + std::cout << YELLOW << "\\r" << RESET; else if (c == '\n') - std::cout << "\\n" << "\n"; + std::cout << YELLOW << "\\n" << RESET << "\n"; else std::cout << c; fflush(stdout); diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 6e325cf..bc47588 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -11,6 +11,7 @@ # include // tolower # include // transform # include // fflush +# include "colors.h" # define CR "\r" # define LF "\n" diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index d3d075a..941df21 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -56,7 +56,7 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring if (client->raw_request.find(CRLF CRLF) != std::string::npos) { client->header_complete = true; - client->parse_request(_servers); + client->parse_request_headers(_servers); std::cerr << client->get_rq_method_str() << " " << client->get_rq_uri() << " " << client->get_rq_version() << "\n"; // DEBUG if (client->status) return READ_COMPLETE; From 3a58b5d92180d1fb66f09a7377985eed08213da2 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 11 Aug 2022 19:42:09 +0200 Subject: [PATCH 10/14] wip debug comparison fields output script --- srcs/Client.cpp | 35 +++++++++-------- srcs/cgi-bin/php-cgi | 1 + srcs/utils.cpp | 2 +- srcs/utils.hpp | 2 +- srcs/webserv/Webserv.hpp | 6 +-- srcs/webserv/cgi_script.cpp | 76 +++++++++++++++++++++++++++++-------- srcs/webserv/method_get.cpp | 3 -- 7 files changed, 86 insertions(+), 39 deletions(-) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index a4d58b8..94259af 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -79,12 +79,10 @@ void Client::parse_request_headers(std::vector &servers) { clear_request(); // not mandatory _parse_request_line(); -// debug -print_client("first line"); if (status) return; _parse_request_fields(); -// debug +// DEBUG print_client("headers"); if (status) return; @@ -93,8 +91,7 @@ print_client("headers"); _check_request_errors(); if (status) return; - // use getter for headers because it works case insensitive - _parse_port_hostname(this->get_rq_headers("Host")); + _parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive // dont clear raw_request, we need it for future reparsing of body // see call of parse_request() in _read_request() @@ -169,7 +166,7 @@ void Client::print_client(std::string message) std::map::iterator it; std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; - std::cout << "\n" << message << ":----------\n\n" << "raw_request:\n__\n"; + std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n"; ::print_special(raw_request); std::cout << "\n__\n" << "get_cl_fd() : [" << get_cl_fd() << "]\n" @@ -187,8 +184,8 @@ void Client::print_client(std::string message) << "get_rq_script_info() : [" << get_rq_script_info() << "]\n" << "headers :\n"; for (it = _request.headers.begin(); it != _request.headers.end(); it++) - std::cout << " " << it->first << ": " << it->second << "\n"; - std::cout << "\n=== END DEBUG ===\n\n"; + std::cout << " " << it->first << ": [" << it->second << "]\n"; + std::cout << "\n=== END PRINT CLIENT ===\n\n"; } /********************************************* @@ -269,16 +266,22 @@ void Client::_parse_request_fields() int ret; headers = raw_request; - // extract header part - ::extract_line(headers, 0, CRLF); // delete first line + // delete first line + pos = headers.find(CRLF); + if (pos != std::string::npos) + headers.erase(0, pos + std::string(CRLF).size()); + // delete body part pos = headers.find(CRLF CRLF); -// ::extract_line(headers, pos); // delete from empty line to the end - headers.erase(pos); //delete from empty line to the end + if (pos != std::string::npos) + headers.erase(pos); + else { + std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n"; + status = 400; // "bad request" + } // copy result of parser into headers - ret = ::parse_http_headers(raw_request, _request.headers); - if (ret > 0) - { - std::cerr << "err _parse_request_headers(): " << ret << " field are bad formated\n"; + ret = ::parse_http_headers(headers, _request.headers); + if (ret > 0) { + std::cerr << "err _parse_request_fields(): " << ret << " fields are bad formated\n"; status = 400; // "bad request" } } diff --git a/srcs/cgi-bin/php-cgi b/srcs/cgi-bin/php-cgi index b8e85dc..6c90e0c 100755 --- a/srcs/cgi-bin/php-cgi +++ b/srcs/cgi-bin/php-cgi @@ -2,6 +2,7 @@ fields ) + std::map & fields ) { std::vector list; std::vector::iterator it; diff --git a/srcs/utils.hpp b/srcs/utils.hpp index bc47588..2a766d5 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -50,7 +50,7 @@ void replace_all_substr(std::string &str, const std::string &ori_substr, co 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 ); +size_t parse_http_headers (std::string headers, std::map & fields ); void throw_test(); // debug void print_special(std::string str); diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 35c082d..bbaf9f5 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -104,9 +104,9 @@ class Webserv char* _dup_env(std::string var, std::string val); char* _dup_env(std::string var, int i); std::string _exec_script(Client *client, char **env); - 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 _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); // 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 5f92af8..d15b391 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -106,9 +106,9 @@ std::string Webserv::_exec_script(Client *client, char **env) dup2(FD_WR_TO_PRNT, STDOUT_FILENO); // DEBUG std::cerr << "execve:\n"; - //execve(client->get_rq_script_path().c_str(), nll, env); + execve(client->get_rq_script_path().c_str(), nll, env); // for tests execve crash : - execve("wrong", nll, env); + //execve("wrong", nll, env); std::cerr << "execve crashed.\n"; } else @@ -135,19 +135,13 @@ std::string Webserv::_exec_script(Client *client, char **env) return script_output; } -void Webserv::_check_script_output(Client *client, std::string output) +void Webserv::_check_script_output(Client *client, std::string & output) { -// DEBUG -std::cout << "outpu:__________\n" << output << "__________\n" - << "\nstatus:" << client->status << "__________\n"; _check_script_status(client, output); -// DEBUG -std::cout << "outpu:__________\n" << output << "__________\n" - << "\nstatus:" << client->status << "__________\n"; _check_script_fields(client, output); } -void Webserv::_check_script_status(Client *client, std::string output) +void Webserv::_check_script_status(Client *client, std::string & output) { size_t pos; int status_pos; @@ -163,27 +157,79 @@ void Webserv::_check_script_status(Client *client, std::string output) client->status = 200; } -void Webserv::_check_script_fields(Client *client, std::string output) +void Webserv::_check_script_fields(Client *client, std::string & output) { std::map srv_fld; // server_field std::map scr_fld; // script_field std::map::iterator it_srv; std::map::iterator it_scr; + std::string tmp; size_t pos; - ::parse_http_headers(client->response, srv_fld); - ::parse_http_headers(output, scr_fld); - // wip: compare both map to supress duplicates + // put server headers in map + tmp = client->response; + pos = tmp.find(CRLF CRLF); + if (pos != std::string::npos) + tmp.erase(pos); + ::parse_http_headers(tmp, srv_fld); + // put script headers in map + tmp = output; + pos = tmp.find(CRLF CRLF); + if (pos != std::string::npos) + tmp.erase(pos); + ::parse_http_headers(tmp, scr_fld); + // compare both map to supress duplicates + +// debug +std::map::iterator it; +std::cout << "\n\n+++++\ndebug comparison:\nBEFORE\nserver headers:\n"; +for (it = srv_fld.begin(); it != srv_fld.end(); it++) { + std::cout << " " << it->first << ": [" << it->second << "]\n"; } +std::cout << "\nscript headers:\n"; +for (it = scr_fld.begin(); it != scr_fld.end(); it++) { + std::cout << " " << it->first << ": [" << it->second << "]\n"; } +// en debug + for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++) { for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++) { - if (it_srv->first == it_scr->first) + if (str_tolower(it_srv->first) == str_tolower(it_scr->first)) { pos = client->response.find(it_srv->first); ::extract_line(client->response, pos, CRLF); + // debug + std::cout << "helloooooooooooooooooooooooooooooo\n"; + std::cout << pos << "\n"; } } } + +// debug + srv_fld.clear(); + scr_fld.clear(); + // put server headers in map + tmp = client->response; + pos = tmp.find(CRLF CRLF); + if (pos != std::string::npos) + tmp.erase(pos); + ::parse_http_headers(tmp, srv_fld); + // put script headers in map + tmp = output; + pos = tmp.find(CRLF CRLF); + if (pos != std::string::npos) + tmp.erase(pos); + ::parse_http_headers(tmp, scr_fld); + // compare both map to supress duplicates + +std::cout << "\nAFTER\nserver headers:\n"; +for (it = srv_fld.begin(); it != srv_fld.end(); it++) { + std::cout << " " << it->first << ": [" << it->second << "]\n"; } +std::cout << "\nscript headers:\n"; +for (it = scr_fld.begin(); it != scr_fld.end(); it++) { + std::cout << " " << it->first << ": [" << it->second << "]\n"; } +std::cout << "\nend debug comparison\n+++++\n\n"; +// end debug + } diff --git a/srcs/webserv/method_get.cpp b/srcs/webserv/method_get.cpp index 7c97cc5..d6088a7 100644 --- a/srcs/webserv/method_get.cpp +++ b/srcs/webserv/method_get.cpp @@ -61,9 +61,6 @@ Where does cgi fit in in all this ??? if (_is_cgi(client)) { script_output = _exec_cgi(client); - // DEBUG - std::cout << "\n____script_output____\n" << script_output << "\n_______________\n"; - // wip check output of script _check_script_output(client, script_output); client->response += script_output; return; From 400efbe720c89ec8d7aa43a56454051a9e70ac37 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Fri, 12 Aug 2022 05:50:00 +0200 Subject: [PATCH 11/14] Wip chunked decoding + Need to test normal body parsing + path_is_valid() renamed eval_file_type() + replaced atoi with strtol/strtoul --- memo.txt | 8 ++- srcs/Client.cpp | 118 +++++++++++++++++++++++++++++---- srcs/Client.hpp | 3 +- srcs/config/ConfigParser.hpp | 13 +--- srcs/config/ServerConfig.hpp | 2 +- srcs/config/parser.cpp | 6 +- srcs/config/postProcessing.cpp | 2 +- srcs/utils.cpp | 19 +++--- srcs/utils.hpp | 23 +++++-- srcs/webserv/Webserv.hpp | 2 +- srcs/webserv/cgi_script.cpp | 2 +- srcs/webserv/close.cpp | 2 +- srcs/webserv/http_status.hpp | 1 + srcs/webserv/init.cpp | 2 +- srcs/webserv/method_get.cpp | 5 +- srcs/webserv/request.cpp | 41 +++++------- srcs/webserv/run_loop.cpp | 3 +- test_chunk.txt | 15 +++++ 18 files changed, 185 insertions(+), 82 deletions(-) create mode 100644 test_chunk.txt diff --git a/memo.txt b/memo.txt index b6cd444..88c5890 100644 --- a/memo.txt +++ b/memo.txt @@ -1,12 +1,16 @@ IN 42 SUBJECT AND/OR PRIORITY : - CGI - chunked request (response not mandatory it seems) +- Need to test normal body parsing - Ecrire des tests ! - handle redirection (Work, but weird behavior need deeper test) - upload files with config "upload_dir" - _determine_location() review (New version to complete and test) -- replace atoi() with a better function to avoid overflow - like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/ +- replace std::string::npos with macro NPOS ? +----------------------------- +Si ce n'est pas deja fait : +- dans config, check erreur si port > 16bits +(peut-être check si ip > 32bits) ----------------------------- - gerer le champ "Accept" du client - gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root") diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 0b30006..e388257 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -8,6 +8,7 @@ Client::Client() : status(0), header_complete(false), + body_complete(false), request_complete(false), read_body_size(0), assigned_server(NULL), @@ -23,6 +24,7 @@ Client::Client() Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip) : status(0), header_complete(false), + body_complete(false), request_complete(false), read_body_size(0), assigned_server(NULL), @@ -82,8 +84,9 @@ void Client::parse_request(std::vector &servers) std::map headers; std::string body; -// DEBUG -// std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n"; + if (raw_request.find(CRLF CRLF) == NPOS) + return ; + header_complete = true; clear_request(); // not mandatory _parse_request_line(); @@ -97,6 +100,7 @@ void Client::parse_request(std::vector &servers) return; _parse_port_hostname(this->get_rq_headers("Host")); + std::cerr << get_rq_method_str() << " " << get_rq_uri() << " " << get_rq_version() << "\n"; // DEBUG /* dont clear raw_request, we need it for future reparsing of body see call of parse_request() in _read_request() */ // raw_request.clear(); @@ -104,8 +108,89 @@ void Client::parse_request(std::vector &servers) void Client::parse_request_body() { - // TODO: check error and adjust status - _request.body = ::parse_http_body(raw_request); + size_t pos; + pos = raw_request.find(CRLF CRLF); + if (pos == NPOS) + { + std::cerr << "parse_request_body() bad call, header incomplete\n"; + return; + } + pos += CRLF_SIZE*2; + + // Chunked decoding WIP. Dont work. + if (!get_rq_headers("Transfer-Encoding").empty() + && get_rq_headers("Transfer-Encoding") == "chunked") + { + size_t chunk_size = 1; + size_t chunk_field_end = 0; + char *endptr = NULL; + char *endptr_copy = NULL; + /* TODO: verify if last chunk in raw_request (to avoid multiples complete parsing) + but how ? with "raw_request.rfind("0" CRLF CRLF)", there no confirmation + that we have found the last last-chunk OR just some data */ + + _request.body = raw_request.substr(pos); + + std::cerr << "______Chunked\n" << _request.body << "\n______\n"; + pos = 0; + while (chunk_size != 0) + { + if (pos > _request.body.size()) + { + std::cerr << "parse_request_body(), pos > size()\n"; + // status = 400; + return; + } + + if (pos == _request.body.size()) + { + std::cerr << "parse_request_body(), will reread till last chunk\n"; + return; + } + + endptr_copy = endptr; + chunk_size = std::strtoul(&_request.body[pos], &endptr, 16); + if (chunk_size == LONG_MAX && errno == ERANGE) + status = 413; + if (endptr == endptr_copy) + { + std::cerr << "parse_request_body(), no conversion possible\n"; + return; + } + + + chunk_field_end = _request.body.find(CRLF, pos); + if (chunk_field_end == NPOS) + { + std::cerr << "parse_request_body(), chunk_field no CRLF\n"; + // status = 400; + return; + } + + chunk_field_end += CRLF_SIZE; + _request.body.erase(pos, chunk_field_end); + pos += chunk_size + CRLF_SIZE; + } + + _request.headers.erase("Transfer-Encoding"); + body_complete = true; + } + else + { + if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10)) + { + _request.body = raw_request.substr(pos); + body_complete = true; + } + + /* Should be equivalent */ + // _request.body = raw_request.substr(pos); + // if (_request.body.size() >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10)) + // body_complete = true; + } + +/////////////// +// Body checks if (_request.body.size() > assigned_server->client_body_limit) status = 413; } @@ -132,6 +217,7 @@ void Client::clear() { clear_request(); header_complete = false; + body_complete = false; request_complete = false; read_body_size = 0; assigned_server = NULL; @@ -185,6 +271,7 @@ std::string Client::get_rq_port() const { return _request.port; } std::string Client::get_rq_hostname() const { return _request.hostname; } std::string Client::get_rq_script_path()const { return _request.script.path; } std::string Client::get_rq_script_info()const { return _request.script.info; } + std::string Client::get_rq_headers(const std::string & key) const { std::map::const_iterator it; @@ -259,7 +346,7 @@ void Client::_parse_port_hostname(std::string host) void Client::_check_request_errors() { -////////////////////// +/////////////////////// // Request line checks if (_request.method == UNKNOWN) status = 501; @@ -281,15 +368,21 @@ void Client::_check_request_errors() response.append(CRLF CRLF); } - if (status) - return; - -///////////////// +////////////////// // Headers checks - if (!this->get_rq_headers("Content-Length").empty() - && ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit) + else if (!this->get_rq_headers("Content-Length").empty() + && std::strtoul(this->get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit) status = 413; - + else if (!this->get_rq_headers("Transfer-Encoding").empty() + && this->get_rq_headers("Transfer-Encoding") != "chunked" ) + status = 501; + else if (!this->get_rq_headers("Content-Encoding").empty()) + { + status = 415; + response.append("Accept-Encoding:"); // empty, no encoding accepted + response.append(CRLF); + } + return; } @@ -303,4 +396,3 @@ bool operator==(const Client& lhs, int fd) { return lhs.get_cl_fd() == fd; } bool operator==(int fd, const Client& rhs) { return fd == rhs.get_cl_fd(); } - diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 325abc3..b87c84c 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -46,8 +46,9 @@ class Client std::string response; unsigned int status; bool header_complete; + bool body_complete; bool request_complete; - size_t read_body_size; + size_t read_body_size; // unused for now ServerConfig *assigned_server; // cant be const cause of error_pages.operator[] const LocationConfig *assigned_location; diff --git a/srcs/config/ConfigParser.hpp b/srcs/config/ConfigParser.hpp index 32197eb..4f0d406 100644 --- a/srcs/config/ConfigParser.hpp +++ b/srcs/config/ConfigParser.hpp @@ -1,14 +1,3 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* ConfigParser.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: lperrey +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2022/07/11 23:01:41 by me #+# #+# */ -/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */ -/* */ -/* ************************************************************************** */ #ifndef CONFIGPARSER_HPP # define CONFIGPARSER_HPP @@ -22,7 +11,7 @@ # include // exception, what # include // runtime_error, invalid_argument # include // string -# include // atoi (athough it's already cover by ) +# include // strtol, stroul # include // cout, cin # include // ifstream //# include // access() diff --git a/srcs/config/ServerConfig.hpp b/srcs/config/ServerConfig.hpp index c0feecf..c2fbbb0 100644 --- a/srcs/config/ServerConfig.hpp +++ b/srcs/config/ServerConfig.hpp @@ -25,7 +25,7 @@ public: 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 + size_t client_body_limit; // set to default max if none set std::vector index; std::map error_pages; diff --git a/srcs/config/parser.cpp b/srcs/config/parser.cpp index fe47799..6e6a914 100644 --- a/srcs/config/parser.cpp +++ b/srcs/config/parser.cpp @@ -231,7 +231,7 @@ 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 = atoi(tmp_val[0].c_str()); + server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10); } else if (key == "index") { @@ -245,7 +245,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ { if (!(isNumeric_btw(400, 599, tmp_val[i]))) throw std::invalid_argument("invalid error code"); - int status_code = atoi(tmp_val[i].c_str()); + int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10); if (server->error_pages.find(status_code) != server->error_pages.end()) throw std::invalid_argument("redeclaring error page"); server->error_pages[status_code] = path; @@ -316,7 +316,7 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ && tmp_val[1].compare(0, 8, "https://")) throw std::invalid_argument("bad redirect uri"); - location->redirect_status = atoi(tmp_val[0].c_str()); + location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10); location->redirect_uri = tmp_val[1]; } else if (key == "upload_dir" && size == 1 && location->upload_dir == "") diff --git a/srcs/config/postProcessing.cpp b/srcs/config/postProcessing.cpp index bfc3a29..6ccfc2f 100644 --- a/srcs/config/postProcessing.cpp +++ b/srcs/config/postProcessing.cpp @@ -57,7 +57,7 @@ void ConfigParser::_post_processing(std::vector *servers) // nothing to be done for cgi_ext, error_pages, redirect -// if (path_is_valid(it_l->root) == IS_DIR +// if (eval_file_type(it_l->root) == IS_DIR // && it_l->path[it_l->path.size() - 1] != '/') // it_l->path.push_back('/'); if (it_l->path[it_l->path.size() - 1] == '/' diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 1961183..1e1b359 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -65,7 +65,7 @@ bool isNumeric_btw(int low, int high, std::string str) if (std::isdigit(str[i]) == false) return false; } - int n = std::atoi(str.c_str()); + int n = std::strtol(str.c_str(), NULL, 10); if (n < low || n > high) return false; return true; @@ -106,26 +106,23 @@ std::string http_methods_to_str(unsigned int methods) # include -// you could make this &path... -int path_is_valid(std::string path) +file_type eval_file_type(const std::string &path) { const char *tmp_path = path.c_str(); struct stat s; - if (stat(tmp_path, &s) == 0) + if (stat(tmp_path, &s) != -1) { if (S_ISREG(s.st_mode)) - { -// std::cout << "is a file\n"; return (IS_FILE); - } else if (S_ISDIR(s.st_mode)) - { -// std::cout << "is a Dir\n"; return (IS_DIR); - } } -// std::cout << "path is neither dir nor file\n"; + else + { + std::perror("err stat()"); + } + return (IS_OTHER); } diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 8dfd258..7667c5e 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -5,19 +5,32 @@ # include # include # include -# include // atoi +# include // strtol, strtoul +# include // LONG_MAX +# include // errno # include // stat() # include // tolower # include // transform +# include // perror # define CR "\r" # define LF "\n" # define CRLF CR LF +# define CRLF_SIZE 2 +# define NPOS std::string::npos -# define IS_FILE 2 -# define IS_DIR 1 -# define IS_OTHER 0 +/* Equivalent for end of http header size : +** std::string(CRLF CRLF).size(); +** sizeof(CRLF CRLF) - 1; +** CRLF_SIZE*2 +*/ +enum file_type +{ + IS_OTHER, + IS_FILE, + IS_DIR +}; enum http_method { @@ -46,7 +59,7 @@ 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); +file_type eval_file_type(const std::string &path); void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr); std::string str_tolower(std::string str); void del_line_in_str(std::string * str, size_t pos, std::string delim); diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index 71b8e21..b0c5339 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -23,7 +23,7 @@ # include // find # include // string # include // perror, remove -# include // atoi (athough it's already cover by ) +# include // strtol, strtoul # include // opendir() # include "Client.hpp" diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index be03cbe..7e237dd 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -146,7 +146,7 @@ void Webserv::_check_script_status(Client *client, std::string output) if (pos != std::string::npos) { status_pos = pos + std::string("Status:").size(); - client->status = atoi(output.c_str() + status_pos); + client->status = std::strtoul(output.c_str() + status_pos, NULL, 10); ::del_line_in_str(&output, pos, CRLF); } client->status = 200; diff --git a/srcs/webserv/close.cpp b/srcs/webserv/close.cpp index d021245..f71add7 100644 --- a/srcs/webserv/close.cpp +++ b/srcs/webserv/close.cpp @@ -76,7 +76,7 @@ void Webserv::_reopen_lsocket(std::vector::iterator it) // HUGO ADD END try { - _bind(it->fd, std::atoi(it->port.c_str()), it->host); + _bind(it->fd, std::strtoul(it->port.c_str(), NULL, 10), it->host); _listen(it->fd, 42); // 42 arbitrary } catch (const std::exception& e) { diff --git a/srcs/webserv/http_status.hpp b/srcs/webserv/http_status.hpp index b002129..f519ec7 100644 --- a/srcs/webserv/http_status.hpp +++ b/srcs/webserv/http_status.hpp @@ -43,6 +43,7 @@ # define S405 "405 Method Not Allowed" # define S408 "408 Request Timeout" # define S413 "413 Content Too Large" +# define S415 "415 Unsupported Media Type" # define S500 "500 Internal Server Error" # define S501 "501 Not Implemented" diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 4f66cfd..d2ab890 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -48,7 +48,7 @@ void Webserv::init_virtual_servers(std::vector* servers) // // HUGO ADD END - _bind(new_socket.fd, std::atoi(it->port.c_str()), it->host); + _bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host); _listen(new_socket.fd, 42); // 42 arbitrary if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1) diff --git a/srcs/webserv/method_get.cpp b/srcs/webserv/method_get.cpp index 12b5751..b4b9baa 100644 --- a/srcs/webserv/method_get.cpp +++ b/srcs/webserv/method_get.cpp @@ -1,7 +1,6 @@ #include "Webserv.hpp" -// TODO : path_is_valid() Macro for return value void Webserv::_get(Client *client) { std::string path = client->get_rq_abs_path(); @@ -30,14 +29,14 @@ void Webserv::_get(Client *client) // END TMP HUGO // Index/Autoindex block - if (path_is_valid(path) == IS_DIR) + if (eval_file_type(path) == IS_DIR) { std::cout << "made it to Index/Autoindex\n"; if (path[path.size() - 1] != '/') path.push_back('/'); for (size_t i = 0; i < client->assigned_location->index.size(); i++) { - if (path_is_valid(path + client->assigned_location->index[i]) == 2) + if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE) { path.append(client->assigned_location->index[i]); _get_file(client, path); diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index b70056c..24f2a80 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -25,12 +25,14 @@ void Webserv::_request(Client *client) } else if (ret == READ_COMPLETE) { + if (client->body_complete) + std::cerr << "______BODY\n" << client->get_rq_body() << "\n______\n"; // DEBUG _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD); client->request_complete = true; } } -int Webserv::_read_request(Client *client) // Messy, Need refactoring +int Webserv::_read_request(Client *client) { char buf[BUFSIZE]; ssize_t ret; @@ -41,8 +43,6 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring if (ret == -1) { std::perror("err recv()"); - std::cerr << "client ptr =" << client << "\n"; // DEBUG - std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG return READ_CLOSE; } if (ret == 0) @@ -50,41 +50,32 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG return READ_CLOSE; } - client->raw_request.append(buf, ret); + if (!client->header_complete) { - if (client->raw_request.find(CRLF CRLF) != std::string::npos) + client->parse_request(_servers); + if (client->status) + return READ_COMPLETE; + if (client->header_complete) { - client->header_complete = true; - client->parse_request(_servers); - std::cerr << client->get_rq_method_str() << " " << client->get_rq_uri() << " " << client->get_rq_version() << "\n"; // DEBUG - if (client->status) - return READ_COMPLETE; - - if (client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty()) // No body case - return READ_COMPLETE; + if (client->get_rq_headers("Content-Type").empty() + && client->get_rq_headers("Content-Length").empty() + && client->get_rq_headers("Transfer-Encoding").empty()) + return READ_COMPLETE; // No body case } else if (client->raw_request.size() > MAX_HEADER_SIZE) - { - // 413 or 400 ? 413 seems common among http server, but don't fit perfectly. + { // 413 or 400 ? 413 seems common among http server, but don't fit perfectly. client->status = 413; 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; + // client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now + client->parse_request_body(); + if (client->status || client->body_complete) return READ_COMPLETE; - } - if ((int)client->read_body_size >= ::atoi(client->get_rq_headers("Content-Length").c_str())) - { - client->parse_request_body(); - return READ_COMPLETE; - } } return READ_IN_PROGRESS; diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index 108a226..cf54e81 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -20,8 +20,9 @@ void Webserv::run() nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT); if (nfds == -1) { + int errno_copy = errno; std::perror("err epoll_wait()"); - if (errno == EINTR) + if (errno_copy == EINTR) g_run = false; else throw std::runtime_error("Epoll wait"); diff --git a/test_chunk.txt b/test_chunk.txt new file mode 100644 index 0000000..8c55d27 --- /dev/null +++ b/test_chunk.txt @@ -0,0 +1,15 @@ +https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding + +GET / HTTP/1.1 +Host: localhost:4040 +Accept: */* +Transfer-Encoding: chunked + +7 +Mozilla +9 +Developer +7 +Network +0 From c2250633618543648d385cff8fb446dae18b715b Mon Sep 17 00:00:00 2001 From: hugogogo Date: Fri, 12 Aug 2022 11:45:06 +0200 Subject: [PATCH 12/14] script output fields is checked for duplicate --- Makefile | 2 +- srcs/Client.cpp | 1 + srcs/utils.cpp | 20 +++++++++++++++++++- srcs/utils.hpp | 1 + srcs/webserv/cgi_script.cpp | 3 --- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index b8b6734..dd6deaf 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NAME = webserv -CXX = c++ +CXX = clang++ CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS += $(HEADERS_D:%=-I%) diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 94259af..0dd22b3 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -284,6 +284,7 @@ void Client::_parse_request_fields() std::cerr << "err _parse_request_fields(): " << ret << " fields are bad formated\n"; status = 400; // "bad request" } + ::str_map_key_tolower(_request.headers); } void Client::_parse_port_hostname(std::string host) diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 832187c..a153684 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -267,7 +267,8 @@ size_t err++; continue; } - key = ::str_tolower(key); // to make "key" case_insensitive +// bad idea, in cgi we need to have the original value +// key = ::str_tolower(key); // to make "key" case_insensitive val = (*it).substr(pos + 1); val = ::trim(val, ' '); fields.insert( std::pair(key, val) ); @@ -275,6 +276,23 @@ size_t return err; } +void str_map_key_tolower(std::map & mp) +{ + std::map new_mp; + std::map::iterator it; + std::string key; + std::string value; + + for (it = mp.begin(); it != mp.end(); it++) + { + key = it->first; + value = it->second; + key = ::str_tolower(key); + new_mp.insert( std::pair(key, value) ); + } + mp.swap(new_mp); +} + // DEBUG void print_special(std::string str) { diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 2a766d5..bb9fae4 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -51,6 +51,7 @@ 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 print_special(std::string str); diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index d15b391..45b333b 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -198,9 +198,6 @@ for (it = scr_fld.begin(); it != scr_fld.end(); it++) { { pos = client->response.find(it_srv->first); ::extract_line(client->response, pos, CRLF); - // debug - std::cout << "helloooooooooooooooooooooooooooooo\n"; - std::cout << pos << "\n"; } } } From de09c2357c28a5b14e689625fe6e37c18fb7e7a5 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Fri, 12 Aug 2022 12:00:20 +0200 Subject: [PATCH 13/14] php newline pbm resolved --- srcs/cgi-bin/php-cgi | 2 -- srcs/webserv/cgi_script.cpp | 41 +++---------------------------------- 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/srcs/cgi-bin/php-cgi b/srcs/cgi-bin/php-cgi index 6c90e0c..fa20b65 100755 --- a/srcs/cgi-bin/php-cgi +++ b/srcs/cgi-bin/php-cgi @@ -1,5 +1,4 @@ #! /usr/bin/php - - diff --git a/srcs/webserv/cgi_script.cpp b/srcs/webserv/cgi_script.cpp index 45b333b..1b9fee4 100644 --- a/srcs/webserv/cgi_script.cpp +++ b/srcs/webserv/cgi_script.cpp @@ -139,6 +139,9 @@ void Webserv::_check_script_output(Client *client, std::string & output) { _check_script_status(client, output); _check_script_fields(client, output); + // _check_script_empty_lines(client, output); + // _check_script_space_colons(client, output); + // _check_script_new_lines(client, output); } void Webserv::_check_script_status(Client *client, std::string & output) @@ -179,17 +182,6 @@ void Webserv::_check_script_fields(Client *client, std::string & output) tmp.erase(pos); ::parse_http_headers(tmp, scr_fld); // compare both map to supress duplicates - -// debug -std::map::iterator it; -std::cout << "\n\n+++++\ndebug comparison:\nBEFORE\nserver headers:\n"; -for (it = srv_fld.begin(); it != srv_fld.end(); it++) { - std::cout << " " << it->first << ": [" << it->second << "]\n"; } -std::cout << "\nscript headers:\n"; -for (it = scr_fld.begin(); it != scr_fld.end(); it++) { - std::cout << " " << it->first << ": [" << it->second << "]\n"; } -// en debug - for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++) { for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++) @@ -201,32 +193,5 @@ for (it = scr_fld.begin(); it != scr_fld.end(); it++) { } } } - -// debug - srv_fld.clear(); - scr_fld.clear(); - // put server headers in map - tmp = client->response; - pos = tmp.find(CRLF CRLF); - if (pos != std::string::npos) - tmp.erase(pos); - ::parse_http_headers(tmp, srv_fld); - // put script headers in map - tmp = output; - pos = tmp.find(CRLF CRLF); - if (pos != std::string::npos) - tmp.erase(pos); - ::parse_http_headers(tmp, scr_fld); - // compare both map to supress duplicates - -std::cout << "\nAFTER\nserver headers:\n"; -for (it = srv_fld.begin(); it != srv_fld.end(); it++) { - std::cout << " " << it->first << ": [" << it->second << "]\n"; } -std::cout << "\nscript headers:\n"; -for (it = scr_fld.begin(); it != scr_fld.end(); it++) { - std::cout << " " << it->first << ": [" << it->second << "]\n"; } -std::cout << "\nend debug comparison\n+++++\n\n"; -// end debug - } From 71c07140e22c81c89d4dafec573bc27799366ae7 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Fri, 12 Aug 2022 12:08:56 +0200 Subject: [PATCH 14/14] added text inside youpibanane files --- YoupiBanane/Yeah/not_happy.bad_extension | 1 + YoupiBanane/nop/other.pouic | 1 + YoupiBanane/nop/youpi.bad_extension | 1 + YoupiBanane/youpi.bad_extension | 1 + YoupiBanane/youpi.bla | 1 + 5 files changed, 5 insertions(+) diff --git a/YoupiBanane/Yeah/not_happy.bad_extension b/YoupiBanane/Yeah/not_happy.bad_extension index e69de29..70cf8a7 100644 --- a/YoupiBanane/Yeah/not_happy.bad_extension +++ b/YoupiBanane/Yeah/not_happy.bad_extension @@ -0,0 +1 @@ +inside YoupiBanane/Yeah/not_happy.bad_extension diff --git a/YoupiBanane/nop/other.pouic b/YoupiBanane/nop/other.pouic index e69de29..e8d843d 100644 --- a/YoupiBanane/nop/other.pouic +++ b/YoupiBanane/nop/other.pouic @@ -0,0 +1 @@ +inside YoupiBanane/nop/other.pouic diff --git a/YoupiBanane/nop/youpi.bad_extension b/YoupiBanane/nop/youpi.bad_extension index e69de29..a2f4e0a 100644 --- a/YoupiBanane/nop/youpi.bad_extension +++ b/YoupiBanane/nop/youpi.bad_extension @@ -0,0 +1 @@ +inside YoupiBanane/nop/youpi.bad_extension diff --git a/YoupiBanane/youpi.bad_extension b/YoupiBanane/youpi.bad_extension index e69de29..b3b2172 100644 --- a/YoupiBanane/youpi.bad_extension +++ b/YoupiBanane/youpi.bad_extension @@ -0,0 +1 @@ +inside YoupiBanane/youpi.bad_extension diff --git a/YoupiBanane/youpi.bla b/YoupiBanane/youpi.bla index e69de29..d5bdbde 100644 --- a/YoupiBanane/youpi.bla +++ b/YoupiBanane/youpi.bla @@ -0,0 +1 @@ +inside YoupiBanane/youpi.bla