diff --git a/Makefile b/Makefile index 2dbe568..1719f68 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ $(NAME): $(OBJS) $(CXX) $(OBJS) -o $(NAME) debug: - $(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g -fsanitize=address $(SRCS) -o $(NAME) -D DEBUG=1 + $(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g $(SRCS) -o $(NAME) -D DEBUG=1 clean: rm -rf $(OBJS) diff --git a/includes/Client.hpp b/includes/Client.hpp index e2470f9..41e1e42 100644 --- a/includes/Client.hpp +++ b/includes/Client.hpp @@ -2,16 +2,19 @@ #include "webserv.hpp" class Client { + int _poll_id; int _fd; ip_port_t _ip_port; Master *_parent; Server *_server; Env *_env; Route *_route; - string _method, _uri, _query, _host, _header, _body; + string _header, _body; + string _method, _uri, _query, _host; int _len; bool _last_chunk; - std::map _request; + std::map _headers; + bool _finish; void init(void); bool getBody(string paquet); diff --git a/includes/Env.hpp b/includes/Env.hpp index 3bac0e0..bdf534d 100644 --- a/includes/Env.hpp +++ b/includes/Env.hpp @@ -10,6 +10,5 @@ public: Env(JSONNode *conf); ~Env(void); void cycle(void); - void pre_select(void); - void post_select(void); + void post_poll(void); }; diff --git a/includes/Master.hpp b/includes/Master.hpp index 61cd0c0..3956e22 100644 --- a/includes/Master.hpp +++ b/includes/Master.hpp @@ -2,6 +2,7 @@ #include "webserv.hpp" class Master { + int _poll_id; int _fd; std::vector _childs; struct sockaddr_in _address; @@ -10,11 +11,11 @@ public: Master(ip_port_t listen); ~Master(void); - void pre_select(void); - void post_select(Env *env); + void post_poll(Env *env); Server *choose_server(Env *env, string host); - ip_port_t _listen; - static fd_set _readfds; - static int _max_fd, _min_fd; + ip_port_t _listen; + static int _poll_id_amount; + static int _first_cli_id; + static struct pollfd *_pollfds; }; diff --git a/includes/webserv.hpp b/includes/webserv.hpp index 94b2ca5..59cba68 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -2,11 +2,15 @@ #ifndef DEBUG #define DEBUG 0 #endif +#ifndef MAX_CLIENTS + #define MAX_CLIENTS 5000 +#endif #include #include #include #include +#include #include #include #include diff --git a/public/html/basique.html b/public/html/basique.html index e31e0ae..7d8be2e 100644 --- a/public/html/basique.html +++ b/public/html/basique.html @@ -16,6 +16,7 @@
  • Documentss
  • Images
  • php-cgi test
  • +
  • big_cgi
  • Query and python cgi test

    diff --git a/public/html/big_cgi.py b/public/html/big_cgi.py new file mode 100644 index 0000000..456fe2a --- /dev/null +++ b/public/html/big_cgi.py @@ -0,0 +1,15 @@ +import string +import random + +print("Content-type: text/html") +print() +# initializing size of string +N = 10000000 + +# using random.choices() +# generating random strings +res = ''.join(random.choices(string.ascii_uppercase + + string.digits, k=N)) + +# print result +print("The generated random string : " + str(res)) diff --git a/public/html/index.py b/public/html/index.py index a753078..2df8621 100644 --- a/public/html/index.py +++ b/public/html/index.py @@ -1,13 +1,16 @@ +#!/usr/bin/python -import cgi +# Import modules for CGI handling +import cgi, cgitb +# Create instance of FieldStorage form = cgi.FieldStorage() +# Get data from fields first_name = form.getvalue('first_name') last_name = form.getvalue('last_name') -print("Content-Type: text/html") -print() +print("Content-type:text/html\r\n\r\n") print("") print("") print("Hello - Second CGI Program") diff --git a/srcs/load/Env.cpp b/srcs/load/Env.cpp index 5d15647..6b6b76c 100644 --- a/srcs/load/Env.cpp +++ b/srcs/load/Env.cpp @@ -27,6 +27,7 @@ Env::Env(JSONNode *conf) { this->_masters.insert(this->_masters.end(), tmp_s.begin(), tmp_s.end()); } } + Master::_first_cli_id = Master::_poll_id_amount - 1; if ((node = conf->obj()["allowed_methods"])) { JSONList lst = node->lst(); for (JSONList::iterator it = lst.begin(); it < lst.end(); it++) { @@ -54,29 +55,19 @@ Env::~Env() { * - refresh and handle requests */ void Env::cycle(void) { - FD_ZERO(&Master::_readfds); - Master::_max_fd = Master::_min_fd; - pre_select(); cout << "|===||===| Waiting some HTTP request... |===||===|\n"; - int activity = select(Master::_max_fd + 1, &(Master::_readfds), NULL, NULL, NULL); - if ((activity < 0) && (errno != EINTR)) std::cerr << "Select: " << strerror(errno) << "\n"; - post_select(); -} - -/// @brief Append each master_sockets and their clients to list of fds SELECT must look at. -void Env::pre_select(void) { - cout << "==> Check sockets still alive to listen\n"; - for (std::vector::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) - (*it)->pre_select(); + 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) post_poll(); } /** * @brief Refresh all master_sockets and their clients datas (disconnect, new * connection, etc..) and parse requests recieved. */ -void Env::post_select(void) { +void Env::post_poll() { cout << "==> Handle requests and answers:\n"; for (std::vector::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) try { - (*it)->post_select(this); + (*it)->post_poll(this); } catch (std::exception &e) { std::cerr << e.what(); } } diff --git a/srcs/load/Server.cpp b/srcs/load/Server.cpp index 830d894..af11447 100644 --- a/srcs/load/Server.cpp +++ b/srcs/load/Server.cpp @@ -64,7 +64,9 @@ std::vector Server::create_masters(JSONNode *server) { for (std::vector::iterator listen = _listens.begin(); listen < _listens.end(); listen++) { if (listen->ip.at(0) != '[') try { ret.push_back(new Master(*listen)); - } catch (std::exception &e) { std::cerr << e.what() << '\n'; } + } catch (std::exception &e) { + std::cerr << "Ip: " << listen->ip << ", port: " << listen->port << " -> " << e.what() << '\n'; + } else cout << "Listen: IPv6 isn't supported\n"; } return ret; diff --git a/srcs/sock/Client.cpp b/srcs/sock/Client.cpp index 91a668c..df532a5 100644 --- a/srcs/sock/Client.cpp +++ b/srcs/sock/Client.cpp @@ -20,16 +20,18 @@ Client::Client(int fd, ip_port_t ip_port, Master *parent) : _fd(fd), _ip_port(ip Client::~Client(void) { close(_fd); + _headers.clear(); cout << "Host disconnected, ip " << _ip_port.ip << ", port " << _ip_port.port << "\n"; } void Client::init(void) { + _finish = false; _server = NULL; _route = NULL; _method = _uri = _host = _header = _body = ""; _len = 0; _last_chunk = false; - _request.clear(); + _headers.clear(); } bool Client::getRequest(Env *env, string paquet) { @@ -74,11 +76,11 @@ bool Client::parseHeader(Env *env) { if (DEBUG) cout << "Parsing header...\n"; lines = split(_header, "\r\n"); method = split(lines.at(0), " "); - _request["Method:"] = method; + _headers["Method:"] = method; if (lines.size() > 0) { for (vec_string::iterator it = lines.begin() + 1; it < lines.end(); it++) { line = split(*it, " "); - _request[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); @@ -105,7 +107,7 @@ bool Client::parseHeader(Env *env) { return true; } -string Client::header_pick(string key, size_t id) { return _request[key].size() <= id ? "" : _request[key].at(id); } +string Client::header_pick(string key, size_t id) { return _headers[key].size() <= id ? "" : _headers[key].at(id); } bool Client::check_method(void) { vec_string allowed; @@ -147,47 +149,46 @@ void Client::create_file(string path) { else { file << _body; file.close(); - send_answer("HTTP/1.1 201 Accepted\r\nContent-Length: 0\r\n\r\n"); + send_answer("HTTP/1.1 201 Accepted\r\n\r\n"); } } /** -* @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 path The path to the file requested. -*/ + * @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 path The path to the file requested. + */ void Client::cgi(string cgi_path, string path) { - int status; - int fd[2]; - std::stringstream ss; - string ret; + int pipe_in[2]; + send(_fd, "HTTP/1.1 200 OK\r\n", 17, MSG_NOSIGNAL); if (!std::ifstream(cgi_path.c_str()).good()) return send_error(404); - pipe(fd); + if (DEBUG) std::cout << "Send cgi\n"; if (fork() == 0) { - const char **args = new const char *[cgi_path.length() + path.length() + 2]; + const char **args = new const char *[cgi_path.length() + 1]; args[0] = cgi_path.c_str(); - args[1] = path.c_str(); - args[2] = NULL; + args[1] = NULL; string path_info = "PATH_INFO=" + _route->getRoot(); string query = "QUERY_STRING=" + _query; const char **env = new const char *[path_info.length() + query.length() + 2]; env[0] = path_info.c_str(); env[1] = query.c_str(); env[2] = NULL; - dup2(fd[1], STDOUT_FILENO); - close(fd[1]); - close(fd[0]); + pipe(pipe_in); + std::stringstream tmp; + tmp << std::ifstream(path.c_str()).rdbuf(); + string file = tmp.str(); + write(pipe_in[1], file.c_str(), file.size()); + close(pipe_in[1]); + dup2(pipe_in[0], STDIN_FILENO); + close(pipe_in[0]); + dup2(_fd, STDOUT_FILENO); + close(_fd); execve(cgi_path.c_str(), (char **)args, (char **)env); + exit(1); } - close(fd[1]); - waitpid(-1, &status, 0); - char buffer[10000]; - buffer[read(fd[0], buffer, 10000)] = 0; - ret = string(buffer); - ss << "HTTP/1.1 200 OK\r\nContent-Length: " << ret.length() - ret.find("\r\n\r\n") - 4 << "\r\n\r\n" << ret; - send_answer(ss.str()); + _finish = true; } /** @@ -195,22 +196,22 @@ void Client::cgi(string cgi_path, string path) { * * @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) { case 301: return send_answer("HTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + "\r\n\r\n"); case 400: - return send_answer("HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"); + return send_answer("HTTP/1.1 400 Bad Request\r\n\r\n"); case 403: - return send_answer("HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n"); + return send_answer("HTTP/1.1 403 Forbidden\r\n\r\n"); case 404: - return send_answer("HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); + return send_answer("HTTP/1.1 404 Not Found\r\n\r\n"); case 405: return send_answer("HTTP/1.1 405 Method Not Allowed\r\nConnection: " - "close\r\nContent-Length: 0\r\n\r\n"); + "close\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"); + "Large\r\nConnection: close\r\n\r\n"); } } diff --git a/srcs/sock/Master.cpp b/srcs/sock/Master.cpp index dff893c..0c520d4 100644 --- a/srcs/sock/Master.cpp +++ b/srcs/sock/Master.cpp @@ -41,28 +41,12 @@ Master::Master(ip_port_t list) : _listen(list) { fcntl(socket, F_SETFL, O_NONBLOCK); #endif cout << "New master socket with fd " << _fd << " which listen " << ip << ":" << port << "\n"; - if (_fd < _min_fd) _min_fd = _fd; + _pollfds[_poll_id_amount].fd = _fd; + _pollfds[_poll_id_amount].events = POLLIN | POLLPRI; + _poll_id = _poll_id_amount; + _poll_id_amount++; } -/** - * @brief The pre select operations: - * Add master's socket descriptor and each one of his childs to the select list of descriptor. - */ -void Master::pre_select(void) { - FD_SET(_fd, &_readfds); - if (_fd > _max_fd) _max_fd = _fd; - for (std::vector::iterator child = _childs.begin(); child < _childs.end(); child++) { - FD_SET((*child)->_fd, &_readfds); - if ((*child)->_fd > _max_fd) _max_fd = (*child)->_fd; - } -} - -/* |==========| - * Refresh master socket datas after select() - * - look first for new clients - * - look then if known clients sent requests or disconnected - * - if client sent request, handle it to generate answer adapted - */ /** * @brief Checkk master and his clients sockets after select performed. * - First look for new clients @@ -72,32 +56,54 @@ void Master::pre_select(void) { * * @param env The environment object which contain the liste of servers to know which one the client is trying to reach. */ -void Master::post_select(Env *env) { - int valread; - int addrlen = sizeof(_address); - char buffer[128]; +void Master::post_poll(Env *env) { + int addrlen = sizeof(_address); - if (FD_ISSET(_fd, &_readfds)) { /// < incomming master request + if (_pollfds[_poll_id].revents & POLLIN) { /// < incomming master request int new_socket = accept(_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); if (new_socket < 0) throw std::runtime_error("accept() error:" + string(strerror(errno))); #ifdef __APPLE__ fcntl(new_socket, F_SETFL, O_NONBLOCK); #endif 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)); + Client *new_cli = new Client(new_socket, cli_listen, this); + _childs.push_back(new_cli); + for (int i = _first_cli_id; i < MAX_CLIENTS; i++) { + if (_pollfds[i].fd != 0) continue; + _pollfds[i].fd = new_socket; + _pollfds[i].events = POLLIN | POLLPRI; + new_cli->_poll_id = i; + _poll_id_amount++; + break; + } } int child_fd; for (std::vector::iterator it = _childs.begin(); it < _childs.end(); it++) { child_fd = (*it)->_fd; - if (FD_ISSET(child_fd, &_readfds)) { - valread = read(child_fd, buffer, 127); + int i = (*it)->_poll_id; + if (_pollfds[i].fd > 0 && _pollfds[i].revents & POLLIN) { + + char buffer[128]; + int valread = read(child_fd, buffer, 127); buffer[valread] = '\0'; if (valread == 0) { - getpeername(child_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); + // getpeername(child_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); delete (*it); _childs.erase(it); + _pollfds[i].fd = 0; + _pollfds[i].events = 0; + _pollfds[i].revents = 0; + _poll_id_amount--; } else if ((*it)->getRequest(env, buffer)) { (*it)->handleRequest(); + if ((*it)->_finish) { + delete (*it); + _childs.erase(it); + _pollfds[i].fd = 0; + _pollfds[i].events = 0; + _pollfds[i].revents = 0; + _poll_id_amount--; + } } } } @@ -133,9 +139,10 @@ Server *Master::choose_server(Env *env, string host) { } bool is_inrange = true; ip_listen = split((*it).ip, "."); - vec_string::iterator r = ip_required.begin(); - for (vec_string::iterator l = ip_listen.end(); l >= ip_listen.begin(); --l) { - if (*l != *r && *l != "0") is_inrange = false; + vec_string::iterator r = ip_required.end(); + vec_string::iterator l = ip_listen.end(); + while (r > ip_required.begin()) { + if (*(--l) != *(--r) && *l != "0") is_inrange = false; } if (is_inrange) inrange.push_back(*server); } diff --git a/srcs/webserv.cpp b/srcs/webserv.cpp index 4bb76dd..15c8e3d 100644 --- a/srcs/webserv.cpp +++ b/srcs/webserv.cpp @@ -8,11 +8,10 @@ #include "webserv.hpp" -fd_set Master::_readfds; /// < The sockets fd which will be select -int Master::_min_fd = INT_MAX; /// < The lower socket fd -int Master::_max_fd = 0; /// < The higher one +int Master::_poll_id_amount = 0; +int Master::_first_cli_id = 0; +struct pollfd *Master::_pollfds = new struct pollfd[MAX_CLIENTS + 1]; -/* *******************************/ /** * @brief The server launcher * @@ -37,8 +36,10 @@ int main(int ac, char **av) { if (!conf) return EXIT_FAILURE; // Here we start the server and his environment using conf cout << "Initialization of server...\n"; + std::memset(Master::_pollfds, 0, sizeof(*Master::_pollfds) * (MAX_CLIENTS)); Env env(conf); while (1) env.cycle(); + delete[] Master::_pollfds; } catch (const std::exception &e) { std::cerr << e.what() << "\n"; return EXIT_FAILURE;