diff --git a/default.json b/default.json index f623e18..0e248ba 100644 --- a/default.json +++ b/default.json @@ -1,30 +1,35 @@ { - "max_clients": 30, "root_folder": "./www", "servers": [ { "server_name": "localhost", - "listens": ["localhost:8080"], - "root": "tester/", + "listens": [ + "localhost:8080" + ], + "root": "public/", "return": "301 https://$host$uri" }, { "server_name": "localhost", - "listens": ["192.168.62.61:8080", "localhost", "555"], + "listens": [ + "192.168.62.61:8080", + "localhost", + "555" + ], "root": "public/testsite", - "indexs": ["basique.html"], - "cgi": { - ".php": "/usr/bin/php-cgi" - }, - "locations": - { - "docs/": - { + "indexs": [ + "basique.html" + ], + "cgi": { + ".php": "/usr/bin/php-cgi" + }, + "client_max_body_size": 100, + "locations": { + "docs/": { "root": "public/documents/", "autoindex": true }, - "img/": - { + "img/": { "root": "public/images/", "autoindex": true } diff --git a/includes/Client.hpp b/includes/Client.hpp index 9cfec6c..59c00ab 100644 --- a/includes/Client.hpp +++ b/includes/Client.hpp @@ -5,20 +5,29 @@ typedef std::map< string, std::vector< string > > request_t; class Client { int _fd; - listen_t _listen; + ip_port_t _ip_port; Master *_parent; + + string _method; + string _uri; + string _host; + string _len; + Server *_server; + Route *_route; + string _header; string _content; request_t _request; public: - Client(int fd, listen_t listen, Master *parent); + Client(int fd, ip_port_t ip_port, Master *parent); ~Client(void); - bool getRequest(string paquet); - bool parseHeader(); - string header_pick(string key, int id); - void answer(Env *env); - bool check_method(Server *server, Route *route, string method); + void clean(void); + bool getRequest(Env *env, string paquet); + bool parseHeader(Env *env); + string header_pick(string key, size_t id); + void answer(); + bool check_method(string method); void send_cgi(string cgi, string path); void send_error(int error_code); void send_answer(string msg); diff --git a/includes/Env.hpp b/includes/Env.hpp index 569b452..b9be2ee 100644 --- a/includes/Env.hpp +++ b/includes/Env.hpp @@ -5,6 +5,7 @@ class Env { public: std::vector< Server * > _servers; std::vector< Master * > _masters; + Env(JSONNode *conf); void cycle(void); void set_fds(void); diff --git a/includes/Master.hpp b/includes/Master.hpp index f2c89ec..0f2e68e 100644 --- a/includes/Master.hpp +++ b/includes/Master.hpp @@ -7,7 +7,7 @@ class Master { struct sockaddr_in _address; public: - Master(listen_t listen); + Master(ip_port_t listen); Master(int fd, Master *parent); ~Master(void); @@ -15,7 +15,7 @@ class Master { void refresh(Env *env); Server *choose_server(Env *env, string host); - listen_t _listen; + ip_port_t _listen; static fd_set _readfds; static int _max_fd; static int _min_fd; diff --git a/includes/Route.hpp b/includes/Route.hpp index a28267a..f2699f3 100644 --- a/includes/Route.hpp +++ b/includes/Route.hpp @@ -13,6 +13,7 @@ class Route { std::vector< string > _indexs; std::vector< string > _headers; std::map< string, string > _cgi; + int _client_max_body_size; Route(Server *server, string location, JSONNode *datas); ~Route(void); diff --git a/includes/Server.hpp b/includes/Server.hpp index 530fddd..18a2a18 100644 --- a/includes/Server.hpp +++ b/includes/Server.hpp @@ -6,7 +6,7 @@ class Server : public Route { std::map< string, Route * > _routes; public: - std::vector< listen_t > _listens; + std::vector< ip_port_t > _listens; Server(JSONNode *server); ~Server(void); Master *create_master(string str); diff --git a/includes/webserv.hpp b/includes/webserv.hpp index 5c963a0..e4bf716 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -33,11 +33,11 @@ using std::cout; using std::strerror; using std::string; -typedef struct listen_s { +typedef struct ip_port_s { int fd; string ip; int port; -} listen_t; +} ip_port_t; class JSONNode; class Env; @@ -52,8 +52,8 @@ typedef std::vector< JSONNode * > JSONList; void *ft_memset(void *b, int c, size_t len); bool isInt(string str); std::vector< string > split(string str, string delim); -listen_t get_listen_t(string listen); -listen_t get_listen_t(string ip, int port); +ip_port_t get_ip_port_t(string listen); +ip_port_t get_ip_port_t(string ip, int port); string getMime(string path); string read_file(string path); diff --git a/srcs/load/Env.cpp b/srcs/load/Env.cpp index 8a17d8e..705303e 100644 --- a/srcs/load/Env.cpp +++ b/srcs/load/Env.cpp @@ -25,6 +25,7 @@ Env::~Env() { */ Env::Env(JSONNode *conf) { try { + JSONList servers = conf->obj()["servers"]->lst(); for (std::vector< JSONNode * >::iterator it = servers.begin(); it < servers.end(); it++) { diff --git a/srcs/load/Route.cpp b/srcs/load/Route.cpp index 6529e31..3679f2b 100644 --- a/srcs/load/Route.cpp +++ b/srcs/load/Route.cpp @@ -39,6 +39,8 @@ Route::Route(Server *server, string location, JSONNode *datas) _cgi[(*it).first] = (*it).second->str(); } } + if ((tmp = object["client_max_body_size"])) + _client_max_body_size = tmp->nbr(); } /* Route destructor */ @@ -62,6 +64,7 @@ string Route::getIndex(string uri, string path) { struct dirent *entry; struct stat info; std::vector< string >::iterator it; + cout << "get index(): path=" << path << "\n"; if ((dir = opendir(path.c_str())) == NULL) return ""; diff --git a/srcs/load/Server.cpp b/srcs/load/Server.cpp index df7aed9..2ceea18 100644 --- a/srcs/load/Server.cpp +++ b/srcs/load/Server.cpp @@ -45,7 +45,7 @@ string Server::getName(void) { return _name; } */ Master *Server::create_master(string str) { - listen_t listen = get_listen_t(str); + ip_port_t listen = get_ip_port_t(str); if (listen.ip.at(0) == '[') { cout << "Listen: IPv6 isn't supported\n"; } @@ -69,7 +69,7 @@ Master *Server::create_master(string str) { std::vector< Master * > Server::get_sockets(JSONNode *server) { JSONObject datas = server->obj(); std::vector< Master * > ret; - listen_t listen; + ip_port_t listen; Master *tmp; if (datas["listens"]) { JSONList listens = datas["listens"]->lst(); @@ -99,10 +99,12 @@ Route *Server::choose_route(string uri) { std::vector< string >::iterator root_it = root.begin(); for (std::vector< string >::iterator it = req.begin(); it < req.end(); it++) { - if (*it == "") - continue; + while (it != req.end() && *it == "") + it++; if (*it != *(root_it++)) break; + while (root_it != root.end() && *root_it == "") + root_it++; if (root_it == root.end()) return ((*rit).second); } diff --git a/srcs/sock/Client.cpp b/srcs/sock/Client.cpp index 9e7117c..d7c60dc 100644 --- a/srcs/sock/Client.cpp +++ b/srcs/sock/Client.cpp @@ -1,43 +1,58 @@ #include "webserv.hpp" -Client::Client(int fd, listen_t listen, Master *parent) - : _fd(fd), _listen(listen), _parent(parent) { - cout << "New connection, socket fd is " << fd << ", ip is : " << _listen.ip - << ", port : " << _listen.port << "\n"; +Client::Client(int fd, ip_port_t ip_port, Master *parent) + : _fd(fd), _ip_port(ip_port), _parent(parent) { + cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip + << ", port : " << _ip_port.port << "\n"; } + Client::~Client(void) { close(_fd); - cout << "Host disconnected, ip " << _listen.ip << ", port " << _listen.port - << "\n"; + cout << "Host disconnected, ip " << _ip_port.ip << ", port " + << _ip_port.port << "\n"; +} + +void Client::clean(void) { + _server = NULL; + _route = NULL; + + _method = ""; + _uri = ""; + _host = ""; + + _header = ""; + _content = ""; + _request.clear(); } -bool Client::getRequest(string paquet) { - cout << "|===|paquet|===>" << paquet << "|===||\n"; +bool Client::getRequest(Env *env, string paquet) { + // cout << "|===|paquet|===>" << paquet << "|===||\n"; if (paquet.length() < 1) // HTTPS? return false; std::vector< string > lines = split(paquet, "\n"); - long chunk_len = - (_content.length() > 0 && _request["Transfer-Encoding:"].size() && - _request["Transfer-Encoding:"].at(0) == "chunked") - ? std::strtol(lines.at(0).substr(1).c_str(), 0, 16) - : -1; + long chunk_len; + chunk_len = (_content.length() > 0 && + header_pick("Transfer-Encoding:", 0) == "chunked") + ? std::strtol(lines.at(0).c_str(), 0, 16) + : -1; cout << "Chunk length: " << chunk_len << "\n"; for (std::vector< string >::iterator it = lines.begin(); it < lines.end(); it++) { - if (*it == "\r" && _content.length() == 0) - this->parseHeader(); + if (*it == "\r" && _content.length() == 0) { + if (!this->parseHeader(env)) + return false; + } if (*it != "\r" && _content.length() == 0) _header += *it + "\n"; else if (chunk_len == -1 || it != lines.begin()) _content += *it + "\n"; } - cout << "Header: \n-|" << _header << "|-\n"; cout << "Content: \n-|" << _content << "|-\n"; if (_content.length() > 0) { _content.resize(_content.length() - 1); - if (_request["Method:"].at(0) == "GET" || + if (header_pick("Method:", 0) == "GET" || (chunk_len == 0 || - std::strtoul(_request["Content-Length:"].at(0).c_str(), 0, 10) <= + std::strtoul(header_pick("Content-Length:", 0).c_str(), 0, 10) <= _content.length())) { cout << "Request received\n"; return true; @@ -47,8 +62,12 @@ bool Client::getRequest(string paquet) { return false; } -bool Client::parseHeader() { +bool Client::parseHeader(Env *env) { + cout << "Header: \n-|" << _header << "|-\n"; std::vector< string > lines = split(_header, "\r\n"); + std::vector< string > method = split(lines.at(0), " "); + _request["Method:"] = method; + std::vector< string > line; if (lines.size() > 0) { for (std::vector< string >::iterator it = lines.begin() + 1; @@ -58,24 +77,34 @@ bool Client::parseHeader() { std::vector< string >(line.begin() + 1, line.end()); } } - std::vector< string > method = split(lines.at(0), " "); - if (method.at(0) == "POST" && - _request.find("Content-Length:") == _request.end() && - _request.find("Transfer-Encoding:") == _request.end()) + if (method.at(0) == "POST" && header_pick("Content-Length:", 0) == "" && + header_pick("Transfer-Encoding:", 0) != "chunked") { + send_error(400); return false; - _request["Method:"] = method; + } + + _method = header_pick("Method:", 0); + _uri = header_pick("Method:", 1); + _host = header_pick("Host:", 0); + _server = _parent->choose_server(env, _host); + _route = _server->choose_route(_uri); + _len = header_pick("Content-Length:", 0).c_str(); + if (_len != "" && std::atoi(_len.c_str()) > _route->_client_max_body_size) { + send_error(413); + return false; + } return true; } -bool Client::check_method(Server *server, Route *route, string method) { +bool Client::check_method(string method) { std::vector< string > allowed; if (method != "GET" && method != "POST" && method != "DELETE") send_error(405); - else if ((allowed = route->_headers).size() > 0 && + else if ((allowed = _route->_headers).size() > 0 && std::find(allowed.begin(), allowed.end(), method) == allowed.end()) { send_error(405); - } else if ((allowed = server->_headers).size() > 0 && + } else if ((allowed = _server->_headers).size() > 0 && std::find(allowed.begin(), allowed.end(), method) == allowed.end()) { send_error(405); @@ -84,31 +113,25 @@ bool Client::check_method(Server *server, Route *route, string method) { return (false); } -string Client::header_pick(string key, int id) { - if (_request[key].size() == 0) - throw std::runtime_error("The key is not in request's header."); - string ret = _request[key].at(id); - return ret; +string Client::header_pick(string key, size_t id) { + if (_request[key].size() <= id) + return ""; + return _request[key].at(id); } inline string get_extension(string str) { return str.substr(str.rfind('.')); } -void Client::answer(Env *env) { - string method = header_pick("Method:", 0); - string uri = header_pick("Method:", 1); - string host = header_pick("Host:", 0); - cout << "Method: " << method << "\n"; - cout << "URI: " << uri << "\n"; - cout << "Host: " << host << "\n"; +void Client::answer() { + cout << "Method: " << _method << "\n"; + cout << "URI: " << _uri << "\n"; + cout << "Host: " << _host << "\n"; string ret; - - Server *server = _parent->choose_server(env, host); - Route *route = server->choose_route(uri); - if (check_method(server, route, method)) { - string path = route->correctUri(uri); - string cgi = route->_cgi.size() ? route->_cgi[get_extension(path)] : ""; + if (check_method(_method)) { + string path = _route->correctUri(_uri); + string cgi = + _route->_cgi.size() ? _route->_cgi[get_extension(path)] : ""; if (cgi == "") { - if ((ret = route->getIndex(uri, path)) == "" && + if ((ret = _route->getIndex(_uri, path)) == "" && (ret = read_file(path)) == "") send_error(404); else @@ -116,8 +139,6 @@ void Client::answer(Env *env) { } else send_cgi(cgi, path); } - _content = ""; - _header = ""; } void Client::send_cgi(string cgi, string path) { @@ -145,7 +166,7 @@ void Client::send_cgi(string cgi, string path) { char buffer[10000]; buffer[read(fd[0], buffer, 10000)] = 0; ret = string(buffer); - ss << "HTTP/1.1 200 OK\r\nContent-length: " + ss << "HTTP/1.1 200 OK\r\nContent-Length: " << ret.length() - ret.find("\r\n\r\n") - 4 << "\r\n" << ret; send_answer(ss.str()); @@ -153,19 +174,28 @@ void Client::send_cgi(string cgi, string path) { void Client::send_error(int error_code) { switch (error_code) { + case 400: + return send_answer( + "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"); case 404: return send_answer( - "HTTP/1.1 404 Not Found\r\nContent-length: 0\r\n\r\n"); + "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); case 405: return send_answer( - "HTTP/1.1 405 Method Not Allowed\r\nContent-length: 0\r\n\r\n"); + "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n"); + case 413: + return send_answer( + "HTTP/1.1 413 Payload Too " + "Large\r\nConnection:close\r\nContent-Length: 0\r\n\r\n"); } } void Client::send_answer(string msg) { #ifdef __linux__ + cout << "Answer: \n-|" << msg << "|-\n"; send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); #elif __APPLE__ send(_fd, msg.c_str(), msg.length(), 0); #endif + clean(); } diff --git a/srcs/sock/Master.cpp b/srcs/sock/Master.cpp index b79925c..e093567 100644 --- a/srcs/sock/Master.cpp +++ b/srcs/sock/Master.cpp @@ -10,12 +10,12 @@ Master::~Master(void) { * Try to create a socket listening to ip and port defined by input. * If the creation success, the socket is then ready to select for new clients. * - * Input: A listen_t structure which contain the ip and the port the master care - * about. + * Input: A ip_port_t structure which contain the ip and the port the master + * care about. * Output: A Master object. */ -Master::Master(listen_t list) : _listen(list) { +Master::Master(ip_port_t list) : _listen(list) { int opt = 1; string ip = _listen.ip; int port = _listen.port; @@ -78,8 +78,8 @@ void Master::refresh(Env *env) { #ifdef __APPLE__ fcntl(new_socket, F_SETFL, O_NONBLOCK); #endif - listen_t cli_listen = get_listen_t(inet_ntoa(_address.sin_addr), - ntohs(_address.sin_port)); + ip_port_t cli_listen = get_ip_port_t(inet_ntoa(_address.sin_addr), + ntohs(_address.sin_port)); _childs.push_back(new Client(new_socket, cli_listen, this)); } int child_fd; @@ -94,11 +94,28 @@ void Master::refresh(Env *env) { (socklen_t *)&addrlen); delete (*it); _childs.erase(it); - } else if ((*it)->getRequest(buffer)) - (*it)->answer(env); + } else { + if ((*it)->getRequest(env, buffer)) + (*it)->answer(); + } } } } +/* |==========| + * Choose the server which must handle a request + * Each server can listen multiple range_ip:port and each range_ip:port can be + * listen by multiple servers. So for each request, we must look at the socket + * which given us the client to know how the client came. If multiple servers + * listen the range from where the client came, ones with exact correspondance + * are prefered. + * + * If there are multiples servers listening exactly the ip the client try to + * reach or whic listen a range which contain it, the first one which have the + * same server_name as the host the client used to reach server is used, else + * it's the first one of exact correspondance or first one which have the ip + * requested in his listen range. + * + */ Server *Master::choose_server(Env *env, string host) { std::vector< Server * > exact; @@ -110,8 +127,8 @@ Server *Master::choose_server(Env *env, string host) { for (std::vector< Server * >::iterator sit = env->_servers.begin(); sit < env->_servers.end(); sit++) { - std::vector< listen_t > serv_listens = (*sit)->_listens; - for (std::vector< listen_t >::iterator it = serv_listens.begin(); + std::vector< ip_port_t > serv_listens = (*sit)->_listens; + for (std::vector< ip_port_t >::iterator it = serv_listens.begin(); it < serv_listens.end(); it++) { if (_listen.port != (*it).port) diff --git a/srcs/tools.cpp b/srcs/tools.cpp index 43eddd0..cdeaa0b 100644 --- a/srcs/tools.cpp +++ b/srcs/tools.cpp @@ -33,10 +33,10 @@ std::vector< string > split(string str, string delim) { return tokens; } -listen_t get_listen_t(string listen) { - listen_t ret; - size_t sep_pos = listen.rfind(':'); - string tmp = listen.substr(0, sep_pos); +ip_port_t get_ip_port_t(string listen) { + ip_port_t ret; + size_t sep_pos = listen.rfind(':'); + string tmp = listen.substr(0, sep_pos); ret.ip = isInt(tmp) ? "0.0.0.0" : (tmp == "localhost" ? "127.0.0.1" : tmp); tmp = listen.substr(sep_pos + 1, listen.length() - sep_pos - 1).c_str(); @@ -44,8 +44,8 @@ listen_t get_listen_t(string listen) { return ret; } -listen_t get_listen_t(string ip, int port) { - listen_t ret; +ip_port_t get_ip_port_t(string ip, int port) { + ip_port_t ret; ret.ip = ip; ret.port = port; return ret;