Merge branch 'hugo2'
+ changed the appropriate getters for client + moved the functions to parse the http message (first line, headers, and body) outside client, in parsing_http_message.cpp + resolved some of the throw problems
This commit is contained in:
3
Makefile
3
Makefile
@@ -5,7 +5,7 @@ CXX = c++
|
|||||||
CXXFLAGS = -Wall -Wextra #-Werror
|
CXXFLAGS = -Wall -Wextra #-Werror
|
||||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||||
CXXFLAGS += -std=c++98
|
CXXFLAGS += -std=c++98
|
||||||
CXXFLAGS += -g
|
CXXFLAGS += -g3
|
||||||
CXXFLAGS += -MMD -MP #header dependencie
|
CXXFLAGS += -MMD -MP #header dependencie
|
||||||
#CXXFLAGS += -O3
|
#CXXFLAGS += -O3
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ SRCS = main.cpp \
|
|||||||
utils.cpp \
|
utils.cpp \
|
||||||
cgi_script.cpp \
|
cgi_script.cpp \
|
||||||
Client.cpp \
|
Client.cpp \
|
||||||
|
parsing_message_http.cpp \
|
||||||
|
|
||||||
OBJS_D = builds
|
OBJS_D = builds
|
||||||
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
||||||
|
|||||||
69
README.md
69
README.md
@@ -1,4 +1,30 @@
|
|||||||
|
|
||||||
|
## work together
|
||||||
|
|
||||||
|
#### TODO hugo
|
||||||
|
- `_is_cgi()` and `_fill_cgi_path()`
|
||||||
|
- two cgi tests :
|
||||||
|
? - a basic form with "name" and "something", that return a html page with that
|
||||||
|
? - for GET and POST
|
||||||
|
? - a script called by a file extension in URI
|
||||||
|
|
||||||
|
#### questions
|
||||||
|
- in client, fd is in privat, so accesible by getter, is it ok ?
|
||||||
|
- in client.cpp i fill the port, is there a default one in case it's not in the request ?
|
||||||
|
- timeout server but still works ?
|
||||||
|
- path contains double "//" from `Webserv::_get()` in response.cpp
|
||||||
|
- cgi path ? defined in config ? and root path ? :
|
||||||
|
- `Client.cpp : fill_script_path()`
|
||||||
|
- `cgi.cpp : is_cgi()`
|
||||||
|
- `cgi.cpp : set_env()`
|
||||||
|
- what if the uri contains a php file, and the config said php must be handled by cgi, but the path to this php in the uri is wrong ?
|
||||||
|
- is it ok ? `http://my_site.com/cgi-bin/php-cgi` (real path)
|
||||||
|
- is it ok ? `http://my_site.com/php-cgi` (reconstruct path ?)
|
||||||
|
- is it ok ? `http://my_site.com/something/php-cgi` (what about 'something' ?)
|
||||||
|
- is it ok ? `http://my_site.com/something/cgi-bin/php-cgi` (real path with 'something' before ? )
|
||||||
|
- I don't save the STDIN and STDOUT before dup2 in child process, is it wrong ?
|
||||||
|
- the response page is received long after the cgi-script is done, why ?
|
||||||
|
|
||||||
---
|
---
|
||||||
## man
|
## man
|
||||||
|
|
||||||
@@ -77,6 +103,37 @@
|
|||||||
## cgi rfc
|
## cgi rfc
|
||||||
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||||
|
|
||||||
|
#### output cgi script :
|
||||||
|
! TODO : change all the '\n' by '\r\n'
|
||||||
|
! TODO : there is at least one header field followed by '\r\n\r\n' :
|
||||||
|
- "Content-Type"
|
||||||
|
- "Location"
|
||||||
|
- "Status"
|
||||||
|
! TODO : there is no space between filed name and ":"
|
||||||
|
! TODO?: handle Location field, either :
|
||||||
|
- local : start with '/' --> rerun the request with new uri
|
||||||
|
- client : start with '<scheme>:' --> send back status code 302
|
||||||
|
-> TODO : there is no field duplicate (resolve conflicts)
|
||||||
|
-> TODO : if status field, change server status for this one
|
||||||
|
-> TODO : if no Location field && no Status field -> status code = 200
|
||||||
|
|
||||||
|
#### summary :
|
||||||
|
- the cgi-script will send back at least one header field followed by an empty line
|
||||||
|
- this header field will be one of three :
|
||||||
|
- "Content-Type"
|
||||||
|
- "Location"
|
||||||
|
- "Status"
|
||||||
|
- the cgi-script may send back more header fields
|
||||||
|
- the server must check and modify few things :
|
||||||
|
- there is no duplicate in headers fields (if there is, resolve conflict)
|
||||||
|
- there is no space between the field name and the ":"
|
||||||
|
- the newlines are of form "\r\n", and not "\n" only
|
||||||
|
- if the location field is not present, then if the status field is not present either, then the status code is 200
|
||||||
|
- the cgi-script can return a location field, of two types :
|
||||||
|
- local redirection : start with a "/", the server must answer as if this was the request uri
|
||||||
|
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
|
||||||
|
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||||
|
|
||||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||||
|
|
||||||
- The server [...] receives the request from the client
|
- The server [...] receives the request from the client
|
||||||
@@ -124,10 +181,12 @@
|
|||||||
- local redirect response :
|
- local redirect response :
|
||||||
- it must return only a Location field
|
- it must return only a Location field
|
||||||
- it contains a local path URI and query string ('local-pathquery')
|
- it contains a local path URI and query string ('local-pathquery')
|
||||||
|
- the local path URI must start with a "/"
|
||||||
- the server must generate the response for this local-pathquery
|
- the server must generate the response for this local-pathquery
|
||||||
- client redirect response :
|
- client redirect response :
|
||||||
- it must return only a Location field
|
- it must return only a Location field
|
||||||
- it contains an absolute URI path, to indicate the client that it should reprocess the request with this URI
|
- it contains an absolute URI path, to indicate the client that it should reprocess the request with this URI
|
||||||
|
- the absolute URI always start with the name of scheme followed by ":"
|
||||||
- the http server must generate a 302 'Found' message
|
- the http server must generate a 302 'Found' message
|
||||||
- client redirect response with document
|
- client redirect response with document
|
||||||
- it must return a Location field with an absolute URI path
|
- it must return a Location field with an absolute URI path
|
||||||
@@ -172,16 +231,6 @@
|
|||||||
|
|
||||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||||
|
|
||||||
#### questions :
|
|
||||||
- le cgi-script doit renvoyer au moins un header suivit d'une ligne vide
|
|
||||||
- il peut dans certains cas envoyer d'autres headers
|
|
||||||
- le serveur doit verifier qu'il n'y a pas de doublons dans les headers
|
|
||||||
- le serveur doit verifier le formatage des headers (typiquement l'encodage, par exemple pour les newlines)
|
|
||||||
- ? comment on passe le body-message au script ? section 4.2
|
|
||||||
- ? on doit gerer l'authentification ?
|
|
||||||
- ? pourquoi on doit construire un script-cgi ? section 3.3
|
|
||||||
- ? si l'uri correspond au script-cgi, ca appel le script donc ? section 3.3
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## cgi env variables
|
## cgi env variables
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ server {
|
|||||||
location /board {
|
location /board {
|
||||||
allow_methods GET;
|
allow_methods GET;
|
||||||
root ./www/html;
|
root ./www/html;
|
||||||
|
cgi_ext php cgi
|
||||||
}
|
}
|
||||||
|
|
||||||
location /board/content {
|
location /board/content {
|
||||||
|
|||||||
249
srcs/Client.cpp
249
srcs/Client.cpp
@@ -6,13 +6,29 @@
|
|||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
Client::Client()
|
Client::Client()
|
||||||
: fd(0),
|
: status(0),
|
||||||
lsocket(NULL),
|
|
||||||
status(0),
|
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
read_body_size(0),
|
read_body_size(0),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL)
|
assigned_location(NULL),
|
||||||
|
_fd(0),
|
||||||
|
_port(""),
|
||||||
|
_ip(""),
|
||||||
|
_lsocket(NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||||
|
: status(0),
|
||||||
|
header_complete(false),
|
||||||
|
read_body_size(0),
|
||||||
|
assigned_server(NULL),
|
||||||
|
assigned_location(NULL),
|
||||||
|
_fd(afd),
|
||||||
|
_port(aport),
|
||||||
|
_ip(aip),
|
||||||
|
_lsocket(lsocket)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -21,12 +37,33 @@ Client::~Client() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy constructor :
|
// WIP not sure fo what is more logic here
|
||||||
// Client::Client( Client const & src ) {}
|
Client::Client( Client const & src )
|
||||||
|
: status ( src.status ),
|
||||||
// assignement operator :
|
header_complete ( src.header_complete ),
|
||||||
// Client & Client::operator=( Client const & rhs ) {}
|
read_body_size ( src.read_body_size ),
|
||||||
|
assigned_server ( src.assigned_server ),
|
||||||
|
assigned_location ( src.assigned_location ),
|
||||||
|
_fd ( src._fd ),
|
||||||
|
_port ( src._port ),
|
||||||
|
_ip ( src._ip ),
|
||||||
|
_lsocket ( src._lsocket )
|
||||||
|
{
|
||||||
|
raw_request = src.raw_request;
|
||||||
|
response = src.response;
|
||||||
|
// buf = strdup(src.buf); // TODO: this doesn't work
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WIP placeholder because of const values
|
||||||
|
Client & Client::operator=( Client const & rhs )
|
||||||
|
{
|
||||||
|
if ( this != &rhs )
|
||||||
|
{
|
||||||
|
// stuff
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
@@ -39,22 +76,35 @@ Client::~Client() {
|
|||||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||||
void Client::parse_request()
|
void Client::parse_request()
|
||||||
{
|
{
|
||||||
std::string sub;
|
std::map<std::string, std::string> headers;
|
||||||
std::vector<std::string> list;
|
std::string body;
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n";
|
||||||
|
|
||||||
|
_parse_request_line();
|
||||||
|
_parse_request_headers();
|
||||||
|
_parse_request_body();
|
||||||
|
_parse_port_hostname(this->get_rq_headers("Host"));
|
||||||
|
raw_request.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::fill_script_path(std::string script)
|
||||||
|
{
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
int len = script.size();
|
||||||
|
std::string path = this->get_rq_abs_path();
|
||||||
|
std::string tmp;
|
||||||
|
|
||||||
pos = (raw_request).find(CRLF CRLF);
|
pos = path.find(script);
|
||||||
sub = (raw_request).substr(0, pos);
|
if (pos == 0)
|
||||||
list = split(sub, '\n');
|
{
|
||||||
// request_line
|
tmp = path.substr(0, pos + len);
|
||||||
_parse_request_line(*list.begin());
|
_request.script.path = "./srcs" + tmp; // TODO: root path ?
|
||||||
list.erase(list.begin());
|
_request.script.info = path.substr(pos + len);
|
||||||
// headers
|
return true;
|
||||||
_parse_request_headers(list);
|
}
|
||||||
//body- message
|
return false;
|
||||||
_parse_request_body(pos + 4);
|
|
||||||
|
|
||||||
// add "raw_request.clear()" after parsing ? for little less memory usage ?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clear()
|
void Client::clear()
|
||||||
@@ -71,88 +121,135 @@ void Client::clear()
|
|||||||
|
|
||||||
void Client::clear_request()
|
void Client::clear_request()
|
||||||
{
|
{
|
||||||
|
clear_script();
|
||||||
_request.method = UNKNOWN;
|
_request.method = UNKNOWN;
|
||||||
_request.path.clear();
|
_request.uri.clear();
|
||||||
_request.version.clear();
|
_request.version.clear();
|
||||||
_request.headers.clear();
|
_request.headers.clear();
|
||||||
_request.body.clear();
|
_request.body.clear();
|
||||||
|
_request.abs_path.clear();
|
||||||
|
_request.query.clear();
|
||||||
|
_request.port.clear();
|
||||||
|
_request.hostname.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::clear_script()
|
||||||
|
{
|
||||||
|
_request.script.path.clear();
|
||||||
|
_request.script.info.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************
|
||||||
|
* GETTERS
|
||||||
|
*********************************************/
|
||||||
|
|
||||||
|
// client side
|
||||||
|
int Client::get_cl_fd() const { return _fd; }
|
||||||
|
const std::string & Client::get_cl_ip() const { return _ip; }
|
||||||
|
const std::string & Client::get_cl_port() const { return _port; }
|
||||||
|
const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
|
||||||
|
|
||||||
|
// requette
|
||||||
|
http_method Client::get_rq_method() const { return _request.method; }
|
||||||
|
std::string Client::get_rq_method_str() const
|
||||||
|
{ return ::http_methods_to_str(_request.method); }
|
||||||
|
std::string Client::get_rq_uri() const { return _request.uri; }
|
||||||
|
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
||||||
|
std::string Client::get_rq_query() const { return _request.query; }
|
||||||
|
std::string Client::get_rq_version() const { return _request.version; }
|
||||||
|
std::string Client::get_rq_body() const { return _request.body; }
|
||||||
|
std::string Client::get_rq_port() const { return _request.port; }
|
||||||
|
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
||||||
|
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
||||||
|
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
||||||
|
std::string Client::get_rq_headers(const std::string & key) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
|
||||||
|
it = _request.headers.find(::str_tolower(key));
|
||||||
|
if (it == _request.headers.end())
|
||||||
|
return "";
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_method 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(const std::string &key) { return _request.headers[key]; }
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* PRIVATE MEMBER FUNCTIONS
|
* PRIVATE MEMBER FUNCTIONS
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
void Client::_parse_request_line( std::string rline )
|
void Client::_parse_request_line()
|
||||||
{
|
{
|
||||||
std::vector<std::string> sline;
|
std::vector<std::string> line;
|
||||||
std::string tmp;
|
int ret;
|
||||||
|
|
||||||
sline = split(rline, ' ');
|
ret = ::parse_http_first_line(raw_request, line);
|
||||||
if (sline.size() != 3)
|
if (ret != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "err _parse_request_line(): ";
|
std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n";
|
||||||
throw std::runtime_error("bad request-line header");
|
status = 400; // "bad request"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_request.method = str_to_http_method(line[0]);
|
||||||
|
_request.uri = line[1];
|
||||||
|
_parse_request_uri(line[1]);
|
||||||
|
_request.version = line[2];
|
||||||
}
|
}
|
||||||
// method
|
|
||||||
tmp = ::trim(sline[0], ' ');
|
|
||||||
tmp = ::trim(tmp, '\r');
|
|
||||||
_request.method = str_to_http_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 )
|
void Client::_parse_request_uri( std::string uri )
|
||||||
{
|
{
|
||||||
std::string key;
|
|
||||||
std::string val;
|
|
||||||
std::vector<std::string>::iterator it;
|
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
for (it = list.begin(); it != list.end(); it++)
|
pos = uri.find("?");
|
||||||
{
|
if (pos != std::string::npos)
|
||||||
pos = (*it).find(':');
|
_request.query = uri.substr(pos + 1);
|
||||||
key = (*it).substr( 0, pos );
|
else
|
||||||
key = ::trim(key, ' ');
|
_request.query = "";
|
||||||
key = ::trim(key, '\r');
|
_request.abs_path = uri.substr(0, pos);
|
||||||
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 )
|
void Client::_parse_request_headers()
|
||||||
{
|
{
|
||||||
// TODO : a revoir avec une std::string,
|
// TODO: check error and adjust status
|
||||||
// pour ne pas avoir le probleme d'un '0' qui marque la fin des données
|
_request.headers = ::parse_http_headers(raw_request);
|
||||||
std::string body = &raw_request[pos];
|
|
||||||
|
|
||||||
_request.body = body;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::_parse_request_body()
|
||||||
|
{
|
||||||
|
// TODO: check error and adjust status
|
||||||
|
_request.body = ::parse_http_body(raw_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::_parse_port_hostname(std::string host)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
if (host == "")
|
||||||
|
std::cerr << "no host\n";
|
||||||
|
|
||||||
|
pos = host.find(':');
|
||||||
|
// port :
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
_request.port = "4040"; // TODO: make equal to default port in config
|
||||||
|
else
|
||||||
|
_request.port = host.substr(pos);
|
||||||
|
if (_request.port == ":")
|
||||||
|
_request.port = "";
|
||||||
|
// hostname :
|
||||||
|
_request.hostname = host.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* OVERLOAD
|
* OVERLOAD
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
bool operator==(const Client& lhs, const Client& rhs)
|
bool operator==(const Client& lhs, const Client& rhs)
|
||||||
{ return lhs.fd == rhs.fd; }
|
{ return lhs.get_cl_fd() == rhs.get_cl_fd(); }
|
||||||
|
|
||||||
bool operator==(const Client& lhs, int fd)
|
bool operator==(const Client& lhs, int fd)
|
||||||
{ return lhs.fd == fd; }
|
{ return lhs.get_cl_fd() == fd; }
|
||||||
|
|
||||||
bool operator==(int fd, const Client& rhs)
|
bool operator==(int fd, const Client& rhs)
|
||||||
{ return fd == rhs.fd; }
|
{ return fd == rhs.get_cl_fd(); }
|
||||||
|
|
||||||
|
|||||||
@@ -6,55 +6,88 @@
|
|||||||
# include <string>
|
# include <string>
|
||||||
# include <map>
|
# include <map>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
# include <string.h> // strdup
|
||||||
|
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||||
|
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
# include "ServerConfig.hpp"
|
# include "ServerConfig.hpp"
|
||||||
|
# include "parsing_message_http.hpp"
|
||||||
|
|
||||||
|
struct Script
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
std::string info;
|
||||||
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
http_method method;
|
http_method method;
|
||||||
std::string path;
|
std::string uri;
|
||||||
|
std::string abs_path;
|
||||||
|
std::string query;
|
||||||
std::string version;
|
std::string version;
|
||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
|
std::string port;
|
||||||
|
std::string hostname;
|
||||||
|
struct Script script;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Client();
|
Client();
|
||||||
|
Client(int afd, listen_socket *lsocket, std::string aport, std::string aip);
|
||||||
~Client();
|
~Client();
|
||||||
//Client(Client const &src);
|
Client(Client const &src);
|
||||||
//Client &operator=(Client const &rhs);
|
Client &operator=(Client const &rhs);
|
||||||
|
|
||||||
int fd;
|
|
||||||
const listen_socket *lsocket;
|
|
||||||
|
|
||||||
std::string raw_request;
|
std::string raw_request;
|
||||||
std::string response;
|
std::string response;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
|
|
||||||
bool header_complete;
|
bool header_complete;
|
||||||
size_t read_body_size;
|
size_t read_body_size;
|
||||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||||
const LocationConfig *assigned_location;
|
const LocationConfig *assigned_location;
|
||||||
|
|
||||||
// const functions ?
|
// getters
|
||||||
http_method get_method();
|
int get_cl_fd() const;
|
||||||
std::string &get_path();
|
const std::string & get_cl_port() const;
|
||||||
std::string &get_version();
|
const std::string & get_cl_ip() const;
|
||||||
std::string &get_body();
|
const listen_socket * get_cl_lsocket() const;
|
||||||
std::string &get_headers(const std::string &key);
|
|
||||||
|
// requests getters
|
||||||
|
http_method get_rq_method() const;
|
||||||
|
std::string get_rq_method_str() const;
|
||||||
|
std::string get_rq_uri() const;
|
||||||
|
std::string get_rq_abs_path() const;
|
||||||
|
std::string get_rq_query() const;
|
||||||
|
std::string get_rq_version() const;
|
||||||
|
std::string get_rq_body() const;
|
||||||
|
std::string get_rq_port() const;
|
||||||
|
std::string get_rq_hostname() const;
|
||||||
|
std::string get_rq_script_path() const;
|
||||||
|
std::string get_rq_script_info() const;
|
||||||
|
std::string get_rq_headers(const std::string & key) const;
|
||||||
|
|
||||||
void parse_request();
|
void parse_request();
|
||||||
void clear();
|
void clear();
|
||||||
void clear_request();
|
void clear_request();
|
||||||
|
void clear_script();
|
||||||
|
bool fill_script_path(std::string script);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int _fd;
|
||||||
|
std::string _port;
|
||||||
|
std::string _ip;
|
||||||
|
listen_socket * _lsocket;
|
||||||
struct Request _request;
|
struct Request _request;
|
||||||
|
|
||||||
void _parse_request_line( std::string rline );
|
void _parse_request_line();
|
||||||
void _parse_request_headers( std::vector<std::string> list );
|
void _parse_request_headers();
|
||||||
void _parse_request_body( size_t pos );
|
void _parse_request_body();
|
||||||
|
void _parse_request_uri( std::string uri );
|
||||||
|
void _parse_port_hostname(std::string host);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,3 +96,4 @@ bool operator==(const Client& lhs, int fd);
|
|||||||
bool operator==(int fd, const Client& rhs);
|
bool operator==(int fd, const Client& rhs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#! /usr/bin/php
|
#! /usr/bin/php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
echo "Status: 200\r\n";
|
||||||
|
echo "\r\n";
|
||||||
echo "BEGIN PHP-CGI\n-----------\n\n";
|
echo "BEGIN PHP-CGI\n-----------\n\n";
|
||||||
//phpinfo();
|
|
||||||
|
|
||||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||||
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
||||||
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
|
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
|
||||||
|
|||||||
@@ -23,8 +23,19 @@ std::vector<std::string> split(std::string input, char delimiter)
|
|||||||
|
|
||||||
std::string trim(std::string str, char c)
|
std::string trim(std::string str, char c)
|
||||||
{
|
{
|
||||||
str = str.substr(str.find_first_not_of(c));
|
size_t pos;
|
||||||
str = str.substr(0, str.find_last_not_of(c) + 1);
|
|
||||||
|
// delete leadings c
|
||||||
|
pos = str.find_first_not_of(c);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return str;
|
||||||
|
str = str.substr(pos);
|
||||||
|
|
||||||
|
// delete endings c
|
||||||
|
pos = str.find_last_not_of(c);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return str;
|
||||||
|
str = str.substr(0, pos + 1);
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@@ -137,8 +148,37 @@ void replace_all_substr(std::string &str, const std::string &ori_substr, const s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string str_tolower(std::string str)
|
||||||
|
{
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void del_line_in_str(std::string * str, size_t pos, std::string delim)
|
||||||
|
{
|
||||||
|
size_t begin;
|
||||||
|
size_t end;
|
||||||
|
|
||||||
|
begin = (*str).rfind(delim, pos);
|
||||||
|
if (begin == std::string::npos)
|
||||||
|
begin = 0;
|
||||||
|
else
|
||||||
|
begin += delim.size();
|
||||||
|
|
||||||
|
end = (*str).find(delim, pos);
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = 0;
|
||||||
|
else
|
||||||
|
end += delim.size();
|
||||||
|
|
||||||
|
(*str).erase(begin, end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool operator==(const listen_socket& lhs, int fd)
|
bool operator==(const listen_socket& lhs, int fd)
|
||||||
{ return lhs.fd == fd; }
|
{ return lhs.fd == fd; }
|
||||||
|
|
||||||
bool operator==(int fd, const listen_socket& rhs)
|
bool operator==(int fd, const listen_socket& rhs)
|
||||||
{ return fd == rhs.fd; }
|
{ return fd == rhs.fd; }
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
# include <sstream>
|
# include <sstream>
|
||||||
# include <cstdlib> // atoi
|
# include <cstdlib> // atoi
|
||||||
# include <sys/stat.h> // stat()
|
# include <sys/stat.h> // stat()
|
||||||
|
# include <cctype> // tolower
|
||||||
|
# include <algorithm> // transform
|
||||||
|
|
||||||
# define CR "\r"
|
# define CR "\r"
|
||||||
# define LF "\n"
|
# define LF "\n"
|
||||||
@@ -41,6 +43,8 @@ http_method str_to_http_method(std::string &str);
|
|||||||
std::string http_methods_to_str(unsigned int methods);
|
std::string http_methods_to_str(unsigned int methods);
|
||||||
int path_is_valid(std::string path);
|
int path_is_valid(std::string path);
|
||||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||||
|
std::string str_tolower(std::string str);
|
||||||
|
void del_line_in_str(std::string * str, size_t pos, std::string delim);
|
||||||
void throw_test();
|
void throw_test();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
# include <cstring> // memset
|
# include <cstring> // memset
|
||||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr
|
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||||
# include <netinet/in.h> // sockaddr_in
|
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||||
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
|
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
|
||||||
# include <algorithm> // find
|
# include <algorithm> // find
|
||||||
# include <string> // string
|
# include <string> // string
|
||||||
@@ -70,6 +70,8 @@ class Webserv
|
|||||||
|
|
||||||
// accept.cpp
|
// accept.cpp
|
||||||
void _accept_connection(listen_socket &lsocket);
|
void _accept_connection(listen_socket &lsocket);
|
||||||
|
std::map<std::string, std::string>
|
||||||
|
_extract_infos(struct sockaddr_in addr);
|
||||||
// request.cpp
|
// request.cpp
|
||||||
void _request(Client *client);
|
void _request(Client *client);
|
||||||
int _read_request(Client *client);
|
int _read_request(Client *client);
|
||||||
@@ -97,11 +99,14 @@ class Webserv
|
|||||||
void _delete_file(Client *client, const std::string &path);
|
void _delete_file(Client *client, const std::string &path);
|
||||||
// cgi_script.cpp
|
// cgi_script.cpp
|
||||||
bool _is_cgi(Client *client);
|
bool _is_cgi(Client *client);
|
||||||
void _exec_cgi(Client *client);
|
std::string _exec_cgi(Client *client);
|
||||||
void _construct_client(Client *client);
|
|
||||||
char** _set_env(Client *client);
|
char** _set_env(Client *client);
|
||||||
char* _dup_env(std::string var, std::string val);
|
char* _dup_env(std::string var, std::string val);
|
||||||
void _exec_script(Client *client, char **env);
|
char* _dup_env(std::string var, int i);
|
||||||
|
std::string _exec_script(Client *client, char **env);
|
||||||
|
void _check_script_output(Client *client, std::string output);
|
||||||
|
void _check_script_status(Client *client, std::string output);
|
||||||
|
void _check_script_fields(Client *client, std::string output);
|
||||||
// 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);
|
||||||
@@ -116,6 +121,8 @@ class Webserv
|
|||||||
void _listen(int socket_fd, unsigned int max_connections);
|
void _listen(int socket_fd, unsigned int max_connections);
|
||||||
void _init_http_status_map();
|
void _init_http_status_map();
|
||||||
void _init_mime_types_map();
|
void _init_mime_types_map();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ void Webserv::_accept_connection(listen_socket &lsocket)
|
|||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addr_len;
|
socklen_t addr_len;
|
||||||
int accepted_fd;
|
int accepted_fd;
|
||||||
|
std::map<std::string, std::string> infos;
|
||||||
|
|
||||||
std::cerr << "accept()\n";
|
std::cerr << "accept()\n";
|
||||||
addr_len = sizeof addr;
|
addr_len = sizeof addr;
|
||||||
@@ -19,9 +20,22 @@ void Webserv::_accept_connection(listen_socket &lsocket)
|
|||||||
}
|
}
|
||||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
_clients.push_back(Client());
|
infos = _extract_infos(addr);
|
||||||
_clients.back().fd = accepted_fd;
|
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||||
_clients.back().lsocket = &lsocket;
|
_clients.push_back(client);
|
||||||
|
|
||||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string>
|
||||||
|
Webserv::_extract_infos(struct sockaddr_in addr)
|
||||||
|
{
|
||||||
|
struct in_addr ip_conversion;
|
||||||
|
std::map<std::string, std::string> infos;
|
||||||
|
|
||||||
|
infos["port"] = ::itos( addr.sin_port );
|
||||||
|
ip_conversion.s_addr = addr.sin_addr.s_addr;
|
||||||
|
infos["ip"] = inet_ntoa( ip_conversion );
|
||||||
|
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,27 @@
|
|||||||
|
|
||||||
bool Webserv::_is_cgi(Client *client)
|
bool Webserv::_is_cgi(Client *client)
|
||||||
{
|
{
|
||||||
if (client->get_path().find("/cgi-bin/") != std::string::npos)
|
// TODO see how it works with config
|
||||||
|
if (client->fill_script_path("/cgi-bin/php-cgi"))
|
||||||
|
return true;
|
||||||
|
if (client->fill_script_path("/cgi-bin/cgi_cpp.cgi"))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_exec_cgi(Client *client)
|
std::string Webserv::_exec_cgi(Client *client)
|
||||||
{
|
{
|
||||||
char** env;
|
char** env;
|
||||||
|
std::string script_output;
|
||||||
|
|
||||||
env = _set_env(client);
|
env = _set_env(client);
|
||||||
_exec_script(client, env);
|
script_output = _exec_script(client, env);
|
||||||
// _construct_response(client);
|
|
||||||
|
for (int i = 0; env[i]; i++)
|
||||||
|
delete[] env[i];
|
||||||
|
delete[] env;
|
||||||
|
|
||||||
|
return script_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Webserv::_dup_env(std::string var, std::string val = "")
|
char* Webserv::_dup_env(std::string var, std::string val = "")
|
||||||
@@ -25,56 +34,145 @@ char* Webserv::_dup_env(std::string var, std::string val = "")
|
|||||||
return ( strdup(str.c_str()) );
|
return ( strdup(str.c_str()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* Webserv::_dup_env(std::string var, int i)
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::string val;
|
||||||
|
|
||||||
|
val = ::itos(i);
|
||||||
|
str = var + "=" + val;
|
||||||
|
// TODO change strdup for something with new
|
||||||
|
return ( strdup(str.c_str()) );
|
||||||
|
}
|
||||||
|
|
||||||
char** Webserv::_set_env(Client *client)
|
char** Webserv::_set_env(Client *client)
|
||||||
{
|
{
|
||||||
char** env = new char*[19];
|
char** env = new char*[19];
|
||||||
|
|
||||||
env[0] = _dup_env("AUTH_TYPE");
|
env[0] = _dup_env("AUTH_TYPE"); // authentification not supported
|
||||||
env[1] = _dup_env("CONTENT_LENGTH", "665");
|
env[1] = _dup_env("CONTENT_LENGTH" , client->get_rq_body().size());
|
||||||
env[2] = _dup_env("CONTENT_TYPE");
|
env[2] = _dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type"));
|
||||||
env[3] = _dup_env("GATEWAY_INTERFACE");
|
env[3] = _dup_env("GATEWAY_INTERFACE" , "CGI/1.1"); // https://www.rfc-editor.org/rfc/rfc3875
|
||||||
env[4] = _dup_env("PATH_INFO");
|
env[4] = _dup_env("PATH_INFO" , client->get_rq_script_info());
|
||||||
env[5] = _dup_env("PATH_TRANSLATED");
|
env[5] = _dup_env("PATH_TRANSLATED"); // not supported
|
||||||
env[6] = _dup_env("QUERY_STRING");
|
env[6] = _dup_env("QUERY_STRING" , client->get_rq_query());
|
||||||
env[7] = _dup_env("REMOTE_ADDR");
|
env[7] = _dup_env("REMOTE_ADDR" , client->get_cl_ip());
|
||||||
env[8] = _dup_env("REMOTE_HOST", client->get_headers("Host")); // just test
|
env[8] = _dup_env("REMOTE_HOST" , client->get_rq_headers("Host")); // just test
|
||||||
env[9] = _dup_env("REMOTE_IDENT");
|
env[9] = _dup_env("REMOTE_IDENT"); // authentification not supported
|
||||||
env[10] = _dup_env("REMOTE_USER");
|
env[10] = _dup_env("REMOTE_USER"); // authentification not supported
|
||||||
env[11] = _dup_env("REQUEST_METHOD", ::http_methods_to_str(client->get_method()));
|
env[11] = _dup_env("REQUEST_METHOD" , client->get_rq_method_str());
|
||||||
env[12] = _dup_env("SCRIPT_NAME");
|
env[12] = _dup_env("SCRIPT_NAME" , client->get_rq_script_path());
|
||||||
env[13] = _dup_env("SERVER_NAME");
|
env[13] = _dup_env("SERVER_NAME" , client->get_rq_hostname());
|
||||||
env[14] = _dup_env("SERVER_PORT");
|
env[14] = _dup_env("SERVER_PORT" , client->get_rq_port());
|
||||||
env[15] = _dup_env("SERVER_PROTOCOL", client->get_version());
|
env[15] = _dup_env("SERVER_PROTOCOL" , client->get_rq_version());
|
||||||
env[16] = _dup_env("SERVER_SOFTWARE");
|
env[16] = _dup_env("SERVER_SOFTWARE" , "webser/1.0");
|
||||||
env[17] = _dup_env("REDIRECT_STATUS");
|
env[17] = _dup_env("REDIRECT_STATUS" , "200");
|
||||||
env[18] = NULL;
|
env[18] = NULL;
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_exec_script(Client *client, char **env)
|
std::string Webserv::_exec_script(Client *client, char **env)
|
||||||
{
|
{
|
||||||
int save_stdout;
|
#define RD 0
|
||||||
|
#define WR 1
|
||||||
|
#define CGI_BUF_SIZE 10
|
||||||
|
#define FD_WR_TO_CHLD fd_in[WR]
|
||||||
|
#define FD_WR_TO_PRNT fd_out[WR]
|
||||||
|
#define FD_RD_FR_CHLD fd_out[RD]
|
||||||
|
#define FD_RD_FR_PRNT fd_in[RD]
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
char buf[CGI_BUF_SIZE]; // WIP define buffer
|
||||||
char * const * nll = NULL;
|
char * const * nll = NULL;
|
||||||
|
std::string script_output;
|
||||||
|
std::string body = client->get_rq_body();
|
||||||
|
int fd_in[2];
|
||||||
|
int fd_out[2];
|
||||||
|
|
||||||
// save STDOUT
|
pipe(fd_in);
|
||||||
save_stdout = dup(STDOUT_FILENO);
|
pipe(fd_out);
|
||||||
// inside child process
|
|
||||||
if (fork() == 0)
|
pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
std::cerr << "fork crashed" << std::endl;
|
||||||
|
else if (pid == 0)
|
||||||
{
|
{
|
||||||
dup2(client->fd, STDOUT_FILENO);
|
close(FD_WR_TO_CHLD);
|
||||||
// execve("./srcs/cgi-bin/cgi_cpp.cgi", nll, client->env);
|
close(FD_RD_FR_CHLD);
|
||||||
execve("./srcs/cgi-bin/php-cgi", nll, env);
|
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
||||||
|
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
||||||
|
// DEBUG
|
||||||
|
std::cerr << "execve:\n";
|
||||||
|
execve(client->get_rq_script_path().c_str(), nll, env);
|
||||||
|
// for tests execve crash :
|
||||||
|
//execve("wrong", nll, env);
|
||||||
|
std::cerr << "execve crashed.\n";
|
||||||
}
|
}
|
||||||
// inside parent process
|
|
||||||
else
|
else
|
||||||
waitpid(-1, NULL, 0);
|
|
||||||
// restore stdout
|
|
||||||
dup2(save_stdout, STDOUT_FILENO);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Webserv::_construct_client(Client *client)
|
|
||||||
{
|
{
|
||||||
(void)client;
|
close(FD_RD_FR_PRNT);
|
||||||
|
close(FD_WR_TO_PRNT);
|
||||||
|
write(FD_WR_TO_CHLD, body.c_str(), body.size());
|
||||||
|
close(FD_WR_TO_CHLD);
|
||||||
|
waitpid(-1, NULL, 0);
|
||||||
|
|
||||||
|
memset(buf, '\0', CGI_BUF_SIZE);
|
||||||
|
while (read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE - 1) > 0)
|
||||||
|
{
|
||||||
|
script_output += buf;
|
||||||
|
memset(buf, '\0', CGI_BUF_SIZE);
|
||||||
|
}
|
||||||
|
close(FD_RD_FR_CHLD);
|
||||||
|
}
|
||||||
|
if (script_output.empty())
|
||||||
|
script_output = "Status: 500\r\n\r\n";
|
||||||
|
return script_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_check_script_output(Client *client, std::string output)
|
||||||
|
{
|
||||||
|
// TODO: it doesn't work with execve error, i don't know why yet ?
|
||||||
|
_check_script_status(client, output);
|
||||||
|
_check_script_fields(client, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_check_script_status(Client *client, std::string output)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
int status_pos;
|
||||||
|
|
||||||
|
pos = output.find("Status:");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
status_pos = pos + std::string("Status:").size();
|
||||||
|
client->status = atoi(output.c_str() + status_pos);
|
||||||
|
::del_line_in_str(&output, pos, CRLF);
|
||||||
|
}
|
||||||
|
client->status = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_check_script_fields(Client *client, std::string output)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> srv_fld; // server_field
|
||||||
|
std::map<std::string, std::string> scr_fld; // script_field
|
||||||
|
std::map<std::string, std::string>::iterator it_srv;
|
||||||
|
std::map<std::string, std::string>::iterator it_scr;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
srv_fld = parse_http_headers(client->response);
|
||||||
|
scr_fld = parse_http_headers(output);
|
||||||
|
// wip: compare both map to supress duplicates
|
||||||
|
for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++)
|
||||||
|
{
|
||||||
|
for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++)
|
||||||
|
{
|
||||||
|
if (it_srv->first == it_scr->first)
|
||||||
|
{
|
||||||
|
pos = client->response.find(it_srv->first);
|
||||||
|
::del_line_in_str(&client->response, pos, CRLF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ void Webserv::_close_all_clients()
|
|||||||
while (!_clients.empty())
|
while (!_clients.empty())
|
||||||
{
|
{
|
||||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||||
if (::close(_clients.back().fd) == -1)
|
if (::close(_clients.back().get_cl_fd()) == -1)
|
||||||
std::perror("err close()");
|
std::perror("err close()");
|
||||||
else
|
else
|
||||||
std::cerr << "close fd " << _clients.back().fd << "\n";
|
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||||
_clients.pop_back();
|
_clients.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,3 +237,4 @@ void Webserv::_init_mime_types_map()
|
|||||||
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
|
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
|
||||||
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
|
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ void Webserv::_delete(Client *client)
|
|||||||
WIP
|
WIP
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_path();
|
std::string path = client->get_rq_abs_path();
|
||||||
path.insert(0, client->assigned_location->root);
|
path.insert(0, client->assigned_location->root);
|
||||||
|
|
||||||
/* CGI Here ? */
|
/* CGI Here ? */
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Where does cgi fit in in all this ???
|
|||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_path();
|
std::string path = client->get_rq_abs_path();
|
||||||
|
|
||||||
// this might not be the best thing, a voir
|
// this might not be the best thing, a voir
|
||||||
path.insert(0, client->assigned_location->root);
|
path.insert(0, client->assigned_location->root);
|
||||||
@@ -57,9 +57,15 @@ Where does cgi fit in in all this ???
|
|||||||
|
|
||||||
// TMP HUGO
|
// TMP HUGO
|
||||||
//
|
//
|
||||||
|
std::string script_output;
|
||||||
if (_is_cgi(client))
|
if (_is_cgi(client))
|
||||||
{
|
{
|
||||||
_exec_cgi(client);
|
script_output = _exec_cgi(client);
|
||||||
|
// DEBUG
|
||||||
|
std::cout << "\n____script_output____\n" << script_output << "\n_______________\n";
|
||||||
|
// wip check output of script
|
||||||
|
_check_script_output(client, script_output);
|
||||||
|
client->response += script_output;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ void Webserv::_post(Client *client)
|
|||||||
WIP
|
WIP
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_path();
|
std::string path = client->get_rq_abs_path();
|
||||||
path.insert(0, client->assigned_location->root);
|
path.insert(0, client->assigned_location->root);
|
||||||
|
|
||||||
/* CGI Here ? */
|
/* CGI Here ? */
|
||||||
@@ -46,7 +46,7 @@ void Webserv::_post_file(Client *client, const std::string &path)
|
|||||||
// Maybe usefull in _read_request() for rejecting too big content.
|
// Maybe usefull in _read_request() for rejecting too big content.
|
||||||
// Need to _determine_process_server() as soon as possible,
|
// Need to _determine_process_server() as soon as possible,
|
||||||
// like in _read_request() for stopping read if body is too big ?
|
// like in _read_request() for stopping read if body is too big ?
|
||||||
ofd << client->get_body();
|
ofd << client->get_rq_body();
|
||||||
if (!ofd)
|
if (!ofd)
|
||||||
{
|
{
|
||||||
std::cerr << path << ": ofd.write fail" << '\n';
|
std::cerr << path << ": ofd.write fail" << '\n';
|
||||||
|
|||||||
90
srcs/webserv/parsing_message_http.cpp
Normal file
90
srcs/webserv/parsing_message_http.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
#include "parsing_message_http.hpp"
|
||||||
|
|
||||||
|
size_t
|
||||||
|
parse_http_first_line(std::string message, std::vector<std::string> &line)
|
||||||
|
{
|
||||||
|
std::vector<std::string> sline;
|
||||||
|
std::string sub;
|
||||||
|
std::string tmp;
|
||||||
|
size_t pos;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
// TODO: check for err in substr
|
||||||
|
pos = message.find(CRLF);
|
||||||
|
sub = message.substr(0, pos);
|
||||||
|
sline = ::split(sub, ' ');
|
||||||
|
ret = sline.size();
|
||||||
|
if (ret != 3)
|
||||||
|
return ret;
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
tmp = ::trim(sline[i], ' ');
|
||||||
|
tmp = ::trim(tmp, '\r');
|
||||||
|
line.push_back(tmp);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string>
|
||||||
|
parse_http_headers(std::string message)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::vector<std::string> list;
|
||||||
|
std::vector<std::string>::iterator it;
|
||||||
|
std::string sub;
|
||||||
|
std::string key;
|
||||||
|
std::string val;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
pos = (message).find(CRLF CRLF);
|
||||||
|
sub = (message).substr(0, pos);
|
||||||
|
list = ::split(sub, '\n');
|
||||||
|
if ( maybe_http_first_line( *list.begin() ) )
|
||||||
|
list.erase(list.begin());
|
||||||
|
|
||||||
|
for (it = list.begin(); it != list.end(); it++)
|
||||||
|
{
|
||||||
|
// TODO: if pattern is not "NAME: value" return error
|
||||||
|
pos = (*it).find(':');
|
||||||
|
key = (*it).substr( 0, pos );
|
||||||
|
key = ::trim(key, ' ');
|
||||||
|
key = ::trim(key, '\r');
|
||||||
|
key = ::str_tolower(key);
|
||||||
|
val = (*it).substr( pos + 1 );
|
||||||
|
val = ::trim(val, ' ');
|
||||||
|
val = ::trim(val, '\r');
|
||||||
|
headers.insert( std::pair<std::string, std::string>(key, val) );
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
parse_http_body(std::string message)
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
pos = message.find(CRLF CRLF);
|
||||||
|
pos += std::string(CRLF CRLF).size();
|
||||||
|
// TODO: copying just like that might fail in case of binary or images
|
||||||
|
body = message.substr(pos);
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maybe_http_first_line(std::string str)
|
||||||
|
{
|
||||||
|
// method SP target SP version https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.1
|
||||||
|
// version SP status SP reason https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.2
|
||||||
|
|
||||||
|
std::vector<std::string> sline;
|
||||||
|
|
||||||
|
sline = ::split(str, ' ');
|
||||||
|
if (sline.size() != 3)
|
||||||
|
return false;
|
||||||
|
if (sline[0].find(':') != std::string::npos)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
36
srcs/webserv/parsing_message_http.hpp
Normal file
36
srcs/webserv/parsing_message_http.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
#ifndef PARSING_MESSAGE_HTTP_HPP
|
||||||
|
# define PARSING_MESSAGE_HTTP_HPP
|
||||||
|
|
||||||
|
# include <iostream>
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
# include <map>
|
||||||
|
# include "utils.hpp"
|
||||||
|
|
||||||
|
size_t
|
||||||
|
parse_http_first_line(std::string message, std::vector<std::string> &line);
|
||||||
|
|
||||||
|
std::map<std::string, std::string>
|
||||||
|
parse_http_headers(std::string message);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
parse_http_body(std::string message);
|
||||||
|
|
||||||
|
bool
|
||||||
|
maybe_http_first_line(std::string);
|
||||||
|
|
||||||
|
// http message structure :
|
||||||
|
//
|
||||||
|
// start-line
|
||||||
|
// request-line
|
||||||
|
// method SP target SP version
|
||||||
|
// response-line
|
||||||
|
// version SP status SP reason
|
||||||
|
// header-fields
|
||||||
|
// name ":" SP value
|
||||||
|
// CRLF
|
||||||
|
// body
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
2
srcs/webserv/parsing_request.cpp
Normal file
2
srcs/webserv/parsing_request.cpp
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#include "parsing_request.hpp"
|
||||||
|
|
||||||
@@ -20,11 +20,11 @@ void Webserv::_request(Client *client)
|
|||||||
|
|
||||||
if (ret == READ_CLOSE)
|
if (ret == READ_CLOSE)
|
||||||
{
|
{
|
||||||
_close_client(client->fd);
|
_close_client(client->get_cl_fd());
|
||||||
}
|
}
|
||||||
else if (ret == READ_COMPLETE)
|
else if (ret == READ_COMPLETE)
|
||||||
{
|
{
|
||||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
|
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
|||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
std::cerr << "call recv()" << "\n" ;
|
std::cerr << "call recv()" << "\n" ;
|
||||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
||||||
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
|
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
std::perror("err recv()");
|
std::perror("err recv()");
|
||||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||||
return READ_CLOSE;
|
return READ_CLOSE;
|
||||||
}
|
}
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
@@ -60,14 +60,14 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
|||||||
if (client->status) // WIP, need to change client->parse_request() for status update
|
if (client->status) // WIP, need to change client->parse_request() for status update
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
client->assigned_server = _determine_process_server(client);
|
client->assigned_server = _determine_process_server(client);
|
||||||
client->assigned_location = _determine_location(*client->assigned_server, client->get_path());
|
client->assigned_location = _determine_location(*client->assigned_server, client->get_rq_abs_path());
|
||||||
if (client->get_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
if (client->get_rq_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||||
{ // TODO : move in Client parsing ?
|
{ // TODO : move in Client parsing ?
|
||||||
client->status = 505;
|
client->status = 505;
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
}
|
}
|
||||||
if (!client->get_headers("Content-Length").empty()
|
if (!client->get_rq_headers("Content-Length").empty()
|
||||||
&& ::atoi(client->get_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
|
&& ::atoi(client->get_rq_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
|
||||||
{
|
{
|
||||||
client->status = 413;
|
client->status = 413;
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
@@ -87,7 +87,7 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
|||||||
client->status = 413;
|
client->status = 413;
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
}
|
}
|
||||||
if ((int)client->read_body_size >= ::atoi(client->get_headers("Content-Length").c_str()))
|
if ((int)client->read_body_size >= ::atoi(client->get_rq_headers("Content-Length").c_str()))
|
||||||
{
|
{
|
||||||
client->parse_request(); // reparse for the body
|
client->parse_request(); // reparse for the body
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
@@ -95,7 +95,7 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (client->header_complete && client->get_headers("Content-Type").empty() && client->get_headers("Content-Length").empty() )
|
if (client->header_complete && client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty() )
|
||||||
{
|
{
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ void Webserv::_response(Client *client)
|
|||||||
|
|
||||||
if (ret == SEND_CLOSE)
|
if (ret == SEND_CLOSE)
|
||||||
{
|
{
|
||||||
_close_client(client->fd);
|
_close_client(client->get_cl_fd());
|
||||||
}
|
}
|
||||||
else if (ret == SEND_COMPLETE)
|
else if (ret == SEND_COMPLETE)
|
||||||
{
|
{
|
||||||
if (client->get_headers("Connection") == "close")
|
if (client->get_rq_headers("Connection") == "close")
|
||||||
_close_client(client->fd);
|
_close_client(client->get_cl_fd());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||||
client->clear();
|
client->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,11 +45,11 @@ int Webserv::_send_response(Client *client)
|
|||||||
_error_html_response(client);
|
_error_html_response(client);
|
||||||
|
|
||||||
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
||||||
ret = ::send(client->fd, client->response.c_str(), client->response.size(), 0);
|
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
std::perror("err send()");
|
std::perror("err send()");
|
||||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||||
return SEND_CLOSE;
|
return SEND_CLOSE;
|
||||||
}
|
}
|
||||||
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
||||||
@@ -61,7 +61,7 @@ void Webserv::_append_base_headers(Client *client)
|
|||||||
{
|
{
|
||||||
client->response.append("Server: Webserv/0.1" CRLF);
|
client->response.append("Server: Webserv/0.1" CRLF);
|
||||||
|
|
||||||
if (client->get_headers("Connection") == "close")
|
if (client->get_rq_headers("Connection") == "close")
|
||||||
client->response.append("Connection: close" CRLF);
|
client->response.append("Connection: close" CRLF);
|
||||||
else
|
else
|
||||||
client->response.append("Connection: keep-alive" CRLF);
|
client->response.append("Connection: keep-alive" CRLF);
|
||||||
@@ -70,7 +70,7 @@ void Webserv::_append_base_headers(Client *client)
|
|||||||
void Webserv::_construct_response(Client *client)
|
void Webserv::_construct_response(Client *client)
|
||||||
{
|
{
|
||||||
// TODO : Move this in read(), stop read if content too large
|
// TODO : Move this in read(), stop read if content too large
|
||||||
if (client->get_body().size() > client->assigned_server->client_body_limit)
|
if (client->get_rq_body().size() > client->assigned_server->client_body_limit)
|
||||||
{
|
{
|
||||||
client->status = 413;
|
client->status = 413;
|
||||||
return;
|
return;
|
||||||
@@ -83,7 +83,7 @@ void Webserv::_construct_response(Client *client)
|
|||||||
client->response.append(client->assigned_location->redirect_uri);
|
client->response.append(client->assigned_location->redirect_uri);
|
||||||
client->response.append(CRLF);
|
client->response.append(CRLF);
|
||||||
} */
|
} */
|
||||||
if (client->get_path().find("redirect_test") != std::string::npos) // Test block
|
if (client->get_rq_abs_path().find("redirect_test") != std::string::npos) // Test block
|
||||||
{ // Weird behavior. The web browser seems to wait for a complete response until timeout.
|
{ // Weird behavior. The web browser seems to wait for a complete response until timeout.
|
||||||
// (for codes 301, 302, 303, 307, and 308)
|
// (for codes 301, 302, 303, 307, and 308)
|
||||||
client->status = 307;
|
client->status = 307;
|
||||||
@@ -100,13 +100,13 @@ void Webserv::_process_method(Client *client)
|
|||||||
{
|
{
|
||||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||||
std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
||||||
if (client->get_method() == UNKNOWN)
|
if (client->get_rq_method() == UNKNOWN)
|
||||||
{
|
{
|
||||||
client->status = 501;
|
client->status = 501;
|
||||||
}
|
}
|
||||||
else if (client->assigned_location->allow_methods & client->get_method())
|
else if (client->assigned_location->allow_methods & client->get_rq_method())
|
||||||
{
|
{
|
||||||
switch (client->get_method())
|
switch (client->get_rq_method())
|
||||||
{
|
{
|
||||||
case (GET):
|
case (GET):
|
||||||
_get(client); break;
|
_get(client); break;
|
||||||
@@ -181,14 +181,14 @@ ServerConfig *Webserv::_determine_process_server(Client *client)
|
|||||||
TODO : test it
|
TODO : test it
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::string &server_name = client->get_headers("Host");
|
std::string const &server_name = client->get_rq_headers("Host");
|
||||||
std::vector<ServerConfig>::iterator it = _servers.begin();
|
std::vector<ServerConfig>::iterator it = _servers.begin();
|
||||||
std::vector<ServerConfig>::iterator default_server = _servers.end();
|
std::vector<ServerConfig>::iterator default_server = _servers.end();
|
||||||
|
|
||||||
while (it != _servers.end())
|
while (it != _servers.end())
|
||||||
{
|
{
|
||||||
if (it->host == client->lsocket->host
|
if (it->host == client->get_cl_lsocket()->host
|
||||||
&& it->port == client->lsocket->port)
|
&& it->port == client->get_cl_lsocket()->port)
|
||||||
{
|
{
|
||||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||||
break;
|
break;
|
||||||
|
|||||||
13
www/form_get.html
Normal file
13
www/form_get.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method="get">
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
13
www/form_post.html
Normal file
13
www/form_post.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method="post">
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
Reference in New Issue
Block a user