Browse Source

some more errors call, added client_max_body_size, fixed choose_route

master
nicolas-arnaud 2 years ago
parent
commit
52562105be
  1. 33
      default.json
  2. 23
      includes/Client.hpp
  3. 1
      includes/Env.hpp
  4. 4
      includes/Master.hpp
  5. 1
      includes/Route.hpp
  6. 2
      includes/Server.hpp
  7. 8
      includes/webserv.hpp
  8. 1
      srcs/load/Env.cpp
  9. 3
      srcs/load/Route.cpp
  10. 10
      srcs/load/Server.cpp
  11. 132
      srcs/sock/Client.cpp
  12. 35
      srcs/sock/Master.cpp
  13. 12
      srcs/tools.cpp

33
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
}

23
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);

1
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);

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

1
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);

2
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);

8
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);

1
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++) {

3
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 "";

10
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);
}

132
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();
}

35
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)

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

Loading…
Cancel
Save