merged and added colors to makefile
This commit is contained in:
26
Makefile
26
Makefile
@@ -1,6 +1,6 @@
|
||||
|
||||
NAME = webserv
|
||||
CXX = c++
|
||||
CXX = clang++
|
||||
|
||||
CXXFLAGS = -Wall -Wextra #-Werror
|
||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||
@@ -30,7 +30,6 @@ SRCS = main.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
parsing_message_http.cpp \
|
||||
|
||||
OBJS_D = builds
|
||||
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
||||
@@ -44,12 +43,14 @@ all: $(NAME)
|
||||
|
||||
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
|
||||
|
||||
$(OBJS_D):
|
||||
mkdir $@
|
||||
|
||||
$(NAME): $(OBJS)
|
||||
$(CXX) $^ -o $(NAME)
|
||||
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJS_D)
|
||||
@@ -63,3 +64,24 @@ re: fclean all
|
||||
|
||||
-include $(DEPS) # header dependencie
|
||||
|
||||
.SILENT:
|
||||
|
||||
# ------------------
|
||||
# ----- COLORS -----
|
||||
# ------------------
|
||||
|
||||
_GREY=$ \033[30m
|
||||
_RED=$ \033[31m
|
||||
_GREEN=$ \033[32m
|
||||
_YELLOW=$ \033[33m
|
||||
_BLUE=$ \033[34m
|
||||
_PURPLE=$ \033[35m
|
||||
_CYAN=$ \033[36m
|
||||
_WHITE=$ \033[37m
|
||||
_END=$ \033[0m
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
85
README.md
85
README.md
@@ -9,20 +9,6 @@
|
||||
? - 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 ?
|
||||
|
||||
---
|
||||
@@ -104,18 +90,6 @@
|
||||
[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
|
||||
@@ -125,13 +99,18 @@
|
||||
- "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
|
||||
- there is no field duplicate (resolve conflicts)
|
||||
- there is no space between filed name and ":"
|
||||
- change all the '\n' by '\r\n'
|
||||
- if no Location field && no Status field -> status code = 200
|
||||
- handle Location field, either :
|
||||
- local : start with '/' --> rerun the request with new uri
|
||||
- client : start with '<scheme>:' --> send back status code 302
|
||||
- there is at least one header field followed by '\r\n\r\n' :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
- if status field, change server status for this one
|
||||
- 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)
|
||||
@@ -262,6 +241,46 @@ SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
|
||||
REDIRECT_STATUS : for exemple, 200
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
## http errors
|
||||
|
||||
#### HTTP Client Errors
|
||||
- 400 Bad Request This error code indicates that the request cannot be processed because of wrong syntax usage by the client.
|
||||
- 401 Unauthorized This error code indicates that the client is not authorized to receive the requested data, without authentication. A login name and password based authentication might be required to access the requested data.
|
||||
- 403 Forbidden There is no way you can access the requested data. A 403 error announces that the data is off limits.
|
||||
- 404 Not Found This error indicates that the resources requested by the client are currently unavailable.
|
||||
- 405 Method Not Allowed This error indicates wrong usage of request method. Depending on the kind of data requested, the appropriate request method must be chosen.
|
||||
- 406 Not Acceptable When the data provided by a web server does not match the specifications made in ‘Accept’ header of the client HTTP request, this error is the result.
|
||||
- 407 Proxy Authentication Required This error clearly indicates that an authentication from the proxy server is required to gain access to requested resources.
|
||||
- 408 Request Timeout This type of error indicates that the client was delayed in making a request, within the specified time allocated to it, by the server.
|
||||
- 409 Conflict This error code is displayed when the server perceives a conflict between two requests made simultaneously by different clients, for the same resource.
|
||||
- 410 Gone This error code indicates that the requested data is no longer hosted on the server and therefore further requests made for it, would be futile.
|
||||
- 411 Length Required If the request made by the client does not include information about the length of the requested data or resource, this error code is displayed.
|
||||
- 412 Precondition Failed Some requests made by clients come attached with conditions that need to be satisfied by the server, before data transaction may happen. If these conditions are not met, error 412 results.
|
||||
- 413 Request Entity Too Large When a client makes a request which is too overwhelming for the server’s resources to handle, it presents this error code.
|
||||
- 414 Requested URI Too Long A Uniform Resource Identifier (URI) is a character string used to describe a data stream or resource on a server. Error 414 occurs when the server is unable to process the URI, because of limited resources and long string length.
|
||||
- 415 Unsupported Media Type A server may be designed to allow only certain formats for media files. When error 415 is displayed, it indicates that the format of file being uploaded through a client request, does not match the requisite format.
|
||||
- 416 Request Range Not Satisfiable Sometimes, a client may request for only a small part of a file, instead of asking for the entire file. If this request is not specified properly and the part of the file requested does not exist, this error is displayed.
|
||||
- 417 Expectation Failed This error code is displayed when the server cannot meet the specifications provided in the request.
|
||||
- 422 Unprocessable Entity This error is displayed when the request made, cannot be processed due to an error in semantic structure.
|
||||
- 423 Locked This error is displayed when a requested piece of data or resource has been locked, making it inaccessible for a server.
|
||||
- 424 Failed Dependency A server may process a succession of requests from a client with the fulfillment of each, dependent on the one made before. This error is displayed when a request made before is not fulfilled, due to which the current request cannot be processed.
|
||||
- 426 Upgrade Required This error signifies that the client may need to switch over to a secure protocol like TLS to get the request processed.
|
||||
- 444 No Response This error signifies that the server has simply rejected the client request and terminated connection.
|
||||
- 449 Retry With This is a request made by the server to the client, to make the request again after executing certain actions or making specific changes in request. This is an error code introduced by Microsoft.
|
||||
- 499 Client Closed Request When client terminates a connection made with the server, while its processing the associated request, this error code is displayed.
|
||||
- 450 Blocked By Windows Parental Controls Another error code introduced by Microsoft, this one is displayed when a URL is blocked by parental control settings on the web browsers.
|
||||
|
||||
#### HTTP Server Errors
|
||||
- 500 Internal Server Error A generic message displayed by the server, when the problem with the request cannot be specified by any other appropriate code.
|
||||
- 501 Not Implemented This error indicates the inability of the server to process a request, as it hasn’t been configured to respond to the request method used.
|
||||
- 502 Bad Gateway Sometimes, hosted pages on web servers are transmitted to client via proxy servers. If the proxy server (to which a client has sent a request), fails connecting with the web server (known as the upstream server), error 502 results.
|
||||
- 503 Service Unavailable When the server is already overloaded with multiple requests, it will temporarily stop entertaining new requests, by displaying a 503 error code.
|
||||
- 504 Gateway Timeout When the request made by a proxy server to the web server hosting a resource times out, error 504 is reported.
|
||||
- 505 HTTP Version Not Supported An error code seen rarely, it is displayed when the web server does not support the protocol version of the client request.
|
||||
|
||||
|
||||
---
|
||||
## ressources
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/Yeah/not_happy.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/nop/other.pouic
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/nop/youpi.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/youpi.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/youpi.bla
|
||||
|
||||
8
memo.txt
8
memo.txt
@@ -1,12 +1,16 @@
|
||||
IN 42 SUBJECT AND/OR PRIORITY :
|
||||
- CGI
|
||||
- chunked request (response not mandatory it seems)
|
||||
- Need to test normal body parsing
|
||||
- Ecrire des tests !
|
||||
- handle redirection (Work, but weird behavior need deeper test)
|
||||
- upload files with config "upload_dir"
|
||||
- _determine_location() review (New version to complete and test)
|
||||
- replace atoi() with a better function to avoid overflow
|
||||
like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/
|
||||
- replace std::string::npos with macro NPOS ?
|
||||
-----------------------------
|
||||
Si ce n'est pas deja fait :
|
||||
- dans config, check erreur si port > 16bits
|
||||
(peut-être check si ip > 32bits)
|
||||
-----------------------------
|
||||
- gerer le champ "Accept" du client
|
||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||
|
||||
212
srcs/Client.cpp
212
srcs/Client.cpp
@@ -8,6 +8,7 @@
|
||||
Client::Client()
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
@@ -23,6 +24,7 @@ Client::Client()
|
||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
@@ -77,37 +79,127 @@ Client & Client::operator=( Client const & rhs )
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||
void Client::parse_request(std::vector<ServerConfig> &servers)
|
||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
|
||||
// DEBUG
|
||||
// std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n";
|
||||
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||
return ;
|
||||
header_complete = true;
|
||||
clear_request(); // not mandatory
|
||||
|
||||
_parse_request_line();
|
||||
if (status)
|
||||
return;
|
||||
_parse_request_headers();
|
||||
_parse_request_fields();
|
||||
|
||||
// DEBUG
|
||||
print_client("headers");
|
||||
|
||||
if (status)
|
||||
return;
|
||||
assigned_server = ::_determine_process_server(this, servers);
|
||||
assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
|
||||
_check_request_errors();
|
||||
if (status)
|
||||
return;
|
||||
_parse_port_hostname(this->get_rq_headers("Host"));
|
||||
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
||||
|
||||
/* dont clear raw_request, we need it for future reparsing of body
|
||||
see call of parse_request() in _read_request() */
|
||||
// DEBUG
|
||||
std::cerr << get_rq_method_str() << " " << get_rq_uri() << " " << get_rq_version() << "\n";
|
||||
|
||||
// dont clear raw_request, we need it for future reparsing of body
|
||||
// see call of parse_request() in _read_request()
|
||||
// raw_request.clear();
|
||||
}
|
||||
|
||||
void Client::parse_request_body()
|
||||
{
|
||||
// TODO: check error and adjust status
|
||||
_request.body = ::parse_http_body(raw_request);
|
||||
size_t pos;
|
||||
|
||||
pos = raw_request.find(CRLF CRLF);
|
||||
if (pos == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body() bad call, header incomplete\n";
|
||||
// QUESTION from hugo : don't we change the status here ?
|
||||
return;
|
||||
}
|
||||
pos += CRLF_SIZE*2;
|
||||
|
||||
// Chunked decoding WIP. Dont work.
|
||||
if (!get_rq_headers("Transfer-Encoding").empty()
|
||||
&& get_rq_headers("Transfer-Encoding") == "chunked")
|
||||
{
|
||||
size_t chunk_size = 1;
|
||||
size_t chunk_field_end = 0;
|
||||
char *endptr = NULL;
|
||||
char *endptr_copy = NULL;
|
||||
/* TODO: verify if last chunk in raw_request (to avoid multiples complete parsing)
|
||||
but how ? with "raw_request.rfind("0" CRLF CRLF)", there no confirmation
|
||||
that we have found the last last-chunk OR just some data */
|
||||
|
||||
_request.body = raw_request.substr(pos);
|
||||
|
||||
std::cerr << "______Chunked\n" << _request.body << "\n______\n";
|
||||
pos = 0;
|
||||
while (chunk_size != 0)
|
||||
{
|
||||
if (pos > _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), pos > size()\n";
|
||||
// status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos == _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), will reread till last chunk\n";
|
||||
return;
|
||||
}
|
||||
|
||||
endptr_copy = endptr;
|
||||
chunk_size = std::strtoul(&_request.body[pos], &endptr, 16);
|
||||
if (chunk_size == LONG_MAX && errno == ERANGE)
|
||||
status = 413;
|
||||
if (endptr == endptr_copy)
|
||||
{
|
||||
std::cerr << "parse_request_body(), no conversion possible\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
chunk_field_end = _request.body.find(CRLF, pos);
|
||||
if (chunk_field_end == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body(), chunk_field no CRLF\n";
|
||||
// status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
chunk_field_end += CRLF_SIZE;
|
||||
_request.body.erase(pos, chunk_field_end);
|
||||
pos += chunk_size + CRLF_SIZE;
|
||||
}
|
||||
|
||||
_request.headers.erase("Transfer-Encoding");
|
||||
body_complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
{
|
||||
_request.body = raw_request.substr(pos);
|
||||
body_complete = true;
|
||||
}
|
||||
|
||||
/* Should be equivalent */
|
||||
// _request.body = raw_request.substr(pos);
|
||||
// if (_request.body.size() >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
// body_complete = true;
|
||||
}
|
||||
|
||||
///////////////
|
||||
// Body checks
|
||||
if (_request.body.size() > assigned_server->client_body_limit)
|
||||
status = 413;
|
||||
status = 413; // HTTP Client Errors
|
||||
}
|
||||
|
||||
bool Client::fill_script_path(std::string script)
|
||||
@@ -132,6 +224,7 @@ void Client::clear()
|
||||
{
|
||||
clear_request();
|
||||
header_complete = false;
|
||||
body_complete = false;
|
||||
request_complete = false;
|
||||
read_body_size = 0;
|
||||
assigned_server = NULL;
|
||||
@@ -161,6 +254,33 @@ void Client::clear_script()
|
||||
_request.script.info.clear();
|
||||
}
|
||||
|
||||
// debug
|
||||
void Client::print_client(std::string message)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
|
||||
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n";
|
||||
::print_special(raw_request);
|
||||
std::cout << "\n__\n"
|
||||
<< "get_cl_fd() : [" << get_cl_fd() << "]\n"
|
||||
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
||||
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
||||
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\n"
|
||||
<< "get_rq_uri() : [" << get_rq_uri() << "]\n"
|
||||
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
|
||||
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
||||
<< "get_rq_version() : [" << get_rq_version() << "]\n"
|
||||
<< "get_rq_body() : [" << get_rq_body() << "]\n"
|
||||
<< "get_rq_port() : [" << get_rq_port() << "]\n"
|
||||
<< "get_rq_hostname() : [" << get_rq_hostname() << "]\n"
|
||||
<< "get_rq_script_path() : [" << get_rq_script_path() << "]\n"
|
||||
<< "get_rq_script_info() : [" << get_rq_script_info() << "]\n"
|
||||
<< "headers :\n";
|
||||
for (it = _request.headers.begin(); it != _request.headers.end(); it++)
|
||||
std::cout << " " << it->first << ": [" << it->second << "]\n";
|
||||
std::cout << "\n=== END PRINT CLIENT ===\n\n";
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* GETTERS
|
||||
@@ -185,6 +305,7 @@ 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;
|
||||
@@ -203,12 +324,13 @@ std::string Client::get_rq_headers(const std::string & key) const
|
||||
void Client::_parse_request_line()
|
||||
{
|
||||
std::vector<std::string> line;
|
||||
int ret;
|
||||
std::string raw_line;
|
||||
|
||||
ret = ::parse_http_first_line(raw_request, line);
|
||||
if (ret != 3)
|
||||
raw_line = ::get_line(raw_request, 0, CRLF);
|
||||
line = ::split_trim(raw_line, " ");
|
||||
if (line.size() != 3)
|
||||
{
|
||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n";
|
||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
else
|
||||
@@ -232,10 +354,32 @@ void Client::_parse_request_uri( std::string uri )
|
||||
_request.abs_path = uri.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_headers()
|
||||
void Client::_parse_request_fields()
|
||||
{
|
||||
// TODO: check error and adjust status
|
||||
_request.headers = ::parse_http_headers(raw_request);
|
||||
std::string headers;
|
||||
size_t pos;
|
||||
int ret;
|
||||
|
||||
headers = raw_request;
|
||||
// delete first line
|
||||
pos = headers.find(CRLF);
|
||||
if (pos != std::string::npos)
|
||||
headers.erase(0, pos + std::string(CRLF).size());
|
||||
// delete body part
|
||||
pos = headers.find(CRLF CRLF);
|
||||
if (pos != std::string::npos)
|
||||
headers.erase(pos);
|
||||
else {
|
||||
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
// copy result of parser into headers
|
||||
ret = ::parse_http_headers(headers, _request.headers);
|
||||
if (ret > 0) {
|
||||
std::cerr << "err _parse_request_fields(): " << ret << " fields are bad formated\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
::str_map_key_tolower(_request.headers);
|
||||
}
|
||||
|
||||
void Client::_parse_port_hostname(std::string host)
|
||||
@@ -259,15 +403,15 @@ void Client::_parse_port_hostname(std::string host)
|
||||
|
||||
void Client::_check_request_errors()
|
||||
{
|
||||
//////////////////////
|
||||
///////////////////////
|
||||
// Request line checks
|
||||
if (_request.method == UNKNOWN)
|
||||
status = 501;
|
||||
status = 501; // HTTP Client Errors
|
||||
else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
status = 505;
|
||||
status = 505; // HTTP Client Errors
|
||||
else if (!(assigned_location->allow_methods & _request.method))
|
||||
{
|
||||
status = 405;
|
||||
status = 405; // HTTP Client Errors
|
||||
response.append("Allow: ");
|
||||
response.append(::http_methods_to_str(assigned_location->allow_methods));
|
||||
response.append(CRLF);
|
||||
@@ -281,15 +425,21 @@ void Client::_check_request_errors()
|
||||
response.append(CRLF CRLF);
|
||||
}
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
/////////////////
|
||||
//////////////////
|
||||
// Headers checks
|
||||
if (!this->get_rq_headers("Content-Length").empty()
|
||||
&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
|
||||
else if (!this->get_rq_headers("Content-Length").empty()
|
||||
&& std::strtoul(this->get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
|
||||
status = 413;
|
||||
|
||||
else if (!this->get_rq_headers("Transfer-Encoding").empty()
|
||||
&& this->get_rq_headers("Transfer-Encoding") != "chunked" )
|
||||
status = 501;
|
||||
else if (!this->get_rq_headers("Content-Encoding").empty())
|
||||
{
|
||||
status = 415;
|
||||
response.append("Accept-Encoding:"); // empty, no encoding accepted
|
||||
response.append(CRLF);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
# 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
|
||||
{
|
||||
@@ -46,8 +45,9 @@ class Client
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
bool header_complete;
|
||||
bool body_complete;
|
||||
bool request_complete;
|
||||
size_t read_body_size;
|
||||
size_t read_body_size; // unused for now
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *assigned_location;
|
||||
|
||||
@@ -71,12 +71,14 @@ class Client
|
||||
std::string get_rq_script_info() const;
|
||||
std::string get_rq_headers(const std::string & key) const;
|
||||
|
||||
void parse_request(std::vector<ServerConfig> &servers);
|
||||
void parse_request_headers(std::vector<ServerConfig> &servers);
|
||||
void parse_request_body();
|
||||
void clear();
|
||||
void clear_request();
|
||||
void clear_script();
|
||||
bool fill_script_path(std::string script);
|
||||
// DEBUG
|
||||
void print_client(std::string message = "");
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
@@ -86,7 +88,7 @@ class Client
|
||||
struct Request _request;
|
||||
|
||||
void _parse_request_line();
|
||||
void _parse_request_headers();
|
||||
void _parse_request_fields();
|
||||
void _parse_request_uri( std::string uri );
|
||||
void _parse_port_hostname(std::string host);
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#! /usr/bin/php
|
||||
|
||||
<?php
|
||||
echo "Status: 200\r\n";
|
||||
echo "false: 300\r\n";
|
||||
echo "server: Webserv/0.2\r\n";
|
||||
echo "Status: 300\r\n";
|
||||
echo "\r\n";
|
||||
echo "BEGIN PHP-CGI\n-----------\n\n";
|
||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||
@@ -26,4 +27,3 @@
|
||||
// echo $_POST['REQUEST_METHOD'];
|
||||
echo "\n\n-----------\nEND PHP-CGI\n\n";
|
||||
?>
|
||||
|
||||
|
||||
25
srcs/colors.h
Normal file
25
srcs/colors.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef COLORS_H
|
||||
# define COLORS_H
|
||||
|
||||
# define GRAY "\e[0;30m"
|
||||
# define RED "\e[0;31m"
|
||||
# define GREEN "\e[0;32m"
|
||||
# define YELLOW "\e[0;33m"
|
||||
# define BLUE "\e[0;34m"
|
||||
# define PURPLE "\e[0;35m"
|
||||
# define CYAN "\e[0;36m"
|
||||
# define WHITE "\e[0;37m"
|
||||
|
||||
# define B_GRAY "\e[1;30m"
|
||||
# define B_RED "\e[1;31m"
|
||||
# define B_GREEN "\e[1;32m"
|
||||
# define B_YELLOW "\e[1;33m"
|
||||
# define B_BLUE "\e[1;34m"
|
||||
# define B_PURPLE "\e[1;35m"
|
||||
# define B_CYAN "\e[1;36m"
|
||||
# define B_WHITE "\e[1;37m"
|
||||
|
||||
# define RESET "\e[0m"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
|
||||
/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef CONFIGPARSER_HPP
|
||||
# define CONFIGPARSER_HPP
|
||||
@@ -22,7 +11,7 @@
|
||||
# include <exception> // exception, what
|
||||
# include <stdexcept> // runtime_error, invalid_argument
|
||||
# include <string> // string
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // strtol, stroul
|
||||
# include <iostream> // cout, cin
|
||||
# include <fstream> // ifstream
|
||||
//# include <unistd.h> // access()
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
std::string root; // ./www/ or www work www/ and www work
|
||||
// i do remove trailing / tho
|
||||
|
||||
unsigned int client_body_limit; // set to default max if none set
|
||||
size_t client_body_limit; // set to default max if none set
|
||||
// 413 (Request Entity Too Large) if exceeded
|
||||
// default is 1m 1 000 000 ?
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
server->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
@@ -244,7 +244,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||
throw std::invalid_argument("invalid error code");
|
||||
int status_code = atoi(tmp_val[i].c_str());
|
||||
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
|
||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||
throw std::invalid_argument("redeclaring error page");
|
||||
server->error_pages[status_code] = path;
|
||||
@@ -315,7 +315,7 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
&& tmp_val[1].compare(0, 8, "https://"))
|
||||
throw std::invalid_argument("bad redirect uri");
|
||||
|
||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
||||
location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
||||
|
||||
@@ -57,7 +57,7 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
|
||||
// nothing to be done for cgi_ext, error_pages, redirect
|
||||
|
||||
// if (path_is_valid(it_l->root) == IS_DIR
|
||||
// if (eval_file_type(it_l->root) == IS_DIR
|
||||
// && it_l->path[it_l->path.size() - 1] != '/')
|
||||
// it_l->path.push_back('/');
|
||||
if (it_l->path[it_l->path.size() - 1] == '/'
|
||||
|
||||
197
srcs/utils.cpp
197
srcs/utils.cpp
@@ -9,6 +9,14 @@ void throw_test()
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
// notice : the use of getline make it such as
|
||||
// it doesn't identify multiple delim as one :
|
||||
// " something \n else " -> 1 - something
|
||||
// 2 - else
|
||||
// is not the same as :
|
||||
// " something \n\n else " -> 1 - something
|
||||
// 2 -
|
||||
// 3 - else
|
||||
std::vector<std::string> split(std::string input, char delimiter)
|
||||
{
|
||||
std::vector<std::string> answer;
|
||||
@@ -21,25 +29,62 @@ std::vector<std::string> split(std::string input, char delimiter)
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string trim(std::string str, char c)
|
||||
std::vector<std::string>
|
||||
split_trim(std::string input, std::string delim, char ctrim)
|
||||
{
|
||||
std::vector<std::string> split_str;
|
||||
std::string tmp;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while (end != std::string::npos)
|
||||
{
|
||||
end = input.find(delim, start);
|
||||
len = end - start;
|
||||
if (end == std::string::npos)
|
||||
len = end;
|
||||
tmp = input.substr(start, len);
|
||||
if (ctrim != '\0')
|
||||
tmp = trim(tmp, ctrim);
|
||||
if (tmp.size() != 0)
|
||||
split_str.push_back( tmp );
|
||||
start = end + delim.size();
|
||||
}
|
||||
return split_str;
|
||||
}
|
||||
|
||||
std::string trim(std::string str, char del)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
// delete leadings c
|
||||
pos = str.find_first_not_of(c);
|
||||
// delete leadings del
|
||||
pos = str.find_first_not_of(del);
|
||||
if (pos == std::string::npos)
|
||||
return str;
|
||||
pos = str.size();
|
||||
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);
|
||||
// delete trailing del
|
||||
pos = str.find_last_not_of(del);
|
||||
if (pos != std::string::npos)
|
||||
str = str.substr(0, pos + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//// trim a set of char
|
||||
//std::string trim(std::string str, std::string del)
|
||||
//{
|
||||
// std::string new_str;
|
||||
//
|
||||
// while (new_str.compare(str) != 0)
|
||||
// {
|
||||
// for (size_t i = 0; i < del.size(); i++)
|
||||
// trim(str, del[i]);
|
||||
// }
|
||||
// return str;
|
||||
//}
|
||||
|
||||
std::string itos(int n)
|
||||
{
|
||||
std::stringstream strs;
|
||||
@@ -65,7 +110,7 @@ bool isNumeric_btw(int low, int high, std::string str)
|
||||
if (std::isdigit(str[i]) == false)
|
||||
return false;
|
||||
}
|
||||
int n = std::atoi(str.c_str());
|
||||
int n = std::strtol(str.c_str(), NULL, 10);
|
||||
if (n < low || n > high)
|
||||
return false;
|
||||
return true;
|
||||
@@ -106,31 +151,32 @@ std::string http_methods_to_str(unsigned int methods)
|
||||
|
||||
# include <iostream>
|
||||
|
||||
// you could make this &path...
|
||||
int path_is_valid(std::string path)
|
||||
file_type eval_file_type(const std::string &path)
|
||||
{
|
||||
const char *tmp_path = path.c_str();
|
||||
struct stat s;
|
||||
|
||||
if (stat(tmp_path, &s) == 0)
|
||||
if (stat(tmp_path, &s) != -1)
|
||||
{
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a file\n";
|
||||
return (IS_FILE);
|
||||
}
|
||||
else if (S_ISDIR(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a Dir\n";
|
||||
return (IS_DIR);
|
||||
}
|
||||
}
|
||||
// std::cout << "path is neither dir nor file\n";
|
||||
else
|
||||
{
|
||||
std::perror("err stat()");
|
||||
}
|
||||
|
||||
return (IS_OTHER);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
if (ori_substr.empty())
|
||||
return;
|
||||
@@ -151,27 +197,116 @@ std::string str_tolower(std::string str)
|
||||
return str;
|
||||
}
|
||||
|
||||
void del_line_in_str(std::string * str, size_t pos, std::string delim)
|
||||
// identify a line in a string, by delim (ex. '\n')
|
||||
// delete this line from the string
|
||||
// and return the deleted line
|
||||
std::string
|
||||
extract_line(std::string & str, size_t pos, std::string delim)
|
||||
{
|
||||
size_t begin;
|
||||
size_t end;
|
||||
std::string del_str;
|
||||
size_t begin;
|
||||
size_t end;
|
||||
size_t len;
|
||||
|
||||
begin = (*str).rfind(delim, pos);
|
||||
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();
|
||||
end = str.find(delim, pos);
|
||||
len = end;
|
||||
if (end != std::string::npos)
|
||||
len = end - begin;
|
||||
|
||||
(*str).erase(begin, end - begin);
|
||||
del_str = str.substr(begin, len);
|
||||
str.erase(begin, len);
|
||||
return del_str;
|
||||
}
|
||||
|
||||
// get a line in a string, by delim
|
||||
// same as extract, except it doesn't delete it
|
||||
std::string get_line(std::string str, size_t pos, std::string delim)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
ret = ::extract_line(str, pos, delim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
parse_http_headers (
|
||||
std::string headers,
|
||||
std::map<std::string, std::string> & fields )
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
std::vector<std::string>::iterator it;
|
||||
std::vector<std::string>::iterator it_end;
|
||||
size_t err = 0;
|
||||
size_t pos;
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
||||
list = ::split_trim(headers, CRLF, ' ');
|
||||
|
||||
it_end = list.end();
|
||||
for (it = list.begin(); it != it_end; it++)
|
||||
{
|
||||
pos = (*it).find(':');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
}
|
||||
key = (*it).substr(0, pos);
|
||||
if ( key.find(' ') != std::string::npos )
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
}
|
||||
// bad idea, in cgi we need to have the original value
|
||||
// key = ::str_tolower(key); // to make "key" case_insensitive
|
||||
val = (*it).substr(pos + 1);
|
||||
val = ::trim(val, ' ');
|
||||
fields.insert( std::pair<std::string, std::string>(key, val) );
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void str_map_key_tolower(std::map<std::string, std::string> & mp)
|
||||
{
|
||||
std::map<std::string, std::string> new_mp;
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
for (it = mp.begin(); it != mp.end(); it++)
|
||||
{
|
||||
key = it->first;
|
||||
value = it->second;
|
||||
key = ::str_tolower(key);
|
||||
new_mp.insert( std::pair<std::string, std::string>(key, value) );
|
||||
}
|
||||
mp.swap(new_mp);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
void print_special(std::string str)
|
||||
{
|
||||
char c;
|
||||
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
{
|
||||
c = str[i];
|
||||
if (c == '\r')
|
||||
std::cout << YELLOW << "\\r" << RESET;
|
||||
else if (c == '\n')
|
||||
std::cout << YELLOW << "\\n" << RESET << "\n";
|
||||
else
|
||||
std::cout << c;
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const listen_socket& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
@@ -3,21 +3,36 @@
|
||||
# define UTILS_HPP
|
||||
|
||||
# include <vector>
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <climits> // LONG_MAX
|
||||
# include <cerrno> // errno
|
||||
# include <sys/stat.h> // stat()
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
# include <cstdio> // perror, fflush
|
||||
# include "colors.h" // for debug print_special
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
# define CRLF_SIZE 2
|
||||
# define NPOS std::string::npos
|
||||
|
||||
# define IS_FILE 2
|
||||
# define IS_DIR 1
|
||||
# define IS_OTHER 0
|
||||
/* Equivalent for end of http header size :
|
||||
** std::string(CRLF CRLF).size();
|
||||
** sizeof(CRLF CRLF) - 1;
|
||||
** CRLF_SIZE*2
|
||||
*/
|
||||
|
||||
enum file_type
|
||||
{
|
||||
IS_OTHER,
|
||||
IS_FILE,
|
||||
IS_DIR
|
||||
};
|
||||
|
||||
enum http_method
|
||||
{
|
||||
@@ -40,16 +55,22 @@ bool operator==(const listen_socket& lhs, int fd);
|
||||
bool operator==(int fd, const listen_socket& rhs);
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter);
|
||||
std::vector<std::string> split_trim(std::string input, std::string delim = "\n", char ctrim = '\0');
|
||||
bool isNumeric(std::string str);
|
||||
bool isNumeric_btw(int low, int high, std::string str);
|
||||
std::string itos(int n);
|
||||
std::string trim(std::string str, char c);
|
||||
std::string trim(std::string str, char del);
|
||||
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);
|
||||
file_type eval_file_type(const std::string &path);
|
||||
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);
|
||||
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
||||
std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n");
|
||||
size_t parse_http_headers (std::string headers, std::map<std::string, std::string> & fields );
|
||||
void str_map_key_tolower(std::map<std::string, std::string> & mp);
|
||||
void throw_test();
|
||||
// debug
|
||||
void print_special(std::string str);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# include <algorithm> // find
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include "Client.hpp"
|
||||
@@ -104,9 +104,9 @@ class Webserv
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -89,6 +89,8 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
||||
std::string body = client->get_rq_body();
|
||||
int fd_in[2];
|
||||
int fd_out[2];
|
||||
int save_in = dup(STDIN_FILENO);
|
||||
int save_out = dup(STDOUT_FILENO);
|
||||
|
||||
pipe(fd_in);
|
||||
pipe(fd_out);
|
||||
@@ -127,17 +129,22 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
||||
}
|
||||
if (script_output.empty())
|
||||
script_output = "Status: 500\r\n\r\n";
|
||||
|
||||
dup2(save_in, STDIN_FILENO);
|
||||
dup2(save_out, STDOUT_FILENO);
|
||||
return script_output;
|
||||
}
|
||||
|
||||
void Webserv::_check_script_output(Client *client, std::string 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);
|
||||
// _check_script_empty_lines(client, output);
|
||||
// _check_script_space_colons(client, output);
|
||||
// _check_script_new_lines(client, output);
|
||||
}
|
||||
|
||||
void Webserv::_check_script_status(Client *client, std::string output)
|
||||
void Webserv::_check_script_status(Client *client, std::string & output)
|
||||
{
|
||||
size_t pos;
|
||||
int status_pos;
|
||||
@@ -146,31 +153,43 @@ void Webserv::_check_script_status(Client *client, std::string output)
|
||||
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 = std::strtoul(output.c_str() + status_pos, NULL, 10);
|
||||
::extract_line(output, pos, CRLF);
|
||||
}
|
||||
client->status = 200;
|
||||
else
|
||||
client->status = 200;
|
||||
}
|
||||
|
||||
void Webserv::_check_script_fields(Client *client, std::string output)
|
||||
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;
|
||||
std::string tmp;
|
||||
size_t pos;
|
||||
|
||||
srv_fld = parse_http_headers(client->response);
|
||||
scr_fld = parse_http_headers(output);
|
||||
// wip: compare both map to supress duplicates
|
||||
// put server headers in map
|
||||
tmp = client->response;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != std::string::npos)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, srv_fld);
|
||||
// put script headers in map
|
||||
tmp = output;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != std::string::npos)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, scr_fld);
|
||||
// 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)
|
||||
if (str_tolower(it_srv->first) == str_tolower(it_scr->first))
|
||||
{
|
||||
pos = client->response.find(it_srv->first);
|
||||
::del_line_in_str(&client->response, pos, CRLF);
|
||||
::extract_line(client->response, pos, CRLF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
|
||||
// HUGO ADD END
|
||||
|
||||
try {
|
||||
_bind(it->fd, std::atoi(it->port.c_str()), it->host);
|
||||
_bind(it->fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||
_listen(it->fd, 42); // 42 arbitrary
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
# define S405 "405 Method Not Allowed"
|
||||
# define S408 "408 Request Timeout"
|
||||
# define S413 "413 Content Too Large"
|
||||
# define S415 "415 Unsupported Media Type"
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
|
||||
@@ -48,7 +48,7 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
//
|
||||
// HUGO ADD END
|
||||
|
||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
||||
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||
_listen(new_socket.fd, 42); // 42 arbitrary
|
||||
|
||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
|
||||
@@ -19,9 +19,6 @@ void Webserv::_get(Client *client)
|
||||
if (_is_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;
|
||||
@@ -30,14 +27,14 @@ void Webserv::_get(Client *client)
|
||||
// END TMP HUGO
|
||||
|
||||
// Index/Autoindex block
|
||||
if (path_is_valid(path) == IS_DIR)
|
||||
if (eval_file_type(path) == IS_DIR)
|
||||
{
|
||||
std::cout << "made it to Index/Autoindex\n";
|
||||
if (path[path.size() - 1] != '/')
|
||||
path.push_back('/');
|
||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||
{
|
||||
if (path_is_valid(path + client->assigned_location->index[i]) == 2)
|
||||
if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE)
|
||||
{
|
||||
path.append(client->assigned_location->index[i]);
|
||||
_get_file(client, path);
|
||||
@@ -49,8 +46,6 @@ void Webserv::_get(Client *client)
|
||||
}
|
||||
else
|
||||
_get_file(client, path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
|
||||
@@ -1,64 +1,6 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -73,18 +15,3 @@ std::string
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,14 @@
|
||||
# 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);
|
||||
parse_http_headers (
|
||||
std::string headers,
|
||||
std::map<std::string, std::string> fields )
|
||||
|
||||
std::string
|
||||
parse_http_body(std::string message);
|
||||
|
||||
bool
|
||||
maybe_http_first_line(std::string);
|
||||
|
||||
// http message structure :
|
||||
//
|
||||
// start-line
|
||||
|
||||
@@ -25,12 +25,14 @@ void Webserv::_request(Client *client)
|
||||
}
|
||||
else if (ret == READ_COMPLETE)
|
||||
{
|
||||
if (client->body_complete)
|
||||
std::cerr << "______BODY\n" << client->get_rq_body() << "\n______\n"; // DEBUG
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
client->request_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
int Webserv::_read_request(Client *client)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
ssize_t ret;
|
||||
@@ -41,8 +43,6 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err recv()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
if (ret == 0)
|
||||
@@ -50,41 +50,32 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
|
||||
client->raw_request.append(buf, ret);
|
||||
|
||||
if (!client->header_complete)
|
||||
{
|
||||
if (client->raw_request.find(CRLF CRLF) != std::string::npos)
|
||||
client->parse_request_headers(_servers);
|
||||
if (client->status)
|
||||
return READ_COMPLETE;
|
||||
if (client->header_complete)
|
||||
{
|
||||
client->header_complete = true;
|
||||
client->parse_request(_servers);
|
||||
std::cerr << client->get_rq_method_str() << " " << client->get_rq_uri() << " " << client->get_rq_version() << "\n"; // DEBUG
|
||||
if (client->status)
|
||||
return READ_COMPLETE;
|
||||
|
||||
if (client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty()) // No body case
|
||||
return READ_COMPLETE;
|
||||
if (client->get_rq_headers("Content-Type").empty()
|
||||
&& client->get_rq_headers("Content-Length").empty()
|
||||
&& client->get_rq_headers("Transfer-Encoding").empty())
|
||||
return READ_COMPLETE; // No body case
|
||||
}
|
||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
||||
{
|
||||
// 413 or 400 ? 413 seems common among http server, but don't fit perfectly.
|
||||
{ // 413 or 400 ? 413 seems common among http server, but don't fit perfectly.
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
else if (client->header_complete)
|
||||
{
|
||||
client->read_body_size += ret;
|
||||
if (client->read_body_size > client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
// client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now
|
||||
client->parse_request_body();
|
||||
if (client->status || client->body_complete)
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
if ((int)client->read_body_size >= ::atoi(client->get_rq_headers("Content-Length").c_str()))
|
||||
{
|
||||
client->parse_request_body();
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
return READ_IN_PROGRESS;
|
||||
|
||||
@@ -20,8 +20,9 @@ void Webserv::run()
|
||||
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
||||
if (nfds == -1)
|
||||
{
|
||||
int errno_copy = errno;
|
||||
std::perror("err epoll_wait()");
|
||||
if (errno == EINTR)
|
||||
if (errno_copy == EINTR)
|
||||
g_run = false;
|
||||
else
|
||||
throw std::runtime_error("Epoll wait");
|
||||
|
||||
15
test_chunk.txt
Normal file
15
test_chunk.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
|
||||
|
||||
GET / HTTP/1.1
|
||||
Host: localhost:4040
|
||||
Accept: */*
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
7
|
||||
Mozilla
|
||||
9
|
||||
Developer
|
||||
7
|
||||
Network
|
||||
0
|
||||
Reference in New Issue
Block a user