From 948e0872277992d896bf8e7858015f83789ddd47 Mon Sep 17 00:00:00 2001 From: nicolas-arnaud Date: Fri, 20 Jan 2023 09:17:03 +0100 Subject: [PATCH] save to modify cgi --- Makefile | 7 +++-- default.json | 5 ++++ includes/Client.hpp | 3 +-- includes/Master.hpp | 1 - includes/Server.hpp | 3 +-- includes/webserv.hpp | 4 ++- public/html/index.py | 25 ++++++++---------- srcs/json/Nodes.cpp | 13 ++++++++++ srcs/json/Parser.cpp | 11 ++++++-- srcs/json/Token.cpp | 61 ++++++++++++++++++++------------------------ srcs/load/Env.cpp | 21 ++++++++++----- srcs/load/Server.cpp | 38 +++++++++------------------ srcs/sock/Client.cpp | 30 ++++++++++++++-------- srcs/sock/Master.cpp | 57 ++++++++++++++++++++++++----------------- srcs/webserv.cpp | 2 +- 15 files changed, 157 insertions(+), 124 deletions(-) diff --git a/Makefile b/Makefile index 5f6a5f7..2dbe568 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,16 @@ SRCS= srcs/webserv.cpp srcs/tools.cpp srcs/debug.cpp \ srcs/json/Nodes.cpp srcs/json/Token.cpp srcs/json/Parser.cpp OBJS= $(SRCS:.cpp=.o) CXX=c++ -CXXFLAGS= -g -I includes -Werror -Wextra -Wall -std=c++98 +CXXFLAGS= -I includes -Werror -Wextra -Wall -std=c++98 all : $(NAME) $(NAME): $(OBJS) - $(CXX) -g -fsanitize=address $(OBJS) -o $(NAME) + $(CXX) $(OBJS) -o $(NAME) + +debug: + $(CXX) -I includes -Werror -Wextra -Wall -std=c++98 -g -fsanitize=address $(SRCS) -o $(NAME) -D DEBUG=1 clean: rm -rf $(OBJS) diff --git a/default.json b/default.json index cf702e3..d3435ad 100644 --- a/default.json +++ b/default.json @@ -13,6 +13,11 @@ "listens": ["localhost"], "root": "html/", "indexs": ["index.html"] + }, + { + "server_name": "localhost", + "root": "html/", + "indexs": ["index.html"] }, { "server_name": "narnaud.42.fr", diff --git a/includes/Client.hpp b/includes/Client.hpp index 74e84a4..e2470f9 100644 --- a/includes/Client.hpp +++ b/includes/Client.hpp @@ -20,8 +20,7 @@ class Client { bool check_method(void); void create_file(string path); void cgi(string cgi_path, string path); - void send_redir(int redir_code, string opt); - void send_error(int error_code); + void send_error(int error_code, string opt = ""); void send_answer(string msg); #ifdef DEBUG diff --git a/includes/Master.hpp b/includes/Master.hpp index 44f86c7..61cd0c0 100644 --- a/includes/Master.hpp +++ b/includes/Master.hpp @@ -8,7 +8,6 @@ class Master { public: Master(ip_port_t listen); - Master(int fd, Master *parent); ~Master(void); void pre_select(void); diff --git a/includes/Server.hpp b/includes/Server.hpp index 261fefe..7a0d2c7 100644 --- a/includes/Server.hpp +++ b/includes/Server.hpp @@ -9,8 +9,7 @@ public: std::vector _listens; ///< The list of listens the server which are linked to the server. Server(JSONNode *server); ~Server(void); - Master *create_master(string str); - std::vector get_sockets(JSONNode *server); + std::vector create_masters(JSONNode *server); Route *choose_route(string uri); string getName(void); }; diff --git a/includes/webserv.hpp b/includes/webserv.hpp index b00810a..94b2ca5 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -1,5 +1,7 @@ #pragma once -#define DEBUG 1 +#ifndef DEBUG + #define DEBUG 0 +#endif #include #include diff --git a/public/html/index.py b/public/html/index.py index 50e81b2..a753078 100644 --- a/public/html/index.py +++ b/public/html/index.py @@ -1,21 +1,18 @@ -#!/usr/bin/python -# Import modules for CGI handling -import cgi, cgitb +import cgi -# 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\r\n\r\n" -print "" -print "" -print "Hello - Second CGI Program" -print "" -print "" -print "

Hello %s %s

" % (first_name, last_name) -print "" -print "" +print("Content-Type: text/html") +print() +print("") +print("") +print("Hello - Second CGI Program") +print("") +print("") +print("

Hello %s %s

" % (first_name, last_name)) +print("") +print("") diff --git a/srcs/json/Nodes.cpp b/srcs/json/Nodes.cpp index 177a6d3..ec195b5 100644 --- a/srcs/json/Nodes.cpp +++ b/srcs/json/Nodes.cpp @@ -1,5 +1,18 @@ +/** + * @file Nodes.cpp + * @brief A node object is a container for each json values or blocks. It can contain either a std::map a std::vector a String* a number or a bool. + * + * @author Narnaud + * @version 0.1 + * @date 2023-01-17 + */ #include "webserv.hpp" +/** + * @brief Destructor + * Destroy Node and all sub Nodes he contain. + */ JSONNode::~JSONNode(void) { switch (type) { case OBJECT: diff --git a/srcs/json/Parser.cpp b/srcs/json/Parser.cpp index d06cffb..02a14f8 100644 --- a/srcs/json/Parser.cpp +++ b/srcs/json/Parser.cpp @@ -1,5 +1,12 @@ #include "webserv.hpp" +/** + * @file Parser.cpp + * @brief The parser lead the Tokenizer to travel along configuration file and create nodes with tokens. + * @author Narnaud + * @version 0.1 + * @date 2023-01-17 + */ JSONParser::JSONParser(const string filename) : tokenizer(filename) {} JSONNode *JSONParser::parse() { @@ -8,7 +15,7 @@ JSONNode *JSONParser::parse() { Token token; token = tokenizer.getToken(); if (token.type == CURLY_OPEN) parsed = parseObject(); - if (!parsed) std::cout << "Configuration file isn't valid\n"; + else throw std::logic_error("Invalid json syntax: json file must be an unique object block"); return parsed; } @@ -23,7 +30,7 @@ JSONNode *JSONParser::parseObject() { throw std::logic_error("No more tokens"); } Token token = tokenizer.getToken(); - if (token.type != STRING) throw std::logic_error("Invalid json syntax: object key isn't a string"); + if (token.type != STRING) throw std::logic_error("Invalid json syntax: Unclosed string"); string key = token.value; token = tokenizer.getToken(); if (token.type != COLON) throw std::logic_error("Invalid json syntax: missing colon"); diff --git a/srcs/json/Token.cpp b/srcs/json/Token.cpp index e1d8c3f..efb577c 100644 --- a/srcs/json/Token.cpp +++ b/srcs/json/Token.cpp @@ -1,19 +1,30 @@ +/** + * @file Token.cpp + * @brief The tokenizer read json file and when asked for give us the next token encounter and his type. + * @author Narnaud + * @version 0.1 + * @date 2023-01-17 + */ #include "webserv.hpp" +/** + * @brief Constructor + * Open conf file if he is readeable. + * + * @param fileName conf file path. + */ Tokenizer::Tokenizer(string fileName) { file.open(fileName.c_str(), std::ios::in); - if (!file.good()) - cout << "File open error" - << "\n"; + if (!file.good()) std::cerr << "File open error.\n"; } + bool Tokenizer::hasMoreTokens() { return !file.eof(); } char Tokenizer::getWithoutWhiteSpace() { char c = ' '; while ((c == ' ' || c == '\n') || c == '\t') { - file.get(c); // check + file.get(c); if ((c == ' ' || c == '\n') && !file.good()) { - // cout << file.eof() << " " << file.fail() << "\n"; throw std::logic_error("Ran out of tokens"); } else if (!file.good()) { return c; @@ -22,20 +33,17 @@ char Tokenizer::getWithoutWhiteSpace() { return c; } + void Tokenizer::rollBackToken() { if (file.eof()) file.clear(); file.seekg(prevPos); } + Token Tokenizer::getToken() { char c; - if (file.eof()) { - cout << "Exhaused tokens" - << "\n"; - // throw std::exception("Exhausted tokens"); - } + if (file.eof()) { cout << "Exhaused tokens\n"; } prevPos = file.tellg(); c = getWithoutWhiteSpace(); - Token token; token.type = NULL_TYPE; if (c == '"') { @@ -43,16 +51,11 @@ Token Tokenizer::getToken() { token.value = ""; file.get(c); while (c != '"') { - if (c == '}' || c == ']' || c == ',') throw std::logic_error("Invalid json syntax: a string is not close"); + if (c == '}' || c == ']' || c == ',') throw std::logic_error("Invalid json syntax: a string is not close"); token.value += c; file.get(c); } - } else if (c == '{') { - token.type = CURLY_OPEN; - } else if (c == '}') { - token.type = CURLY_CLOSE; } else if (c == '-' || (c >= '0' && c <= '9')) { - // Check if string is numeric token.type = NUMBER; token.value = ""; token.value += c; @@ -60,15 +63,11 @@ Token Tokenizer::getToken() { while ((c == '-') || (c >= '0' && c <= '9') || c == '.') { prevCharPos = file.tellg(); file.get(c); - if (file.eof()) { break; } else { - if ((c == '-') || (c >= '0' && c <= '9') || (c == '.')) { - token.value += c; - } else { - file.seekg(prevCharPos); - } + if ((c == '-') || (c >= '0' && c <= '9') || (c == '.')) token.value += c; + else file.seekg(prevCharPos); } } } else if (c == 'f') { @@ -82,15 +81,11 @@ Token Tokenizer::getToken() { } else if (c == 'n') { token.type = NULL_TYPE; file.seekg(3, std::ios_base::cur); - } else if (c == '[') { - token.type = ARRAY_OPEN; - } else if (c == ']') { - token.type = ARRAY_CLOSE; - } else if (c == ':') { - token.type = COLON; - } else if (c == ',') { - token.type = COMMA; - } - // cout << token.type << " : " << token.value << "\n"; + } else if (c == '{') token.type = CURLY_OPEN; + else if (c == '}') token.type = CURLY_CLOSE; + else if (c == '[') token.type = ARRAY_OPEN; + else if (c == ']') token.type = ARRAY_CLOSE; + else if (c == ':') token.type = COLON; + else if (c == ',') token.type = COMMA; return token; } diff --git a/srcs/load/Env.cpp b/srcs/load/Env.cpp index 6afd63d..5d15647 100644 --- a/srcs/load/Env.cpp +++ b/srcs/load/Env.cpp @@ -1,11 +1,18 @@ +/** + * @file Env.cpp + * @brief The main server object. Contain all servers and sockets. + * @author Narnaud + * @version 0.1 + * @date 2023-01-17 + */ #include "webserv.hpp" -/*|==========| - * Environment constructor: +/** + * @brief Constructor * - * Input: The JSONParser output - * Output: The env object containing servers and sockets vectors defined inside - * conf file by servers blocks and listens. + * The instance contain servers defined in configuration file by servers list and sockets by listens ones. + * + * @param conf The JsonParser output */ Env::Env(JSONNode *conf) { try { @@ -16,7 +23,7 @@ Env::Env(JSONNode *conf) { for (std::vector::iterator it = lst.begin(); it < lst.end(); it++) { Server *server = new Server(*it); this->_servers.push_back(server); - std::vector tmp_s = server->get_sockets(*it); + std::vector tmp_s = server->create_masters(*it); this->_masters.insert(this->_masters.end(), tmp_s.begin(), tmp_s.end()); } } @@ -62,11 +69,11 @@ void Env::pre_select(void) { for (std::vector::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) (*it)->pre_select(); } + /** * @brief Refresh all master_sockets and their clients datas (disconnect, new * connection, etc..) and parse requests recieved. */ - void Env::post_select(void) { cout << "==> Handle requests and answers:\n"; for (std::vector::iterator it = this->_masters.begin(); it < this->_masters.end(); it++) try { diff --git a/srcs/load/Server.cpp b/srcs/load/Server.cpp index b0e9bc1..830d894 100644 --- a/srcs/load/Server.cpp +++ b/srcs/load/Server.cpp @@ -44,25 +44,6 @@ Server::~Server(void) { */ string Server::getName(void) { return _name; } -/** - * @brief Master socket safe creation. - * - * @param str a "ip:port" string - * - * @return a Master socket object or NULL if the creation failed. - */ -Master *Server::create_master(string str) { - ip_port_t listen = get_ip_port_t(str); - if (listen.ip.at(0) != '[') { - try { - _listens.push_back(listen); - Master *sock = new Master(listen); - return (sock); - } catch (std::exception &e) { std::cerr << e.what() << '\n'; } - } else cout << "Listen: IPv6 isn't supported\n"; - return NULL; -} - /** * @brief Create server's defined sockets: * @@ -71,18 +52,23 @@ Master *Server::create_master(string str) { * @retrn A vector containing all the succesfull created sockets using * listens from the server block. */ -std::vector Server::get_sockets(JSONNode *server) { +std::vector Server::create_masters(JSONNode *server) { JSONObject datas = server->obj(); std::vector ret; - Master *tmp; - ip_port_t listen; + if (datas["listens"]) { JSONList listens = datas["listens"]->lst(); - for (JSONList::iterator it = listens.begin(); it != listens.end(); it++) { - if ((tmp = create_master((*it)->str()))) ret.push_back(tmp); - } - } else if ((tmp = create_master("0.0.0.0"))) ret.push_back(tmp); + for (JSONList::iterator it = listens.begin(); it != listens.end(); it++) + _listens.push_back(get_ip_port_t((*it)->str())); + } else _listens.push_back(get_ip_port_t("0.0.0.0")); + 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'; } + else cout << "Listen: IPv6 isn't supported\n"; + } return ret; + // else if ((tmp = create_master("0.0.0.0"))) ret.push_back(tmp); return ret; } /** diff --git a/srcs/sock/Client.cpp b/srcs/sock/Client.cpp index b69deb3..91a668c 100644 --- a/srcs/sock/Client.cpp +++ b/srcs/sock/Client.cpp @@ -34,8 +34,7 @@ void Client::init(void) { bool Client::getRequest(Env *env, string paquet) { if (paquet.length() < 1) send_error(403); - // if (DEBUG) - debug_block("Paquet: ", paquet); + if (DEBUG) debug_block("Paquet: ", paquet); if (header_pick("Method:", 0) != "") return getBody(paquet); vec_string lines = split(paquet, "\r\n"); for (vec_string::iterator it = lines.begin(); it < lines.end(); it++) { @@ -152,6 +151,12 @@ void Client::create_file(string path) { } } +/** +* @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]; @@ -185,15 +190,15 @@ void Client::cgi(string cgi_path, string path) { send_answer(ss.str()); } -void Client::send_redir(int redir_code, string opt) { - switch (redir_code) { - case 301: - return send_answer("HTTTP/1.1 301 Moved Permanently\r\nLocation: " + opt + "\r\n\r\n"); - } -} - -void Client::send_error(int error_code) { +/** + * @brief Send an error answer to the client. + * + * @param error_code The HTTP response code to send. + */ +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"); case 403: @@ -209,6 +214,11 @@ void Client::send_error(int error_code) { } } +/** + * @brief Send an answer to the client. + * + * @param msg The HTTP message to send. + */ void Client::send_answer(string msg) { if (DEBUG) debug_block("ANSWER: ", msg); #ifdef __linux__ diff --git a/srcs/sock/Master.cpp b/srcs/sock/Master.cpp index 8fd6dd6..dff893c 100644 --- a/srcs/sock/Master.cpp +++ b/srcs/sock/Master.cpp @@ -7,20 +7,21 @@ */ #include "webserv.hpp" -/* Master destructor */ +/** + * @brief Destructor + * Close master socket descriptor. + */ Master::~Master(void) { close(_fd); cout << "Destroyed master socket\n"; } -/* |==========| - * Master constructor +/** + * @brief Constructor * 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. + * If no exception if thrown, the creation success and the socket is then ready to select for new clients. * - * Input: A ip_port_t structure which contain the ip and the port the master - * care about. - * Output: A Master object. + * @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) { int x = 1, port = _listen.port; @@ -43,7 +44,10 @@ Master::Master(ip_port_t list) : _listen(list) { if (_fd < _min_fd) _min_fd = _fd; } -/* Set into static Master::readfds the active fds which will be select.*/ +/** + * @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; @@ -59,6 +63,15 @@ void Master::pre_select(void) { * - 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 + * - Then handle clients awaiting action: + * - Disconnect them if nothing's new on the socket (request altready handled) + * - Read and parse request else until it's ready to answer and does it. + * + * @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); @@ -77,7 +90,7 @@ void Master::post_select(Env *env) { 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, 128); + valread = read(child_fd, buffer, 127); buffer[valread] = '\0'; if (valread == 0) { getpeername(child_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); @@ -90,20 +103,20 @@ void Master::post_select(Env *env) { } } -/* |==========| - * 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. +/** + * + * @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. + * 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 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. * - * 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. + * @param env The environment object. + * @param host The host the client used to reached the server. * + * @return The server object choosen to handle the request. */ Server *Master::choose_server(Env *env, string host) { std::vector exact, inrange; @@ -129,13 +142,11 @@ Server *Master::choose_server(Env *env, string host) { } if (DEBUG) std::cout << "req: " << _listen.ip << ":" << _listen.port << "\n"; if (exact.size() == 0) { - std::cout << "in range server check\n"; for (std::vector::iterator server = inrange.begin(); server < inrange.end(); server++) { if (host == (*server)->getName()) return *server; } return inrange.front(); } else { - std::cout << "exact server check\n"; for (std::vector::iterator server = exact.begin(); server < exact.end(); server++) { if (host == (*server)->getName()) return *server; } diff --git a/srcs/webserv.cpp b/srcs/webserv.cpp index 112ef00..4bb76dd 100644 --- a/srcs/webserv.cpp +++ b/srcs/webserv.cpp @@ -40,7 +40,7 @@ int main(int ac, char **av) { Env env(conf); while (1) env.cycle(); } catch (const std::exception &e) { - std::cerr << e.what(); + std::cerr << e.what() << "\n"; return EXIT_FAILURE; } return EXIT_SUCCESS;