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 += $(HEADERS_D:%=-I%)
|
||||
CXXFLAGS += -std=c++98
|
||||
CXXFLAGS += -g
|
||||
CXXFLAGS += -g3
|
||||
CXXFLAGS += -MMD -MP #header dependencie
|
||||
#CXXFLAGS += -O3
|
||||
|
||||
@@ -30,6 +30,7 @@ SRCS = main.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
parsing_message_http.cpp \
|
||||
|
||||
OBJS_D = builds
|
||||
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
|
||||
|
||||
@@ -77,6 +103,37 @@
|
||||
## cgi rfc
|
||||
[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)
|
||||
|
||||
- The server [...] receives the request from the client
|
||||
@@ -124,10 +181,12 @@
|
||||
- local redirect response :
|
||||
- it must return only a Location field
|
||||
- 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
|
||||
- client redirect response :
|
||||
- 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
|
||||
- the absolute URI always start with the name of scheme followed by ":"
|
||||
- the http server must generate a 302 'Found' message
|
||||
- client redirect response with document
|
||||
- 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)
|
||||
|
||||
#### 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
|
||||
|
||||
@@ -16,6 +16,7 @@ server {
|
||||
location /board {
|
||||
allow_methods GET;
|
||||
root ./www/html;
|
||||
cgi_ext php cgi
|
||||
}
|
||||
|
||||
location /board/content {
|
||||
|
||||
253
srcs/Client.cpp
253
srcs/Client.cpp
@@ -6,13 +6,29 @@
|
||||
*********************************************/
|
||||
|
||||
Client::Client()
|
||||
: fd(0),
|
||||
lsocket(NULL),
|
||||
status(0),
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
read_body_size(0),
|
||||
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;
|
||||
}
|
||||
@@ -21,12 +37,33 @@ Client::~Client() {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy constructor :
|
||||
// Client::Client( Client const & src ) {}
|
||||
|
||||
// assignement operator :
|
||||
// Client & Client::operator=( Client const & rhs ) {}
|
||||
// WIP not sure fo what is more logic here
|
||||
Client::Client( Client const & src )
|
||||
: status ( src.status ),
|
||||
header_complete ( src.header_complete ),
|
||||
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
|
||||
void Client::parse_request()
|
||||
{
|
||||
std::string sub;
|
||||
std::vector<std::string> list;
|
||||
size_t pos;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
|
||||
pos = (raw_request).find(CRLF CRLF);
|
||||
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);
|
||||
// DEBUG
|
||||
std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n";
|
||||
|
||||
// add "raw_request.clear()" after parsing ? for little less memory usage ?
|
||||
_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;
|
||||
int len = script.size();
|
||||
std::string path = this->get_rq_abs_path();
|
||||
std::string tmp;
|
||||
|
||||
pos = path.find(script);
|
||||
if (pos == 0)
|
||||
{
|
||||
tmp = path.substr(0, pos + len);
|
||||
_request.script.path = "./srcs" + tmp; // TODO: root path ?
|
||||
_request.script.info = path.substr(pos + len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Client::clear()
|
||||
@@ -71,88 +121,135 @@ void Client::clear()
|
||||
|
||||
void Client::clear_request()
|
||||
{
|
||||
clear_script();
|
||||
_request.method = UNKNOWN;
|
||||
_request.path.clear();
|
||||
_request.uri.clear();
|
||||
_request.version.clear();
|
||||
_request.headers.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
|
||||
*********************************************/
|
||||
|
||||
void Client::_parse_request_line( std::string rline )
|
||||
void Client::_parse_request_line()
|
||||
{
|
||||
std::vector<std::string> sline;
|
||||
std::string tmp;
|
||||
std::vector<std::string> line;
|
||||
int ret;
|
||||
|
||||
sline = split(rline, ' ');
|
||||
if (sline.size() != 3)
|
||||
ret = ::parse_http_first_line(raw_request, line);
|
||||
if (ret != 3)
|
||||
{
|
||||
std::cerr << "err _parse_request_line(): ";
|
||||
throw std::runtime_error("bad request-line header");
|
||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
// 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 )
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
std::vector<std::string>::iterator it;
|
||||
size_t pos;
|
||||
|
||||
for (it = list.begin(); it != list.end(); it++)
|
||||
else
|
||||
{
|
||||
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) );
|
||||
_request.method = str_to_http_method(line[0]);
|
||||
_request.uri = line[1];
|
||||
_parse_request_uri(line[1]);
|
||||
_request.version = line[2];
|
||||
}
|
||||
}
|
||||
|
||||
void Client::_parse_request_body( size_t pos )
|
||||
void Client::_parse_request_uri( std::string uri )
|
||||
{
|
||||
// TODO : a revoir avec une std::string,
|
||||
// pour ne pas avoir le probleme d'un '0' qui marque la fin des données
|
||||
std::string body = &raw_request[pos];
|
||||
size_t pos;
|
||||
|
||||
_request.body = body;
|
||||
pos = uri.find("?");
|
||||
if (pos != std::string::npos)
|
||||
_request.query = uri.substr(pos + 1);
|
||||
else
|
||||
_request.query = "";
|
||||
_request.abs_path = uri.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_headers()
|
||||
{
|
||||
// TODO: check error and adjust status
|
||||
_request.headers = ::parse_http_headers(raw_request);
|
||||
}
|
||||
|
||||
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
|
||||
*********************************************/
|
||||
|
||||
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)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
{ return lhs.get_cl_fd() == fd; }
|
||||
bool operator==(int fd, const Client& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
{ return fd == rhs.get_cl_fd(); }
|
||||
|
||||
|
||||
@@ -6,55 +6,88 @@
|
||||
# include <string>
|
||||
# include <map>
|
||||
# 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 "ServerConfig.hpp"
|
||||
# include "parsing_message_http.hpp"
|
||||
|
||||
struct Script
|
||||
{
|
||||
std::string path;
|
||||
std::string info;
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
http_method method;
|
||||
std::string path;
|
||||
std::string uri;
|
||||
std::string abs_path;
|
||||
std::string query;
|
||||
std::string version;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
std::string port;
|
||||
std::string hostname;
|
||||
struct Script script;
|
||||
};
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
Client(int afd, listen_socket *lsocket, std::string aport, std::string aip);
|
||||
~Client();
|
||||
//Client(Client const &src);
|
||||
//Client &operator=(Client const &rhs);
|
||||
Client(Client const &src);
|
||||
Client &operator=(Client const &rhs);
|
||||
|
||||
int fd;
|
||||
const listen_socket *lsocket;
|
||||
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
|
||||
bool header_complete;
|
||||
size_t read_body_size;
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
bool header_complete;
|
||||
size_t read_body_size;
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *assigned_location;
|
||||
|
||||
// const functions ?
|
||||
http_method get_method();
|
||||
std::string &get_path();
|
||||
std::string &get_version();
|
||||
std::string &get_body();
|
||||
std::string &get_headers(const std::string &key);
|
||||
// getters
|
||||
int get_cl_fd() const;
|
||||
const std::string & get_cl_port() const;
|
||||
const std::string & get_cl_ip() const;
|
||||
const listen_socket * get_cl_lsocket() const;
|
||||
|
||||
// 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 clear();
|
||||
void clear_request();
|
||||
void clear_script();
|
||||
bool fill_script_path(std::string script);
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
std::string _port;
|
||||
std::string _ip;
|
||||
listen_socket * _lsocket;
|
||||
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 );
|
||||
void _parse_request_line();
|
||||
void _parse_request_headers();
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#! /usr/bin/php
|
||||
|
||||
<?php
|
||||
echo "Status: 200\r\n";
|
||||
echo "\r\n";
|
||||
echo "BEGIN PHP-CGI\n-----------\n\n";
|
||||
//phpinfo();
|
||||
|
||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
||||
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)
|
||||
{
|
||||
str = str.substr(str.find_first_not_of(c));
|
||||
str = str.substr(0, str.find_last_not_of(c) + 1);
|
||||
size_t pos;
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -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)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
bool operator==(int fd, const listen_socket& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <sys/stat.h> // stat()
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
|
||||
# define CR "\r"
|
||||
# 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);
|
||||
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 throw_test();
|
||||
std::string str_tolower(std::string str);
|
||||
void del_line_in_str(std::string * str, size_t pos, std::string delim);
|
||||
void throw_test();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# include <iostream> // cout, cin
|
||||
# include <cstring> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# 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 <algorithm> // find
|
||||
# include <string> // string
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
void signal_handler(int signum);
|
||||
void signal_handler(int signum);
|
||||
|
||||
// these might only be TMP
|
||||
# define FAILURE -1
|
||||
@@ -70,21 +70,23 @@ class Webserv
|
||||
|
||||
// accept.cpp
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
std::map<std::string, std::string>
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
// method_get.cpp
|
||||
void _get(Client *client);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
@@ -96,12 +98,15 @@ class Webserv
|
||||
void _delete(Client *client);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
// 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);
|
||||
bool _is_cgi(Client *client);
|
||||
std::string _exec_cgi(Client *client);
|
||||
char** _set_env(Client *client);
|
||||
char* _dup_env(std::string var, std::string val);
|
||||
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
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
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 _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
void Webserv::_accept_connection(listen_socket &lsocket)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
std::map<std::string, std::string> infos;
|
||||
|
||||
std::cerr << "accept()\n";
|
||||
addr_len = sizeof addr;
|
||||
@@ -19,9 +20,22 @@ void Webserv::_accept_connection(listen_socket &lsocket)
|
||||
}
|
||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
_clients.push_back(Client());
|
||||
_clients.back().fd = accepted_fd;
|
||||
_clients.back().lsocket = &lsocket;
|
||||
|
||||
infos = _extract_infos(addr);
|
||||
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||
_clients.push_back(client);
|
||||
_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)
|
||||
{
|
||||
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 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);
|
||||
_exec_script(client, env);
|
||||
// _construct_response(client);
|
||||
script_output = _exec_script(client, env);
|
||||
|
||||
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 = "")
|
||||
@@ -25,56 +34,145 @@ char* Webserv::_dup_env(std::string var, std::string val = "")
|
||||
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** 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", ::http_methods_to_str(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[0] = _dup_env("AUTH_TYPE"); // authentification not supported
|
||||
env[1] = _dup_env("CONTENT_LENGTH" , client->get_rq_body().size());
|
||||
env[2] = _dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type"));
|
||||
env[3] = _dup_env("GATEWAY_INTERFACE" , "CGI/1.1"); // https://www.rfc-editor.org/rfc/rfc3875
|
||||
env[4] = _dup_env("PATH_INFO" , client->get_rq_script_info());
|
||||
env[5] = _dup_env("PATH_TRANSLATED"); // not supported
|
||||
env[6] = _dup_env("QUERY_STRING" , client->get_rq_query());
|
||||
env[7] = _dup_env("REMOTE_ADDR" , client->get_cl_ip());
|
||||
env[8] = _dup_env("REMOTE_HOST" , client->get_rq_headers("Host")); // just test
|
||||
env[9] = _dup_env("REMOTE_IDENT"); // authentification not supported
|
||||
env[10] = _dup_env("REMOTE_USER"); // authentification not supported
|
||||
env[11] = _dup_env("REQUEST_METHOD" , client->get_rq_method_str());
|
||||
env[12] = _dup_env("SCRIPT_NAME" , client->get_rq_script_path());
|
||||
env[13] = _dup_env("SERVER_NAME" , client->get_rq_hostname());
|
||||
env[14] = _dup_env("SERVER_PORT" , client->get_rq_port());
|
||||
env[15] = _dup_env("SERVER_PROTOCOL" , client->get_rq_version());
|
||||
env[16] = _dup_env("SERVER_SOFTWARE" , "webser/1.0");
|
||||
env[17] = _dup_env("REDIRECT_STATUS" , "200");
|
||||
env[18] = NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
void Webserv::_exec_script(Client *client, char **env)
|
||||
std::string Webserv::_exec_script(Client *client, char **env)
|
||||
{
|
||||
int save_stdout;
|
||||
char * const * nll = NULL;
|
||||
#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]
|
||||
|
||||
// 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);
|
||||
pid_t pid;
|
||||
char buf[CGI_BUF_SIZE]; // WIP define buffer
|
||||
char * const * nll = NULL;
|
||||
std::string script_output;
|
||||
std::string body = client->get_rq_body();
|
||||
int fd_in[2];
|
||||
int fd_out[2];
|
||||
|
||||
pipe(fd_in);
|
||||
pipe(fd_out);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
std::cerr << "fork crashed" << std::endl;
|
||||
else if (pid == 0)
|
||||
{
|
||||
close(FD_WR_TO_CHLD);
|
||||
close(FD_RD_FR_CHLD);
|
||||
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
|
||||
{
|
||||
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);
|
||||
// restore stdout
|
||||
dup2(save_stdout, STDOUT_FILENO);
|
||||
|
||||
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::_construct_client(Client *client)
|
||||
void Webserv::_check_script_output(Client *client, std::string output)
|
||||
{
|
||||
(void)client;
|
||||
// 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())
|
||||
{
|
||||
// _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()");
|
||||
else
|
||||
std::cerr << "close fd " << _clients.back().fd << "\n";
|
||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||
_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("avi", "video/x-msvideo"));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ void Webserv::_delete(Client *client)
|
||||
WIP
|
||||
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);
|
||||
|
||||
/* 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
|
||||
path.insert(0, client->assigned_location->root);
|
||||
@@ -57,9 +57,15 @@ Where does cgi fit in in all this ???
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
std::string script_output;
|
||||
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;
|
||||
}
|
||||
//
|
||||
|
||||
@@ -8,7 +8,7 @@ void Webserv::_post(Client *client)
|
||||
WIP
|
||||
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);
|
||||
|
||||
/* 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.
|
||||
// Need to _determine_process_server() as soon as possible,
|
||||
// like in _read_request() for stopping read if body is too big ?
|
||||
ofd << client->get_body();
|
||||
ofd << client->get_rq_body();
|
||||
if (!ofd)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
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;
|
||||
|
||||
std::cerr << "call recv()" << "\n" ;
|
||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
|
||||
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err recv()");
|
||||
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;
|
||||
}
|
||||
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
|
||||
return READ_COMPLETE;
|
||||
client->assigned_server = _determine_process_server(client);
|
||||
client->assigned_location = _determine_location(*client->assigned_server, client->get_path());
|
||||
if (client->get_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
client->assigned_location = _determine_location(*client->assigned_server, client->get_rq_abs_path());
|
||||
if (client->get_rq_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
{ // TODO : move in Client parsing ?
|
||||
client->status = 505;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
if (!client->get_headers("Content-Length").empty()
|
||||
&& ::atoi(client->get_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
|
||||
if (!client->get_rq_headers("Content-Length").empty()
|
||||
&& ::atoi(client->get_rq_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
@@ -87,7 +87,7 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
client->status = 413;
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ void Webserv::_response(Client *client)
|
||||
|
||||
if (ret == SEND_CLOSE)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
else if (ret == SEND_COMPLETE)
|
||||
{
|
||||
if (client->get_headers("Connection") == "close")
|
||||
_close_client(client->fd);
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
||||
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
}
|
||||
@@ -45,11 +45,11 @@ int Webserv::_send_response(Client *client)
|
||||
_error_html_response(client);
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
if (client->get_headers("Connection") == "close")
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
client->response.append("Connection: close" CRLF);
|
||||
else
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
@@ -70,7 +70,7 @@ void Webserv::_append_base_headers(Client *client)
|
||||
void Webserv::_construct_response(Client *client)
|
||||
{
|
||||
// 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;
|
||||
return;
|
||||
@@ -83,7 +83,7 @@ void Webserv::_construct_response(Client *client)
|
||||
client->response.append(client->assigned_location->redirect_uri);
|
||||
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.
|
||||
// (for codes 301, 302, 303, 307, and 308)
|
||||
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 << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
||||
if (client->get_method() == UNKNOWN)
|
||||
if (client->get_rq_method() == UNKNOWN)
|
||||
{
|
||||
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):
|
||||
_get(client); break;
|
||||
@@ -181,14 +181,14 @@ ServerConfig *Webserv::_determine_process_server(Client *client)
|
||||
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 default_server = _servers.end();
|
||||
|
||||
while (it != _servers.end())
|
||||
{
|
||||
if (it->host == client->lsocket->host
|
||||
&& it->port == client->lsocket->port)
|
||||
if (it->host == client->get_cl_lsocket()->host
|
||||
&& it->port == client->get_cl_lsocket()->port)
|
||||
{
|
||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||
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