wip cgi handling, client contains it's own privates functions to handle request headers and body

This commit is contained in:
hugogogo
2022-08-01 19:30:07 +02:00
parent 031932e887
commit 979a3d20b8
12 changed files with 382 additions and 154 deletions

View File

@@ -3,33 +3,27 @@ NAME = webserv
CXX = c++ CXX = c++
CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS = -Wall -Wextra #-Werror
CXXFLAGS += $(HEADERS_I) CXXFLAGS += $(HEADERS_D:%=-I%)
CXXFLAGS += -std=c++98 CXXFLAGS += -std=c++98
CXXFLAGS += -g CXXFLAGS += -g
CXXFLAGS += -MMD -MP #header dependencie CXXFLAGS += -MMD -MP #header dependencie
#CXXFLAGS += -O3 #CXXFLAGS += -O3
#SHELL = /bin/zsh
VPATH = $(SRCS_D) VPATH = $(SRCS_D)
HEADERS_I = $(HEADERS_D:%=-I%)
HEADERS_D = srcs \ HEADERS_D = srcs \
headers headers
HEADERS_F = Webserv.hpp \
ConfigParser.hpp \
ServerConfig.hpp \
LocationConfig.hpp \
Client.hpp \
MethodType.hpp \
utils.hpp
SRCS_D = srcs srcs/webserv SRCS_D = srcs \
srcs/webserv
SRCS = main.cpp \ SRCS = main.cpp \
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \ base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
accept.cpp request.cpp response.cpp \ accept.cpp request.cpp response.cpp \
run_loop.cpp \ run_loop.cpp \
ConfigParser.cpp \ ConfigParser.cpp \
utils.cpp utils.cpp \
cgi_script.cpp \
Client.cpp
OBJS_D = builds OBJS_D = builds
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o) OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)

View File

@@ -1,4 +1,20 @@
---
## questions
- mettre les fonctions specifiques a la requete, dans la class client ?
- où est-ce que j'inclus le cgi ?
- est-ce que le cgi est appellé par `/cgi-bin` ?
- non
- g rajouté `char ** env` dans client.cpp
- non
- ajouter un champ "message body" dans client ?
- non
- comment organiser la creation du message reponse (cgi ou pas) et des headers ?
- comment je gere le path `/cgi-bin/` avec la suite ?
- qu'est-ce que le cgi renvoit comme headers ? comment c'est géré ?
- https://www.rfc-editor.org/rfc/rfc3875
--- ---
## man ## man
@@ -115,4 +131,7 @@ REDIRECT_STATUS : for exemple, 200
- [hack with CGI](https://www.youtube.com/watch?v=ph6-AKByBU4) - [hack with CGI](https://www.youtube.com/watch?v=ph6-AKByBU4)
- [http headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) - [http headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
- [list of http headers fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) - [list of http headers fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
- [http request ibm](https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests)
- [http request other](https://www.tutorialspoint.com/http/http_requests.htm)
- [request line uri](https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs)

View File

@@ -5,25 +5,47 @@
# include <iostream> # include <iostream>
# include <string> # include <string>
# include <map> # include <map>
# include <vector>
# include "utils.hpp"
struct Client struct Request
{ {
// public: std::map<std::string, std::string> headers;
// Client(Placeholder); std::string method;
// Client(); std::string path;
std::string version;
std::string body;
};
class Client
{
public:
Client();
~Client();
//Client(Client const &src); //Client(Client const &src);
// ~Client();
//Client &operator=(Client const &rhs); //Client &operator=(Client const &rhs);
int fd; int fd;
std::string raw_request; std::string raw_request;
std::map<std::string, std::string> request;
// std::map<std::string, std::string> response;
std::string response; std::string response;
unsigned int status; unsigned int status;
// private: std::string get_method();
std::string get_path();
std::string get_version();
std::string get_body();
std::string get_headers(std::string key);
void parse_request();
private:
struct Request _request;
void _parse_request_line( std::string rline );
void _parse_request_headers( std::vector<std::string> list );
void _parse_request_body( size_t pos );
}; };
#endif #endif

View File

@@ -1,14 +1,3 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ServerConfig.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/07/23 15:55:16 by me #+# #+# */
/* Updated: 2022/07/23 16:19:43 by me ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef SERVERCONFIG_HPP #ifndef SERVERCONFIG_HPP
# define SERVERCONFIG_HPP # define SERVERCONFIG_HPP
@@ -27,7 +16,6 @@ public:
// i mean i guess i need some canonic stuff? // i mean i guess i need some canonic stuff?
// although maybe if i make it a struct i can barebones it? // although maybe if i make it a struct i can barebones it?
std::string server_name; std::string server_name;
std::string root; std::string root;
@@ -54,10 +42,8 @@ public:
std::string host; std::string host;
std::string port; std::string port;
// do i need a print all for testing? // do i need a print all for testing?
void print_all() void print_all()
{ {
std::cout << "PRINTING A FULL SERVER CONFIG\n\n"; std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
@@ -87,18 +73,7 @@ public:
std::cout << "\n----------\n"; std::cout << "\n----------\n";
} }
}; };
#endif #endif

View File

@@ -91,6 +91,13 @@ class Webserv
void _construct_response(Client *client); void _construct_response(Client *client);
void _insert_status_line(Client *client); void _insert_status_line(Client *client);
void _get_ressource(Client *client); void _get_ressource(Client *client);
// cgi_script.cpp
bool _is_cgi(Client *client);
void _exec_cgi(Client *client);
void _construct_client(Client *client);
char** _set_env(Client *client);
char* _dup_env(std::string var, std::string val);
void _exec_script(Client *client, char **env);
// epoll_update.cpp // epoll_update.cpp
int _epoll_update(int fd, uint32_t events, int op); int _epoll_update(int fd, uint32_t events, int op);
int _epoll_update(int fd, uint32_t events, int op, void *ptr); int _epoll_update(int fd, uint32_t events, int op, void *ptr);
@@ -102,18 +109,6 @@ class Webserv
// init.cpp // init.cpp
void _bind(int socket_fd, in_port_t port, std::string host); void _bind(int socket_fd, in_port_t port, std::string host);
void _listen(int socket_fd, unsigned int max_connections); void _listen(int socket_fd, unsigned int max_connections);
// TMP HUGO TEST CGI
//
void _serve_file(Client *client, std::string page);
void _exec_cgi_script(Client *client);
void _parse_request(Client *client);
void _parse_request_line(Client *client, std::string rline);
void _parse_request_headers
( Client *client
, std::vector<std::string> list);
//
// END TMP HUGO TEST CGI
}; };
#endif #endif

View File

@@ -7,7 +7,7 @@
# include <vector> # include <vector>
std::vector<std::string> split(std::string input, char delimiter); std::vector<std::string> split(std::string input, char delimiter);
std::string itoa(int n); std::string itos(int n);
std::string trim(std::string str, char c); std::string trim(std::string str, char c);
#endif #endif

116
srcs/Client.cpp Normal file
View File

@@ -0,0 +1,116 @@
#include "Client.hpp"
/*********************************************
* COPLIENS
*********************************************/
Client::Client( ) {
return;
}
Client::~Client() {
return;
}
// copy constructor :
// Client::Client( Client const & src ) {}
// assignement operator :
// Client & Client::operator=( Client const & rhs ) {}
/*********************************************
* PUBLIC MEMBER FUNCTIONS
*********************************************/
// http headers :
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
// https://www.tutorialspoint.com/http/http_requests.htm
void Client::parse_request()
{
std::string sub;
std::vector<std::string> list;
size_t pos;
pos = (raw_request).find("\r\n\r\n");
sub = (raw_request).substr(0, pos);
list = split(sub, '\n');
// request_line
_parse_request_line(*list.begin());
list.erase(list.begin());
// headers
_parse_request_headers(list);
//body- message
_parse_request_body(pos + 4);
}
std::string Client::get_method() { return _request.method; }
std::string Client::get_path() { return _request.path; }
std::string Client::get_version() { return _request.version; }
std::string Client::get_body() { return _request.body; }
std::string Client::get_headers(std::string key) { return _request.headers[key]; }
/*********************************************
* PRIVATE MEMBER FUNCTIONS
*********************************************/
void Client::_parse_request_line( std::string rline )
{
std::vector<std::string> sline;
std::string tmp;
sline = split(rline, ' ');
if (sline.size() != 3)
{
std::cerr << "err _parse_request_line(): ";
throw std::runtime_error("bad request-line header");
}
// method
tmp = ::trim(sline[0], ' ');
tmp = ::trim(tmp, '\r');
_request.method = tmp;
// TODO uri in request_line
// https://www.rfc-editor.org/rfc/rfc7230#section-5.3
// https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs
tmp = ::trim(sline[1], ' ');
tmp = ::trim(tmp, '\r');
_request.path = tmp;
// http version
tmp = ::trim(sline[2], ' ');
tmp = ::trim(tmp, '\r');
_request.version = tmp;
}
void Client::_parse_request_headers( std::vector<std::string> list )
{
std::string key;
std::string val;
std::vector<std::string>::iterator it;
size_t pos;
for (it = list.begin(); it != list.end(); it++)
{
pos = (*it).find(':');
key = (*it).substr( 0, pos );
key = ::trim(key, ' ');
key = ::trim(key, '\r');
val = (*it).substr( pos + 1 );
val = ::trim(val, ' ');
val = ::trim(val, '\r');
_request.headers.insert( std::pair<std::string, std::string>(key, val) );
}
}
void Client::_parse_request_body( size_t pos )
{
std::string body = &raw_request[pos];
_request.body = body;
}

View File

@@ -3,7 +3,27 @@
<?php <?php
echo "BEGIN PHP-CGI\n-----------\n\n"; echo "BEGIN PHP-CGI\n-----------\n\n";
//phpinfo(); //phpinfo();
echo getenv('REQUEST_METHOD');
echo $_POST['REQUEST_METHOD']; echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
echo "\nGATEWAY_INTERFACE: " . getenv("GATEWAY_INTERFACE");
echo "\nPATH_INFO: " . getenv("PATH_INFO");
echo "\nPATH_TRANSLATED: " . getenv("PATH_TRANSLATED");
echo "\nQUERY_STRING: " . getenv("QUERY_STRING");
echo "\nREMOTE_ADDR: " . getenv("REMOTE_ADDR");
echo "\nREMOTE_HOST: " . getenv("REMOTE_HOST");
echo "\nREMOTE_IDENT: " . getenv("REMOTE_IDENT");
echo "\nREMOTE_USER: " . getenv("REMOTE_USER");
echo "\nREQUEST_METHOD: " . getenv("REQUEST_METHOD");
echo "\nSCRIPT_NAME: " . getenv("SCRIPT_NAME");
echo "\nSERVER_NAME: " . getenv("SERVER_NAME");
echo "\nSERVER_PORT: " . getenv("SERVER_PORT");
echo "\nSERVER_PROTOCOL: " . getenv("SERVER_PROTOCOL");
echo "\nSERVER_SOFTWARE: " . getenv("SERVER_SOFTWARE");
echo "\nREDIRECT_STATUS: " . getenv("REDIRECT_STATUS");
// echo $_POST['REQUEST_METHOD'];
echo "\n\n-----------\nEND PHP-CGI\n\n"; echo "\n\n-----------\nEND PHP-CGI\n\n";
?> ?>

View File

@@ -21,7 +21,7 @@ std::string trim(std::string str, char c)
return str; return str;
} }
std::string itoa(int n) std::string itos(int n)
{ {
std::stringstream strs; std::stringstream strs;

View File

@@ -0,0 +1,80 @@
#include "Webserv.hpp"
bool Webserv::_is_cgi(Client *client)
{
if (client->get_path().find("/cgi-bin/") != std::string::npos)
return true;
return false;
}
void Webserv::_exec_cgi(Client *client)
{
char** env;
env = _set_env(client);
_exec_script(client, env);
// _construct_response(client);
}
char* Webserv::_dup_env(std::string var, std::string val = "")
{
std::string str;
str = var + "=" + val;
return ( strdup(str.c_str()) );
}
char** Webserv::_set_env(Client *client)
{
char** env = new char*[19];
env[0] = _dup_env("AUTH_TYPE");
env[1] = _dup_env("CONTENT_LENGTH", "665");
env[2] = _dup_env("CONTENT_TYPE");
env[3] = _dup_env("GATEWAY_INTERFACE");
env[4] = _dup_env("PATH_INFO");
env[5] = _dup_env("PATH_TRANSLATED");
env[6] = _dup_env("QUERY_STRING");
env[7] = _dup_env("REMOTE_ADDR");
env[8] = _dup_env("REMOTE_HOST", client->get_headers("Host")); // just test
env[9] = _dup_env("REMOTE_IDENT");
env[10] = _dup_env("REMOTE_USER");
env[11] = _dup_env("REQUEST_METHOD", client->get_method());
env[12] = _dup_env("SCRIPT_NAME");
env[13] = _dup_env("SERVER_NAME");
env[14] = _dup_env("SERVER_PORT");
env[15] = _dup_env("SERVER_PROTOCOL", client->get_version());
env[16] = _dup_env("SERVER_SOFTWARE");
env[17] = _dup_env("REDIRECT_STATUS");
env[18] = NULL;
return env;
}
void Webserv::_exec_script(Client *client, char **env)
{
int save_stdout;
char * const * nll = NULL;
// save STDOUT
save_stdout = dup(STDOUT_FILENO);
// inside child process
if (fork() == 0)
{
dup2(client->fd, STDOUT_FILENO);
// execve("./srcs/cgi-bin/cgi_cpp.cgi", nll, client->env);
execve("./srcs/cgi-bin/php-cgi", nll, env);
}
// inside parent process
else
waitpid(-1, NULL, 0);
// restore stdout
dup2(save_stdout, STDOUT_FILENO);
}
void Webserv::_construct_client(Client *client)
{
(void)client;
}

View File

@@ -32,87 +32,82 @@ void Webserv::_read_request(Client *client)
buf[ret] = '\0'; buf[ret] = '\0';
client->raw_request.append(buf); client->raw_request.append(buf);
_parse_request(client); client->parse_request();
// _parse_request(client);
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD, client); _epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD, client);
} }
// http headers : // // http headers :
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests // // https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
// https://www.tutorialspoint.com/http/http_requests.htm // // https://www.tutorialspoint.com/http/http_requests.htm
void Webserv::_parse_request(Client *client) // void Webserv::_parse_request(Client *client)
{ // {
std::string sub; // std::string sub;
std::vector<std::string> list; // std::vector<std::string> list;
size_t pos; // size_t pos;
//
pos = (client->raw_request).find("\r\n\r\n"); // pos = (client->raw_request).find("\r\n\r\n");
sub = (client->raw_request).substr(0, pos); // sub = (client->raw_request).substr(0, pos);
list = split(sub, '\n'); // list = split(sub, '\n');
// request_line // // request_line
_parse_request_line(client, *list.begin()); // _parse_request_line(client, *list.begin());
list.erase(list.begin()); // list.erase(list.begin());
// headers // // headers
_parse_request_headers(client, list); // _parse_request_headers(client, list);
// message-body // //body- message
client->request.insert( std::pair<std::string, std::string>("Body-Message", &client->raw_request[pos + 4]) ); // client->request.insert( std::pair<std::string, std::string>("Body-Message", &client->raw_request[pos + 4]) );
// }
// TMP DEBUG //
// std::map<std::string, std::string>::iterator itm; // void Webserv::_parse_request_line(Client *client, std::string rline)
// std::map<std::string, std::string>::iterator itm_end; // {
// std::cout << "\n====== REQUEST HEADERS\n"; // std::vector<std::string> sline;
// for (itm = client->request.begin(), itm_end = client->request.end(); itm != itm_end; itm++) // std::string tmp;
// std::cout << "[" << itm->first << "] [" << itm->second << "]\n"; //
// std::cout << "\n====== END\n"; // sline = split(rline, ' ');
} // if (sline.size() != 3)
// {
void Webserv::_parse_request_line(Client *client, std::string rline) // std::cerr << "err _parse_request_line(): ";
{ // throw std::runtime_error("bad request-line header");
std::vector<std::string> sline; // }
std::string tmp; // // method
// tmp = ::trim(sline[0], ' ');
sline = split(rline, ' '); // tmp = ::trim(tmp, '\r');
if (sline.size() != 3) // client->request.insert( std::pair<std::string, std::string>("Method", tmp) );
{ // // TODO uri in request_line
std::cerr << "err _parse_request_line(): "; // // https://www.rfc-editor.org/rfc/rfc7230#section-5.3
throw std::runtime_error("bad request-line header"); // // https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs
} // tmp = ::trim(sline[1], ' ');
// method // tmp = ::trim(tmp, '\r');
tmp = ::trim(sline[0], ' '); // client->request.insert(
tmp = ::trim(tmp, '\r'); // std::pair<std::string, std::string>("Request-URI", tmp) );
client->request.insert( std::pair<std::string, std::string>("Method", tmp) ); // // http version
// TODO uri in request_line : // tmp = ::trim(sline[2], ' ');
// https://www.rfc-editor.org/rfc/rfc7230#section-5.3 // tmp = ::trim(tmp, '\r');
// https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs // client->request.insert(
tmp = ::trim(sline[1], ' '); // std::pair<std::string, std::string>("HTTP-Version", tmp) );
tmp = ::trim(tmp, '\r'); // }
client->request.insert( std::pair<std::string, std::string>("Request-URI", tmp) ); //
// http version // void Webserv::_parse_request_headers(
tmp = ::trim(sline[2], ' '); // Client *client,
tmp = ::trim(tmp, '\r'); // std::vector<std::string> list )
client->request.insert( std::pair<std::string, std::string>("HTTP-Version", tmp) ); // {
} // std::string key;
// std::string val;
void Webserv::_parse_request_headers // std::vector<std::string>::iterator it;
( Client *client // size_t pos;
, std::vector<std::string> list) //
{ // for (it = list.begin(); it != list.end(); it++)
std::string key; // {
std::string val; // pos = (*it).find(':');
std::vector<std::string>::iterator it; // key = (*it).substr( 0, pos );
size_t pos; // key = ::trim(key, ' ');
// key = ::trim(key, '\r');
for (it = list.begin(); it != list.end(); it++) // val = (*it).substr( pos + 1 );
{ // val = ::trim(val, ' ');
pos = (*it).find(':'); // val = ::trim(val, '\r');
key = (*it).substr( 0, pos ); // client->request.insert( std::pair<std::string, std::string>(key, val) );
key = ::trim(key, ' '); // }
key = ::trim(key, '\r'); // }
val = (*it).substr( pos + 1 );
val = ::trim(val, ' ');
val = ::trim(val, '\r');
client->request.insert( std::pair<std::string, std::string>(key, val) );
}
}

View File

@@ -84,7 +84,17 @@ void Webserv::_get_ressource(Client *client)
{ {
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ? std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
char buf[MAX_FILESIZE+1]; char buf[MAX_FILESIZE+1];
const char *tmp; std::string tmp;
// TMP HUGO
//
if (_is_cgi(client))
{
_exec_cgi(client);
return;
}
//
// END TMP HUGO
// Mini parsing à l'arrache du PATH // Mini parsing à l'arrache du PATH
std::string path; std::string path;
@@ -128,15 +138,17 @@ void Webserv::_get_ressource(Client *client)
client->response.append("Content-Type: text/html; charset=UTF-8\r\n"); client->response.append("Content-Type: text/html; charset=UTF-8\r\n");
client->response.append("Content-Length: "); client->response.append("Content-Length: ");
tmp = ::itoa(ifd.gcount()).c_str();
client->response.append(tmp); tmp = ::itos(ifd.gcount());
client->response.append(tmp.c_str());
client->response.append("\r\n"); client->response.append("\r\n");
// Body // Body
client->response.append("\r\n"); client->response.append("\r\n");
client->response.append(buf); client->response.append(buf);
ifd.close(); ifd.close();
} }
} }