diff --git a/srcs/Client.cpp b/srcs/Client.cpp index 8641916..88aba00 100644 --- a/srcs/Client.cpp +++ b/srcs/Client.cpp @@ -45,9 +45,26 @@ void Client::parse_request() _parse_request_headers(list); //body- message _parse_request_body(pos + 4); + + // add "raw_request.clear()" after parsing ? for little less memory usage ? } +void Client::clear() +{ + clear_request(); + raw_request.clear(); + response.clear(); + status = 0; +} +void Client::clear_request() +{ + _request.method = UNKNOWN; + _request.path.clear(); + _request.version.clear(); + _request.headers.clear(); + _request.body.clear(); +} http_method Client::get_method() { return _request.method; } std::string &Client::get_path() { return _request.path; } diff --git a/srcs/Client.hpp b/srcs/Client.hpp index eef125a..38fcd33 100644 --- a/srcs/Client.hpp +++ b/srcs/Client.hpp @@ -10,10 +10,10 @@ struct Request { - std::map headers; http_method method; std::string path; std::string version; + std::map headers; std::string body; }; @@ -38,6 +38,8 @@ class Client std::string &get_headers(const std::string &key); void parse_request(); + void clear(); + void clear_request(); private: struct Request _request; diff --git a/srcs/config/ServerConfig.hpp b/srcs/config/ServerConfig.hpp index dc658da..1875592 100644 --- a/srcs/config/ServerConfig.hpp +++ b/srcs/config/ServerConfig.hpp @@ -30,7 +30,7 @@ public: // there can only be one. std::string root; - int client_body_limit; // set to default max if none set + unsigned int client_body_limit; // set to default max if none set // might be the only one we let slide if bad input... bool autoindex; diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 73c1606..87b2741 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -84,3 +84,18 @@ std::string http_methods_to_str(unsigned int methods) return (str); } + +void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr) +{ + if (ori_substr.empty()) + return; + size_t pos = 0; + while (1) + { + pos = str.find(ori_substr, pos); + if (pos == std::string::npos) + break; + str.replace(pos, ori_substr.size(), new_substr); + pos += new_substr.size(); + } +} diff --git a/srcs/utils.hpp b/srcs/utils.hpp index c035cfb..fa22d09 100644 --- a/srcs/utils.hpp +++ b/srcs/utils.hpp @@ -32,5 +32,6 @@ std::string itos(int n); std::string trim(std::string str, char c); http_method str_to_http_method(std::string &str); std::string http_methods_to_str(unsigned int methods); +void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr); #endif diff --git a/srcs/webserv/Webserv.hpp b/srcs/webserv/Webserv.hpp index a9d69fe..f859ea3 100644 --- a/srcs/webserv/Webserv.hpp +++ b/srcs/webserv/Webserv.hpp @@ -28,6 +28,7 @@ # include "Client.hpp" # include "ServerConfig.hpp" # include "utils.hpp" +# include "http_status.hpp" extern bool g_run; extern int g_last_signal; @@ -60,6 +61,7 @@ class Webserv std::vector _listen_sockets; std::vector _servers; std::vector _clients; + std::map _http_status; // accept.cpp void _accept_connection(int fd); @@ -69,11 +71,20 @@ class Webserv // response.cpp void _response(Client *client); void _send_response(Client *client, ServerConfig &server); + + void _append_base_headers(Client *client); void _construct_response(Client *client, ServerConfig &server); - void _insert_status_line(Client *client, ServerConfig &server); - void _get_ressource(Client *client, ServerConfig &server, LocationConfig &location); + void _process_method(Client *client, ServerConfig &server, LocationConfig &location); + void _insert_status_line(Client *client); + void _error_html_response(Client *client, ServerConfig &server); + + void _get(Client *client, ServerConfig &server, LocationConfig &location); + void _get_file(Client *client, const std::string &path); + void _append_body(Client *client, const char *body, size_t body_size); + void _post(Client *client, ServerConfig &server, LocationConfig &location); void _delete(Client *client, ServerConfig &server, LocationConfig &location); + ServerConfig &_determine_process_server(Client *client); LocationConfig &_determine_location(ServerConfig &server, std::string &path); // cgi_script.cpp @@ -94,6 +105,7 @@ class Webserv // init.cpp void _bind(int socket_fd, in_port_t port, std::string host); void _listen(int socket_fd, unsigned int max_connections); + void _init_http_status_map(); }; #endif diff --git a/srcs/webserv/base.cpp b/srcs/webserv/base.cpp index 6267ec9..30fa868 100644 --- a/srcs/webserv/base.cpp +++ b/srcs/webserv/base.cpp @@ -12,6 +12,8 @@ Webserv::Webserv() throw std::runtime_error("Epoll init"); } + _init_http_status_map(); + std::signal(SIGPIPE, signal_handler); std::signal(SIGINT, signal_handler); } diff --git a/srcs/webserv/http_status.hpp b/srcs/webserv/http_status.hpp new file mode 100644 index 0000000..efc64b4 --- /dev/null +++ b/srcs/webserv/http_status.hpp @@ -0,0 +1,27 @@ + +#ifndef HTTP_STATUS_HPP +# define HTTP_STATUS_HPP + +// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + +/* + First version of macro HTML_ERROR(STATUS) dont work with call like this : + client->response.append( HTML_ERROR( _http_status[client->status].c_str() ) ); + so I made the other version with dynamic replacement of STATUS . +*/ +// # define HTML_ERROR(STATUS) "\r\n"STATUS"

"STATUS"


Le Webserv/0.1

" + +# define STATUS_PLACEHOLDER "$STATUS" +# define HTML_ERROR "" STATUS_PLACEHOLDER "

" STATUS_PLACEHOLDER "


Le Webserv/0.1

" + +// When new status added, need to update _init_http_status_map() +# define S200 "200 OK" + +# define S400 "400 Bad Request" +# define S404 "404 Not Found" +# define S405 "405 Method Not Allowed" +# define S413 "413 Content Too Large" + +# define S500 "500 Internal Server Error" + +#endif diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 4c5956e..4445ca8 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -76,3 +76,15 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections) throw std::runtime_error("Socket listen"); } } + +void Webserv::_init_http_status_map() +{ + _http_status[200] = S200; + + _http_status[400] = S400; + _http_status[404] = S404; + _http_status[405] = S405; + _http_status[413] = S413; + + _http_status[500] = S500; +} diff --git a/srcs/webserv/response.cpp b/srcs/webserv/response.cpp index e751ac0..359a327 100644 --- a/srcs/webserv/response.cpp +++ b/srcs/webserv/response.cpp @@ -3,6 +3,8 @@ void Webserv::_response(Client *client) { + client->status = 200; // default value + ServerConfig &server = _determine_process_server(client); _send_response(client, server); if (g_last_signal) @@ -14,9 +16,12 @@ void Webserv::_send_response(Client *client, ServerConfig &server) ssize_t ret; std::cerr << "send()\n"; - // std::cerr << "RAW_REQUEST\n|\n" << client->raw_request << "|\n"; // DEBUG + _append_base_headers(client); _construct_response(client, server); + _insert_status_line(client); + if (client->status >= 400) + _error_html_response(client, server); ret = ::send(client->fd, client->response.c_str(), client->response.size(), 0); if (ret == -1) @@ -27,30 +32,38 @@ void Webserv::_send_response(Client *client, ServerConfig &server) return ; } - if (client->raw_request.find("Connection: close") != std::string::npos) + if (client->get_headers("Connection") == "close") _close_client(client->fd); else { _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD); - client->raw_request.clear(); - client->response.clear(); + client->clear(); } } -void Webserv::_construct_response(Client *client, ServerConfig &server) +void Webserv::_append_base_headers(Client *client) { - LocationConfig &location = _determine_location(server, client->get_path()); - - client->status = 200; // default value - client->response.append("Server: Webserv/0.1\r\n"); if (client->get_headers("Connection") == "close") client->response.append("Connection: close\r\n"); else client->response.append("Connection: keep-alive\r\n"); +} +void Webserv::_construct_response(Client *client, ServerConfig &server) +{ + if (client->get_body().size() > server.client_body_limit) + { + client->status = 413; + return; + } + LocationConfig &location = _determine_location(server, client->get_path()); + _process_method(client, server, location); +} +void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location) +{ unsigned int allow_methods = ALL_METHODS; // TEMP VARIABLE // after update in ConfigParser, use the "allow_methods" of location. // TODO in ConfigParser : by default if no field in config file, "allow_methods" must be set to ALL_METHODS @@ -61,12 +74,20 @@ void Webserv::_construct_response(Client *client, ServerConfig &server) } else if (allow_methods & client->get_method()) { - if (client->get_method() & GET) - _get_ressource(client, server, location); - else if (client->get_method() & POST) - _post(client, server, location); - else if (client->get_method() & DELETE) - _delete(client, server, location); + switch (client->get_method()) + { + case (GET): + _get(client, server, location); + break; + case (POST): + _post(client, server, location); + break; + case (DELETE): + _delete(client, server, location); + break; + default: + break; + } } else { @@ -75,58 +96,38 @@ void Webserv::_construct_response(Client *client, ServerConfig &server) client->response.append(::http_methods_to_str(allow_methods)); client->response.append("\r\n"); } - - _insert_status_line(client, server); } -// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml -#define HTML_ERROR(STATUS) "\r\n"STATUS"

"STATUS"


Le Webserv/0.1

" -#define S200 "200 OK" -#define S400 "400 Bad Request" -#define S404 "404 Not Found" -#define S500 "500 Internal Server Error" -void Webserv::_insert_status_line(Client *client, ServerConfig &server) +void Webserv::_insert_status_line(Client *client) { std::string status_line; status_line.append("HTTP/1.1 "); - // WIP, maybe make a map for status response - switch (client->status) - { - case (200): - status_line.append(S200); - break; - case (400): - status_line.append(S400); - client->response.append(HTML_ERROR(S400)); - break; - case (404): - status_line.append(S404); - client->response.append(HTML_ERROR(S404)); - break; - case (500): - status_line.append(S500); - client->response.append(HTML_ERROR(S500)); - break; - } + status_line.append(_http_status[client->status]); status_line.append("\r\n"); - client->response.insert(0, status_line); } -#define ROOT "www" -#define INDEX "index.html" -#define MAX_FILESIZE 1000000 // (1Mo) -void Webserv::_get_ressource(Client *client, ServerConfig &server, LocationConfig &location) +void Webserv::_error_html_response(Client *client, ServerConfig &server) +{ + if (server.error_pages[client->status].empty()) + { + std::string html_page = HTML_ERROR; + ::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]); + _append_body(client, html_page.c_str(), html_page.size()); + } + else + _get_file(client, server.error_pages[client->status]); +} + +#define INDEX "index.html" // temp wip +void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location) { - std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ? - char buf[MAX_FILESIZE+1]; - std::string tmp; std::string path = client->get_path(); - if (path == "/") // TODO ; With server config + if (path == "/") // TODO : index and autoindex path.append(INDEX); - path.insert(0, ROOT); + path.insert(0, location.root); std::cerr << "path = " << path << "\n"; @@ -140,7 +141,15 @@ void Webserv::_get_ressource(Client *client, ServerConfig &server, LocationConfi // // END TMP HUGO - + _get_file(client, path); +} + +#define MAX_FILESIZE 1000000 // (1Mo) +void Webserv::_get_file(Client *client, const std::string &path) +{ + std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ? + char buf[MAX_FILESIZE+1]; + if (access(path.c_str(), R_OK) == -1) { std::perror("err access()"); @@ -171,21 +180,32 @@ void Webserv::_get_ressource(Client *client, ServerConfig &server, LocationConfi ifd.read(buf, size); buf[ifd.gcount()] = '\0'; - client->response.append("Content-Type: text/html; charset=UTF-8\r\n"); // TODO : determine Content-Type - - client->response.append("Content-Length: "); - tmp = ::itos(ifd.gcount()); - client->response.append(tmp); - client->response.append("\r\n"); - - // Body - client->response.append("\r\n"); - client->response.append(buf); + _append_body(client, buf, ifd.gcount()); ifd.close(); } } +void Webserv::_append_body(Client *client, const char *body, size_t body_size) +{ +/* + TODO : determine Content-Type + how ? read the body ? + or before in other way (like based and file extension) and pass here as argument ? + http://nginx.org/en/docs/http/ngx_http_core_module.html#types + Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that. +*/ + client->response.append("Content-Type: text/html; charset=UTF-8\r\n"); + + client->response.append("Content-Length: "); + std::string tmp = ::itos(body_size); + client->response.append(tmp); + client->response.append("\r\n"); + + client->response.append("\r\n"); + client->response.append(body); +} + void Webserv::_post(Client *client, ServerConfig &server, LocationConfig &location) { /*