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';