Browse Source

handle custom error pages, redirection and keepalive parameters

master
nicolas-arnaud 2 years ago
parent
commit
566505d8b0
  1. 2
      Makefile
  2. 7
      README.md
  3. 13
      default.json
  4. 6
      includes/Client.hpp
  5. 34
      includes/Route.hpp
  6. 5
      includes/webserv.hpp
  7. 4
      public/html/error403.html
  8. 4
      public/html/error404.html
  9. 4
      public/html/error405.html
  10. 4
      public/html/error414.html
  11. 4
      public/html/error429.html
  12. 4
      public/html/error503.html
  13. 4
      public/html/error505.html
  14. 4
      srcs/load/Env.cpp
  15. 168
      srcs/load/Route.cpp
  16. 6
      srcs/load/Server.cpp
  17. 490
      srcs/sock/Client.cpp
  18. 244
      srcs/sock/Master.cpp
  19. 9
      srcs/tools.cpp

2
Makefile

@ -14,7 +14,7 @@ $(NAME): $(OBJS)
$(CXX) $(OBJS) -o $(NAME) $(CXX) $(OBJS) -o $(NAME)
debug: debug:
$(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g $(SRCS) -o $(NAME) -D DEBUG=1 SILENT=0 $(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g $(SRCS) -o $(NAME) -D DEBUG=1 -D SILENT=0
verbose: verbose:
$(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g $(SRCS) -o $(NAME) -D SILENT=0 $(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g $(SRCS) -o $(NAME) -D SILENT=0

7
README.md

@ -4,7 +4,6 @@
## ToDo/ToFix: ## ToDo/ToFix:
- [] compare host and server_name for route choice
- [] organize answer structure - [] organize answer structure
- [] default html pages (404, 503 ) ?? - [] default html pages (404, 503 ) ??
- [] limit client body - [] limit client body
@ -13,13 +12,13 @@
- [] hard test segmented request - [] hard test segmented request
- [] verify upload of files - [] verify upload of files
- [] what to do with content ? - [] what to do with content ?
- [] CGI behavior
- [] based on files extensions
- []
- [] STRESS TEST!! Server must stay available!! - [] STRESS TEST!! Server must stay available!!
- [] Server shout never indefinitly wait. - [] Server shout never indefinitly wait.
- [] PUT non-text file may finish because of eof char. - [] PUT non-text file may finish because of eof char.
## Notes:
- too low keep_alive amount badly close sockets and produce infinit client load.
## Behavior: ## Behavior:
### Configuration Parsing: ### Configuration Parsing:

13
default.json

@ -6,13 +6,18 @@
"server_name": "localhost", "server_name": "localhost",
"listens": ["localhost:8080"], "listens": ["localhost:8080"],
"root": "public/", "root": "public/",
"return": "301 https://$host$uri" "return": [301, "http://webserv.doc/"]
}, },
{ {
"server_name": "webserv.doc", "server_name": "webserv.doc",
"listens": ["localhost"], "listens": ["localhost"],
"root": "html/", "root": "html/",
"indexs": ["index.html"] "indexs": ["index.html"],
"keepalive_requests": 20,
"error_pages": {
"/error3xx.html": [301, 302, 307, 308],
"/error4xx.html": [400, 403, 404, 405, 408, 413, 429]
}
}, },
{ {
"server_name": "localhost", "server_name": "localhost",
@ -24,11 +29,15 @@
"listens": ["192.168.62.61:8080", "localhost", "555"], "listens": ["192.168.62.61:8080", "localhost", "555"],
"root": "public/html/", "root": "public/html/",
"indexs": ["basique.html"], "indexs": ["basique.html"],
"error_pages": {
"/error404.html": [404]
},
"cgi": { "cgi": {
".php": "/usr/bin/php-cgi", ".php": "/usr/bin/php-cgi",
".py": "/usr/bin/python" ".py": "/usr/bin/python"
}, },
"client_max_body_size": 10000, "client_max_body_size": 10000,
"keepalive_time": 60,
"locations": { "locations": {
"/docs/": { "/docs/": {
"root": "public/resources/", "root": "public/resources/",

6
includes/Client.hpp

@ -10,13 +10,17 @@ class Client {
Env *_env; Env *_env;
Route *_route; Route *_route;
string _header, _body; string _header, _body;
std::map<string, vec_string> _headers;
string _method, _uri, _query, _host; string _method, _uri, _query, _host;
int _len; int _len;
bool _last_chunk; bool _last_chunk;
std::map<string, vec_string> _headers; bool _keepalive;
int _death_time;
time_t _requests_done;
bool _finish; bool _finish;
void init(void); void init(void);
void debug(bool head);
bool getBody(string paquet); bool getBody(string paquet);
bool parseHeader(Env *env); bool parseHeader(Env *env);
string header_pick(string key, size_t id); string header_pick(string key, size_t id);

34
includes/Route.hpp

@ -3,21 +3,27 @@
class Route { class Route {
protected: protected:
Server *_server; Server *_server;
string _location, _root, _ret; string _location, _root;
bool _autoindex; bool _autoindex;
public: public:
vec_string _indexs, _allowed_methods; vec_string _indexs, _allowed_methods;
std::map<string, string> _cgi; std::map<string, string> _cgi;
int _client_max_body_size; int _client_max_body_size;
int _timeout;
int _max_requests;
int _ret_code;
string _ret_uri;
std::map<int, string> _err_page;
Route(Server *server, string location, JSONNode *datas); Route(Server *server, string location, JSONNode *datas);
~Route(void); ~Route(void);
string getLocation(void); string getLocation(void);
string getRoot(void); string get_custom_err(int error_code);
string getReturn(void); string getRoot(void);
Server *getServer(void); string getReturn(void);
string getIndex(string uri, string path); Server *getServer(void);
string correctUri(string uri); string getIndex(string uri, string path);
string correctUri(string uri);
}; };

5
includes/webserv.hpp

@ -3,10 +3,10 @@
#define DEBUG 0 #define DEBUG 0
#endif #endif
#ifndef SILENT #ifndef SILENT
#define SILENT 1 #define SILENT 1
#endif #endif
#ifndef MAX_CLIENTS #ifndef MAX_CLIENTS
#define MAX_CLIENTS 5000 #define MAX_CLIENTS 5000
#endif #endif
#include <arpa/inet.h> #include <arpa/inet.h>
@ -66,6 +66,7 @@ ip_port_t get_ip_port_t(string listen);
ip_port_t get_ip_port_t(string ip, int port); ip_port_t get_ip_port_t(string ip, int port);
string getMime(string path); string getMime(string path);
string read_file(string path); string read_file(string path);
string file_answer(string path);
// debug // debug
void debug_block(string name, string content); void debug_block(string name, string content);

4
public/html/error403.html

@ -7,10 +7,10 @@
<body> <body>
<h1>403 ERROR</h1> <h1>403 ERROR</h1>
<p>forbidden access</p> <p>forbidden access</p>
<img src="./403error.jpeg" style="width: 25%; height: 25%;"> <img src="/403error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error404.html

@ -7,10 +7,10 @@
<body> <body>
<h1>404 ERROR</h1> <h1>404 ERROR</h1>
<p>there is no page here you monkey, go away</p> <p>there is no page here you monkey, go away</p>
<img src="./404error.jpeg" style="width: 25%; height: 25%;"> <img src="/404error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error405.html

@ -7,10 +7,10 @@
<body> <body>
<h1>405 ERROR</h1> <h1>405 ERROR</h1>
<p>method not allowed</p> <p>method not allowed</p>
<img src="./503error.jpeg" style="width: 25%; height: 25%;"> <img src="/503error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error414.html

@ -7,10 +7,10 @@
<body> <body>
<h1>414 ERROR</h1> <h1>414 ERROR</h1>
<p>url too long</p> <p>url too long</p>
<img src="./414error.jpeg" style="width: 25%; height: 25%;"> <img src="/414error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error429.html

@ -7,10 +7,10 @@
<body> <body>
<h1>429 ERROR</h1> <h1>429 ERROR</h1>
<p>too many requests</p> <p>too many requests</p>
<img src="./429error.jpeg" style="width: 25%; height: 25%;"> <img src="/429error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error503.html

@ -7,10 +7,10 @@
<body> <body>
<h1>503 SERVICE UNAVAILABLE</h1> <h1>503 SERVICE UNAVAILABLE</h1>
<p>sorry for this desagrement, our best engineers are working to better your experience on our website</p> <p>sorry for this desagrement, our best engineers are working to better your experience on our website</p>
<img src="./503error.jpeg" style="width: 25%; height: 25%;"> <img src="/503error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
public/html/error505.html

@ -7,10 +7,10 @@
<body> <body>
<h1>505 ERROR</h1> <h1>505 ERROR</h1>
<p>http version not supported</p> <p>http version not supported</p>
<img src="./404error.jpeg" style="width: 25%; height: 25%;"> <img src="/404error.jpeg" style="width: 25%; height: 25%;">
<ul> <ul>
<li><a href="/docs/">Documents</a></li> <li><a href="/docs/">Documents</a></li>
<li><a href="/img/">Images</a></li> <li><a href="/img/">Images</a></li>
</ul> </ul>
</body> </body>
</html> </html>

4
srcs/load/Env.cpp

@ -54,11 +54,11 @@ Env::~Env() {
* - for each master socket, call his own post_poll method to check and handle the sockets flaged. * - for each master socket, call his own post_poll method to check and handle the sockets flaged.
*/ */
void Env::cycle(void) { void Env::cycle(void) {
if (!SILENT) cout << "|===||===| Waiting some HTTP request... |===||===|\n"; //if (!SILENT) cout << "|===||===| Waiting some HTTP request... |===||===|\n";
int pollResult = poll(Master::_pollfds, Master::_poll_id_amount + 1, 5000); int pollResult = poll(Master::_pollfds, Master::_poll_id_amount + 1, 5000);
if ((pollResult < 0) && (errno != EINTR)) std::cerr << "Select: " << strerror(errno) << "\n"; if ((pollResult < 0) && (errno != EINTR)) std::cerr << "Select: " << strerror(errno) << "\n";
if (pollResult > 0) { if (pollResult > 0) {
if (!SILENT) cout << "==> Handle requests and answers:\n"; //if (!SILENT) cout << "==> Handle requests and answers:\n";
for (std::vector<Master *>::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) try { for (std::vector<Master *>::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) try {
(*it)->check_socket(); (*it)->check_socket();
(*it)->check_childs(this); (*it)->check_childs(this);

168
srcs/load/Route.cpp

@ -10,34 +10,61 @@
/** /**
* @brief Constructor * @brief Constructor
* *
* A route is an object which define how to handle a request. Each Server is Route inherited and each location block * A route is an object which define how to handle a request. Each Server is
* lead to a new Route object. * Route inherited and each location block lead to a new Route object.
* *
* @param server The Server parent of the route. NULL if the object is the server. * @param server The Server parent of the route. NULL if the object is the
* server.
* @param location The uri associatied to the route. * @param location The uri associatied to the route.
* @param datas The JSONNode giving configuration. * @param datas The JSONNode giving configuration.
*/ */
Route::Route(Server *server, string location, JSONNode *datas) : _server(server), _location(location) { Route::Route(Server *server, string location, JSONNode *datas)
JSONObject object = datas->obj(); : _server(server), _location(location) {
JSONNode *tmp; JSONObject object = datas->obj();
_autoindex = false; JSONNode *tmp;
_client_max_body_size = -1; _autoindex = false;
if ((tmp = object["root"])) _root = tmp->str(); _client_max_body_size = -1;
if ((tmp = object["return"])) _ret = tmp->str(); if ((tmp = object["root"]))
if ((tmp = object["autoindex"])) _autoindex = tmp->boo(); _root = tmp->str();
if ((tmp = object["indexs"])) { if ((tmp = object["return"])) {
JSONList indexs = tmp->lst(); _ret_code = tmp->lst()[0]->nbr();
for (JSONList::iterator it = indexs.begin(); it < indexs.end(); it++) _indexs.push_back((*it)->str()); _ret_uri = tmp->lst()[1]->str();
} }
if ((tmp = object["allowed_methods"])) { if ((tmp = object["autoindex"]))
JSONList headers = tmp->lst(); _autoindex = tmp->boo();
for (JSONList::iterator it = headers.begin(); it < headers.end(); it++) _allowed_methods.push_back((*it)->str()); if ((tmp = object["keepalive_time"]))
} _timeout = tmp->nbr();
if ((tmp = object["cgi"])) { else
JSONObject cgis = tmp->obj(); _timeout = 0;
for (JSONObject::iterator it = cgis.begin(); it != cgis.end(); it++) _cgi[(*it).first] = (*it).second->str(); if ((tmp = object["keepalive_requests"]))
} _max_requests = tmp->nbr();
if ((tmp = object["client_max_body_size"])) _client_max_body_size = tmp->nbr(); else
_max_requests = 0;
if ((tmp = object["error_pages"])) {
JSONObject pages = tmp->obj();
for (JSONObject::iterator it = pages.begin(); it != pages.end(); it++) {
JSONList err = (*it).second->lst();
for (JSONList::iterator it2 = err.begin(); it2 != err.end(); it2++)
_err_page[(*it2)->nbr()] = (*it).first;
}
}
if ((tmp = object["indexs"])) {
JSONList indexs = tmp->lst();
for (JSONList::iterator it = indexs.begin(); it < indexs.end(); it++)
_indexs.push_back((*it)->str());
}
if ((tmp = object["allowed_methods"])) {
JSONList headers = tmp->lst();
for (JSONList::iterator it = headers.begin(); it < headers.end(); it++)
_allowed_methods.push_back((*it)->str());
}
if ((tmp = object["cgi"])) {
JSONObject cgis = tmp->obj();
for (JSONObject::iterator it = cgis.begin(); it != cgis.end(); it++)
_cgi[(*it).first] = (*it).second->str();
}
if ((tmp = object["client_max_body_size"]))
_client_max_body_size = tmp->nbr();
} }
/// @brief Destructor /// @brief Destructor
@ -46,7 +73,6 @@ Route::~Route(void) {}
// Getters // Getters
string Route::getLocation(void) { return _location; } string Route::getLocation(void) { return _location; }
string Route::getRoot(void) { return _root; } string Route::getRoot(void) { return _root; }
string Route::getReturn(void) { return _ret; }
/** /**
* @brief Search for an index while generating autoindex * @brief Search for an index while generating autoindex
@ -54,35 +80,45 @@ string Route::getReturn(void) { return _ret; }
* @param uri The uri requested by client. * @param uri The uri requested by client.
* @param path The correct path associated with uri. * @param path The correct path associated with uri.
* *
* @return The index content to give to client or an empty string if there is nothing for him. * @return The index content to give to client or an empty string if there is
* nothing for him.
*/ */
string Route::getIndex(string uri, string path) { string Route::getIndex(string uri, string path) {
std::stringstream body, ret; std::stringstream body, ret;
DIR *dir; DIR *dir;
struct dirent *entry; struct dirent *entry;
struct stat info; struct stat info;
vec_string::iterator it; vec_string::iterator it;
if ((dir = opendir(path.c_str()))) { if ((dir = opendir(path.c_str()))) {
if (DEBUG) cout << "get index(): path=" << path << "\n"; if (DEBUG)
body << "<h3 style=\"text-align: center;\">" << path << " files :</h3>\n<ul>\n"; cout << "get index(): path=" << path << "\n";
while ((entry = readdir(dir)) != NULL) { body << "<h3 style=\"text-align: center;\">" << path
if (entry->d_name[0] == '.') continue; << " files :</h3>\n<ul>\n";
for (it = _indexs.begin(); it < _indexs.end(); it++) { while ((entry = readdir(dir)) != NULL) {
if (entry->d_name == *it) return (read_file(path + "/" + *it)); if (entry->d_name[0] == '.')
} continue;
body << "<li><a href=\"" << uri + "/" + entry->d_name << "\">" << entry->d_name << "</a></li>\n"; for (it = _indexs.begin(); it < _indexs.end(); it++) {
if (stat(path.c_str(), &info) != 0) std::cerr << "stat() error on " << path << ": " << strerror(errno) << "\n"; if (entry->d_name == *it)
} return (file_answer(path + "/" + *it));
body << "</ul>"; }
closedir(dir); body << "<li><a href=\"" << uri + "/" + entry->d_name << "\">"
} << entry->d_name << "</a></li>\n";
if (!dir || !_autoindex) return ""; if (stat(path.c_str(), &info) != 0)
if (DEBUG) cout << "Getting autoindex\n"; std::cerr << "stat() error on " << path << ": " << strerror(errno)
ret << "Content-type: text/html \r\n"; << "\n";
ret << "Content-length: " << body.str().length() << "\r\n"; }
ret << "\r\n" << body.str(); body << "</ul>";
return ret.str(); closedir(dir);
}
if (!dir || !_autoindex)
return "";
if (DEBUG)
cout << "Getting autoindex\n";
ret << "Content-type: text/html \r\n";
ret << "Content-length: " << body.str().length() << "\r\n";
ret << "\r\n" << body.str();
return ret.str();
} }
/** /**
@ -94,18 +130,22 @@ string Route::getIndex(string uri, string path) {
* @deprecated Not used by nginx until config use rewrite keyword. * @deprecated Not used by nginx until config use rewrite keyword.
*/ */
string Route::correctUri(string uri) { string Route::correctUri(string uri) {
std::stringstream ret; std::stringstream ret;
vec_string::iterator loc_word, uri_word; vec_string::iterator loc_word, uri_word;
vec_string loc_words = split(_location, "/"); vec_string loc_words = split(_location, "/");
vec_string uri_words = split(uri, "/"); vec_string uri_words = split(uri, "/");
uri_word = uri_words.begin(); uri_word = uri_words.begin();
for (loc_word = loc_words.begin(); loc_word < loc_words.end(); loc_word++) { for (loc_word = loc_words.begin(); loc_word < loc_words.end(); loc_word++) {
while (uri_word < uri_words.end() && *uri_word == "") uri_word++; while (uri_word < uri_words.end() && *uri_word == "")
while (loc_word < loc_words.end() && *loc_word == "") loc_word++; uri_word++;
if (loc_word != loc_words.end()) uri_word++; while (loc_word < loc_words.end() && *loc_word == "")
} loc_word++;
ret << "./" << _root; if (loc_word != loc_words.end())
while (uri_word < uri_words.end()) ret << "/" << *(uri_word++); uri_word++;
return ret.str(); }
ret << "./" << _root;
while (uri_word < uri_words.end())
ret << "/" << *(uri_word++);
return ret.str();
} }

6
srcs/load/Server.cpp

@ -87,9 +87,9 @@ Route *Server::choose_route(string uri) {
loc_words = split((*loc_it).first, "/"); loc_words = split((*loc_it).first, "/");
vec_string::iterator loc_word = loc_words.begin(); vec_string::iterator loc_word = loc_words.begin();
vec_string::iterator uri_word = uri_words.begin(); vec_string::iterator uri_word = uri_words.begin();
while (*loc_word == *uri_word) { while (loc_word != loc_words.end() && uri_word != uri_words.end() && *loc_word == *uri_word) {
while (*++loc_word == "") {} while (++loc_word != loc_words.end() && *loc_word == "") {}
while (*++uri_word == "") {} while (++uri_word != uri_words.end() && *uri_word == "") {}
} }
if (loc_word == loc_words.end()) return ((*loc_it).second); if (loc_word == loc_words.end()) return ((*loc_it).second);
} }

490
srcs/sock/Client.cpp

@ -1,200 +1,312 @@
/** /**
* @file Client.cpp * @file Client.cpp
* @brief The client sockets class which keep keep clients information and handle answer to them. * @brief The client sockets class which keep keep clients information and
* handle answer to them.
* @author Narnaud * @author Narnaud
* @version 0.1 * @version 0.1
* @date 2023-01-12 * @date 2023-01-12
*/ */
#include "webserv.hpp" #include "webserv.hpp"
#include <iomanip>
#include <iostream>
inline string get_extension(string str) { inline string get_extension(string str) {
size_t pos = str.rfind('.'); size_t pos = str.rfind('.');
if (pos != string::npos) return str.substr(pos); if (pos != string::npos)
else return ""; return str.substr(pos);
else
return "";
} }
Client::Client(int fd, ip_port_t ip_port, Master *parent) : _fd(fd), _ip_port(ip_port), _parent(parent) { Client::Client(int fd, ip_port_t ip_port, Master *parent)
init(); : _fd(fd), _ip_port(ip_port), _parent(parent) {
if (!SILENT) _requests_done = 0;
cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip << ", port : " << _ip_port.port _death_time = 0;
<< "\n"; _finish = false;
_route = NULL;
_server = NULL;
init();
if (!SILENT)
cout << "New connection, socket fd is " << fd << ", ip is : " << _ip_port.ip
<< ", port : " << _ip_port.port << "\n";
} }
Client::~Client(void) { Client::~Client(void) {
close(_fd); close(_fd);
Master::_pollfds[_poll_id].fd = 0; Master::_pollfds[_poll_id].fd = 0;
Master::_pollfds[_poll_id].events = 0; Master::_pollfds[_poll_id].events = 0;
Master::_pollfds[_poll_id].revents = 0; Master::_pollfds[_poll_id].revents = 0;
Master::_poll_id_amount--; Master::_poll_id_amount--;
_headers.clear(); _headers.clear();
if (!SILENT) cout << "Host disconnected, ip " << _ip_port.ip << ", port " << _ip_port.port << "\n"; if (!SILENT)
cout << "Host disconnected, ip " << _ip_port.ip << ", port "
<< _ip_port.port << "\n";
} }
void Client::init(void) { void Client::init(void) {
_finish = false; _requests_done++;
_server = NULL; if (_route && _route->_max_requests > 0) {
_route = NULL; if (_requests_done > _route->_max_requests)
_method = _uri = _host = _header = _body = ""; _finish = true;
_len = 0; } else if (_server && _server->_max_requests > 0) {
_last_chunk = false; if (_requests_done > _server->_max_requests)
_headers.clear(); _finish = true;
}
_method = _uri = _host = _header = _body = "";
_len = 0;
_last_chunk = false;
_headers.clear();
}
template <typename T> void tab(T t, const int &width) {
std::cout << std::left << std::setw(width) << std::setfill(' ') << t;
}
void Client::debug(bool head) {
if (head) {
std::cout << "Client " << _poll_id
<< " debug ===================================\n";
tab("Fd", 4);
tab("Ip", 12);
tab("Port", 6);
tab("Servername", 12);
tab("Route", 12);
tab("Method", 6);
tab("URI", 20);
tab("Query", 12);
tab("Host", 12);
tab("Len", 6);
tab("Keep", 4);
tab("Death", 6);
tab("Request", 6);
tab("Finish", 6);
std::cout << "\n";
}
tab(_fd, 4);
tab(_ip_port.ip, 12);
tab(_ip_port.port, 6);
tab(_server->getName(), 12);
tab(_route->getLocation(), 12);
tab(_method, 6);
tab(_uri, 20);
tab(_query, 12);
tab(_host, 12);
tab(_len, 6);
tab(_keepalive, 4);
tab(_death_time, 6);
tab(_requests_done, 6);
tab(_finish, 6);
std::cout << "\n";
} }
bool Client::getRequest(Env *env, string paquet) { bool Client::getRequest(Env *env, string paquet) {
if (paquet.length() < 1) send_error(403); if (DEBUG)
if (DEBUG) debug_block("Paquet: ", paquet); debug_block("Paquet: ", paquet);
if (header_pick("Method:", 0) != "") return getBody(paquet); if (header_pick("Method:", 0) != "")
vec_string lines = split(paquet, "\r\n"); return getBody(paquet);
for (vec_string::iterator it = lines.begin(); it < lines.end(); it++) { vec_string lines = split(paquet, "\r\n");
size_t pos = paquet.find("\r\n"); for (vec_string::iterator it = lines.begin(); it < lines.end(); it++) {
if (pos != string::npos) paquet.erase(0, pos + 2); size_t pos = paquet.find("\r\n");
else paquet.clear(); if (pos != string::npos)
_header += *it + (it + 1 != lines.end() ? "\r\n" : ""); paquet.erase(0, pos + 2);
if (_header.find("\r\n\r\n") != string::npos) else
return !parseHeader(env) ? false : (_len != 0 ? getBody(paquet) : true); paquet.clear();
} _header += *it + (it + 1 != lines.end() ? "\r\n" : "");
return false; if (_header.find("\r\n\r\n") != string::npos)
return !parseHeader(env) ? false : (_len != 0 ? getBody(paquet) : true);
}
return false;
} }
bool Client::getBody(string paquet) { bool Client::getBody(string paquet) {
vec_string lines = split(paquet, "\r\n"); vec_string lines = split(paquet, "\r\n");
vec_string::iterator it; vec_string::iterator it;
for (it = lines.begin(); it < lines.end(); it++) { for (it = lines.begin(); it < lines.end(); it++) {
if (DEBUG) cout << "Remaining length: " << _len << "\n"; if (DEBUG)
if ((*it).length() && _len <= 0 && header_pick("Transfer-Encoding:", 0) == "chunked") { cout << "Remaining length: " << _len << "\n";
_len = std::strtol((*it).c_str(), 0, 16) + 2; if ((*it).length() && _len <= 0 &&
_last_chunk = _len == 2 ? true : false; header_pick("Transfer-Encoding:", 0) == "chunked") {
} else if (_len > 0 || it != lines.begin()) { _len = std::strtol((*it).c_str(), 0, 16) + 2;
_body += *it + "\r\n"; _last_chunk = _len == 2 ? true : false;
_len -= ((*it).length() + 2); } else if (_len > 0 || it != lines.begin()) {
} _body += *it + "\r\n";
} _len -= ((*it).length() + 2);
// if (_body.size()) }
_body.resize(_body.length() - 2); }
_len += 2; // if (_body.size())
return (_last_chunk && _len == 0) ? true : false; _body.resize(_body.length() - 2);
_len += 2;
return (_last_chunk && _len == 0) ? true : false;
} }
bool Client::parseHeader(Env *env) { bool Client::parseHeader(Env *env) {
vec_string lines, method, line; vec_string lines, method, line;
if (DEBUG)
if (DEBUG) cout << "Parsing header...\n"; cout << "Parsing header...\n";
lines = split(_header, "\r\n"); lines = split(_header, "\r\n");
method = split(lines.at(0), " "); method = split(lines.at(0), " ");
_headers["Method:"] = method; _headers["Method:"] = method;
if (lines.size() > 0) { if (lines.size() > 0) {
for (vec_string::iterator it = lines.begin() + 1; it < lines.end(); it++) { for (vec_string::iterator it = lines.begin() + 1; it < lines.end(); it++) {
line = split(*it, " "); line = split(*it, " ");
_headers[line.at(0)] = vec_string(line.begin() + 1, line.end()); _headers[line.at(0)] = vec_string(line.begin() + 1, line.end());
} }
} }
_method = header_pick("Method:", 0); _method = header_pick("Method:", 0);
if ((_method == "POST" || _method == "PUT") && header_pick("Content-Length:", 0) == "" && if ((_method == "POST" || _method == "PUT") &&
header_pick("Transfer-Encoding:", 0) != "chunked") header_pick("Content-Length:", 0) == "" &&
return (send_error(400), false); header_pick("Transfer-Encoding:", 0) != "chunked")
vec_string uri_split = split(header_pick("Method:", 1), "?"); return (send_error(400), false);
_uri = uri_split.at(0); vec_string uri_split = split(header_pick("Method:", 1), "?");
if (uri_split.size() > 1) _query = uri_split.at(1); _uri = uri_split.at(0);
_host = header_pick("Host:", 0); if (uri_split.size() > 1)
_env = env; _query = uri_split.at(1);
_server = _parent->choose_server(env, _host); _host = header_pick("Host:", 0);
_route = _server->choose_route(_uri); _keepalive = header_pick("Connection:", 0) == "keep-alive";
if (DEBUG) debug_header(); _env = env;
string len = header_pick("Content-Length:", 0).c_str(); struct timeval t;
if (len != "") { gettimeofday(&t, NULL);
_len = std::atoi(len.c_str()); _server = _parent->choose_server(env, _host);
_last_chunk = true; if (_server->_timeout)
int max_len = _route->_client_max_body_size > 0 ? _route->_client_max_body_size _death_time = _server->_timeout + t.tv_sec;
: _server->_client_max_body_size > 0 ? _server->_client_max_body_size _route = _server->choose_route(_uri);
: INT_MAX; if (_route->_timeout)
if (_len > max_len) return (send_error(413), false); _death_time = _route->_timeout + t.tv_sec;
} else _len = 0; if (DEBUG)
return true; debug_header();
} string len = header_pick("Content-Length:", 0).c_str();
if (len != "") {
string Client::header_pick(string key, size_t id) { return _headers[key].size() <= id ? "" : _headers[key].at(id); } _len = std::atoi(len.c_str());
_last_chunk = true;
int max_len =
_route->_client_max_body_size > 0 ? _route->_client_max_body_size
: _server->_client_max_body_size > 0 ? _server->_client_max_body_size
: INT_MAX;
if (_len > max_len)
return (send_error(413), false);
} else
_len = 0;
return true;
}
string Client::header_pick(string key, size_t id) {
return _headers[key].size() <= id ? "" : _headers[key].at(id);
}
bool Client::check_method(void) { bool Client::check_method(void) {
vec_string allowed; vec_string allowed;
if ((_route && (allowed = _route->_allowed_methods).size() > 0) || if ((_route && (allowed = _route->_allowed_methods).size() > 0) ||
(_server && (allowed = _server->_allowed_methods).size() > 0) || ((allowed = _env->_allowed_methods).size() > 0)) (_server && (allowed = _server->_allowed_methods).size() > 0) ||
return std::find(allowed.begin(), allowed.end(), _method) < allowed.end() ? true : false; ((allowed = _env->_allowed_methods).size() > 0))
else if (_method == "GET" || _method == "POST" || _method == "DELETE" || _method == "PUT") return (true); return std::find(allowed.begin(), allowed.end(), _method) < allowed.end()
return (false); ? true
: false;
else if (_method == "GET" || _method == "POST" || _method == "DELETE" ||
_method == "PUT")
return (true);
return (false);
} }
void Client::handleRequest(void) { void Client::handleRequest(void) {
if (DEBUG) { if (DEBUG) {
debug_block("Header: ", _header); debug_block("Header: ", _header);
debug_block("Body: ", _body); debug_block("Body: ", _body);
} }
string ret; if (_route->_ret_uri != "")
string req_path = _route->getRoot() + _uri; send_error(_route->_ret_code, _route->_ret_uri);
if (!SILENT) std::cout << "||-> Request for " << req_path << " received <-||\n"; else if (_server->_ret_uri != "")
string cgi_path = _route->_cgi.size() ? _route->_cgi[get_extension(req_path)] send_error(_server->_ret_code, _server->_ret_uri);
: _server->_cgi.size() ? _server->_cgi[get_extension(req_path)] else {
: ""; string ret;
if (DEBUG) cout << "Path: " << req_path << "\n"; struct timeval t;
if (!check_method()) send_error(405); gettimeofday(&t, NULL);
else { if (_death_time && t.tv_sec > _death_time) {
if ((ret = _route->getIndex(_uri, req_path)) == "") ret = read_file(req_path); send_error(408);
if (ret == "404") { _finish = true;
if (_method == "POST" || _method == "PUT") create_file(req_path); return;
else send_error(404); }
} else if (ret == "403") send_error(403); string req_path = _route->getRoot() + _uri;
else if (_method == "DELETE") std::remove(req_path.c_str()); if (!SILENT)
else if (cgi_path != "") cgi(cgi_path, req_path); std::cout << "||-> Request for " << req_path << " received <-||\n";
else send_answer("HTTP/1.1 200 OK\r\n" + ret); string cgi_path =
} _route->_cgi.size() ? _route->_cgi[get_extension(req_path)]
: _server->_cgi.size() ? _server->_cgi[get_extension(req_path)]
: "";
if (DEBUG)
cout << "Path: " << req_path << "\n";
if (!check_method())
send_error(405);
else {
if ((ret = _route->getIndex(_uri, req_path)) == "")
ret = file_answer(req_path);
if (ret == "404") {
if (_method == "POST" || _method == "PUT")
create_file(req_path);
else
send_error(404);
} else if (ret == "403")
send_error(403);
else if (_method == "DELETE")
std::remove(req_path.c_str());
else if (cgi_path != "")
cgi(cgi_path, req_path);
else
send_answer("HTTP/1.1 200 OK\r\n" + ret);
}
}
} }
void Client::create_file(string path) { void Client::create_file(string path) {
std::ofstream file(path.c_str()); std::ofstream file(path.c_str());
if (!file.good()) send_error(403); if (!file.good())
else { send_error(403);
file << _body; else {
file.close(); file << _body;
send_answer("HTTP/1.1 201 Accepted\r\n\r\n"); file.close();
} send_answer("HTTP/1.1 201 Accepted\r\n\r\n");
}
} }
/** /**
* @brief Launch cgi binary to parse the file requested by the client. * @brief Launch cgi binary to parse the file requested by the client.
* *
* @param cgi_path The cgi binary location specified in configuration file according to the file requested. * @param cgi_path The cgi binary location specified in configuration file
* according to the file requested.
* @param path The path to the file requested. * @param path The path to the file requested.
*/ */
void Client::cgi(string cgi_path, string path) { void Client::cgi(string cgi_path, string path) {
int pipe_in[2]; int pipe_in[2];
send(_fd, "HTTP/1.1 200 OK\r\n", 17, MSG_NOSIGNAL); send(_fd, "HTTP/1.1 200 OK\r\n", 17, MSG_NOSIGNAL);
if (!std::ifstream(cgi_path.c_str()).good()) return send_error(404); if (!std::ifstream(cgi_path.c_str()).good())
if (DEBUG) std::cout << "Send cgi\n"; return send_error(404);
if (fork() == 0) { if (DEBUG)
const char **args = new const char *[cgi_path.length() + 1]; std::cout << "Send cgi\n";
args[0] = cgi_path.c_str(); if (fork() == 0) {
args[1] = NULL; const char **args = new const char *[cgi_path.length() + 1];
string path_info = "PATH_INFO=" + _route->getRoot(); args[0] = cgi_path.c_str();
string query = "QUERY_STRING=" + _query; args[1] = NULL;
const char **env = new const char *[path_info.length() + query.length() + 2]; string path_info = "PATH_INFO=" + _route->getRoot();
env[0] = path_info.c_str(); string query = "QUERY_STRING=" + _query;
env[1] = query.c_str(); const char **env =
env[2] = NULL; new const char *[path_info.length() + query.length() + 2];
pipe(pipe_in); env[0] = path_info.c_str();
std::stringstream tmp; env[1] = query.c_str();
tmp << std::ifstream(path.c_str()).rdbuf(); env[2] = NULL;
string file = tmp.str(); pipe(pipe_in);
write(pipe_in[1], file.c_str(), file.size()); std::stringstream tmp;
close(pipe_in[1]); tmp << std::ifstream(path.c_str()).rdbuf();
dup2(pipe_in[0], STDIN_FILENO); string file = tmp.str();
close(pipe_in[0]); write(pipe_in[1], file.c_str(), file.size());
dup2(_fd, STDOUT_FILENO); close(pipe_in[1]);
close(_fd); dup2(pipe_in[0], STDIN_FILENO);
execve(cgi_path.c_str(), (char **)args, (char **)env); close(pipe_in[0]);
exit(1); dup2(_fd, STDOUT_FILENO);
} close(_fd);
_finish = true; execve(cgi_path.c_str(), (char **)args, (char **)env);
exit(1);
}
_finish = true;
} }
/** /**
@ -203,22 +315,50 @@ void Client::cgi(string cgi_path, string path) {
* @param error_code The HTTP response code to send. * @param error_code The HTTP response code to send.
*/ */
void Client::send_error(int error_code, string opt) { void Client::send_error(int error_code, string opt) {
switch (error_code) { string error_path, body;
case 301: error_path = _route->_err_page[error_code];
return send_answer("HTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + "\r\n\r\n"); if (error_path == "") {
case 400: error_path = _server->_err_page[error_code];
return send_answer("HTTP/1.1 400 Bad Request\r\n\r\n"); if (error_path != "")
case 403: body = file_answer(_server->correctUri(error_path));
return send_answer("HTTP/1.1 403 Forbidden\r\n\r\n"); } else
case 404: body = file_answer(_route->correctUri(error_path));
return send_answer("HTTP/1.1 404 Not Found\r\n\r\n");
case 405: switch (error_code) {
return send_answer("HTTP/1.1 405 Method Not Allowed\r\nConnection: " case 301:
"close\r\n\r\n"); return send_answer("HTTP/1.1 301 Moved Permanently\r\nLocation: " + opt +
case 413: "\r\n" + body + "\r\n");
return send_answer("HTTP/1.1 413 Payload Too " case 302:
"Large\r\nConnection: close\r\n\r\n"); return send_answer("HTTP/1.1 302 Found\r\nLocation: " + opt + "\r\n" +
} body + "\r\n");
case 307:
return send_answer("HTTP/1.1 307 Temporary Redirect\r\nLocation: " + opt +
"\r\n" + body + "\r\n");
case 308:
return send_answer("HTTP/1.1 308 Permanent Redirect\r\nLocation: " + opt +
"\r\n" + body + "\r\n");
case 400:
return send_answer("HTTP/1.1 400 Bad Request\r\n" + body + "\r\n");
case 403:
return send_answer("HTTP/1.1 403 Forbidden\r\n" + body + "\r\n");
case 404:
return send_answer("HTTP/1.1 404 Not Found\r\n" + body + "\r\n");
case 405:
return send_answer("HTTP/1.1 405 Method Not Allowed\r\nConnection: "
"close\r\n" +
body + "\r\n");
case 408:
return send_answer("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n" +
body + "\r\n");
case 413:
return send_answer(
"HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n" + body +
"\r\n");
case 429:
return send_answer(
"HTTP/1.1 429 Too Many Requests\r\nConnection: close\r\n" + body +
"\r\n");
}
} }
/** /**
@ -227,12 +367,14 @@ void Client::send_error(int error_code, string opt) {
* @param msg The HTTP message to send. * @param msg The HTTP message to send.
*/ */
void Client::send_answer(string msg) { void Client::send_answer(string msg) {
if (DEBUG) debug_block("ANSWER: ", msg); if (DEBUG)
debug_block("ANSWER: ", msg);
#ifdef __linux__ #ifdef __linux__
send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL);
#elif __APPLE__ #elif __APPLE__
write(_fd, msg.c_str(), msg.length()); write(_fd, msg.c_str(), msg.length());
#endif #endif
init(); init();
_finish = true; if (!_keepalive)
_finish = true;
} }

244
srcs/sock/Master.cpp

@ -8,74 +8,85 @@
#include "webserv.hpp" #include "webserv.hpp"
/** /**
* @brief Destructor * @brief Destructor Close master socket descriptor.
* Close master socket descriptor.
*/ */
Master::~Master(void) { Master::~Master(void) {
close(_fd); close(_fd);
if (DEBUG) cout << "Destroyed master socket\n"; if (DEBUG)
cout << "Destroyed master socket\n";
} }
/** /**
* @brief Constructor * @brief Constructor
* Try to create a socket listening to ip and port defined by input. * Try to create a socket listening to ip and port defined by input.
* If no exception if thrown, the creation success and the socket is then ready to listen for new clients. * If no exception if thrown, the creation success and the socket is then ready
* to listen for new clients.
* *
* @param list An ip_port_t struct which contain the ip and the port the master listen. * @param list An ip_port_t struct which contain the ip and the port the master
* listen.
*/ */
Master::Master(ip_port_t list) : _listen(list) { Master::Master(ip_port_t list) : _listen(list) {
int x = 1, port = _listen.port; int x = 1, port = _listen.port;
string ip = _listen.ip; string ip = _listen.ip;
_fd = socket(AF_INET, SOCK_STREAM, 0); _fd = socket(AF_INET, SOCK_STREAM, 0);
if (_fd == 0) throw std::runtime_error("socket() error" + string(strerror(errno))); if (_fd == 0)
if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&x, sizeof(x)) < 0 && close(_fd) <= 0) throw std::runtime_error("socket() error" + string(strerror(errno)));
throw std::runtime_error("setsockopt() error: " + string(strerror(errno))); if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&x, sizeof(x)) < 0 &&
_address.sin_family = AF_INET; close(_fd) <= 0)
_address.sin_addr.s_addr = inet_addr(ip.c_str()); throw std::runtime_error("setsockopt() error: " + string(strerror(errno)));
_address.sin_port = htons(port); _address.sin_family = AF_INET;
if (bind(_fd, (struct sockaddr *)&_address, sizeof(_address)) && close(_fd) <= 0) _address.sin_addr.s_addr = inet_addr(ip.c_str());
throw std::runtime_error("bind() error: " + string(strerror(errno))); _address.sin_port = htons(port);
if (listen(_fd, 3) < 0 && close(_fd) <= 0) throw std::runtime_error("listen() error: " + string(strerror(errno))); if (bind(_fd, (struct sockaddr *)&_address, sizeof(_address)) &&
close(_fd) <= 0)
throw std::runtime_error("bind() error: " + string(strerror(errno)));
if (listen(_fd, 3) < 0 && close(_fd) <= 0)
throw std::runtime_error("listen() error: " + string(strerror(errno)));
#ifdef __APPLE__ #ifdef __APPLE__
fcntl(socket, F_SETFL, O_NONBLOCK); fcntl(socket, F_SETFL, O_NONBLOCK);
#endif #endif
cout << "New master socket with fd " << _fd << " which listen " << ip << ":" << port << "\n"; cout << "New master socket with fd " << _fd << " which listen " << ip << ":"
_pollfds[_poll_id_amount].fd = _fd; << port << "\n";
_pollfds[_poll_id_amount].events = POLLIN | POLLPRI; _pollfds[_poll_id_amount].fd = _fd;
_poll_id = _poll_id_amount; _pollfds[_poll_id_amount].events = POLLIN | POLLPRI;
_poll_id_amount++; _poll_id = _poll_id_amount;
_poll_id_amount++;
} }
/** /**
* @brief Check master and his clients sockets after poll performed. * @brief Check master and his clients sockets after poll performed.
*/ */
void Master::check_socket(void) { void Master::check_socket(void) {
int addrlen = sizeof(_address); int addrlen = sizeof(_address);
if (_pollfds[_poll_id].revents & POLLIN) { /// < incomming master request if (_pollfds[_poll_id].revents & POLLIN) { /// < incomming master request
int new_socket = accept(_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); int new_socket =
if (new_socket < 0) throw std::runtime_error("accept() error:" + string(strerror(errno))); accept(_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen);
if (new_socket < 0)
throw std::runtime_error("accept() error:" + string(strerror(errno)));
#ifdef __APPLE__ #ifdef __APPLE__
fcntl(new_socket, F_SETFL, O_NONBLOCK); fcntl(new_socket, F_SETFL, O_NONBLOCK);
#endif #endif
ip_port_t cli_listen = get_ip_port_t(inet_ntoa(_address.sin_addr), ntohs(_address.sin_port)); ip_port_t cli_listen =
Client *new_cli = new Client(new_socket, cli_listen, this); get_ip_port_t(inet_ntoa(_address.sin_addr), ntohs(_address.sin_port));
if (_poll_id_amount > MAX_CLIENTS) { Client *new_cli = new Client(new_socket, cli_listen, this);
new_cli->send_error(503); if (_poll_id_amount > MAX_CLIENTS) {
delete new_cli; new_cli->send_error(503);
} else { delete new_cli;
_childs.push_back(new_cli); } else {
for (int i = _first_cli_id; i < MAX_CLIENTS; i++) { _childs.push_back(new_cli);
if (_pollfds[i].fd != 0) continue; for (int i = _first_cli_id; i < MAX_CLIENTS; i++) {
_pollfds[i].fd = new_socket; if (_pollfds[i].fd != 0)
_pollfds[i].events = POLLIN | POLLPRI; continue;
new_cli->_poll_id = i; _pollfds[i].fd = new_socket;
_poll_id_amount++; _pollfds[i].events = POLLIN | POLLPRI;
break; new_cli->_poll_id = i;
} _poll_id_amount++;
} break;
} }
}
}
} }
/** /**
@ -83,45 +94,52 @@ void Master::check_socket(void) {
* Loop along _childs * Loop along _childs
* if poll even incomming on the child socket: * if poll even incomming on the child socket:
* - give 1023 next sockets bits to Client methods to handle. * - give 1023 next sockets bits to Client methods to handle.
* - if Request is fully received, does the answer and flag the socket for outcomming event. * - if Request is fully received, does the answer and flag the socket for
* outcomming event.
* - if request isn't fully received, flag the socket for incomming event. * - if request isn't fully received, flag the socket for incomming event.
* - if read returned 0, delete client. * - if read returned 0, delete client.
* @param env The environment object. * @param env The environment object.
*/ */
void Master::check_childs(Env *env) { void Master::check_childs(Env *env) {
int child_fd; int child_fd;
for (std::vector<Client *>::iterator it = _childs.begin(); it < _childs.end(); it++) { for (std::vector<Client *>::iterator it = _childs.begin(); it < _childs.end();
child_fd = (*it)->_fd; it++) {
int i = (*it)->_poll_id; child_fd = (*it)->_fd;
if (_pollfds[i].fd > 0 && _pollfds[i].revents & POLLIN) { int i = (*it)->_poll_id;
if (_pollfds[i].fd > 0 && _pollfds[i].revents & POLLIN) {
char buffer[1024]; char buffer[1024];
int valread = read(child_fd, buffer, 1023); int valread = read(child_fd, buffer, 1023);
buffer[valread] = '\0'; buffer[valread] = '\0';
if (valread == 0) { if (valread == 0) {
delete (*it); delete (*it);
_childs.erase(it); _childs.erase(it);
} else if ((*it)->getRequest(env, buffer)) { } else if ((*it)->getRequest(env, buffer)) {
(*it)->handleRequest(); (*it)->debug(true);
_pollfds[i].events = POLLOUT; (*it)->handleRequest();
if ((*it)->_finish) { _pollfds[i].events = POLLIN | POLLPRI | POLLOUT;
delete (*it); (*it)->debug(false);
_childs.erase(it); if ((*it)->_finish) {
} delete (*it);
} else _pollfds[i].events = POLLIN | POLLPRI; _childs.erase(it);
} }
} } else
_pollfds[i].events = POLLIN | POLLPRI;
}
}
} }
/** /**
* *
* @brief Choose the server which must handle a request * @brief Choose the server which must handle a request
* Each server can lsiten multiple range_ip:port and each range_it:port can be listen by multiple servers. * Each server can lsiten multiple range_ip:port and each range_it:port can be
* So for each request, we must look at the socket which given us the client to know how the client came. If multiple * listen by multiple servers. So for each request, we must look at the socket
* servers listen the range from where the client came, ones with exact correspondance are prefered. If there are * which given us the client to know how the client came. If multiple servers
* multiples servers listening exactly the ip the client try to reach or which listen a range which contain it, the * listen the range from where the client came, ones with exact correspondance
* first one which have the same server_name as the host the client used to reach server is used, else it's the first * are prefered. If there are multiples servers listening exactly the ip the
* one of exact correspondance or first one which have the ip requested in his listen range. * client try to reach or which 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.
* *
* @param env The environment object. * @param env The environment object.
* @param host The host the client used to reached the server. * @param host The host the client used to reached the server.
@ -129,38 +147,48 @@ void Master::check_childs(Env *env) {
* @return The server object choosen to handle the request. * @return The server object choosen to handle the request.
*/ */
Server *Master::choose_server(Env *env, string host) { Server *Master::choose_server(Env *env, string host) {
std::vector<Server *> exact, inrange; std::vector<Server *> exact, inrange;
vec_string ip_listen, ip_required; vec_string ip_listen, ip_required;
ip_required = split(_listen.ip, "."); ip_required = split(_listen.ip, ".");
for (std::vector<Server *>::iterator server = env->_servers.begin(); server < env->_servers.end(); server++) { for (std::vector<Server *>::iterator server = env->_servers.begin();
std::vector<ip_port_t> serv_listens = (*server)->_listens; server < env->_servers.end(); server++) {
for (std::vector<ip_port_t>::iterator it = serv_listens.begin(); it < serv_listens.end(); it++) { std::vector<ip_port_t> serv_listens = (*server)->_listens;
if (_listen.port != (*it).port) continue; for (std::vector<ip_port_t>::iterator it = serv_listens.begin();
if (_listen.ip == (*it).ip) { it < serv_listens.end(); it++) {
exact.push_back(*server); if (_listen.port != (*it).port)
continue; continue;
} if (_listen.ip == (*it).ip) {
bool is_inrange = true; exact.push_back(*server);
ip_listen = split((*it).ip, "."); continue;
vec_string::iterator r = ip_required.end(); }
vec_string::iterator l = ip_listen.end(); bool is_inrange = true;
while (r > ip_required.begin()) { ip_listen = split((*it).ip, ".");
if (*(--l) != *(--r) && *l != "0") is_inrange = false; vec_string::iterator r = ip_required.end();
} vec_string::iterator l = ip_listen.end();
if (is_inrange) inrange.push_back(*server); while (r > ip_required.begin()) {
} if (*(--l) != *(--r) && *l != "0")
} is_inrange = false;
if (DEBUG) std::cout << "req: " << _listen.ip << ":" << _listen.port << "\n"; }
if (exact.size() == 0) { if (is_inrange)
for (std::vector<Server *>::iterator server = inrange.begin(); server < inrange.end(); server++) { inrange.push_back(*server);
if (host == (*server)->getName()) return *server; }
} }
return inrange.front(); if (DEBUG)
} else { std::cout << "req: " << _listen.ip << ":" << _listen.port << "\n";
for (std::vector<Server *>::iterator server = exact.begin(); server < exact.end(); server++) { if (exact.size() == 0) {
if (host == (*server)->getName()) return *server; for (std::vector<Server *>::iterator server = inrange.begin();
} server < inrange.end(); server++) {
return exact.front(); if (host == (*server)->getName())
} return *server;
}
return inrange.front();
} else {
for (std::vector<Server *>::iterator server = exact.begin();
server < exact.end(); server++) {
if (host == (*server)->getName())
return *server;
}
return exact.front();
}
} }

9
srcs/tools.cpp

@ -56,6 +56,14 @@ ip_port_t get_ip_port_t(string ip, int port) {
} }
string read_file(string path) { string read_file(string path) {
std::ifstream file(path.c_str());
if (!file.good()) return "";
std::stringstream tmp;
tmp << file.rdbuf();
return tmp.str();
}
string file_answer(string path){
struct stat info; struct stat info;
if (stat(path.c_str(), &info) != 0) { if (stat(path.c_str(), &info) != 0) {
std::cerr << "stat() error on " << path << ": " << strerror(errno) << "\n"; std::cerr << "stat() error on " << path << ": " << strerror(errno) << "\n";
@ -75,6 +83,7 @@ string read_file(string path) {
<< "\r\n" << "\r\n"
<< body; << body;
return (ret.str()); return (ret.str());
} }
string getMime(string path) { string getMime(string path) {

Loading…
Cancel
Save