diff --git a/Makefile b/Makefile index 0168787..a775cd8 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,7 @@ CXXFLAGS += -MMD -MP #header dependencie VPATH = $(SRCS_D) -HEADERS_D = srcs \ - headers +HEADERS_D = srcs SRCS_D = srcs \ srcs/webserv @@ -21,9 +20,11 @@ SRCS = main.cpp \ accept.cpp request.cpp response.cpp \ run_loop.cpp \ ConfigParser.cpp \ + ConfigParserUtils.cpp \ + ConfigParserPost.cpp \ utils.cpp \ cgi_script.cpp \ - Client.cpp + Client.cpp \ OBJS_D = builds OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o) diff --git a/README.md b/README.md index 07f3a72..20c8b93 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ - --- ## questions - mettre les fonctions specifiques a la requete, dans la class client ? diff --git a/default.config b/default.config index 395acfc..a3de0b3 100644 --- a/default.config +++ b/default.config @@ -6,6 +6,8 @@ server { listen 0.0.0.0:4040; +# client_body_limit asdfa; +# client_body_limit 400; index index.html; # this is another comment root ./www/; @@ -13,17 +15,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/headers/utils.hpp b/headers/utils.hpp deleted file mode 100644 index b45ea55..0000000 --- a/headers/utils.hpp +++ /dev/null @@ -1,14 +0,0 @@ - -#ifndef UTILS_HPP -# define UTILS_HPP - -# include -# include -# include - -std::vector split(std::string input, char delimiter); -std::string itos(int n); -std::string trim(std::string str, char c); - -#endif - diff --git a/headers/Client.hpp b/srcs/Client.hpp similarity index 86% rename from headers/Client.hpp rename to srcs/Client.hpp index 652144c..002f1c5 100644 --- a/headers/Client.hpp +++ b/srcs/Client.hpp @@ -47,5 +47,8 @@ class Client }; -#endif +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/ConfigParser.cpp b/srcs/ConfigParser.cpp index d7a24a3..ad9257b 100644 --- a/srcs/ConfigParser.cpp +++ b/srcs/ConfigParser.cpp @@ -6,26 +6,12 @@ /* 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 */ /* */ /* ************************************************************************** */ #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? - - -*/ - - - - // Default ConfigParser::ConfigParser() { @@ -33,8 +19,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 +45,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,20 +92,19 @@ 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)); } + _post_processing(ret); return (ret); } -// might need new names for Prev and Curr, not super descriptive... 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"); @@ -141,17 +123,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); @@ -165,12 +142,13 @@ 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" 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 +169,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,203 +177,185 @@ 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 - - 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); - - 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) - { - server->server_name = tmp_val[0]; - } - else if (key == "listen" && size == 1) - { - if (tmp_val[0].find_first_of(":") == std::string::npos) - { - // why not store as vector [4] ? - 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 (server->host != "" && server->host != tmp2[0]) - throw std::invalid_argument("bad listen"); - server->host = tmp2[0]; - server->port = tmp2[1]; - } - } - else if (key == "root" && size == 1) - { - server->root = tmp_val[0]; - } - else if (key == "autoindex" && size == 1) - { - server->autoindex = (tmp_val[0] == "on" ? true : false); - } - else if (key == "client_body_limit" && size == 1) - { - 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 - { - 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... - 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? - for (unsigned long i = 0; i != tmp_val.size(); i++) - server->allow_methods.push_back(_str_to_method_type(tmp_val[i])); - } - else if (key == "return") - { - // could run more checks here too - // like tmp_val.size() must be 2 - // and tmp_val[0] should be a number and tmp_val[1] a string? - 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? - std::string path = tmp_val[tmp_val.size() - 1]; - for (unsigned long i = 0; i != tmp_val.size() - 1; i++) - { - int status_code = atoi(tmp_val[i].c_str()); - // yea IDK i might not want to store this like that... - if (server->error_pages.find(status_code) != server->error_pages.end()) - continue ; - server->error_pages[status_code] = path; - } - } - else - { - throw std::invalid_argument("wrong number of values"); - } -} - -// 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(); if (size < 1) throw std::invalid_argument("missing value"); - else if (key == "root" && size == 1) + else if (key == "server_name" && size == 1) { - location->root = 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 == "client_body_limit" && size == 1) + else if (key == "listen" && size == 1 && server->host == "" \ + && server->port == "") { - location->client_body_limit = atoi(tmp_val[0].c_str()); + if (tmp_val[0].find_first_of(":") == std::string::npos) + { + // 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], ':'); + + 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 + else if (key == "root" && size == 1 && server->root == "") { - throw std::invalid_argument("should only have 1 argument"); + 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 \ + && server->client_body_limit == 0) + { + 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 == "index") { + // i think you can call index several times... + // should i be doing an access? + // since index is at the root, but root might not yet be defined + // will check index later in post + for (unsigned long i = 0; i != tmp_val.size(); i++) + server->index.push_back(tmp_val[i]); + } + else if (key == "allow_methods" && server->allow_methods.empty()) + { + for (unsigned long i = 0; i != tmp_val.size(); i++) + { + 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 == "error_page") + { + + // 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++) + { + // 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 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; + } + } +/* else if (key == "recv_timeout" && size == 1 && server->server_name == "") + { + // what is tv_sec and do i need it? +// ok so i don't fully understand this part but ok, keep for now... + server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str()); + } + else if (key == "send_timeout" && size == 1 && server->server_name == "") + { + server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); + } +*/ else + { + // means either you didn't write the right key, or the value is + // missing, or the value has already been filled. + throw std::invalid_argument("bad key value pair"); + } +} + + +void ConfigParser::_set_location_values(LocationConfig *location, \ + const std::string key, std::string value) +{ + value = _pre_set_val_check(key, value); + + std::vector tmp_val = ::split(value, ' '); + size_t size = tmp_val.size(); + + if (size < 1) + throw std::invalid_argument("missing value"); + else if (key == "root" && size == 1 && location->root == "") + { + DIR* dir = opendir(tmp_val[0].c_str()); + if (dir) + closedir(dir); + else + throw std::invalid_argument("root dir could not be opened"); + location->root = tmp_val[0]; + } + 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) @@ -406,76 +364,32 @@ 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"); } } -// 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/headers/ConfigParser.hpp b/srcs/ConfigParser.hpp similarity index 72% rename from headers/ConfigParser.hpp rename to srcs/ConfigParser.hpp index 81b1cdf..9ae1577 100644 --- a/headers/ConfigParser.hpp +++ b/srcs/ConfigParser.hpp @@ -13,26 +13,21 @@ #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 +//# include // access() +# include // opendir() class ConfigParser { @@ -74,6 +69,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? @@ -81,13 +79,24 @@ private: // why static? it's an enum... static MethodType _str_to_method_type(std::string str); - // just for testing purposes + + + // some sort of post processing... + + void _post_processing(std::vector *servers); + }; - +// 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/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 new file mode 100644 index 0000000..861ae83 --- /dev/null +++ b/srcs/ConfigParserUtils.cpp @@ -0,0 +1,104 @@ + + + +#include "ConfigParser.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/headers/LocationConfig.hpp b/srcs/LocationConfig.hpp similarity index 78% rename from headers/LocationConfig.hpp rename to srcs/LocationConfig.hpp index 72ab187..2a077f6 100644 --- a/headers/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 @@ -26,20 +26,27 @@ 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. + + }; - - #endif diff --git a/headers/MethodType.hpp b/srcs/MethodType.hpp similarity index 100% rename from headers/MethodType.hpp rename to srcs/MethodType.hpp diff --git a/headers/Server.hpp b/srcs/Server.hpp similarity index 100% rename from headers/Server.hpp rename to srcs/Server.hpp diff --git a/headers/ServerConfig.hpp b/srcs/ServerConfig.hpp similarity index 54% rename from headers/ServerConfig.hpp rename to srcs/ServerConfig.hpp index 868b0f8..ce49255 100644 --- a/headers/ServerConfig.hpp +++ b/srcs/ServerConfig.hpp @@ -2,53 +2,67 @@ #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 { 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; + + // there can be several + std::vector server_name; + // we could shove default in here if we wanted to... + + // there can only be 1 per server... + std::string host; + std::string port; // port needs to be something else... not quite an int + // should a Server be able to handle several? + + // there can only be one. std::string root; + 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; - // might do something diff - struct timeval send_timeout; - struct timeval recv_timeout; + // not convinced we need these... +// struct timeval send_timeout; +// struct timeval recv_timeout; - int client_body_limit; - bool autoindex; - // not sure what these look like in config file - int redirect_status; - std::string redirect_uri; - - // is this the best way? - std::string host; - std::string port; - - // 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++) @@ -66,8 +80,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'; diff --git a/headers/Webserv.hpp b/srcs/Webserv.hpp similarity index 96% rename from headers/Webserv.hpp rename to srcs/Webserv.hpp index 21b0138..6b858a4 100644 --- a/headers/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.cpp b/srcs/utils.cpp index 542bddc..48af746 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -29,3 +29,26 @@ std::string itos(int n) return ( strs.str() ); } +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 = std::atoi(str.c_str()); + if (n < low || n > high) + return false; + return true; +} + diff --git a/srcs/utils.hpp b/srcs/utils.hpp new file mode 100644 index 0000000..a2e2409 --- /dev/null +++ b/srcs/utils.hpp @@ -0,0 +1,16 @@ + +#ifndef UTILS_HPP +# define UTILS_HPP + +# include +# include +# include +# include // atoi + +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); + +#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 3bcde4a..817c127 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 @@ -35,7 +40,7 @@ void Webserv::_read_request(Client *client) client->parse_request(); // _parse_request(client); - _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD, client); + _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD); } // // http headers : diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index f049352..da818e7 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); @@ -98,12 +105,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) { @@ -141,7 +157,6 @@ void Webserv::_get_ressource(Client *client) tmp = ::itos(ifd.gcount()); client->response.append(tmp.c_str()); - client->response.append("\r\n"); // Body 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;