Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71c07140e2 | ||
|
|
de09c2357c | ||
|
|
c225063361 | ||
|
|
3a58b5d921 | ||
|
|
a1fff0f8c2 | ||
|
|
ad2b5a629a | ||
|
|
ff77dfd298 | ||
|
|
27b4f96618 | ||
|
|
1d67e6988d | ||
|
|
11f71ea74f | ||
|
|
9a379c835d | ||
|
|
c7905ebd19 | ||
|
|
17230ccc42 | ||
|
|
86f7740984 | ||
|
|
58f67bf42e | ||
|
|
f7f3e42b15 | ||
|
|
8e90221058 | ||
|
|
f393aa46bf | ||
|
|
1eb989a3fd | ||
|
|
56f4cf7e15 | ||
|
|
482a38952e | ||
|
|
ef034a356b | ||
|
|
53a548e314 | ||
|
|
3dad938e3c | ||
|
|
1d3cdef7a6 | ||
|
|
97c90236b9 | ||
|
|
643b09c4f7 | ||
|
|
a44b9b493a | ||
|
|
ae9a9b37f1 | ||
|
|
e1a68bc3e4 | ||
|
|
7fdc81f5f4 | ||
|
|
0a72778c8c | ||
|
|
02cfdf43bc | ||
|
|
f87024c32f | ||
|
|
9ee7205b95 | ||
|
|
cf69168a84 | ||
|
|
94852babc6 | ||
|
|
f777441edf | ||
|
|
1ccf61bc68 | ||
|
|
4ab099ee4d | ||
|
|
4870b2c05d | ||
|
|
8ec1353723 | ||
|
|
4b1baca126 | ||
|
|
6f0c3b4d6d | ||
|
|
f7dc5ccde4 | ||
|
|
9ac14aa1aa | ||
|
|
f7e6b61811 | ||
|
|
fce1bcbece | ||
|
|
ca8427262d | ||
|
|
3d46505411 | ||
|
|
b0c524a8bd | ||
|
|
fef26aee5b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,3 +20,5 @@ ubuntu_cgi_tester
|
||||
webserv
|
||||
!**/webserv/
|
||||
*.log
|
||||
|
||||
large.jpg
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,11 +1,11 @@
|
||||
|
||||
NAME = webserv
|
||||
CXX = c++
|
||||
CXX = clang++
|
||||
|
||||
CXXFLAGS = -Wall -Wextra #-Werror
|
||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||
CXXFLAGS += -std=c++98
|
||||
CXXFLAGS += -g
|
||||
CXXFLAGS += -g3
|
||||
CXXFLAGS += -MMD -MP #header dependencie
|
||||
#CXXFLAGS += -O3
|
||||
|
||||
@@ -22,10 +22,11 @@ SRCS_D = srcs \
|
||||
SRCS = main.cpp \
|
||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||
accept.cpp request.cpp response.cpp \
|
||||
method_get.cpp method_post.cpp method_delete.cpp \
|
||||
run_loop.cpp \
|
||||
ConfigParser.cpp \
|
||||
ConfigParserUtils.cpp \
|
||||
ConfigParserPost.cpp \
|
||||
parser.cpp \
|
||||
extraConfig.cpp \
|
||||
postProcessing.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
|
||||
113
README.md
113
README.md
@@ -1,25 +1,15 @@
|
||||
|
||||
## work together
|
||||
|
||||
#### 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 field duplicate (resolve conflicts)
|
||||
-> TODO : if status field, change server status for this one
|
||||
! TODO : there is no space between filed name and ":"
|
||||
! TODO : if no Location field && no Status field -> status code = 200
|
||||
! TODO?: handle Location field, either :
|
||||
- local : start with '/' --> rerun the request with new uri
|
||||
- client : start with '<scheme>:' --> send back status code 302
|
||||
|
||||
#### what cgi ?
|
||||
- a basic form with "name" and "something", that return a html page with that
|
||||
- a script called by a file extension in URI
|
||||
#### 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
|
||||
@@ -35,42 +25,6 @@
|
||||
- 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 ?
|
||||
|
||||
#### notifications
|
||||
- i changed the Client getters in two categories :
|
||||
- getters for requests infos : `get_rq_<info>`
|
||||
- getters for client sides infos : `get_cl_<info>` (such as ip of client)
|
||||
- i changed the variables in request struct in Client :
|
||||
- `path` become `uri` (ex. `/path/to/file?var=val`)
|
||||
- add `abs_path` (ex. `/path/to/file` )
|
||||
- add `query` (ex. `var=val`)
|
||||
- the header fields names, as key in map, are stored in lowercase, and getters are case-insensitives
|
||||
|
||||
|
||||
respsonse.cpp
|
||||
```
|
||||
_response()
|
||||
{
|
||||
_determine_process_server()
|
||||
_send_response()
|
||||
{
|
||||
_append_base_headers()
|
||||
_construct_response()
|
||||
{
|
||||
_process_method()
|
||||
{
|
||||
_get()
|
||||
{
|
||||
_exec_cgi()
|
||||
}
|
||||
}
|
||||
_insert_status_line()
|
||||
::send(headers)
|
||||
::send(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## man
|
||||
|
||||
@@ -149,6 +103,20 @@ _response()
|
||||
## 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 :
|
||||
@@ -264,6 +232,45 @@ _response()
|
||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||
|
||||
|
||||
---
|
||||
## 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.
|
||||
|
||||
|
||||
---
|
||||
## cgi env variables
|
||||
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,7 @@ server {
|
||||
location /board {
|
||||
allow_methods GET;
|
||||
root ./www/html;
|
||||
cgi_ext php cgi
|
||||
}
|
||||
|
||||
location /board/content {
|
||||
|
||||
@@ -10,14 +10,63 @@ server {
|
||||
# client_body_limit 400;
|
||||
|
||||
index index.html; # this is another comment
|
||||
|
||||
root ./www/;
|
||||
|
||||
# If not explicitly set, ConfigParser need to genererate a location block
|
||||
# like this for path "/" (based on field "root" and "index" of the server)
|
||||
location / {
|
||||
root ./www/;
|
||||
index index.html;
|
||||
error_page 404 ./www/error_pages/error_404.html;
|
||||
|
||||
location /list {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
allow_methods GET;
|
||||
location /redirect {
|
||||
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
||||
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
||||
}
|
||||
|
||||
location /test {
|
||||
index index1.html subdex.html;
|
||||
}
|
||||
|
||||
location /test/index1.html {
|
||||
index index1.html subdex.html;
|
||||
}
|
||||
|
||||
location /hilarious_404/ {
|
||||
redirect 301 https://berniesanders.com/404/;
|
||||
}
|
||||
|
||||
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
|
||||
location /stylesheet/style.css {
|
||||
# root ./www/../;
|
||||
root ./;
|
||||
}
|
||||
|
||||
location /test/something.html {
|
||||
# allow_methods DELETE;
|
||||
}
|
||||
|
||||
# location /something/long/here {
|
||||
# }
|
||||
|
||||
location /test/test_deeper/ {
|
||||
# allow_methods
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location /test/test_deeper/super_deep {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
# location /test/test_deeper/something.html {
|
||||
# allow_methods DELETE;
|
||||
# }
|
||||
|
||||
|
||||
# ok in theory if one were to go to /test they would get the index in www
|
||||
# as opposed to the one in /website...
|
||||
# location /test {
|
||||
# root /www;
|
||||
# }
|
||||
|
||||
}
|
||||
|
||||
22
memo.txt
22
memo.txt
@@ -1,15 +1,27 @@
|
||||
|
||||
IN 42 SUBJECT AND/OR PRIORITY :
|
||||
- CGI
|
||||
- chunked request (response not mandatory it seems)
|
||||
- fix need for index and autoindex config
|
||||
- Ecrire des tests !
|
||||
- "root" need to replace "location->path" part of "client.path"
|
||||
replace up to the last "/" of the "location->path" part
|
||||
(if its a folder this will be in fact the entire path)
|
||||
- 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/
|
||||
-----------------------------
|
||||
- 408 Request Timeout
|
||||
- gerer le champ "Accept" du client
|
||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||
- handle redirection
|
||||
- maybe add a "last_action_time" in Client for timeout handling
|
||||
little global timeout on epoll, like 100ms, then find client that actualy need to timeout
|
||||
if (actual_time - client.last_action_time > 10000ms){timeout(client)}
|
||||
- add headers "Date" and "Last-Modified" to response
|
||||
- change "std::string" to reference "std::string &" in most functions
|
||||
and add "const" if apropriate.
|
||||
- http_method en mode binary flags. "std::vector<http_method> allow_methods" -> "unsigned int allow_methods;"
|
||||
- Dans le parsing, trier les "locations" par ordre de precision.
|
||||
Compter les "/" dans le chemin, les locations avec le plus de "/" seront en premier dans le vector.
|
||||
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
|
||||
|
||||
|
||||
|
||||
261
srcs/Client.cpp
261
srcs/Client.cpp
@@ -1,29 +1,34 @@
|
||||
|
||||
#include "Client.hpp"
|
||||
|
||||
char Client::buf[MAX_FILESIZE+1];
|
||||
|
||||
|
||||
/*********************************************
|
||||
* COPLIENS
|
||||
*********************************************/
|
||||
|
||||
Client::Client()
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd(0)
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
_fd(0),
|
||||
_port(""),
|
||||
_ip(""),
|
||||
_lsocket(NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Client::Client(int afd, listen_socket *lsocket, struct sockaddr_in addr)
|
||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd(afd)
|
||||
, _port(aport)
|
||||
, _ip(aip)
|
||||
, _lsocket(lsocket)
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
_fd(afd),
|
||||
_port(aport),
|
||||
_ip(aip),
|
||||
_lsocket(lsocket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -32,24 +37,26 @@ Client::~Client() {
|
||||
return;
|
||||
}
|
||||
|
||||
// WIP placeholder because of const values
|
||||
/* // WIP not sure fo what is more logic here
|
||||
Client::Client( Client const & src )
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd ( src.get_cl_fd() )
|
||||
, _port ( src.get_cl_port() )
|
||||
, _ip ( src.get_cl_ip() )
|
||||
, _lsocket ( src.get_cl_lsocket() )
|
||||
: 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
|
||||
body_size = src.body_size;
|
||||
status = src.status;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// WIP placeholder because of const values
|
||||
/* // WIP placeholder because of const values
|
||||
Client & Client::operator=( Client const & rhs )
|
||||
{
|
||||
if ( this != &rhs )
|
||||
@@ -58,7 +65,7 @@ Client & Client::operator=( Client const & rhs )
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*********************************************
|
||||
* PUBLIC MEMBER FUNCTIONS
|
||||
@@ -68,26 +75,39 @@ 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()
|
||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
{
|
||||
std::string sub;
|
||||
std::vector<std::string> list;
|
||||
size_t pos;
|
||||
|
||||
clear_request(); // not mandatory
|
||||
_parse_request_line();
|
||||
if (status)
|
||||
return;
|
||||
_parse_request_fields();
|
||||
// DEBUG
|
||||
std::cout << "\nREQUEST _____________________\n"
|
||||
<< raw_request
|
||||
<< "\n\n";
|
||||
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")); // use getter for headers because it works case insensitive
|
||||
|
||||
pos = (raw_request).find(CRLF CRLF);
|
||||
sub = (raw_request).substr(0, pos);
|
||||
list = split(sub, '\n');
|
||||
_parse_request_line(*list.begin());
|
||||
list.erase(list.begin());
|
||||
_parse_request_headers(list);
|
||||
_parse_request_body(pos + 4);
|
||||
_parse_port_hostname(this->get_rq_headers("Host"));
|
||||
raw_request.clear();
|
||||
// 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()
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
pos = raw_request.find(CRLF CRLF);
|
||||
pos += std::string(CRLF CRLF).size();
|
||||
_request.body = raw_request.substr(pos);
|
||||
|
||||
if (_request.body.size() > assigned_server->client_body_limit)
|
||||
status = 413; // HTTP Client Errors
|
||||
}
|
||||
|
||||
bool Client::fill_script_path(std::string script)
|
||||
@@ -111,9 +131,12 @@ bool Client::fill_script_path(std::string script)
|
||||
void Client::clear()
|
||||
{
|
||||
clear_request();
|
||||
header_complete = false;
|
||||
read_body_size = 0;
|
||||
assigned_server = NULL;
|
||||
assigned_location = NULL;
|
||||
raw_request.clear();
|
||||
response.clear();
|
||||
body_size = 0;
|
||||
status = 0;
|
||||
}
|
||||
|
||||
@@ -137,16 +160,43 @@ 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
|
||||
*********************************************/
|
||||
|
||||
// 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; }
|
||||
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; }
|
||||
@@ -176,30 +226,25 @@ std::string Client::get_rq_headers(const std::string & key) const
|
||||
* 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;
|
||||
std::string raw_line;
|
||||
|
||||
sline = split(rline, ' ');
|
||||
if (sline.size() != 3)
|
||||
raw_line = ::get_line(raw_request, 0, CRLF);
|
||||
line = ::split_trim(raw_line, " ");
|
||||
if (line.size() != 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 (" << line.size() << " instead of 3)\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
else
|
||||
{
|
||||
_request.method = str_to_http_method(line[0]);
|
||||
_request.uri = line[1];
|
||||
_parse_request_uri(line[1]);
|
||||
_request.version = line[2];
|
||||
}
|
||||
// method
|
||||
tmp = ::trim(sline[0], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.method = str_to_http_method(tmp);
|
||||
// uri
|
||||
tmp = ::trim(sline[1], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.uri = tmp;
|
||||
_parse_request_uri( tmp );
|
||||
// http version
|
||||
tmp = ::trim(sline[2], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.version = tmp;
|
||||
}
|
||||
|
||||
void Client::_parse_request_uri( std::string uri )
|
||||
@@ -214,32 +259,32 @@ void Client::_parse_request_uri( std::string uri )
|
||||
_request.abs_path = uri.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_headers( std::vector<std::string> list )
|
||||
void Client::_parse_request_fields()
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
std::vector<std::string>::iterator it;
|
||||
size_t pos;
|
||||
std::string headers;
|
||||
size_t pos;
|
||||
int ret;
|
||||
|
||||
for (it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
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');
|
||||
_request.headers.insert( std::pair<std::string, std::string>(key, val) );
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
void Client::_parse_request_body( size_t pos )
|
||||
{
|
||||
std::string body = &raw_request[pos];
|
||||
|
||||
_request.body = body;
|
||||
// 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)
|
||||
@@ -247,10 +292,7 @@ void Client::_parse_port_hostname(std::string host)
|
||||
size_t pos;
|
||||
|
||||
if (host == "")
|
||||
{
|
||||
std::cerr << "no host\n";
|
||||
throw std::runtime_error("no host in request");
|
||||
}
|
||||
|
||||
pos = host.find(':');
|
||||
// port :
|
||||
@@ -264,6 +306,41 @@ void Client::_parse_port_hostname(std::string host)
|
||||
_request.hostname = host.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_check_request_errors()
|
||||
{
|
||||
//////////////////////
|
||||
// Request line checks
|
||||
if (_request.method == UNKNOWN)
|
||||
status = 501; // HTTP Client Errors
|
||||
else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
status = 505; // HTTP Client Errors
|
||||
else if (!(assigned_location->allow_methods & _request.method))
|
||||
{
|
||||
status = 405; // HTTP Client Errors
|
||||
response.append("Allow: ");
|
||||
response.append(::http_methods_to_str(assigned_location->allow_methods));
|
||||
response.append(CRLF);
|
||||
}
|
||||
else if (assigned_location->redirect_status)
|
||||
{ // Weird behavior. Sometimes, the web browser seems to wait for a complete response until timeout.
|
||||
// (for codes 301, 302, 303, 307, and 308)
|
||||
status = assigned_location->redirect_status;
|
||||
response.append("Location: ");
|
||||
response.append(assigned_location->redirect_uri);
|
||||
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)
|
||||
status = 413; // HTTP Client Errors
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* OVERLOAD
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string.h> // strdup
|
||||
# include "utils.hpp"
|
||||
# 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"
|
||||
|
||||
struct Script
|
||||
{
|
||||
@@ -31,21 +32,22 @@ struct Request
|
||||
struct Script script;
|
||||
};
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
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);
|
||||
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
static char buf[MAX_FILESIZE+1];
|
||||
size_t body_size;
|
||||
unsigned int status;
|
||||
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;
|
||||
|
||||
// getters
|
||||
int get_cl_fd() const;
|
||||
@@ -67,30 +69,37 @@ class Client
|
||||
std::string get_rq_script_info() const;
|
||||
std::string get_rq_headers(const std::string & key) const;
|
||||
|
||||
void parse_request();
|
||||
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:
|
||||
const int _fd;
|
||||
const std::string _port;
|
||||
const std::string _ip;
|
||||
const listen_socket * _lsocket;
|
||||
struct Request _request;
|
||||
int _fd;
|
||||
std::string _port;
|
||||
std::string _ip;
|
||||
listen_socket * _lsocket;
|
||||
struct Request _request;
|
||||
|
||||
void _parse_request_line( std::string rline );
|
||||
void _parse_request_line();
|
||||
void _parse_request_fields();
|
||||
void _parse_request_uri( std::string uri );
|
||||
void _parse_request_headers( std::vector<std::string> list );
|
||||
void _parse_request_body( size_t pos );
|
||||
void _parse_port_hostname(std::string host);
|
||||
|
||||
void _check_request_errors();
|
||||
};
|
||||
|
||||
bool operator==(const Client& lhs, const Client& rhs);
|
||||
bool operator==(const Client& lhs, int fd);
|
||||
bool operator==(int fd, const Client& rhs);
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#! /usr/bin/php
|
||||
|
||||
<?php
|
||||
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";
|
||||
//phpinfo();
|
||||
|
||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
||||
echo "\nCONTENT_TYPE: " . getenv("CONTENT_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
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
# include <iostream> // cout, cin
|
||||
# include <fstream> // ifstream
|
||||
//# include <unistd.h> // access()
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include <dirent.h> // opendir(), doesn't work...
|
||||
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
||||
# include <algorithm> // sort() in Post
|
||||
|
||||
class ConfigParser {
|
||||
|
||||
@@ -51,6 +52,13 @@ public:
|
||||
// private member functions from anywhere...
|
||||
void _print_content() const;
|
||||
|
||||
|
||||
// I don't love that this is here but...
|
||||
// doesn't work use the operator overload
|
||||
// bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::string _content;
|
||||
|
||||
@@ -82,11 +90,20 @@ private:
|
||||
|
||||
void _post_processing(std::vector<ServerConfig> *servers);
|
||||
|
||||
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
// no idea if it should go here...
|
||||
//bool compareLocationConfigs(const LocationConfig &a,
|
||||
// const LocationConfig &b);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// def needs work line a better name an do i even need this?
|
||||
// should it be in Utils instead?
|
||||
class MyException : public std::invalid_argument
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
|
||||
// make certain servers default
|
||||
// fill out empty settings
|
||||
// if special settings are empty throw
|
||||
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port should already be set
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// is that a good default?
|
||||
if (it->root == "")
|
||||
it->root = "/";
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
// autoindex should already be false by default right?
|
||||
|
||||
// what do we do if Allow methods is left empty?
|
||||
// all ?
|
||||
if (it->allow_methods.empty())
|
||||
throw std::invalid_argument("No methods specified");
|
||||
|
||||
|
||||
// what to do if index is left empty? index.html?
|
||||
// ok but i still need to check index, no idea how...
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
// check that path is feasible...
|
||||
// opendir?
|
||||
DIR* dir = opendir(it_l->path.c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("location dir could not be opened");
|
||||
|
||||
if (it_l->client_body_limit == 0)
|
||||
it_l->client_body_limit = 5000; // what is the recomended size?
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
// fill out allow methods from server?
|
||||
if (it_l->allow_methods.empty())
|
||||
it_l->allow_methods = it->allow_methods;
|
||||
|
||||
// fill out index from Server?
|
||||
// or do a bunch of checks on what is in there...
|
||||
|
||||
// same for redirect status i think
|
||||
|
||||
// maybe do something for Cgi_info?
|
||||
|
||||
++it_l;
|
||||
}
|
||||
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
// do the defaults at the end?
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/08/02 14:06:07 by lperrey ### ########.fr */
|
||||
/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string>
|
||||
# include <iostream>
|
||||
# include <sys/stat.h> // stat()
|
||||
|
||||
# include <stdio.h> // printf(), gotta go
|
||||
|
||||
|
||||
# include "utils.hpp"
|
||||
|
||||
// again, struct instead?
|
||||
class LocationConfig
|
||||
@@ -24,22 +30,64 @@ class LocationConfig
|
||||
public:
|
||||
// canonic stuff?
|
||||
|
||||
std::string path;
|
||||
|
||||
int client_body_limit;
|
||||
std::string root;
|
||||
std::string path; // /path and /path/ are fine
|
||||
// i add trailing / if a dir
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
std::vector<http_method> allow_methods;
|
||||
std::map<std::string, std::string> cgi_info;
|
||||
unsigned int allow_methods;
|
||||
std::vector<std::string> cgi_ext; // php not .php
|
||||
bool autoindex;
|
||||
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// wait no there can only be 1 and i think it might have to be in
|
||||
// location only...
|
||||
int redirect_status;
|
||||
std::string redirect_uri;
|
||||
std::string upload_dir;
|
||||
|
||||
int redirect_status; // only in location
|
||||
std::string redirect_uri; // only 1 per location
|
||||
// au pire you do location / { return 301 http://location; }
|
||||
// and that's how you get the redirect from the root.
|
||||
|
||||
void print_all()
|
||||
{
|
||||
std::cout << "\nPRINTING A LOCATION\n";
|
||||
|
||||
std::cout << "Path: " << path << '\n';
|
||||
std::cout << "root: " << root << '\n';
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
|
||||
std::cout << "Skipping index...\n";
|
||||
|
||||
std::cout << "Location allow_methods: ";
|
||||
std::cout << ::http_methods_to_str(allow_methods) << "\n";
|
||||
|
||||
std::cout << "Skipping redirect status etc...\n";
|
||||
|
||||
std::cout << "------\n";
|
||||
}
|
||||
|
||||
// works a lot better than using a compare function...
|
||||
bool operator<(const LocationConfig& rhs) const
|
||||
{
|
||||
int comp_lhs = 0;
|
||||
int comp_rhs = 0;
|
||||
size_t tmp = 0;
|
||||
|
||||
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
|
||||
{
|
||||
++tmp;
|
||||
++comp_lhs;
|
||||
}
|
||||
if (path[path.find_last_of("/") + 1] != '\0')
|
||||
++comp_lhs;
|
||||
tmp = 0;
|
||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
{
|
||||
++tmp;
|
||||
++comp_rhs;
|
||||
}
|
||||
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
|
||||
++comp_rhs;
|
||||
|
||||
return (comp_lhs < comp_rhs); // right comparison ? not <= ?
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -14,48 +14,24 @@
|
||||
class ServerConfig
|
||||
{
|
||||
public:
|
||||
|
||||
// do i need some canonic stuff?
|
||||
|
||||
|
||||
// there can be several
|
||||
std::vector<std::string> server_name;
|
||||
// we could shove default in here if we wanted to...
|
||||
|
||||
// there can only be 1 per server...
|
||||
std::string host;
|
||||
std::string port; // port needs to be something else... not quite an int
|
||||
// should a Server be able to handle several?
|
||||
|
||||
// there can only be one.
|
||||
std::string root;
|
||||
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
|
||||
|
||||
// might be the only one we let slide if bad input...
|
||||
bool autoindex;
|
||||
|
||||
// we will check the index in the post processing with access() ?
|
||||
std::vector<std::string> index;
|
||||
std::map<int, std::string> error_pages;
|
||||
|
||||
// i'm tempted to do something diff for storing method types...
|
||||
// fuck it, you can only call allow_methods once in Server
|
||||
// once more in each location.
|
||||
std::vector<http_method> allow_methods;
|
||||
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
// not convinced we need these...
|
||||
// struct timeval send_timeout;
|
||||
// struct timeval recv_timeout;
|
||||
|
||||
|
||||
// fuck maybe i do need return here...
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// i think actually there can only be 1 and it can only be in a location?
|
||||
// int redirect_status;
|
||||
// std::string redirect_uri;
|
||||
|
||||
void print_all()
|
||||
{
|
||||
@@ -73,15 +49,12 @@ public:
|
||||
std::cout << it->first << "--" << it->second << " ";
|
||||
// for (size_t i = 0; i < error_pages.size(); i++)
|
||||
// std::cout << error_pages->first << "--" << error_pages->second << " ";
|
||||
std::cout << "\nallow_methods: ";
|
||||
for (size_t i = 0; i < allow_methods.size(); i++)
|
||||
std::cout << allow_methods[i] << " ";
|
||||
std::cout << "\nskiping Locations for now...\n";
|
||||
std::cout << "also skiping send_timeout and recv\n";
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
|
||||
// std::cout << "skiping Locations for now...\n";
|
||||
for (std::vector<LocationConfig>::iterator it = locations.begin(); it < locations.end(); it++)
|
||||
it->print_all();
|
||||
|
||||
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
||||
// std::cout << "redirect_status: " << redirect_status << '\n';
|
||||
// std::cout << "redirect_uri: " << redirect_uri << '\n';
|
||||
std::cout << "host: " << host << '\n';
|
||||
std::cout << "port: " << port << '\n';
|
||||
|
||||
|
||||
27
srcs/config/compareLocationConfigs.cpp
Normal file
27
srcs/config/compareLocationConfigs.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
// prolly get rid of this file...
|
||||
|
||||
|
||||
#include "LocationConfig.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
// Ok so maybe it can't be a member functions?
|
||||
bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b)
|
||||
{
|
||||
int len_a;
|
||||
int len_b;
|
||||
size_t tmp = 0;
|
||||
|
||||
// consider adding 1 to path that ends in a file not folder.
|
||||
|
||||
|
||||
while ((tmp = a.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
++len_a;
|
||||
tmp = 0;
|
||||
while ((tmp = b.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
++len_b;
|
||||
return (len_a < len_b); // right comparison ? not <= ?
|
||||
}
|
||||
@@ -19,8 +19,8 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
// there shouldn't be any tabs, right? not between values...
|
||||
if (value.find_first_of("\t") != std::string::npos)
|
||||
{
|
||||
std::cout << value << "\n";
|
||||
throw std::invalid_argument("bad config file arguments 3");
|
||||
// std::cout << value << "\n";
|
||||
throw std::invalid_argument("why would you put tabs between values");
|
||||
}
|
||||
|
||||
size_t i = value.find_first_of(";");
|
||||
@@ -33,11 +33,6 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
throw std::invalid_argument("bad config file arguments 4");
|
||||
|
||||
|
||||
// we Trim value.
|
||||
// is this valid?
|
||||
// would it be better to shove the result directly in tmp_val?
|
||||
// like call substr in split?
|
||||
//value = value.substr(0, i - 1);
|
||||
return (value.substr(0, i));
|
||||
}
|
||||
|
||||
@@ -68,23 +63,18 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
// std::cout << "start + 4 = " << _content.substr(start, 4) << "\n";
|
||||
// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n";
|
||||
|
||||
|
||||
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
std::string values = _content.substr(start, *curr - start);
|
||||
|
||||
// std::cout << "rest of Line values: " << values << "\n";
|
||||
|
||||
return (values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_print_content() const
|
||||
{
|
||||
std::cout << _content;
|
||||
}
|
||||
|
||||
// I might need to make my own Exceptions to throw...
|
||||
@@ -31,8 +31,6 @@ ConfigParser::ConfigParser(const char* path)
|
||||
file.open(path);
|
||||
if (file.is_open())
|
||||
{
|
||||
// are there more throws i need to add in case of errors, what would
|
||||
// those errors be?
|
||||
while (!file.eof())
|
||||
{
|
||||
getline(file, buf);
|
||||
@@ -45,7 +43,7 @@ ConfigParser::ConfigParser(const char* path)
|
||||
}
|
||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||
{
|
||||
// is there a comment at the end of the line
|
||||
// check for comment at the end of the line
|
||||
std::string tmp = buf.substr(0, comment - 1);
|
||||
_content.append(tmp + '\n');
|
||||
}
|
||||
@@ -53,7 +51,7 @@ ConfigParser::ConfigParser(const char* path)
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("open config");
|
||||
throw std::invalid_argument("failed to open config");
|
||||
}
|
||||
|
||||
ConfigParser::~ConfigParser()
|
||||
@@ -85,10 +83,11 @@ std::vector<ServerConfig> * ConfigParser::parse()
|
||||
throw std::invalid_argument("empty config file");
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// why no checks here
|
||||
// if not here do i need them elsewhere?
|
||||
start = _content.find_first_not_of(" \t\n", curr);
|
||||
curr = _content.find_first_of(" \t\n", start);
|
||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos)
|
||||
throw std::invalid_argument("empty config file");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
||||
throw std::invalid_argument("empty config file");
|
||||
std::string key = _content.substr(start, curr - start);
|
||||
if (key != "server")
|
||||
throw std::invalid_argument("bad config file arguments 1");
|
||||
@@ -104,13 +103,12 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
|
||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.autoindex = false;
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 1");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
// are there other things to check for?
|
||||
while (curr != std::string::npos) // here curr == { + 1
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
@@ -142,9 +140,14 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
size_t curr = *start;
|
||||
// start is after the 1st word aka "location"
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.autoindex = false;
|
||||
ret.redirect_status = 0;
|
||||
ret.allow_methods = 0;
|
||||
|
||||
ret.path = _get_first_word(&curr);
|
||||
if (ret.path[0] != '/')
|
||||
throw std::invalid_argument("Location path require a leading /");
|
||||
// ret.path.insert(0, "/");
|
||||
// in theory now curr should be right after the "path"
|
||||
|
||||
curr = _content.find_first_not_of(" \t\n", curr);
|
||||
@@ -152,9 +155,8 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 2");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
@@ -177,11 +179,11 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// should i be sending pointers or references?
|
||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
// should i be sending pointers or references?
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
@@ -189,11 +191,12 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "server_name" && size == 1)
|
||||
else if (key == "server_name" && server->server_name.empty())
|
||||
{
|
||||
for (size_t i = 0; i < server->server_name.size(); i++)
|
||||
for (std::vector<std::string>::iterator it = server->server_name.begin(); \
|
||||
it < server->server_name.end(); it++)
|
||||
{
|
||||
if (server->server_name[i].compare(tmp_val[0]) == 0)
|
||||
if (it->compare(tmp_val[0]) == 0)
|
||||
throw std::invalid_argument("server_name already exists");
|
||||
}
|
||||
server->server_name.push_back(tmp_val[0]);
|
||||
@@ -203,7 +206,6 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
if (tmp_val[0].find_first_of(":") == std::string::npos)
|
||||
{
|
||||
// should i limit which ports can be used?
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
server->host = "0.0.0.0";
|
||||
@@ -229,19 +231,11 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
}
|
||||
else if (key == "root" && size == 1 && server->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
server->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
{
|
||||
// autoindex is a bool, there's no good way for me to see if it has
|
||||
// bet set already
|
||||
server->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& server->client_body_limit == 0)
|
||||
{
|
||||
@@ -251,69 +245,32 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
// i think you can call index several times...
|
||||
// should i be doing an access?
|
||||
// since index is at the root, but root might not yet be defined
|
||||
// will check index later in post
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
server->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && server->allow_methods.empty())
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
server->allow_methods.push_back(m);
|
||||
}
|
||||
}
|
||||
else if (key == "error_page")
|
||||
{
|
||||
|
||||
// so it can either be just a /here/is/the/repo
|
||||
// or it can be http://some_domain.com/here
|
||||
// wtf... how should we handle...
|
||||
|
||||
|
||||
|
||||
// you can definitely call Error_pages several times, i think
|
||||
std::string path = tmp_val[tmp_val.size() - 1];
|
||||
for (unsigned long i = 0; i != tmp_val.size() - 1; i++)
|
||||
{
|
||||
// what are the bounds for Error codes?
|
||||
if (!(isNumeric_btw(0, 600, tmp_val[i])))
|
||||
throw std::invalid_argument("value not a valid number");
|
||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||
throw std::invalid_argument("invalid error code");
|
||||
int status_code = atoi(tmp_val[i].c_str());
|
||||
|
||||
// yea cuz here we continue.. why suddenly flexible not throw ?
|
||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||
continue ;
|
||||
throw std::invalid_argument("redeclaring error page");
|
||||
server->error_pages[status_code] = path;
|
||||
}
|
||||
}
|
||||
/* else if (key == "recv_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
// what is tv_sec and do i need it?
|
||||
// ok so i don't fully understand this part but ok, keep for now...
|
||||
server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "send_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
server->send_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
*/ else
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// should i be sending pointers or references?
|
||||
void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
// should i be sending pointers or references?
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
@@ -323,69 +280,64 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "root" && size == 1 && location->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
location->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& location->client_body_limit == 0)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
location->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
location->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
else if (key == "index")
|
||||
{
|
||||
// you can definitely call Index several times, i think
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
location->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && location->allow_methods.empty())
|
||||
else if (key == "allow_methods" && location->allow_methods == 0)
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
location->allow_methods.push_back(m);
|
||||
location->allow_methods |= m;
|
||||
}
|
||||
}
|
||||
else if (key == "cgi_info")
|
||||
else if (key == "cgi_ext")
|
||||
{
|
||||
// you can call cgi_info several times i think.
|
||||
// ok wtf is all this even doing, figure that out
|
||||
unsigned long i = value.find_first_of(" ");
|
||||
if (i == std::string::npos)
|
||||
throw std::invalid_argument("bad config file arguments 8");
|
||||
// ok why an int now, we gotta be more consistent!
|
||||
int j = value.find_first_not_of(" ", i);
|
||||
location->cgi_info[value.substr(0, i)] = value.substr(j, value.length());
|
||||
for (size_t i = 0; i < tmp_val.size(); i++)
|
||||
{
|
||||
if (tmp_val[i][0] == '.')
|
||||
throw std::invalid_argument("cgi_ext should not have a leading '.'");
|
||||
location->cgi_ext.push_back(tmp_val[i]);
|
||||
}
|
||||
}
|
||||
else if (key == "return" && location->redirect_status == 0 \
|
||||
else if (key == "redirect" && location->redirect_status == 0 \
|
||||
&& location->redirect_uri == "")
|
||||
{
|
||||
// actually i think there can only be one per location...
|
||||
// you can definitely call return several times, i think
|
||||
if (tmp_val.size() != 2)
|
||||
throw std::invalid_argument("wrong number of values");
|
||||
// and tmp_val[0] should be a number and tmp_val[1] a string?
|
||||
if (!(::isNumeric(tmp_val[0])))
|
||||
throw std::invalid_argument("value not a number");
|
||||
if (tmp_val[0] != "301" && tmp_val[0] != "302"
|
||||
&& tmp_val[0] != "303" && tmp_val[0] != "307"
|
||||
&& tmp_val[0] != "308")
|
||||
throw std::invalid_argument("bad redirect status");
|
||||
std::cout << tmp_val[1] << '\n';
|
||||
if (tmp_val[1].compare(0, 7, "http://")
|
||||
&& tmp_val[1].compare(0, 8, "https://"))
|
||||
throw std::invalid_argument("bad redirect uri");
|
||||
|
||||
// somehow check that tmp_val[1] is a string? or valid? how?
|
||||
// something about using access() to see if
|
||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else
|
||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
// what checks to do?
|
||||
// add trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] != '/')
|
||||
tmp_val[0].push_back('/');
|
||||
location->upload_dir = tmp_val[0];
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
|
||||
|
||||
88
srcs/config/postProcessing.cpp
Normal file
88
srcs/config/postProcessing.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port are Mandatory
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// root is mandatory
|
||||
if (it->root == "")
|
||||
throw std::invalid_argument("Config file needs a root");
|
||||
|
||||
// index is mandatory in Server
|
||||
if (it->index.empty())
|
||||
throw std::invalid_argument("Config file needs an Index");
|
||||
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
|
||||
if (!_find_root_path_location(it->locations))
|
||||
{
|
||||
LocationConfig tmp;
|
||||
|
||||
tmp.path = "/";
|
||||
tmp.root = it->root;
|
||||
tmp.index = it->index;
|
||||
tmp.allow_methods = ANY_METHODS;
|
||||
tmp.autoindex = false;
|
||||
tmp.redirect_status = 0;
|
||||
it->locations.push_back(tmp);
|
||||
}
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
if (it_l->allow_methods == UNKNOWN)
|
||||
it_l->allow_methods = ANY_METHODS;
|
||||
|
||||
if (it_l->index.empty())
|
||||
it_l->index = it->index;
|
||||
|
||||
// nothing to be done for cgi_ext, error_pages, redirect
|
||||
|
||||
if (path_is_valid(it_l->root + it_l->path) == 1 \
|
||||
&& it_l->path[it_l->path.size() - 1] != '/')
|
||||
it_l->path.push_back('/');
|
||||
|
||||
++it_l;
|
||||
}
|
||||
std::sort(it->locations.begin(), it->locations.end());
|
||||
std::reverse(it->locations.begin(), it->locations.end());
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
|
||||
{
|
||||
std::vector<LocationConfig>::iterator it = locations.begin();
|
||||
|
||||
while (it != locations.end())
|
||||
{
|
||||
if (it->path.compare("/") == 0)
|
||||
{
|
||||
// std::cout << "in compare: " << it->path << " -- ";
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ int main(int ac, char **av)
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
225
srcs/utils.cpp
225
srcs/utils.cpp
@@ -1,6 +1,22 @@
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
void throw_test()
|
||||
{
|
||||
static int i = 0;
|
||||
++i;
|
||||
if (i % 8 == 0)
|
||||
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;
|
||||
@@ -13,14 +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)
|
||||
{
|
||||
str = str.substr(str.find_first_not_of(c));
|
||||
str = str.substr(0, str.find_last_not_of(c) + 1);
|
||||
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 del
|
||||
pos = str.find_first_not_of(del);
|
||||
if (pos == std::string::npos)
|
||||
pos = str.size();
|
||||
str = str.substr(pos);
|
||||
|
||||
// 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;
|
||||
@@ -85,7 +149,37 @@ std::string http_methods_to_str(unsigned int methods)
|
||||
return (str);
|
||||
}
|
||||
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr)
|
||||
# include <iostream>
|
||||
|
||||
// you could make this &path...
|
||||
int path_is_valid(std::string path)
|
||||
{
|
||||
const char *tmp_path = path.c_str();
|
||||
struct stat s;
|
||||
|
||||
if (stat(tmp_path, &s) == 0)
|
||||
{
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a file\n";
|
||||
return (2);
|
||||
}
|
||||
else if (S_ISDIR(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a Dir\n";
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
// std::cout << "path is neither dir nor file\n";
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
replace_all_substr(
|
||||
std::string &str,
|
||||
const std::string &ori_substr,
|
||||
const std::string &new_substr)
|
||||
{
|
||||
if (ori_substr.empty())
|
||||
return;
|
||||
@@ -100,15 +194,126 @@ void replace_all_substr(std::string &str, const std::string &ori_substr, const s
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const listen_socket& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
bool operator==(int fd, const listen_socket& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
std::string str_tolower(std::string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
std::string del_str;
|
||||
size_t begin;
|
||||
size_t end;
|
||||
size_t len;
|
||||
|
||||
begin = str.rfind(delim, pos);
|
||||
if (begin == std::string::npos)
|
||||
begin = 0;
|
||||
else
|
||||
begin += delim.size();
|
||||
|
||||
end = str.find(delim, pos);
|
||||
len = end;
|
||||
if (end != std::string::npos)
|
||||
len = 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; }
|
||||
|
||||
bool operator==(int fd, const listen_socket& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
|
||||
@@ -3,25 +3,20 @@
|
||||
# define UTILS_HPP
|
||||
|
||||
# include <vector>
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <sys/stat.h> // stat()
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
# include <cstdio> // fflush
|
||||
# include "colors.h"
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
|
||||
// enum http_method
|
||||
// {
|
||||
// UNKNOWN = 0b00000000,
|
||||
// GET = 0b00000001,
|
||||
// POST = 0b00000010,
|
||||
// DELETE = 0b00000100,
|
||||
// ANY_METHODS = 0b11111111,
|
||||
// };
|
||||
|
||||
enum http_method
|
||||
{
|
||||
UNKNOWN = 0b0,
|
||||
@@ -29,6 +24,8 @@ enum http_method
|
||||
POST = 1 << 1,
|
||||
DELETE = 1 << 2,
|
||||
ANY_METHODS = 0b11111111,
|
||||
// ALL_METHODS = 0b11111111,
|
||||
// i would prefer this...
|
||||
};
|
||||
|
||||
struct listen_socket
|
||||
@@ -41,13 +38,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);
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||
std::string str_tolower(std::string str);
|
||||
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
|
||||
|
||||
@@ -24,20 +24,24 @@
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
# include "http_status.hpp"
|
||||
# include "autoindex.hpp"
|
||||
|
||||
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
|
||||
# define SUCCESS 1
|
||||
|
||||
# define MIME_TYPE_DEFAULT "application/octet-stream"
|
||||
|
||||
class Webserv
|
||||
{
|
||||
public:
|
||||
@@ -70,37 +74,39 @@ class Webserv
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
void _read_request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
void _send_response(Client *client, ServerConfig &server);
|
||||
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client, ServerConfig &server);
|
||||
void _process_method(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client, ServerConfig &server);
|
||||
void _append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension = "");
|
||||
|
||||
void _get(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
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;
|
||||
// method_get.cpp
|
||||
void _get(Client *client);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
|
||||
void _post(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _autoindex(Client *client, std::string &path);
|
||||
// method_post.cpp
|
||||
void _post(Client *client);
|
||||
void _post_file(Client *client, const std::string &path);
|
||||
|
||||
void _delete(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
// method_delete.cpp
|
||||
void _delete(Client *client);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
|
||||
ServerConfig &_determine_process_server(Client *client);
|
||||
LocationConfig &_determine_location(ServerConfig &server, std::string const &path);
|
||||
void _response_correction(Client *client);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client);
|
||||
void _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);
|
||||
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);
|
||||
@@ -115,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
|
||||
|
||||
|
||||
32
srcs/webserv/autoindex.hpp
Normal file
32
srcs/webserv/autoindex.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#ifndef AUTOINDEX_HPP
|
||||
# define AUTOINDEX_HPP
|
||||
|
||||
// # define HTML_ERROR(STATUS) "\r\n<!DOCTYPE html><html><head><title>"STATUS"</title></head><body><h1 style=\"text-align:center\">"STATUS"</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
|
||||
# define AUTOINDEX_START \
|
||||
"<!DOCTYPE html>"\
|
||||
"<html>"\
|
||||
"<head>"\
|
||||
"<title>Index of "
|
||||
|
||||
# define AUTOINDEX_MID1 \
|
||||
"</title>"\
|
||||
"</head>"\
|
||||
"<body>" \
|
||||
"<h1>Index of "
|
||||
|
||||
# define AUTOINDEX_MID2 \
|
||||
"</h1>"\
|
||||
"<hr>"\
|
||||
"<pre>"
|
||||
|
||||
# define AUTOINDEX_END \
|
||||
"</pre>"\
|
||||
"<hr>"\
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -11,16 +11,19 @@ bool Webserv::_is_cgi(Client *client)
|
||||
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);
|
||||
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 = "")
|
||||
@@ -49,7 +52,7 @@ char** Webserv::_set_env(Client *client)
|
||||
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.0");
|
||||
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());
|
||||
@@ -69,7 +72,7 @@ char** Webserv::_set_env(Client *client)
|
||||
return env;
|
||||
}
|
||||
|
||||
void Webserv::_exec_script(Client *client, char **env)
|
||||
std::string Webserv::_exec_script(Client *client, char **env)
|
||||
{
|
||||
#define RD 0
|
||||
#define WR 1
|
||||
@@ -82,10 +85,12 @@ void Webserv::_exec_script(Client *client, char **env)
|
||||
pid_t pid;
|
||||
char buf[CGI_BUF_SIZE]; // WIP define buffer
|
||||
char * const * nll = NULL;
|
||||
std::string response;
|
||||
std::string script_output;
|
||||
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);
|
||||
@@ -99,7 +104,11 @@ void Webserv::_exec_script(Client *client, char **env)
|
||||
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";
|
||||
}
|
||||
else
|
||||
@@ -113,19 +122,76 @@ void Webserv::_exec_script(Client *client, char **env)
|
||||
memset(buf, '\0', CGI_BUF_SIZE);
|
||||
while (read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE - 1) > 0)
|
||||
{
|
||||
response += buf;
|
||||
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";
|
||||
|
||||
dup2(save_in, STDIN_FILENO);
|
||||
dup2(save_out, STDOUT_FILENO);
|
||||
return script_output;
|
||||
}
|
||||
|
||||
void Webserv::_check_script_output(Client *client, std::string & output)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
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);
|
||||
::extract_line(output, pos, CRLF);
|
||||
}
|
||||
else
|
||||
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;
|
||||
std::string tmp;
|
||||
size_t pos;
|
||||
|
||||
// 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 (str_tolower(it_srv->first) == str_tolower(it_scr->first))
|
||||
{
|
||||
pos = client->response.find(it_srv->first);
|
||||
::extract_line(client->response, pos, CRLF);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response.empty())
|
||||
response = "Status: 500\r\n\r\n";
|
||||
|
||||
// DEBUG
|
||||
std::cout << "\n_______response_______\n"
|
||||
<< response
|
||||
<< "\n_____end response_____\n";
|
||||
|
||||
// TODO: see how this must be handled
|
||||
client->response += response;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ void Webserv::_close_client(int fd)
|
||||
if (*it == fd)
|
||||
{
|
||||
// _epoll_update(fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
std::cerr << "close fd " << fd << "\n";
|
||||
if (::close(fd) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << fd << "\n";
|
||||
_clients.erase(it);
|
||||
break;
|
||||
}
|
||||
@@ -25,10 +24,9 @@ void Webserv::_close_all_clients()
|
||||
while (!_clients.empty())
|
||||
{
|
||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||
if (::close(_clients.back().get_cl_fd()) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||
_clients.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -38,10 +36,9 @@ void Webserv::_close_all_listen_sockets()
|
||||
while (!_listen_sockets.empty())
|
||||
{
|
||||
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
std::cerr << "close fd " << _listen_sockets.back().fd << "\n";
|
||||
if (::close(_listen_sockets.back().fd) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << _listen_sockets.back().fd << "\n";
|
||||
_listen_sockets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@
|
||||
# define S201 "201 Created"
|
||||
# define S204 "204 No Content"
|
||||
|
||||
# define S301 "301 Moved Permanently"
|
||||
# define S302 "302 Found"
|
||||
# define S303 "303 See Other"
|
||||
# define S304 "304 Not Modified" // unused
|
||||
# define S307 "307 Temporary Redirect"
|
||||
# define S308 "308 Permanent Redirect"
|
||||
|
||||
# define S400 "400 Bad Request"
|
||||
# define S403 "403 Forbidden"
|
||||
# define S404 "404 Not Found"
|
||||
@@ -38,5 +45,6 @@
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
# define S505 "505 HTTP Version Not Supported"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,133 +89,152 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
|
||||
void Webserv::_init_http_status_map()
|
||||
{
|
||||
_http_status[200] = S200;
|
||||
_http_status[201] = S201;
|
||||
_http_status[204] = S204;
|
||||
/* "map.insert()" over "map.operator[]" :
|
||||
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||
*/
|
||||
typedef std::map<int, std::string>::value_type status_pair;
|
||||
|
||||
_http_status[400] = S400;
|
||||
_http_status[403] = S403;
|
||||
_http_status[404] = S404;
|
||||
_http_status[405] = S405;
|
||||
_http_status[413] = S413;
|
||||
// _http_status.insert(std::make_pair(200, S200)); // equivalent
|
||||
_http_status.insert(status_pair(200, S200));
|
||||
_http_status.insert(status_pair(201, S201));
|
||||
_http_status.insert(status_pair(204, S204));
|
||||
|
||||
_http_status[500] = S500;
|
||||
_http_status[501] = S501;
|
||||
_http_status.insert(status_pair(301, S301));
|
||||
_http_status.insert(status_pair(302, S302));
|
||||
_http_status.insert(status_pair(303, S303));
|
||||
_http_status.insert(status_pair(304, S304));
|
||||
_http_status.insert(status_pair(307, S307));
|
||||
_http_status.insert(status_pair(308, S308));
|
||||
|
||||
_http_status.insert(status_pair(400, S400));
|
||||
_http_status.insert(status_pair(403, S403));
|
||||
_http_status.insert(status_pair(404, S404));
|
||||
_http_status.insert(status_pair(405, S405));
|
||||
_http_status.insert(status_pair(413, S413));
|
||||
|
||||
_http_status.insert(status_pair(500, S500));
|
||||
_http_status.insert(status_pair(501, S501));
|
||||
}
|
||||
|
||||
void Webserv::_init_mime_types_map()
|
||||
{
|
||||
_mime_types[""] = "application/octet-stream";
|
||||
/* From :
|
||||
** http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
*/
|
||||
typedef std::map<std::string, std::string>::value_type mime_pair;
|
||||
|
||||
_mime_types["html"] = "text/html";
|
||||
_mime_types["htm"] = "text/html";
|
||||
_mime_types["shtml"] = "text/html";
|
||||
_mime_types["css"] = "text/css";
|
||||
_mime_types["xml"] = "text/xml";
|
||||
_mime_types["gif"] = "image/gif";
|
||||
_mime_types["jpeg"] = "image/jpeg";
|
||||
_mime_types["jpg"] = "image/jpeg";
|
||||
_mime_types["js"] = "application/javascript";
|
||||
_mime_types["atom"] = "application/atom+xml";
|
||||
_mime_types["rss"] = "application/rss+xml";
|
||||
_mime_types.insert(mime_pair("", MIME_TYPE_DEFAULT));
|
||||
|
||||
_mime_types["mml"] = "text/mathml";
|
||||
_mime_types["txt"] = "text/plain";
|
||||
_mime_types["jad"] = "text/vnd.sun.j2me.app-descriptor";
|
||||
_mime_types["wml"] = "text/vnd.wap.wml";
|
||||
_mime_types["htc"] = "text/x-component";
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("htm", "text/html"));
|
||||
_mime_types.insert(mime_pair("shtml", "text/html"));
|
||||
_mime_types.insert(mime_pair("css", "text/css"));
|
||||
_mime_types.insert(mime_pair("xml", "text/xml"));
|
||||
_mime_types.insert(mime_pair("gif", "image/gif"));
|
||||
_mime_types.insert(mime_pair("jpeg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("jpg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("js", "application/javascript"));
|
||||
_mime_types.insert(mime_pair("atom", "application/atom+xml"));
|
||||
_mime_types.insert(mime_pair("rss", "application/rss+xml"));
|
||||
|
||||
_mime_types["png"] = "image/png";
|
||||
_mime_types["tif"] = "image/tiff";
|
||||
_mime_types["tiff"] = "image/tiff";
|
||||
_mime_types["wbmp"] = "image/vnd.wap.wbmp";
|
||||
_mime_types["ico"] = "image/x-icon";
|
||||
_mime_types["jng"] = "image/x-jng";
|
||||
_mime_types["bmp"] = "image/x-ms-bmp";
|
||||
_mime_types["svg"] = "image/svg+xml";
|
||||
_mime_types["svgz"] = "image/svg+xml";
|
||||
_mime_types["webp"] = "image/webp";
|
||||
_mime_types.insert(mime_pair("mml", "text/mathml"));
|
||||
_mime_types.insert(mime_pair("txt", "text/plain"));
|
||||
_mime_types.insert(mime_pair("jad", "text/vnd.sun.j2me.app-descriptor"));
|
||||
_mime_types.insert(mime_pair("wml", "text/vnd.wap.wml"));
|
||||
_mime_types.insert(mime_pair("htc", "text/x-component"));
|
||||
|
||||
_mime_types["woff"] = "application/font-woff";
|
||||
_mime_types["jar"] = "application/java-archive";
|
||||
_mime_types["war"] = "application/java-archive";
|
||||
_mime_types["ear"] = "application/java-archive";
|
||||
_mime_types["json"] = "application/json";
|
||||
_mime_types["hqx"] = "application/mac-binhex40";
|
||||
_mime_types["doc"] = "application/msword";
|
||||
_mime_types["pdf"] = "application/pdf";
|
||||
_mime_types["ps"] = "application/postscript";
|
||||
_mime_types["eps"] = "application/postscript";
|
||||
_mime_types["ai"] = "application/postscript";
|
||||
_mime_types["rtf"] = "application/rtf";
|
||||
_mime_types["m3u8"] = "application/vnd.apple.mpegurl";
|
||||
_mime_types["xls"] = "application/vnd.ms-excel";
|
||||
_mime_types["eot"] = "application/vnd.ms-fontobject";
|
||||
_mime_types["ppt"] = "application/vnd.ms-powerpoint";
|
||||
_mime_types["wmlc"] = "application/vnd.wap.wmlc";
|
||||
_mime_types["kml"] = "application/vnd.google-earth.kml+xml";
|
||||
_mime_types["kmz"] = "application/vnd.google-earth.kmz";
|
||||
_mime_types["7z"] = "application/x-7z-compressed";
|
||||
_mime_types["cco"] = "application/x-cocoa";
|
||||
_mime_types["jardiff"] = "application/x-java-archive-diff";
|
||||
_mime_types["jnlp"] = "application/x-java-jnlp-file";
|
||||
_mime_types["run"] = "application/x-makeself";
|
||||
_mime_types["pl"] = "application/x-perl";
|
||||
_mime_types["pm"] = "application/x-perl";
|
||||
_mime_types["prc"] = "application/x-pilot";
|
||||
_mime_types["pdb"] = "application/x-pilot";
|
||||
_mime_types["rar"] = "application/x-rar-compressed";
|
||||
_mime_types["rpm"] = "application/x-redhat-package-manager";
|
||||
_mime_types["sea"] = "application/x-sea";
|
||||
_mime_types["swf"] = "application/x-shockwave-flash";
|
||||
_mime_types["sit"] = "application/x-stuffit";
|
||||
_mime_types["tcl"] = "application/x-tcl";
|
||||
_mime_types["tk"] = "application/x-tcl";
|
||||
_mime_types["der"] = "application/x-x509-ca-cert";
|
||||
_mime_types["pem"] = "application/x-x509-ca-cert";
|
||||
_mime_types["crt"] = "application/x-x509-ca-cert";
|
||||
_mime_types["xpi"] = "application/x-xpinstall";
|
||||
_mime_types["xhtml"] = "application/xhtml+xml";
|
||||
_mime_types["xspf"] = "application/xspf+xml";
|
||||
_mime_types["zip"] = "application/zip";
|
||||
_mime_types.insert(mime_pair("png", "image/png"));
|
||||
_mime_types.insert(mime_pair("tif", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("tiff", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("wbmp", "image/vnd.wap.wbmp"));
|
||||
_mime_types.insert(mime_pair("ico", "image/x-icon"));
|
||||
_mime_types.insert(mime_pair("jng", "image/x-jng"));
|
||||
_mime_types.insert(mime_pair("bmp", "image/x-ms-bmp"));
|
||||
_mime_types.insert(mime_pair("svg", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("svgz", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("webp", "image/webp"));
|
||||
|
||||
_mime_types["bin"] = "application/octet-stream";
|
||||
_mime_types["exe"] = "application/octet-stream";
|
||||
_mime_types["dll"] = "application/octet-stream";
|
||||
_mime_types["deb"] = "application/octet-stream";
|
||||
_mime_types["dmg"] = "application/octet-stream";
|
||||
_mime_types["iso"] = "application/octet-stream";
|
||||
_mime_types["img"] = "application/octet-stream";
|
||||
_mime_types["msi"] = "application/octet-stream";
|
||||
_mime_types["msp"] = "application/octet-stream";
|
||||
_mime_types["msm"] = "application/octet-stream";
|
||||
_mime_types.insert(mime_pair("woff", "application/font-woff"));
|
||||
_mime_types.insert(mime_pair("jar", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("war", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("ear", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("json", "application/json"));
|
||||
_mime_types.insert(mime_pair("hqx", "application/mac-binhex40"));
|
||||
_mime_types.insert(mime_pair("doc", "application/msword"));
|
||||
_mime_types.insert(mime_pair("pdf", "application/pdf"));
|
||||
_mime_types.insert(mime_pair("ps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("eps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("ai", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("rtf", "application/rtf"));
|
||||
_mime_types.insert(mime_pair("m3u8", "application/vnd.apple.mpegurl"));
|
||||
_mime_types.insert(mime_pair("xls", "application/vnd.ms-excel"));
|
||||
_mime_types.insert(mime_pair("eot", "application/vnd.ms-fontobject"));
|
||||
_mime_types.insert(mime_pair("ppt", "application/vnd.ms-powerpoint"));
|
||||
_mime_types.insert(mime_pair("wmlc", "application/vnd.wap.wmlc"));
|
||||
_mime_types.insert(mime_pair("kml", "application/vnd.google-earth.kml+xml"));
|
||||
_mime_types.insert(mime_pair("kmz", "application/vnd.google-earth.kmz"));
|
||||
_mime_types.insert(mime_pair("7z", "application/x-7z-compressed"));
|
||||
_mime_types.insert(mime_pair("cco", "application/x-cocoa"));
|
||||
_mime_types.insert(mime_pair("jardiff", "application/x-java-archive-diff"));
|
||||
_mime_types.insert(mime_pair("jnlp", "application/x-java-jnlp-file"));
|
||||
_mime_types.insert(mime_pair("run", "application/x-makeself"));
|
||||
_mime_types.insert(mime_pair("pl", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("pm", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("prc", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("pdb", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("rar", "application/x-rar-compressed"));
|
||||
_mime_types.insert(mime_pair("rpm", "application/x-redhat-package-manager"));
|
||||
_mime_types.insert(mime_pair("sea", "application/x-sea"));
|
||||
_mime_types.insert(mime_pair("swf", "application/x-shockwave-flash"));
|
||||
_mime_types.insert(mime_pair("sit", "application/x-stuffit"));
|
||||
_mime_types.insert(mime_pair("tcl", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("tk", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("der", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("pem", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("crt", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("xpi", "application/x-xpinstall"));
|
||||
_mime_types.insert(mime_pair("xhtml", "application/xhtml+xml"));
|
||||
_mime_types.insert(mime_pair("xspf", "application/xspf+xml"));
|
||||
_mime_types.insert(mime_pair("zip", "application/zip"));
|
||||
|
||||
_mime_types["docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
_mime_types["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
_mime_types["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
_mime_types.insert(mime_pair("bin", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("exe", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dll", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("deb", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dmg", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("iso", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("img", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msi", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msp", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msm", "application/octet-stream"));
|
||||
|
||||
_mime_types["mid"] = "audio/midi";
|
||||
_mime_types["midi"] = "audio/midi";
|
||||
_mime_types["kar"] = "audio/midi";
|
||||
_mime_types["mp3"] = "audio/mpeg";
|
||||
_mime_types["ogg"] = "audio/ogg";
|
||||
_mime_types["m4a"] = "audio/x-m4a";
|
||||
_mime_types["ra"] = "audio/x-realaudio";
|
||||
_mime_types.insert(mime_pair("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
|
||||
_mime_types.insert(mime_pair("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||
_mime_types.insert(mime_pair("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"));
|
||||
|
||||
_mime_types["3gpp"] = "video/3gpp";
|
||||
_mime_types["3gp"] = "video/3gpp";
|
||||
_mime_types["ts"] = "video/mp2t";
|
||||
_mime_types["mp4"] = "video/mp4";
|
||||
_mime_types["mpeg"] = "video/mpeg";
|
||||
_mime_types["mpg"] = "video/mpeg";
|
||||
_mime_types["mov"] = "video/quicktime";
|
||||
_mime_types["webm"] = "video/webm";
|
||||
_mime_types["flv"] = "video/x-flv";
|
||||
_mime_types["m4v"] = "video/x-m4v";
|
||||
_mime_types["mng"] = "video/x-mng";
|
||||
_mime_types["asx"] = "video/x-ms-asf";
|
||||
_mime_types["asf"] = "video/x-ms-asf";
|
||||
_mime_types["wmv"] = "video/x-ms-wmv";
|
||||
_mime_types["avi"] = "video/x-msvideo";
|
||||
_mime_types.insert(mime_pair("mid", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("midi", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("kar", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("mp3", "audio/mpeg"));
|
||||
_mime_types.insert(mime_pair("ogg", "audio/ogg"));
|
||||
_mime_types.insert(mime_pair("m4a", "audio/x-m4a"));
|
||||
_mime_types.insert(mime_pair("ra", "audio/x-realaudio"));
|
||||
|
||||
_mime_types.insert(mime_pair("3gpp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("3gp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("ts", "video/mp2t"));
|
||||
_mime_types.insert(mime_pair("mp4", "video/mp4"));
|
||||
_mime_types.insert(mime_pair("mpeg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mpg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mov", "video/quicktime"));
|
||||
_mime_types.insert(mime_pair("webm", "video/webm"));
|
||||
_mime_types.insert(mime_pair("flv", "video/x-flv"));
|
||||
_mime_types.insert(mime_pair("m4v", "video/x-m4v"));
|
||||
_mime_types.insert(mime_pair("mng", "video/x-mng"));
|
||||
_mime_types.insert(mime_pair("asx", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("asf", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
|
||||
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
|
||||
}
|
||||
|
||||
|
||||
40
srcs/webserv/method_delete.cpp
Normal file
40
srcs/webserv/method_delete.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_delete(Client *client)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
std::string path = client->get_rq_abs_path();
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
191
srcs/webserv/method_get.cpp
Normal file
191
srcs/webserv/method_get.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_get(Client *client)
|
||||
{
|
||||
/* RULES **
|
||||
|
||||
if path is a valid dir check if index is specified and serve that
|
||||
if no index and autoindex, server that
|
||||
if file, server that!
|
||||
|
||||
Where does cgi fit in in all this ???
|
||||
|
||||
|
||||
*/
|
||||
std::string path = client->get_rq_abs_path();
|
||||
|
||||
// this might not be the best thing, a voir
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
std::cerr << "path = " << path << "\n";
|
||||
|
||||
// path = root + location.path
|
||||
// we will tack on an index if there is a valid one
|
||||
// or autoindex if allowed
|
||||
// or let _get_file sort out the error otherwise.
|
||||
|
||||
if (path_is_valid(path) == 1)
|
||||
{
|
||||
// std::cout << "path is valid\n";
|
||||
if (path[path.size() - 1] != '/')
|
||||
path.push_back('/');
|
||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||
{
|
||||
// std::cout << "location path: " << client->assigned_location->path << '\n';
|
||||
// std::cout << "location index: " << client->assigned_location->index[i] << '\n';
|
||||
// std::cout << "path with index: " << path + assigned_location->index[i] << '\n';
|
||||
if (path_is_valid(path + client->assigned_location->index[i]) == 2)
|
||||
{
|
||||
// std::cout << "found a valid index\n";
|
||||
path.append(client->assigned_location->index[i]);
|
||||
_get_file(client, path);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (client->assigned_location->autoindex == true)
|
||||
{
|
||||
_autoindex(client, path);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
// else
|
||||
// _get_file(client, path);
|
||||
// what about cgi ???
|
||||
|
||||
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
std::string script_output;
|
||||
if (_is_cgi(client))
|
||||
{
|
||||
script_output = _exec_cgi(client);
|
||||
_check_script_output(client, script_output);
|
||||
client->response += script_output;
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
std::ios::binary
|
||||
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.binary
|
||||
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
|
||||
*/
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
std::stringstream buf;
|
||||
|
||||
std::cout << "made it to get_file\n";
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::ate);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* std::streampos size = ifd.tellg();
|
||||
// WIP Low priority : Chunk or not chunk (if filesize too big)
|
||||
if (size > MAX_FILESIZE)
|
||||
{
|
||||
// Then chunk
|
||||
client->status = 500; // WIP temp
|
||||
std::cerr << "File too large for non chunk body\n";
|
||||
return ;
|
||||
} */
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
buf << ifd.rdbuf();
|
||||
if (!ifd || !buf)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
std::string file_ext = _determine_file_extension(path);
|
||||
_append_body(client, buf.str(), file_ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// i only sort of need &path...
|
||||
// def can improve but works for now...
|
||||
//void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &path)
|
||||
void Webserv::_autoindex(Client *client, std::string &path)
|
||||
{
|
||||
// std::cout << "made it to _autoindex\n";
|
||||
|
||||
(void)path;
|
||||
std::string dir_list;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
// std::cout << "location root: " << client->assigned_location->root << " location path: "
|
||||
// << client->assigned_location->path << '\n';
|
||||
|
||||
// if ((dir = opendir (path.c_str())) != NULL)
|
||||
if ((dir = opendir ((client->assigned_location->root + client->assigned_location->path).c_str())) != NULL)
|
||||
{
|
||||
/* print all the files and directories within directory */
|
||||
dir_list.append(AUTOINDEX_START);
|
||||
dir_list.append(client->assigned_location->path);
|
||||
dir_list.append(AUTOINDEX_MID1);
|
||||
dir_list.append(client->assigned_location->path);
|
||||
dir_list.append(AUTOINDEX_MID2);
|
||||
while ((ent = readdir (dir)) != NULL)
|
||||
{
|
||||
if (strcmp(".", ent->d_name) == 0)
|
||||
continue ;
|
||||
dir_list.append("<a href=\"");
|
||||
dir_list.append(client->assigned_location->path.c_str());
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("\">");
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("</a>");
|
||||
dir_list.append("\r\n"); // is this right?
|
||||
}
|
||||
|
||||
// <a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
// <a href="/test/test_deeper/index1.html">index1.html</a>
|
||||
|
||||
// apparently this is more than good enough!
|
||||
// <a href="/test/test_deeper/..">..</a>
|
||||
|
||||
dir_list.append(AUTOINDEX_END);
|
||||
// std::cout << "\n\n" << dir_list << '\n';
|
||||
closedir (dir);
|
||||
_append_body(client, dir_list, "html");
|
||||
}
|
||||
else
|
||||
{
|
||||
// in theory not possible cuz we already checked...
|
||||
/* could not open directory */
|
||||
// perror ("");
|
||||
std::cout << "could not open dir\n";
|
||||
return ;
|
||||
}
|
||||
}
|
||||
63
srcs/webserv/method_post.cpp
Normal file
63
srcs/webserv/method_post.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
|
||||
void Webserv::_post(Client *client)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
std::string path = client->get_rq_abs_path();
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Content-Length useless at this point ?
|
||||
ofd << client->get_rq_body();
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
}
|
||||
}
|
||||
17
srcs/webserv/parsing_message_http.cpp
Normal file
17
srcs/webserv/parsing_message_http.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
#include "parsing_message_http.hpp"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
32
srcs/webserv/parsing_message_http.hpp
Normal file
32
srcs/webserv/parsing_message_http.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#ifndef PARSING_MESSAGE_HTTP_HPP
|
||||
# define PARSING_MESSAGE_HTTP_HPP
|
||||
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <vector>
|
||||
# include <map>
|
||||
# include "utils.hpp"
|
||||
|
||||
std::map<std::string, std::string>
|
||||
parse_http_headers (
|
||||
std::string headers,
|
||||
std::map<std::string, std::string> fields )
|
||||
|
||||
std::string
|
||||
parse_http_body(std::string message);
|
||||
|
||||
// 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"
|
||||
|
||||
@@ -1,44 +1,90 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
#define BUFSIZE 8192
|
||||
// Arbitrary values
|
||||
#define BUFSIZE 8192 // (8Ko)
|
||||
#define MAX_HEADER_SIZE 16384 // (16Ko)
|
||||
|
||||
enum read_return
|
||||
{
|
||||
READ_IN_PROGRESS,
|
||||
READ_COMPLETE,
|
||||
READ_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_request(Client *client)
|
||||
{
|
||||
_read_request(client);
|
||||
int ret = _read_request(client);
|
||||
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == READ_CLOSE)
|
||||
{
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
else if (ret == READ_COMPLETE)
|
||||
{
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_read_request(Client *client)
|
||||
int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
{
|
||||
char buf[BUFSIZE+1];
|
||||
char buf[BUFSIZE];
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "call recv()" << "\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.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
if (ret == 0) // Not sure what to do in case of 0. Just close ?
|
||||
if (ret == 0)
|
||||
{
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
/*
|
||||
if (ret == BUFSIZE)
|
||||
// send error like "request too long" to client
|
||||
*/
|
||||
|
||||
buf[ret] = '\0';
|
||||
client->raw_request.append(buf);
|
||||
client->parse_request();
|
||||
client->raw_request.append(buf, ret);
|
||||
if (!client->header_complete)
|
||||
{
|
||||
if (client->raw_request.find(CRLF CRLF) != std::string::npos)
|
||||
{
|
||||
client->header_complete = true;
|
||||
client->parse_request_headers(_servers);
|
||||
std::cerr << client->get_rq_method_str() << " " << client->get_rq_uri() << " " << client->get_rq_version() << "\n"; // DEBUG
|
||||
if (client->status)
|
||||
return READ_COMPLETE;
|
||||
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
if (client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty()) // No body case
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
||||
{
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,60 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
enum send_return
|
||||
{
|
||||
SEND_IN_PROGRESS, // unused
|
||||
SEND_COMPLETE,
|
||||
SEND_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_response(Client *client)
|
||||
{
|
||||
client->status = 200; // default value
|
||||
int ret = _send_response(client);
|
||||
|
||||
ServerConfig &server = _determine_process_server(client);
|
||||
_send_response(client, server);
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == SEND_CLOSE)
|
||||
{
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
else if (ret == SEND_COMPLETE)
|
||||
{
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_send_response(Client *client, ServerConfig &server)
|
||||
int Webserv::_send_response(Client *client)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "send()\n";
|
||||
|
||||
_append_base_headers(client);
|
||||
_construct_response(client, server);
|
||||
if (!client->status)
|
||||
_construct_response(client);
|
||||
_insert_status_line(client);
|
||||
if (client->status >= 400)
|
||||
_error_html_response(client, server);
|
||||
_error_html_response(client);
|
||||
|
||||
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
||||
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
return SEND_CLOSE;
|
||||
}
|
||||
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
||||
|
||||
// Body send (WIP for working binary files)
|
||||
if (client->body_size)
|
||||
{
|
||||
ret = ::send(client->get_cl_fd(), client->buf, client->body_size, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
return SEND_COMPLETE;
|
||||
}
|
||||
|
||||
void Webserv::_append_base_headers(Client *client)
|
||||
@@ -64,48 +67,28 @@ void Webserv::_append_base_headers(Client *client)
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
}
|
||||
|
||||
void Webserv::_construct_response(Client *client, ServerConfig &server)
|
||||
void Webserv::_construct_response(Client *client)
|
||||
{
|
||||
// TODO : Move this in read(), stop read if content too large
|
||||
if (client->get_rq_body().size() > server.client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return;
|
||||
}
|
||||
LocationConfig &location = _determine_location(server, client->get_rq_uri());
|
||||
_process_method(client, server, location);
|
||||
/* Switch between normal behavior or CGI here ?
|
||||
maybe better than in _get(), _post(), ...*/
|
||||
_process_method(client);
|
||||
}
|
||||
|
||||
void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
void Webserv::_process_method(Client *client)
|
||||
{
|
||||
unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE
|
||||
// after update in ConfigParser, use the "allow_methods" of location.
|
||||
// TODO in ConfigParser : by default if no field in config file, "allow_methods" must be set to ANY_METHODS
|
||||
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_rq_method() == UNKNOWN)
|
||||
switch (client->get_rq_method())
|
||||
{
|
||||
client->status = 501;
|
||||
}
|
||||
else if (allow_methods & client->get_rq_method())
|
||||
{
|
||||
switch (client->get_rq_method())
|
||||
{
|
||||
case (GET):
|
||||
_get(client, server, location); break;
|
||||
case (POST):
|
||||
_post(client, server, location); break;
|
||||
case (DELETE):
|
||||
_delete(client, server, location); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 405;
|
||||
client->response.append("Allow: ");
|
||||
client->response.append(::http_methods_to_str(allow_methods));
|
||||
client->response.append(CRLF);
|
||||
case (GET):
|
||||
_get(client); break;
|
||||
case (POST):
|
||||
_post(client); break;
|
||||
case (DELETE):
|
||||
_delete(client); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,248 +102,44 @@ void Webserv::_insert_status_line(Client *client)
|
||||
client->response.insert(0, status_line);
|
||||
}
|
||||
|
||||
void Webserv::_error_html_response(Client *client, ServerConfig &server)
|
||||
void Webserv::_error_html_response(Client *client)
|
||||
{
|
||||
if (server.error_pages[client->status].empty())
|
||||
if (!client->assigned_server || client->assigned_server->error_pages[client->status].empty())
|
||||
{
|
||||
std::string html_page = HTML_ERROR;
|
||||
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
|
||||
_append_body(client, html_page.c_str(), html_page.size(), "html");
|
||||
_append_body(client, html_page, "html");
|
||||
}
|
||||
else
|
||||
_get_file(client, server.error_pages[client->status]);
|
||||
_get_file(client, client->assigned_server->error_pages[client->status]);
|
||||
}
|
||||
|
||||
#define INDEX "index.html" // temp wip
|
||||
void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
std::string path = client->get_rq_uri();
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
|
||||
if (path == "/") // TODO : index and autoindex
|
||||
path.append(INDEX);
|
||||
path.insert(0, location.root);
|
||||
|
||||
std::cerr << "path = " << path << "\n";
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
if (_is_cgi(client))
|
||||
{
|
||||
_exec_cgi(client);
|
||||
_response_correction(client);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
// char buf[MAX_FILESIZE+1];
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?)
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
client->response.append("Content-Type: ");
|
||||
if (mime_type.empty())
|
||||
client->response.append(MIME_TYPE_DEFAULT);
|
||||
else
|
||||
{
|
||||
std::streampos size = ifd.tellg();
|
||||
|
||||
// WIP : Chunk or not chunk (if filesize too big)
|
||||
if (size > MAX_FILESIZE)
|
||||
{
|
||||
// Then chunk
|
||||
client->status = 500; // WIP temp
|
||||
std::cerr << "File too large for non chunk body\n";
|
||||
ifd.close();
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
ifd.read(client->buf, size);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
client->buf[ifd.gcount()] = '\0';
|
||||
|
||||
std::string file_ext = "";
|
||||
size_t dot_pos = path.rfind(".");
|
||||
std::cerr << "dot_pos = " << dot_pos << "\n";
|
||||
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
|
||||
file_ext = path.substr(dot_pos + 1);
|
||||
std::cerr << "file_ext = " << file_ext << "\n";
|
||||
|
||||
client->body_size = ifd.gcount();
|
||||
// WIP, pass empty body argument because append to string mess up binary file
|
||||
_append_body(client, "", client->body_size, file_ext);
|
||||
}
|
||||
|
||||
ifd.close();
|
||||
}
|
||||
}
|
||||
|
||||
// WIP HUGO
|
||||
void Webserv::_response_correction(Client *client)
|
||||
{
|
||||
(void)client;
|
||||
}
|
||||
|
||||
void Webserv::_append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension)
|
||||
{
|
||||
/*
|
||||
TODO : determine Content-Type
|
||||
how ? read the body ?
|
||||
or before in other way (like based and file extension) and pass here as argument ?
|
||||
http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that.
|
||||
*/
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
client->response.append("Content-Type: ");
|
||||
client->response.append(mime_type);
|
||||
if (mime_type.find("text/") != std::string::npos)
|
||||
client->response.append("; charset=UTF-8");
|
||||
client->response.append(CRLF);
|
||||
}
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body_size);
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body.size());
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
}
|
||||
|
||||
void Webserv::_post(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
std::string path = client->get_rq_uri();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::binary | std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used body.size() so Content-Length useless at this point ?
|
||||
// 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.write(client->get_rq_body().c_str(), client->get_rq_body().size());
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
|
||||
ofd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_delete(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
std::string path = client->get_rq_uri();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
ServerConfig &Webserv::_determine_process_server(Client *client)
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||
{
|
||||
/*
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
@@ -369,45 +148,246 @@ ServerConfig &Webserv::_determine_process_server(Client *client)
|
||||
*/
|
||||
|
||||
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();
|
||||
std::vector<ServerConfig>::iterator it = servers.begin();
|
||||
std::vector<ServerConfig>::iterator default_server = servers.end();
|
||||
|
||||
while (it != _servers.end())
|
||||
while (it != servers.end())
|
||||
{
|
||||
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;
|
||||
else if (default_server == _servers.end())
|
||||
else if (default_server == servers.end())
|
||||
default_server = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != _servers.end())
|
||||
return (*it);
|
||||
if (it != servers.end())
|
||||
return (&(*it));
|
||||
else
|
||||
return (*default_server);
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string const &path)
|
||||
{
|
||||
/*
|
||||
Assume there is at least one location in vector<LocationConfig> for path "/"
|
||||
TODO in ConfigParser :
|
||||
If no location block in config file, one need to be generated
|
||||
for path "/", and filled with fields "root" and "index" based on parent server block
|
||||
*/
|
||||
|
||||
std::vector<LocationConfig>::iterator it = server.locations.begin();
|
||||
const LocationConfig *_determine_location_COOP_FIX(const ServerConfig &server, const std::string &path)
|
||||
{
|
||||
/* Pseudo-code :
|
||||
- comparer les size(), si location.path > client.path, stop comparaison.
|
||||
- client.path.compare(0, location.path.size(), location.path)
|
||||
if ( == 0)
|
||||
{
|
||||
if (location.path.size() == client.path.size())
|
||||
{
|
||||
FOUND;
|
||||
}
|
||||
else if (client.path[location.path.size()] == '/')
|
||||
{
|
||||
FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NOT FOUND;
|
||||
++next;
|
||||
}
|
||||
*/
|
||||
std::vector<LocationConfig>::const_iterator it = server.locations.begin();
|
||||
while (it != server.locations.end())
|
||||
{
|
||||
if (it->path.compare(0, path.size(), path))
|
||||
break;
|
||||
if (it->path.size() > path.size())
|
||||
{
|
||||
// prendre en compte l'éventuel "/" final si location est un dossier
|
||||
if (it->path.size()-1 > path.size() || it->path[it->path.size()-1] != '/')
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
if (it->path.size() == path.size())
|
||||
break;
|
||||
else if (path[it->path.size()-1] == '/')
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != server.locations.end())
|
||||
return (*it);
|
||||
return (&(*it));
|
||||
else
|
||||
return (server.locations.front());
|
||||
return (&(server.locations.back()));
|
||||
}
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path)
|
||||
{
|
||||
std::cout << "determin location path sent: " << path << '\n';
|
||||
|
||||
|
||||
/// NO FUCKING IDEA WHY BUT...
|
||||
// basically if 2 strings are identical to a point, compare from
|
||||
// longer one or it'll freak out cuz of \0 or something idk
|
||||
//// Basically: str.compare() from the larger string...
|
||||
|
||||
/* RULES ***
|
||||
|
||||
If a path coresponds exactly to a location, use that one
|
||||
if no path coresponds then use the most correct one
|
||||
most correct means the most precise branch that is still above
|
||||
the point we are aiming for
|
||||
|
||||
*/
|
||||
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||
{
|
||||
std::cout << it->path << " -- ";
|
||||
// std::cout << it->path[it->path.size() - 1] << " ";
|
||||
// it->path.size() -1 only when path ends in / because
|
||||
// if path doesn't end in / then we are looking for a file
|
||||
// meaning all it->paths that end in / are wrong if they >=
|
||||
// if (it->path[it->path.size() - 1] == '/' ? it->path.size() - 1 > path.size() : it->path.size() > path.size())
|
||||
if (path[path.size() - 1] == '/' ? it->path.size() > path.size() : it->path.size() - 1 > path.size())
|
||||
{
|
||||
std::cout << "skipping this one\n";
|
||||
continue ;
|
||||
}
|
||||
|
||||
// if (it->path.size() > path.size()) // Warning : il faut aussi prendre en compte l'éventuel "/" final
|
||||
// continue;
|
||||
|
||||
|
||||
// IS THERE A WAY TO SIMPLIFY THIS LOGIC ???
|
||||
|
||||
|
||||
// if (it->path[it->path.size() - 1] == '/')
|
||||
if (path[path.size() - 1] == '/')
|
||||
{
|
||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
std::cout << "checking with last /\n";
|
||||
if (it->path.size() == path.size())
|
||||
{
|
||||
std::cout << "path sizes are equal \n";
|
||||
return (&(*it));
|
||||
}
|
||||
else if (path[it->path.size() - 1] == '/')
|
||||
{
|
||||
std::cout << "ends in /\n";
|
||||
return (&(*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.size() <= it->path.size())
|
||||
{
|
||||
std::cout << "path is missing a /\n";
|
||||
if (it->path.compare(0, path.size(), path) == 0)
|
||||
return (&(*it));
|
||||
// means we are looking for /test/test_deeper/
|
||||
// with /test/test_deeper
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (it->path.compare(0, it->path.size() - 1, path) == 0)
|
||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
std::cout << "checking without last /\n";
|
||||
if (it->path.size() - 1 == path.size())
|
||||
return (&(*it));
|
||||
else if (path[it->path.size() - 1] == '/')
|
||||
return (&(*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
// /test/mdr
|
||||
// /test/mdr/
|
||||
// /test/mdrBST
|
||||
|
||||
/* More stuff to check this still works with ***
|
||||
|
||||
/test/test_
|
||||
/test/test_/
|
||||
/test/test_deeper
|
||||
/test/test_deeper/
|
||||
/test/test_deepei
|
||||
/test/test_deepei/
|
||||
/test/test_deeperi
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
// if (it != server.locations.end())
|
||||
// return (&(*it));
|
||||
// else
|
||||
return (&(server.locations.back()));
|
||||
|
||||
|
||||
/*
|
||||
|
||||
std::vector<LocationConfig>::const_iterator best;
|
||||
std::cout << "\nMade it to weird location picker case.\n";
|
||||
|
||||
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||
{
|
||||
*/
|
||||
// if (rit->path.size() > path.size())
|
||||
/* if ((rit->path[rit->path.size() - 1] == '/' ? rit->path.size() : rit->path.size() - 1) > path.size())
|
||||
{
|
||||
std::cout << "skipping this one\n";
|
||||
continue ;
|
||||
}
|
||||
*/
|
||||
// OK I REALLY DON"T LOVE THIS PART, BUT IT DOES WORK FOR NOW...
|
||||
// if (it->path[it->path.size() - 1] == '/'
|
||||
// && it->path.compare(0, it->path.size(), path + "/") == 0)
|
||||
// HOLD ON THIS MIGHT BE GOOD, BUT I COULD USE SOME HELP...
|
||||
|
||||
//test /test/
|
||||
|
||||
//test/redirect/index /test/redirect
|
||||
//test/redirec_something
|
||||
|
||||
// thing is reverse sorted
|
||||
// if location path is longer than path sent, don't look at it
|
||||
// otherwise compare if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
// do another size compare and look for / if location smaller than client otherwise /redirect_something /redirect
|
||||
|
||||
// compare everything
|
||||
//
|
||||
|
||||
/* if (it->path[it->path.size() - 1] == '/'
|
||||
&& it->path.compare(0, it->path.size() - 1, path) == 0)
|
||||
{
|
||||
best = it;
|
||||
std::cout << "Picked a best! 1\n";
|
||||
}
|
||||
// int comp = path.compare(0, rit->path.size(), rit->path);
|
||||
//int comp = rit->path.compare(0, rit->path.size() - 1, path);
|
||||
// std::cout << "rit path size: " << rit->path.size() << " comp: " << comp << '\n';
|
||||
// if (rit->path.compare(0, rit->path.size(), path) == 0)
|
||||
// if (comp == 0)
|
||||
|
||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
best = it;
|
||||
std::cout << "Picked a best! 2\n";
|
||||
}
|
||||
}
|
||||
// //test.comare(0, 5, /test/something)
|
||||
// /test /test/something
|
||||
return (&(*best));
|
||||
*/
|
||||
}
|
||||
|
||||
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||
{
|
||||
size_t dot_pos = path.rfind(".");
|
||||
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
@@ -34,17 +34,34 @@ void Webserv::run()
|
||||
i = 0;
|
||||
while (i < nfds)
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
++i;
|
||||
if (!g_run)
|
||||
try
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
++i;
|
||||
if (!g_run)
|
||||
break;
|
||||
}
|
||||
catch (const std::bad_alloc& e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
_close_all_clients();
|
||||
/* Swap to free the memory
|
||||
From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */
|
||||
std::vector<Client>().swap(_clients);
|
||||
break;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
stylesheet/style.css
Normal file
4
stylesheet/style.css
Normal file
@@ -0,0 +1,4 @@
|
||||
h1 {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
BIN
www/Van_Eyck_Portrait_Arnolfini.jpg
Normal file
BIN
www/Van_Eyck_Portrait_Arnolfini.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
11
www/error_pages/error_404.html
Normal file
11
www/error_pages/error_404.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>404 Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
|
||||
<hr>
|
||||
<p style=\"text-align:center\">Le Webserv/0.1</p>
|
||||
</body>
|
||||
</html>
|
||||
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>
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
BIN
www/punpun.png
Normal file
BIN
www/punpun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 310 KiB |
14
www/test/index1.html
Normal file
14
www/test/index1.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test index</title>
|
||||
<!-- <link rel="stylesheet" href="stylesheet/style.css"> -->
|
||||
<link rel="stylesheet" href="/stylesheet/style.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>Webserv Test Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/something.html
Normal file
11
www/test/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/index1.html
Normal file
11
www/test/test_deeper/index1.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv Test Deeper Index</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/something.html
Normal file
11
www/test/test_deeper/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/super_deep/something.html
Normal file
11
www/test/test_deeper/super_deep/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Super Deep Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user