From 0cb49ffa5ee1f70c32046dbb99336034c367bf19 Mon Sep 17 00:00:00 2001 From: Me Date: Sun, 31 Jul 2022 04:20:04 +0200 Subject: [PATCH 1/7] started adding more checks to the parser, some things settled, but mostly i have a lot more questions... --- Makefile | 1 + default.config | 15 +-- srcs/ConfigParser.cpp | 196 ++++++++++--------------------------- srcs/ConfigParser.hpp | 11 ++- srcs/ConfigParserUtils.cpp | 109 +++++++++++++++++++++ srcs/ServerConfig.hpp | 6 +- srcs/utils.cpp | 24 +++++ srcs/utils.hpp | 2 + 8 files changed, 205 insertions(+), 159 deletions(-) create mode 100644 srcs/ConfigParserUtils.cpp diff --git a/Makefile b/Makefile index 912ee6b..066f47c 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ SRCS = main.cpp \ accept.cpp request.cpp response.cpp \ run_loop.cpp \ ConfigParser.cpp \ + ConfigParserUtils.cpp \ utils.cpp \ OBJS_D = builds diff --git a/default.config b/default.config index 395acfc..136510b 100644 --- a/default.config +++ b/default.config @@ -6,6 +6,7 @@ server { listen 0.0.0.0:4040; + client_body_limit asdfa; index index.html; # this is another comment root ./www/; @@ -13,17 +14,3 @@ server { allow_methods GET; } -server { - -# this is a comment - - server_name our_server; - - listen 0.0.0.0:4047; - - - index index.html; # this is another comment - root ./www/; - - allow_methods GET; -} diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp index d7a24a3..46df807 100644 --- a/srcs/ConfigParser.cpp +++ b/srcs/ConfigParser.cpp @@ -13,16 +13,6 @@ #include "ConfigParser.hpp" -/***** Stuf to rework - - -need to figure out why return std::vector * rather than just simple - not a pointer... - is there a good reason? - - -*/ - @@ -33,8 +23,6 @@ ConfigParser::ConfigParser() // don't use yet, you have no idea what the defaults are } -//ConfigParser::ConfigParser(std::string &path) -//ConfigParser::ConfigParser(char & path) ConfigParser::ConfigParser(const char* path) { std::cout << "Param Constructor\n"; @@ -61,7 +49,6 @@ ConfigParser::ConfigParser(const char* path) } else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment) { -// else if (comment > 0 && (buf.find_first_not_of(" \t")) != std::string::npos) // is there a comment at the end of the line std::string tmp = buf.substr(0, comment - 1); _content.append(tmp + '\n'); @@ -109,15 +96,11 @@ std::vector * ConfigParser::parse() std::string key = _content.substr(start, curr - start); if (key != "server") throw std::invalid_argument("bad config file arguments 1"); -// Server server = parse_server(&curr); -// ret->push_back(server); - // why not this? ret->push_back(_parse_server(&curr)); } return (ret); } -// might need new names for Prev and Curr, not super descriptive... ServerConfig ConfigParser::_parse_server(size_t *start) { ServerConfig ret; @@ -141,17 +124,12 @@ ServerConfig ConfigParser::_parse_server(size_t *start) break ; } else if (key == "location") - { - // this does assume we have locations in Server... - // could change the name but it's so clear... ret.locations.push_back(_parse_location(&curr)); - } else { std::string values = _get_rest_of_line(&curr); // curr now should be \n - // checking for ; in _set_value, check key and value - _set_server_values(&ret, key, values); // handles the throws + _set_server_values(&ret, key, values); } } return (ret); @@ -170,7 +148,6 @@ LocationConfig ConfigParser::_parse_location(size_t *start) curr = _content.find_first_not_of(" \t\n", curr); - if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 2"); @@ -191,9 +168,7 @@ LocationConfig ConfigParser::_parse_location(size_t *start) { std::string values = _get_rest_of_line(&curr); // curr now should be \n - // checking for ; in _set_value, check key and value - - _set_location_values(&ret, key, values); //handles the throws + _set_location_values(&ret, key, values); } } return (ret); @@ -201,14 +176,13 @@ LocationConfig ConfigParser::_parse_location(size_t *start) -// ok you need to think through these throws, when will each occur? - void ConfigParser::_set_server_values(ServerConfig *server, \ const std::string key, std::string value) { +/* // check key for ; // check values for ; at end and right number of words depending on key @@ -238,30 +212,39 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ // like call substr in split? //value = value.substr(0, i - 1); value = value.substr(0, i); +*/ + + value = _pre_set_val_check(key, value); std::vector tmp_val = split(value, ' '); size_t size = tmp_val.size(); - // would if if be more optimized? if (size < 1) throw std::invalid_argument("missing value"); else if (key == "server_name" && size == 1) { + // should i be checking if the field is already filled server->server_name = tmp_val[0]; } else if (key == "listen" && size == 1) { + // should i be checking if field already filled? + // are we saying only 1 possible? if (tmp_val[0].find_first_of(":") == std::string::npos) { - // why not store as vector [4] ? + if (!(isNumeric(tmp_val[0]))) + throw std::invalid_argument("value not a number"); server->host = "0.0.0.0"; server->port = tmp_val[0]; } else { - // maybe do this differently? std::vector tmp2 = split(tmp_val[0], ':'); - // i might take issue with this, will see + if (!(isNumeric(tmp2[1]))) + throw std::invalid_argument("value not a number"); + + // not sure if this is what we want, means there's only 1 host per + // server... if (server->host != "" && server->host != tmp2[0]) throw std::invalid_argument("bad listen"); server->host = tmp2[0]; @@ -278,6 +261,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ } else if (key == "client_body_limit" && size == 1) { + //std::cout << "made it\n"; + if (!(isNumeric(tmp_val[0]))) + throw std::invalid_argument("value not a number"); server->client_body_limit = atoi(tmp_val[0].c_str()); } else if (key == "recv_timeout" && size == 1) @@ -290,32 +276,33 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ { server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); } -/* else - { - throw std::invalid_argument("should only have 1 value"); - // yea ok but it could also be something else like too many - // args - - } -*/ else if (key == "index") { - // could run more tests on value content but meh... + // should i be doing an access? for (unsigned long i = 0; i != tmp_val.size(); i++) server->index.push_back(tmp_val[i]); } else if (key == "allow_methods") { - // might do something different here - // like change how methods are stored? + // you need to throw if it's a bad method type + // or should we skip? and see if any others are good? for (unsigned long i = 0; i != tmp_val.size(); i++) - server->allow_methods.push_back(_str_to_method_type(tmp_val[i])); + { + MethodType m = _str_to_method_type(tmp_val[i]); + if (m == 3) + throw std::invalid_argument("not a valid method"); + server->allow_methods.push_back(m); + } } else if (key == "return") { - // could run more checks here too - // like tmp_val.size() must be 2 + if (tmp_val.size() != 2) + throw std::invalid_argument("wrong number of values"); // and tmp_val[0] should be a number and tmp_val[1] a string? + if (!(isNumeric(tmp_val[0]))) + throw std::invalid_argument("value not a number"); + + // something about using access() to see if server->redirect_status = atoi(tmp_val[0].c_str()); server->redirect_uri = tmp_val[1]; } @@ -326,8 +313,12 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ std::string path = tmp_val[tmp_val.size() - 1]; for (unsigned long i = 0; i != tmp_val.size() - 1; i++) { + // what are the bounds for Error codes? + if (!(isNumeric_btw(0, 600, tmp_val[i]))) + throw std::invalid_argument("value not a valid number"); int status_code = atoi(tmp_val[i].c_str()); - // yea IDK i might not want to store this like that... + + // yea cuz here we continue.. why suddenly flexible not throw ? if (server->error_pages.find(status_code) != server->error_pages.end()) continue ; server->error_pages[status_code] = path; @@ -337,36 +328,24 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ { throw std::invalid_argument("wrong number of values"); } + + + +// std::cout << "End set\n"; + + } -// again not sure i want an int ret + + + + + + void ConfigParser::_set_location_values(LocationConfig *location, \ const std::string key, std::string value) { - // check key for ; - // check values for ; at end and right number of words depending on key - - if (key.find_first_of(";") != std::string::npos) - throw std::invalid_argument("bad config file arguments 5"); - - // there shouldn't be any tabs, right? not between values... - if (value.find_first_of("\t") != std::string::npos) - throw std::invalid_argument("bad config file arguments 6"); - - size_t i = value.find_first_of(";"); - // so you can't have no ; - // you can't have just ; - // and you can't have a ; not at the end or several ; - // in theory value_find_last_of should find the only ; - if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ - || value.compare(";") == 0) - throw std::invalid_argument("bad config file arguments 7"); - - - // we Trim value. - // is this valid? - // could do like above? - value = value.substr(0, i); + value = _pre_set_val_check(key, value); std::vector tmp_val = ::split(value, ' '); size_t size = tmp_val.size(); @@ -381,11 +360,6 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ { location->client_body_limit = atoi(tmp_val[0].c_str()); } -/* else - { - throw std::invalid_argument("should only have 1 argument"); - } -*/ else if (key == "index") { for (unsigned long i = 0; i != tmp_val.size(); i++) @@ -412,70 +386,8 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ } } -// assumes curr is on a space or \t or \n -// get first word? next word? word? -std::string ConfigParser::_get_first_word(size_t *curr) -{ - size_t start; - -// are these checks excessive? - if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) - throw std::invalid_argument("bad config file arguments"); - if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos) - throw std::invalid_argument("bad config file arguments"); - - std::string key = _content.substr(start, *curr - start); - - return (key); -} - -// also assumes curr is on a space \t or \n -std::string ConfigParser::_get_rest_of_line(size_t *curr) -{ - size_t start; - - if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) - throw std::invalid_argument("bad config file arguments"); - -// std::cout << "start + 4 = " << _content.substr(start, 4) << "\n"; -// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n"; - - - if ((*curr = _content.find_first_of("\n", start)) == std::string::npos) - throw std::invalid_argument("bad config file arguments"); - - std::string values = _content.substr(start, *curr - start); - -// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n"; -// std::cout << "rest of Line values: " << values << "\n"; - - return (values); -} - - -MethodType ConfigParser::_str_to_method_type(std::string str) -{ - if (str == "GET") - return GET; - else if (str == "POST") - return POST; - else if (str == "DELETE") - return DELETE; - return INVALID; -} - - - - -void ConfigParser::_print_content() const -{ - std::cout << _content; -} - - - - -// I might need to make my own Exceptions to throw... + + diff --git a/srcs/ConfigParser.hpp b/srcs/ConfigParser.hpp index 81b1cdf..eeaa93d 100644 --- a/srcs/ConfigParser.hpp +++ b/srcs/ConfigParser.hpp @@ -74,6 +74,9 @@ private: void _set_location_values(LocationConfig *location, const std::string key, std::string value); + 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? @@ -87,7 +90,13 @@ private: }; - +// def needs work line a better name an do i even need this? +// should it be in Utils instead? +class MyException : public std::invalid_argument +{ + MyException(const std::string str) + : std::invalid_argument(str) {} +}; #endif diff --git a/srcs/ConfigParserUtils.cpp b/srcs/ConfigParserUtils.cpp new file mode 100644 index 0000000..6947a4c --- /dev/null +++ b/srcs/ConfigParserUtils.cpp @@ -0,0 +1,109 @@ + + + +#include "Webserv.hpp" + + + +std::string ConfigParser::_pre_set_val_check(const std::string key, \ + const std::string value) +{ + + // check key for ; + // check values for ; at end and right number of words depending on key + +// std::cout << "pre check\n"; + if (key.find_first_of(";") != std::string::npos) + throw std::invalid_argument("bad config file arguments 2"); + + // there shouldn't be any tabs, right? not between values... + if (value.find_first_of("\t") != std::string::npos) + { + std::cout << value << "\n"; + throw std::invalid_argument("bad config file arguments 3"); + } + + size_t i = value.find_first_of(";"); + // so you can't have no ; + // you can't have just ; + // and you can't have a ; not at the end or several ; + // in theory value_find_last_of should find the only ; + if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ + || value.compare(";") == 0) + throw std::invalid_argument("bad config file arguments 4"); + + + // we Trim value. + // is this valid? + // would it be better to shove the result directly in tmp_val? + // like call substr in split? + //value = value.substr(0, i - 1); + return (value.substr(0, i)); +} + + + +// assumes curr is on a space or \t or \n +// get first word? next word? word? +std::string ConfigParser::_get_first_word(size_t *curr) +{ + size_t start; + +// are these checks excessive? + if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + + std::string key = _content.substr(start, *curr - start); + + return (key); +} + +// also assumes curr is on a space \t or \n +std::string ConfigParser::_get_rest_of_line(size_t *curr) +{ + size_t start; + + if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + +// std::cout << "start + 4 = " << _content.substr(start, 4) << "\n"; +// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n"; + + + if ((*curr = _content.find_first_of("\n", start)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + + std::string values = _content.substr(start, *curr - start); + +// std::cout << "rest of Line values: " << values << "\n"; + + return (values); +} + + +MethodType ConfigParser::_str_to_method_type(std::string str) +{ + if (str == "GET") + return GET; + else if (str == "POST") + return POST; + else if (str == "DELETE") + return DELETE; + return INVALID; +} + + + + + + + + +void ConfigParser::_print_content() const +{ + std::cout << _content; +} + +// I might need to make my own Exceptions to throw... diff --git a/srcs/ServerConfig.hpp b/srcs/ServerConfig.hpp index a08d8a1..a020d9d 100644 --- a/srcs/ServerConfig.hpp +++ b/srcs/ServerConfig.hpp @@ -43,7 +43,7 @@ public: struct timeval send_timeout; struct timeval recv_timeout; - int client_body_limit; + int client_body_limit; // set to default max if none set bool autoindex; // not sure what these look like in config file @@ -52,7 +52,9 @@ public: // is this the best way? std::string host; - std::string port; + std::string port; // port needs to be something else... not quite an int + // should a Server be able to handle several? + // do i need a print all for testing? diff --git a/srcs/utils.cpp b/srcs/utils.cpp index b66cd62..6cf4df0 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -16,4 +16,28 @@ std::vector split(std::string input, char delimiter) return answer; } +bool isNumeric(std::string str) +{ + for (size_t i = 0; i < str.length(); i++) + { + if (std::isdigit(str[i]) == false) + return false; + } + return true; +} + + +bool isNumeric_btw(int low, int high, std::string str) +{ + for (size_t i = 0; i < str.length(); i++) + { + if (std::isdigit(str[i]) == false) + return false; + } + int n = atoi(str.c_str()); + if (n < low || n > high) + return false; + return true; +} + diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 3f3b48c..83ebee8 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -6,5 +6,7 @@ std::vector split(std::string input, char delimiter); +bool isNumeric(std::string str); +bool isNumeric_btw(int low, int high, std::string str); #endif From 68af40a79f77d3b856de01fdf1e48567eeffb36c Mon Sep 17 00:00:00 2001 From: hugogogo Date: Sun, 31 Jul 2022 12:05:28 +0200 Subject: [PATCH 2/7] clean makefile includes --- Makefile | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 912ee6b..6ea9ce1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ NAME = webserv CXX = c++ CXXFLAGS = -Wall -Wextra #-Werror -CXXFLAGS += -I$(HEADERS_D) +CXXFLAGS += $(HEADERS_I) CXXFLAGS += -std=c++98 CXXFLAGS += -g CXXFLAGS += -MMD -MP #header dependencie @@ -12,17 +12,19 @@ CXXFLAGS += -MMD -MP #header dependencie #SHELL = /bin/zsh VPATH = $(SRCS_D) -# HEADERS = $(HEADERS_D:%=-I%) -HEADERS_D = ./srcs -HEADERS = Webserv.hpp \ - ConfigParser.hpp \ - ServerConfig.hpp \ - LocationConfig.hpp \ - Client.hpp \ - MethodType.hpp \ - utils.hpp \ +HEADERS_I = $(HEADERS_D:%=-I%) +HEADERS_D = srcs \ + headers +HEADERS = Webserv.hpp \ + ConfigParser.hpp \ + ServerConfig.hpp \ + LocationConfig.hpp \ + Client.hpp \ + MethodType.hpp \ + utils.hpp \ -SRCS_D = srcs srcs/webserv +SRCS_D = srcs \ + srcs/webserv SRCS = main.cpp \ ft_itoa.cpp \ base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \ From af8dc072c1e0ac96483b65a69007b94cb4db2abb Mon Sep 17 00:00:00 2001 From: hugogogo Date: Sun, 31 Jul 2022 12:31:55 +0200 Subject: [PATCH 3/7] added setsockopt to avoid reusability issue on socket fd --- srcs/webserv/init.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index b773964..4c5956e 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -24,6 +24,18 @@ void Webserv::init_virtual_servers(std::vector* servers) std::perror("err socket()"); throw std::runtime_error("Socket init"); } + // HUGO ADD + // + // allow socket descriptor to be reuseable + // I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select + int on = 1; + if (setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + { + ::perror("err setsockopt()"); + throw std::runtime_error("Socket init"); + } + // + // HUGO ADD END _listen_sockets.push_back(ret); _bind(_listen_sockets.back(), std::atoi(it->port.data()), it->host); From ea3f3a390a88e2d122fa5b87039d9860b18f2e02 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Sun, 31 Jul 2022 13:19:11 +0200 Subject: [PATCH 4/7] bugfix invalid ptr in "ev.data.ptr" after vector resize/erase + persistent connections reintroduced + bugfix crash if invalid path in request --- srcs/Client.hpp | 12 ++++++++---- srcs/webserv/accept.cpp | 2 +- srcs/webserv/request.cpp | 9 +++++++-- srcs/webserv/response.cpp | 38 +++++++++++++++++++++++++++----------- srcs/webserv/run_loop.cpp | 15 +++++++++++---- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/srcs/Client.hpp b/srcs/Client.hpp index 2536012..ede81f3 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -6,19 +6,19 @@ # include # include -struct Client +class Client { - // public: + public: // Client(Placeholder); // Client(); // Client(Client const &src); // ~Client(); // Client &operator=(Client const &rhs); + // Client &operator=(int); int fd; std::string raw_request; std::map request; - // std::map response; std::string response; unsigned int status; @@ -26,4 +26,8 @@ struct Client }; -#endif \ No newline at end of file +bool operator==(const Client& lhs, const Client& rhs); +bool operator==(const Client& lhs, int fd); +bool operator==(int fd, const Client& rhs); + +#endif diff --git a/srcs/webserv/accept.cpp b/srcs/webserv/accept.cpp index cd67904..f6db17b 100644 --- a/srcs/webserv/accept.cpp +++ b/srcs/webserv/accept.cpp @@ -22,5 +22,5 @@ void Webserv::_accept_connection(int fd) _clients.push_back(Client()); _clients.back().fd = accepted_fd; - _epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD, &_clients.back()); + _epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD); } diff --git a/srcs/webserv/request.cpp b/srcs/webserv/request.cpp index 17c372b..de761f5 100644 --- a/srcs/webserv/request.cpp +++ b/srcs/webserv/request.cpp @@ -15,8 +15,8 @@ void Webserv::_read_request(Client *client) char buf[BUFSIZE+1]; ssize_t ret; - std::cerr << "recv()\n"; ret = ::recv(client->fd, buf, BUFSIZE, 0); + std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ; if (ret == -1) { std::perror("err recv()"); @@ -25,6 +25,11 @@ void Webserv::_read_request(Client *client) _close_client(client->fd); return ; } + if (ret == 0) // Not sure what to do in case of 0. Just close ? + { + _close_client(client->fd); + return ; + } /* if (ret == BUFSIZE) // send error like "request too long" to client @@ -33,5 +38,5 @@ void Webserv::_read_request(Client *client) buf[ret] = '\0'; client->raw_request.append(buf); - _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD, client); + _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD); } diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index ce785ed..0271a80 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -27,15 +27,14 @@ void Webserv::_send_response(Client *client) return ; } - _close_client(client->fd); - /* if (client->raw_request.find("Connection: keep-alive") == std::string::npos) + if (client->raw_request.find("Connection: close") != std::string::npos) _close_client(client->fd); else { - _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD, client); + _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD); client->raw_request.clear(); client->response.clear(); - } */ + } } void Webserv::_construct_response(Client *client) @@ -43,13 +42,17 @@ void Webserv::_construct_response(Client *client) client->status = 200; client->response.append("Server: Webserv/0.1\r\n"); - client->response.append("Connection: close\r\n"); + if (client->raw_request.find("Connection: close") != std::string::npos) + client->response.append("Connection: close\r\n"); + else + client->response.append("Connection: keep-alive\r\n"); _get_ressource(client); _insert_status_line(client); } +#define E400 "\r\n400 Bad Request

400 Bad Request


Le Webserv/0.1

" #define E404 "\r\n404 Not Found

404 Not Found


Le Webserv/0.1

" #define E500 "\r\n500 Internal Server Error

500 Internal Server Error


Le Webserv/0.1

" void Webserv::_insert_status_line(Client *client) @@ -63,6 +66,10 @@ void Webserv::_insert_status_line(Client *client) case (200): status_line.append("200 OK"); break; + case (400): + status_line.append("400 Not Found"); + client->response.append(E400); + break; case (404): status_line.append("404 Not Found"); client->response.append(E404); @@ -88,12 +95,21 @@ void Webserv::_get_ressource(Client *client) // Mini parsing à l'arrache du PATH std::string path; - path = client->raw_request.substr(0, client->raw_request.find("\r\n")); - path = path.substr(0, path.rfind(" ")); - path = path.substr(path.find("/")); - if (path == "/") - path.append(INDEX); - path.insert(0, ROOT); + try + { + path = client->raw_request.substr(0, client->raw_request.find("\r\n")); + path = path.substr(0, path.rfind(" ")); + path = path.substr(path.find("/")); + if (path == "/") + path.append(INDEX); + path.insert(0, ROOT); + } + catch (std::out_of_range& e) + { + std::cout << e.what() << '\n'; + client->status = 400; + return ; + } if (access(path.data(), R_OK) == -1) { diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index f8ce7cb..215e4f9 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -4,6 +4,14 @@ #define MAX_EVENTS 42 // arbitrary #define TIMEOUT 3000 +// Temp. To move in other file +bool operator==(const Client& lhs, const Client& rhs) + { return lhs.fd == rhs.fd; } +bool operator==(const Client& lhs, int fd) + { return lhs.fd == fd; } +bool operator==(int fd, const Client& rhs) + { return fd == rhs.fd; } + void Webserv::run() { std::cerr << "Server started\n"; @@ -33,15 +41,14 @@ void Webserv::run() i = 0; while (i < nfds) { - // if ((events[i].data.u32 == SERVER_FD) && (events[i].events & EPOLLIN)) // Dont work, see "SERVER_FD" define - // if ((events[i].data.fd == _socket_fd) && (events[i].events & EPOLLIN)) + // TODO : handle EPOLLERR and EPOLLHUP if ((std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd) != _listen_sockets.end()) && (events[i].events & EPOLLIN)) _accept_connection(events[i].data.fd); else if (events[i].events & EPOLLIN) - _request(static_cast(events[i].data.ptr)); + _request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); else if (events[i].events & EPOLLOUT) - _response(static_cast(events[i].data.ptr)); + _response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); ++i; if (!g_run) break; From 537572e2d0226170b70e5dffdae205891d479aac Mon Sep 17 00:00:00 2001 From: hugogogo Date: Sun, 31 Jul 2022 13:19:22 +0200 Subject: [PATCH 5/7] changed itoa to c++ style and put it in utils, and change utils dependencies to not depend on webserv --- Makefile | 1 - srcs/ConfigParser.cpp | 6 ++--- srcs/Webserv.hpp | 1 - srcs/ft_itoa.cpp | 50 --------------------------------------- srcs/utils.cpp | 12 ++++++---- srcs/utils.hpp | 8 ++++--- srcs/webserv/response.cpp | 3 +-- 7 files changed, 17 insertions(+), 64 deletions(-) delete mode 100644 srcs/ft_itoa.cpp diff --git a/Makefile b/Makefile index 6ea9ce1..ef28cb5 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,6 @@ HEADERS = Webserv.hpp \ SRCS_D = srcs \ srcs/webserv SRCS = main.cpp \ - ft_itoa.cpp \ base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \ accept.cpp request.cpp response.cpp \ run_loop.cpp \ diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp index d7a24a3..d5c2326 100644 --- a/srcs/ConfigParser.cpp +++ b/srcs/ConfigParser.cpp @@ -6,7 +6,7 @@ /* By: lperrey +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2022/07/13 22:11:17 by me #+# #+# */ -/* Updated: 2022/07/30 23:07:42 by lperrey ### ########.fr */ +/* Updated: 2022/07/31 13:18:14 by simplonco ### ########.fr */ /* */ /* ************************************************************************** */ @@ -239,7 +239,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ //value = value.substr(0, i - 1); value = value.substr(0, i); - std::vector tmp_val = split(value, ' '); + std::vector tmp_val = ::split(value, ' '); size_t size = tmp_val.size(); // would if if be more optimized? @@ -260,7 +260,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ else { // maybe do this differently? - std::vector tmp2 = split(tmp_val[0], ':'); + std::vector tmp2 = ::split(tmp_val[0], ':'); // i might take issue with this, will see if (server->host != "" && server->host != tmp2[0]) throw std::invalid_argument("bad listen"); diff --git a/srcs/Webserv.hpp b/srcs/Webserv.hpp index 0b88cbc..b5a7b8b 100644 --- a/srcs/Webserv.hpp +++ b/srcs/Webserv.hpp @@ -24,7 +24,6 @@ # include // string # include // perror # include // atoi (athough it's already cover by ) -char *ft_itoa(int n); # include "Client.hpp" # include "ServerConfig.hpp" diff --git a/srcs/ft_itoa.cpp b/srcs/ft_itoa.cpp deleted file mode 100644 index 1e996a9..0000000 --- a/srcs/ft_itoa.cpp +++ /dev/null @@ -1,50 +0,0 @@ - -#include - -static int eval_is_negative(int *n) -{ - if (*n < 0) - { - *n = *n * -1; - return (1); - } - return (0); -} - -static int eval_digit_nbr(int n) -{ - int digit_nbr; - - if (n == 0) - return (1); - digit_nbr = 0; - while (n != 0) - { - digit_nbr++; - n = n / 10; - } - return (digit_nbr); -} - -char *ft_itoa(int n) -{ - int i; - char *str; - int is_negative; - - if (n == -2147483648) - return (strdup("-2147483648")); - is_negative = eval_is_negative(&n); - i = eval_digit_nbr(n) + is_negative; - str = new char[i+1]; - if (is_negative) - str[0] = '-'; - str[i] = '\0'; - while (i > 0 + is_negative) - { - i--; - str[i] = (n % 10) + '0'; - n = n / 10; - } - return (str); -} diff --git a/srcs/utils.cpp b/srcs/utils.cpp index b66cd62..004a461 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -1,8 +1,5 @@ - -#include "Webserv.hpp" - - +#include "utils.hpp" std::vector split(std::string input, char delimiter) { @@ -16,4 +13,11 @@ std::vector split(std::string input, char delimiter) return answer; } +char* itoa(int n) +{ + std::stringstream strs; + strs << n; + // casts : https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used + return ( const_cast( strs.str().c_str() ) ); +} diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 3f3b48c..9d47a02 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -1,10 +1,12 @@ - - #ifndef UTILS_HPP # define UTILS_HPP +# include +# include +# include -std::vector split(std::string input, char delimiter); +std::vector split(std::string input, char delimiter); +char* itoa(int n); #endif diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index ce785ed..2c74f7b 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -128,9 +128,8 @@ void Webserv::_get_ressource(Client *client) client->response.append("Content-Type: text/html; charset=UTF-8\r\n"); client->response.append("Content-Length: "); - tmp = ::ft_itoa(ifd.gcount()); + tmp = ::itoa(ifd.gcount()); client->response.append(tmp); - delete tmp; client->response.append("\r\n"); // Body From 19f7493aac35e3cf3d184d22a9af17468cdeedba Mon Sep 17 00:00:00 2001 From: Me Date: Sun, 31 Jul 2022 16:53:22 +0200 Subject: [PATCH 6/7] Fixed the includes --- srcs/ConfigParser.cpp | 6 +++--- srcs/ConfigParser.hpp | 30 +++++++++++------------------- srcs/ConfigParserUtils.cpp | 2 +- srcs/LocationConfig.hpp | 12 +++++------- srcs/ServerConfig.hpp | 8 +++++--- srcs/Webserv.hpp | 9 +++++---- srcs/main.cpp | 1 + srcs/utils.hpp | 9 +++++---- 8 files changed, 36 insertions(+), 41 deletions(-) diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp index 30f3014..20a8e3a 100644 --- a/srcs/ConfigParser.cpp +++ b/srcs/ConfigParser.cpp @@ -240,7 +240,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ else { std::vector tmp2 = ::split(tmp_val[0], ':'); - if (!(isNumeric(tmp2[1]))) + if (!(::isNumeric(tmp2[1]))) throw std::invalid_argument("value not a number"); // not sure if this is what we want, means there's only 1 host per @@ -262,7 +262,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ else if (key == "client_body_limit" && size == 1) { //std::cout << "made it\n"; - if (!(isNumeric(tmp_val[0]))) + if (!(::isNumeric(tmp_val[0]))) throw std::invalid_argument("value not a number"); server->client_body_limit = atoi(tmp_val[0].c_str()); } @@ -299,7 +299,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ if (tmp_val.size() != 2) throw std::invalid_argument("wrong number of values"); // and tmp_val[0] should be a number and tmp_val[1] a string? - if (!(isNumeric(tmp_val[0]))) + if (!(::isNumeric(tmp_val[0]))) throw std::invalid_argument("value not a number"); // something about using access() to see if diff --git a/srcs/ConfigParser.hpp b/srcs/ConfigParser.hpp index eeaa93d..029080d 100644 --- a/srcs/ConfigParser.hpp +++ b/srcs/ConfigParser.hpp @@ -13,26 +13,19 @@ #ifndef CONFIGPARSER_HPP # define CONFIGPARSER_HPP -# include "Webserv.hpp" // easier to just do this? # include "ServerConfig.hpp" -// add includes properly +# include "LocationConfig.hpp" +# include "MethodType.hpp" +# include "utils.hpp" - -// This is gonna be temporary cuz i don't konw if i like it -#define MAX_REQUEST_SIZE 2048 -#define MAX_URI_SIZE 64 -#define BSIZE 1024 - -/* -// this can't be here... -enum MethodType -{ - GET, - POST, - DELETE, - INVALID, -}; -*/ +# include +# include +# include // exception, what +# include // runtime_error, invalid_argument +# include // string +# include // atoi (athough it's already cover by ) +# include // cout, cin +# include // ifstream class ConfigParser { @@ -84,7 +77,6 @@ private: // why static? it's an enum... static MethodType _str_to_method_type(std::string str); - // just for testing purposes }; diff --git a/srcs/ConfigParserUtils.cpp b/srcs/ConfigParserUtils.cpp index 6947a4c..558de5e 100644 --- a/srcs/ConfigParserUtils.cpp +++ b/srcs/ConfigParserUtils.cpp @@ -1,7 +1,7 @@ -#include "Webserv.hpp" +#include "ConfigParser.hpp" diff --git a/srcs/LocationConfig.hpp b/srcs/LocationConfig.hpp index 72ab187..ea17c1c 100644 --- a/srcs/LocationConfig.hpp +++ b/srcs/LocationConfig.hpp @@ -13,12 +13,12 @@ #ifndef LOCATIONCONFIG_HPP # define LOCATIONCONFIG_HPP -// includes - // add includes properly -# include -# include +# include "MethodType.hpp" + # include -# include "Webserv.hpp" +# include +# include + // again, struct instead? class LocationConfig @@ -38,8 +38,6 @@ public: }; - - #endif diff --git a/srcs/ServerConfig.hpp b/srcs/ServerConfig.hpp index a020d9d..bafe431 100644 --- a/srcs/ServerConfig.hpp +++ b/srcs/ServerConfig.hpp @@ -13,12 +13,14 @@ #ifndef SERVERCONFIG_HPP # define SERVERCONFIG_HPP -// add includes properly... -# include "Webserv.hpp" # include "MethodType.hpp" -//# include "ConfigParser.hpp" # include "LocationConfig.hpp" +# include +# include +# include // string +# include // cout, cin + // a class that's all public? just so we have options? class ServerConfig { diff --git a/srcs/Webserv.hpp b/srcs/Webserv.hpp index b5a7b8b..0c60194 100644 --- a/srcs/Webserv.hpp +++ b/srcs/Webserv.hpp @@ -27,12 +27,13 @@ # include "Client.hpp" # include "ServerConfig.hpp" +# include "utils.hpp" // TODO: A virer -# include "ConfigParser.hpp" -# include "LocationConfig.hpp" -# include "MethodType.hpp" -# include "utils.hpp" +//# include "ConfigParser.hpp" +//# include "LocationConfig.hpp" +//# include "MethodType.hpp" +//# include "utils.hpp" // TODO: A virer extern bool g_run; diff --git a/srcs/main.cpp b/srcs/main.cpp index 4c580f3..fbe96e3 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -3,6 +3,7 @@ #include #include #include "Webserv.hpp" +#include "ConfigParser.hpp" int main(int ac, char **av) { diff --git a/srcs/utils.hpp b/srcs/utils.hpp index 1730236..64ca8f3 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -5,10 +5,11 @@ # include # include # include +# include // atoi (athough it's already cover by ) -std::vector split(std::string input, char delimiter); -bool isNumeric(std::string str); -bool isNumeric_btw(int low, int high, std::string str); -char* itoa(int n); +std::vector split(std::string input, char delimiter); +bool isNumeric(std::string str); +bool isNumeric_btw(int low, int high, std::string str); +char* itoa(int n); #endif From 16af16084b249b9391bc8aa468dae5607277877e Mon Sep 17 00:00:00 2001 From: Me Date: Mon, 1 Aug 2022 00:49:25 +0200 Subject: [PATCH 7/7] ok, so we added a lot more checks for the config file, and some post processing, still needs a few more things, but now some defaults are set too, basically it works under most circumstances --- Makefile | 1 + default.config | 3 +- srcs/ConfigParser.cpp | 202 +++++++++++++++++++------------------ srcs/ConfigParser.hpp | 8 ++ srcs/ConfigParserPost.cpp | 85 ++++++++++++++++ srcs/ConfigParserUtils.cpp | 5 - srcs/LocationConfig.hpp | 11 +- srcs/ServerConfig.hpp | 65 +++++++----- 8 files changed, 245 insertions(+), 135 deletions(-) create mode 100644 srcs/ConfigParserPost.cpp diff --git a/Makefile b/Makefile index c5ff5e4..82cf35a 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ SRCS = main.cpp \ run_loop.cpp \ ConfigParser.cpp \ ConfigParserUtils.cpp \ + ConfigParserPost.cpp \ utils.cpp \ OBJS_D = builds diff --git a/default.config b/default.config index 136510b..a3de0b3 100644 --- a/default.config +++ b/default.config @@ -6,7 +6,8 @@ server { listen 0.0.0.0:4040; - client_body_limit asdfa; +# client_body_limit asdfa; +# client_body_limit 400; index index.html; # this is another comment root ./www/; diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp index 20a8e3a..ad9257b 100644 --- a/srcs/ConfigParser.cpp +++ b/srcs/ConfigParser.cpp @@ -12,10 +12,6 @@ #include "ConfigParser.hpp" - - - - // Default ConfigParser::ConfigParser() { @@ -98,6 +94,7 @@ std::vector * ConfigParser::parse() throw std::invalid_argument("bad config file arguments 1"); ret->push_back(_parse_server(&curr)); } + _post_processing(ret); return (ret); } @@ -106,6 +103,8 @@ ServerConfig ConfigParser::_parse_server(size_t *start) ServerConfig ret; size_t curr = _content.find_first_not_of(" \t\n", *start); + ret.client_body_limit = 0; + ret.autoindex = false; if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 1"); @@ -143,6 +142,8 @@ LocationConfig ConfigParser::_parse_location(size_t *start) size_t curr = *start; // start is after the 1st word aka "location" + ret.client_body_limit = 0; + ret.redirect_status = 0; ret.path = _get_first_word(&curr); // in theory now curr should be right after the "path" @@ -181,39 +182,6 @@ LocationConfig ConfigParser::_parse_location(size_t *start) void ConfigParser::_set_server_values(ServerConfig *server, \ const std::string key, std::string value) { - -/* - // check key for ; - // check values for ; at end and right number of words depending on key - - if (key.find_first_of(";") != std::string::npos) - throw std::invalid_argument("bad config file arguments 2"); - - // there shouldn't be any tabs, right? not between values... - if (value.find_first_of("\t") != std::string::npos) - { - std::cout << value << "\n"; - throw std::invalid_argument("bad config file arguments 3"); - } - - size_t i = value.find_first_of(";"); - // so you can't have no ; - // you can't have just ; - // and you can't have a ; not at the end or several ; - // in theory value_find_last_of should find the only ; - if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ - || value.compare(";") == 0) - throw std::invalid_argument("bad config file arguments 4"); - - - // we Trim value. - // is this valid? - // would it be better to shove the result directly in tmp_val? - // like call substr in split? - //value = value.substr(0, i - 1); - value = value.substr(0, i); -*/ - value = _pre_set_val_check(key, value); std::vector tmp_val = ::split(value, ' '); @@ -223,69 +191,75 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ throw std::invalid_argument("missing value"); else if (key == "server_name" && size == 1) { - // should i be checking if the field is already filled - server->server_name = tmp_val[0]; + for (size_t i = 0; i < server->server_name.size(); i++) + { + if (server->server_name[i].compare(tmp_val[0]) == 0) + throw std::invalid_argument("server_name already exists"); + } + server->server_name.push_back(tmp_val[0]); } - else if (key == "listen" && size == 1) + else if (key == "listen" && size == 1 && server->host == "" \ + && server->port == "") { - // should i be checking if field already filled? - // are we saying only 1 possible? if (tmp_val[0].find_first_of(":") == std::string::npos) { - if (!(isNumeric(tmp_val[0]))) - throw std::invalid_argument("value not a number"); + // should i limit which ports can be used? + if (!::isNumeric(tmp_val[0])) + throw std::invalid_argument("bad port number"); server->host = "0.0.0.0"; server->port = tmp_val[0]; } else { std::vector tmp2 = ::split(tmp_val[0], ':'); - if (!(::isNumeric(tmp2[1]))) - throw std::invalid_argument("value not a number"); - // not sure if this is what we want, means there's only 1 host per - // server... - if (server->host != "" && server->host != tmp2[0]) - throw std::invalid_argument("bad listen"); + std::vector ip = ::split(tmp2[0], '.'); + if (ip.size() != 4) + throw std::invalid_argument("bad host ip"); + for (size_t i = 0; i < ip.size(); i++) + { + if (!::isNumeric_btw(0, 255, ip[i])) + throw std::invalid_argument("bad host ip"); + } + if (!::isNumeric(tmp2[1])) + throw std::invalid_argument("bad port number"); server->host = tmp2[0]; server->port = tmp2[1]; } } - else if (key == "root" && size == 1) + else if (key == "root" && size == 1 && server->root == "") { + DIR* dir = opendir(tmp_val[0].c_str()); + if (dir) + closedir(dir); + else + throw std::invalid_argument("root dir could not be opened"); server->root = tmp_val[0]; } else if (key == "autoindex" && size == 1) { + // autoindex is a bool, there's no good way for me to see if it has + // bet set already server->autoindex = (tmp_val[0] == "on" ? true : false); } - else if (key == "client_body_limit" && size == 1) + else if (key == "client_body_limit" && size == 1 \ + && server->client_body_limit == 0) { - //std::cout << "made it\n"; - if (!(::isNumeric(tmp_val[0]))) - throw std::invalid_argument("value not a number"); + 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()); } - else if (key == "recv_timeout" && size == 1) - { - // what is tv_sec and do i need it? -// ok so i don't fully understand this part but ok, keep for now... - server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str()); - } - else if (key == "send_timeout" && size == 1) - { - server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); - } else if (key == "index") { + // i think you can call index several times... // should i be doing an access? + // since index is at the root, but root might not yet be defined + // will check index later in post for (unsigned long i = 0; i != tmp_val.size(); i++) server->index.push_back(tmp_val[i]); } - else if (key == "allow_methods") + else if (key == "allow_methods" && server->allow_methods.empty()) { - // you need to throw if it's a bad method type - // or should we skip? and see if any others are good? for (unsigned long i = 0; i != tmp_val.size(); i++) { MethodType m = _str_to_method_type(tmp_val[i]); @@ -294,22 +268,16 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ server->allow_methods.push_back(m); } } - else if (key == "return") - { - if (tmp_val.size() != 2) - throw std::invalid_argument("wrong number of values"); - // and tmp_val[0] should be a number and tmp_val[1] a string? - if (!(::isNumeric(tmp_val[0]))) - throw std::invalid_argument("value not a number"); - - // something about using access() to see if - server->redirect_status = atoi(tmp_val[0].c_str()); - server->redirect_uri = tmp_val[1]; - } else if (key == "error_page") { - // something more complicated? - // like make sure ints then 1 string? + + // so it can either be just a /here/is/the/repo + // or it can be http://some_domain.com/here + // wtf... how should we handle... + + + + // you can definitely call Error_pages several times, i think std::string path = tmp_val[tmp_val.size() - 1]; for (unsigned long i = 0; i != tmp_val.size() - 1; i++) { @@ -324,24 +292,25 @@ void ConfigParser::_set_server_values(ServerConfig *server, \ server->error_pages[status_code] = path; } } - else +/* else if (key == "recv_timeout" && size == 1 && server->server_name == "") { - throw std::invalid_argument("wrong number of values"); + // what is tv_sec and do i need it? +// ok so i don't fully understand this part but ok, keep for now... + server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str()); + } + else if (key == "send_timeout" && size == 1 && server->server_name == "") + { + server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); + } +*/ else + { + // means either you didn't write the right key, or the value is + // missing, or the value has already been filled. + throw std::invalid_argument("bad key value pair"); } - - - -// std::cout << "End set\n"; - - } - - - - - void ConfigParser::_set_location_values(LocationConfig *location, \ const std::string key, std::string value) { @@ -352,26 +321,41 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ if (size < 1) throw std::invalid_argument("missing value"); - else if (key == "root" && size == 1) + else if (key == "root" && size == 1 && location->root == "") { + DIR* dir = opendir(tmp_val[0].c_str()); + if (dir) + closedir(dir); + else + throw std::invalid_argument("root dir could not be opened"); location->root = tmp_val[0]; } - else if (key == "client_body_limit" && size == 1) + else if (key == "client_body_limit" && size == 1 \ + && location->client_body_limit == 0) { + if (!::isNumeric(tmp_val[0])) + throw std::invalid_argument("client_body_limit not a number"); location->client_body_limit = atoi(tmp_val[0].c_str()); } else if (key == "index") { + // you can definitely call Index several times, i think for (unsigned long i = 0; i != tmp_val.size(); i++) location->index.push_back(tmp_val[i]); } - else if (key == "allow_methods") + else if (key == "allow_methods" && location->allow_methods.empty()) { for (unsigned long i = 0; i != tmp_val.size(); i++) - location->allow_methods.push_back(_str_to_method_type(tmp_val[i])); + { + MethodType m = _str_to_method_type(tmp_val[i]); + if (m == 3) + throw std::invalid_argument("not a valid method"); + location->allow_methods.push_back(m); + } } else if (key == "cgi_info") { + // you can call cgi_info several times i think. // ok wtf is all this even doing, figure that out unsigned long i = value.find_first_of(" "); if (i == std::string::npos) @@ -380,9 +364,27 @@ void ConfigParser::_set_location_values(LocationConfig *location, \ int j = value.find_first_not_of(" ", i); location->cgi_info[value.substr(0, i)] = value.substr(j, value.length()); } + else if (key == "return" && location->redirect_status == 0 \ + && location->redirect_uri == "") + { +// actually i think there can only be one per location... + // you can definitely call return several times, i think + if (tmp_val.size() != 2) + throw std::invalid_argument("wrong number of values"); + // and tmp_val[0] should be a number and tmp_val[1] a string? + if (!(::isNumeric(tmp_val[0]))) + throw std::invalid_argument("value not a number"); + + // somehow check that tmp_val[1] is a string? or valid? how? + // something about using access() to see if + location->redirect_status = atoi(tmp_val[0].c_str()); + location->redirect_uri = tmp_val[1]; + } else { - throw std::invalid_argument("bad config file arguments 9"); + // means either you didn't write the right key, or the value is + // missing, or the value has already been filled. + throw std::invalid_argument("bad key value pair"); } } diff --git a/srcs/ConfigParser.hpp b/srcs/ConfigParser.hpp index 029080d..9ae1577 100644 --- a/srcs/ConfigParser.hpp +++ b/srcs/ConfigParser.hpp @@ -26,6 +26,8 @@ # include // atoi (athough it's already cover by ) # include // cout, cin # include // ifstream +//# include // access() +# include // opendir() class ConfigParser { @@ -79,6 +81,12 @@ private: + // some sort of post processing... + + void _post_processing(std::vector *servers); + + + }; diff --git a/srcs/ConfigParserPost.cpp b/srcs/ConfigParserPost.cpp new file mode 100644 index 0000000..3968214 --- /dev/null +++ b/srcs/ConfigParserPost.cpp @@ -0,0 +1,85 @@ + + + +#include "ConfigParser.hpp" + + + +void ConfigParser::_post_processing(std::vector *servers) +{ + + // make certain servers default + // fill out empty settings + // if special settings are empty throw + + std::vector::iterator it = servers->begin(); + + while (it != servers->end()) + { + // host and port should already be set + if (it->host == "") + throw std::invalid_argument("Config file needs a host and port"); + + // is that a good default? + if (it->root == "") + it->root = "/"; + if (it->client_body_limit == 0) + it->client_body_limit = 5000; // what is the recomended size? + + // autoindex should already be false by default right? + + // what do we do if Allow methods is left empty? + // all ? + if (it->allow_methods.empty()) + throw std::invalid_argument("No methods specified"); + + + // what to do if index is left empty? index.html? + // ok but i still need to check index, no idea how... + + // if error_pages is left empty, we'll use the defaults which + // i believe are set elsewhere... + + std::vector::iterator it_l = it->locations.begin(); + while (it_l != it->locations.end()) + { + // check that path is feasible... + // opendir? + DIR* dir = opendir(it_l->path.c_str()); + if (dir) + closedir(dir); + else + throw std::invalid_argument("location dir could not be opened"); + + if (it_l->client_body_limit == 0) + it_l->client_body_limit = 5000; // what is the recomended size? + if (it_l->root == "") + it_l->root = it->root; + + // fill out allow methods from server? + if (it_l->allow_methods.empty()) + it_l->allow_methods = it->allow_methods; + + // fill out index from Server? + // or do a bunch of checks on what is in there... + + // same for redirect status i think + + // maybe do something for Cgi_info? + + ++it_l; + } + + + ++it; + } + + // do the defaults at the end? + + +} + + + + + diff --git a/srcs/ConfigParserUtils.cpp b/srcs/ConfigParserUtils.cpp index 558de5e..861ae83 100644 --- a/srcs/ConfigParserUtils.cpp +++ b/srcs/ConfigParserUtils.cpp @@ -96,11 +96,6 @@ MethodType ConfigParser::_str_to_method_type(std::string str) - - - - - void ConfigParser::_print_content() const { std::cout << _content; diff --git a/srcs/LocationConfig.hpp b/srcs/LocationConfig.hpp index ea17c1c..2a077f6 100644 --- a/srcs/LocationConfig.hpp +++ b/srcs/LocationConfig.hpp @@ -26,14 +26,23 @@ class LocationConfig public: // canonic stuff? + std::string path; int client_body_limit; - std::string path; std::string root; std::vector index; std::vector allow_methods; std::map cgi_info; + // wait if i can call several times, shouldn't it be a map? + // wait no there can only be 1 and i think it might have to be in + // location only... + int redirect_status; + std::string redirect_uri; + // au pire you do location / { return 301 http://location; } + // and that's how you get the redirect from the root. + + }; diff --git a/srcs/ServerConfig.hpp b/srcs/ServerConfig.hpp index bafe431..b589d13 100644 --- a/srcs/ServerConfig.hpp +++ b/srcs/ServerConfig.hpp @@ -26,47 +26,56 @@ class ServerConfig { public: - // i mean i guess i need some canonic stuff? - // although maybe if i make it a struct i can barebones it? + // do i need some canonic stuff? - std::string server_name; - std::string root; - - std::vector index; - std::map error_pages; + // there can be several + std::vector server_name; + // we could shove default in here if we wanted to... - // i'm tempted to do something diff for storing method types... - std::vector allow_methods; - - std::vector locations; - - // might do something diff - struct timeval send_timeout; - struct timeval recv_timeout; - - int client_body_limit; // set to default max if none set - bool autoindex; - - // not sure what these look like in config file - int redirect_status; - std::string redirect_uri; - - // is this the best way? + // there can only be 1 per server... std::string host; std::string port; // port needs to be something else... not quite an int // should a Server be able to handle several? + // there can only be one. + std::string root; + + int client_body_limit; // set to default max if none set + + // might be the only one we let slide if bad input... + bool autoindex; + + // we will check the index in the post processing with access() ? + std::vector index; + std::map error_pages; + + // i'm tempted to do something diff for storing method types... + // fuck it, you can only call allow_methods once in Server + // once more in each location. + std::vector allow_methods; + + std::vector locations; + + // not convinced we need these... +// struct timeval send_timeout; +// struct timeval recv_timeout; - // do i need a print all for testing? +// fuck maybe i do need return here... + // wait if i can call several times, shouldn't it be a map? +// i think actually there can only be 1 and it can only be in a location? +// int redirect_status; +// std::string redirect_uri; + void print_all() { std::cout << "PRINTING A FULL SERVER CONFIG\n\n"; - std::cout << "Server_name: " << server_name << '\n'; + for (size_t i = 0; i < server_name.size(); i++) + std::cout << server_name[i] << " "; std::cout << "root: " << root << '\n'; std::cout << "index: "; for (size_t i = 0; i < index.size(); i++) @@ -84,8 +93,8 @@ public: std::cout << "also skiping send_timeout and recv\n"; std::cout << "autoindex: " << autoindex << '\n'; std::cout << "client_body_limit: " << client_body_limit << '\n'; - std::cout << "redirect_status: " << redirect_status << '\n'; - std::cout << "redirect_uri: " << redirect_uri << '\n'; + // std::cout << "redirect_status: " << redirect_status << '\n'; + // std::cout << "redirect_uri: " << redirect_uri << '\n'; std::cout << "host: " << host << '\n'; std::cout << "port: " << port << '\n';