Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6f63931ad | ||
|
|
d663a4c7e6 | ||
|
|
be499328f6 | ||
|
|
41db4fc12b | ||
|
|
a20a5eff27 | ||
|
|
db4c7468cc | ||
|
|
b0949615c8 | ||
|
|
285f2d049f | ||
|
|
defb2ada61 | ||
|
|
058701f657 | ||
|
|
f17bc9fa58 | ||
|
|
c7bbf29a1b | ||
|
|
b44acafefe | ||
|
|
cade79c37f | ||
|
|
52824f30bd | ||
|
|
2a70c6b26d | ||
|
|
400efbe720 | ||
|
|
f7c0ff1a8a | ||
|
|
ab0bc2c4c0 | ||
|
|
08f6929db9 | ||
|
|
c32fc2c8a2 | ||
|
|
0b51d13f13 | ||
|
|
360c27df5f | ||
|
|
69c1a6f6bf |
27
Makefile
27
Makefile
@@ -1,6 +1,6 @@
|
||||
|
||||
NAME = webserv
|
||||
CXX = clang++
|
||||
CXX = c++
|
||||
|
||||
CXXFLAGS = -Wall -Wextra #-Werror
|
||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||
@@ -23,7 +23,7 @@ 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 \
|
||||
run_loop.cpp timeout.cpp \
|
||||
parser.cpp \
|
||||
extraConfig.cpp \
|
||||
postProcessing.cpp \
|
||||
@@ -43,12 +43,14 @@ all: $(NAME)
|
||||
|
||||
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
|
||||
|
||||
$(OBJS_D):
|
||||
mkdir $@
|
||||
|
||||
$(NAME): $(OBJS)
|
||||
$(CXX) $^ -o $(NAME)
|
||||
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJS_D)
|
||||
@@ -62,3 +64,24 @@ re: fclean all
|
||||
|
||||
-include $(DEPS) # header dependencie
|
||||
|
||||
.SILENT:
|
||||
|
||||
# ------------------
|
||||
# ----- COLORS -----
|
||||
# ------------------
|
||||
|
||||
_GREY=$ \033[30m
|
||||
_RED=$ \033[31m
|
||||
_GREEN=$ \033[32m
|
||||
_YELLOW=$ \033[33m
|
||||
_BLUE=$ \033[34m
|
||||
_PURPLE=$ \033[35m
|
||||
_CYAN=$ \033[36m
|
||||
_WHITE=$ \033[37m
|
||||
_END=$ \033[0m
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
120
README.md
120
README.md
@@ -1,28 +1,16 @@
|
||||
|
||||
## work together
|
||||
|
||||
#### TODO hugo
|
||||
- `_is_cgi()` and `_fill_cgi_path()`
|
||||
- two cgi tests :
|
||||
? - a basic form with "name" and "something", that return a html page with that
|
||||
? - for GET and POST
|
||||
? - a script called by a file extension in URI
|
||||
#### next commit
|
||||
|
||||
#### questions
|
||||
- in client, fd is in privat, so accesible by getter, is it ok ?
|
||||
- in client.cpp i fill the port, is there a default one in case it's not in the request ?
|
||||
- timeout server but still works ?
|
||||
- path contains double "//" from `Webserv::_get()` in response.cpp
|
||||
- cgi path ? defined in config ? and root path ? :
|
||||
- `Client.cpp : fill_script_path()`
|
||||
- `cgi.cpp : is_cgi()`
|
||||
- `cgi.cpp : set_env()`
|
||||
- what if the uri contains a php file, and the config said php must be handled by cgi, but the path to this php in the uri is wrong ?
|
||||
- is it ok ? `http://my_site.com/cgi-bin/php-cgi` (real path)
|
||||
- is it ok ? `http://my_site.com/php-cgi` (reconstruct path ?)
|
||||
- is it ok ? `http://my_site.com/something/php-cgi` (what about 'something' ?)
|
||||
- is it ok ? `http://my_site.com/something/cgi-bin/php-cgi` (real path with 'something' before ? )
|
||||
- I don't save the STDIN and STDOUT before dup2 in child process, is it wrong ?
|
||||
- how should we handle a wrong url like `http://localhost/cgi-bin/wrong.phpp/good.php` ?
|
||||
- do we serve `./srcs/cgi-bin/good.php` ?
|
||||
- or do we return 404 "not found" ?
|
||||
-> - for now, execve would crash, but that doesn't produce a 404 error, rather a 500, is it bad ?
|
||||
- could we use errno after execve to choose an appropriate http error ? subject says : "Checking the value of errno is strictly forbidden after a read or a write operation"
|
||||
- if a url has a file with extension, but it's not a cgi extension, is it necessary to look further ?
|
||||
- ex. `http://localhost/file.php/file.py` for `cgi_ext py;` ?
|
||||
- the response page is received long after the cgi-script is done, why ?
|
||||
|
||||
---
|
||||
@@ -104,18 +92,6 @@
|
||||
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||
|
||||
#### output cgi script :
|
||||
! TODO : change all the '\n' by '\r\n'
|
||||
! TODO : there is at least one header field followed by '\r\n\r\n' :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
! TODO : there is no space between filed name and ":"
|
||||
! TODO?: handle Location field, either :
|
||||
- local : start with '/' --> rerun the request with new uri
|
||||
- client : start with '<scheme>:' --> send back status code 302
|
||||
-> TODO : there is no field duplicate (resolve conflicts)
|
||||
-> TODO : if status field, change server status for this one
|
||||
-> TODO : if no Location field && no Status field -> status code = 200
|
||||
|
||||
#### summary :
|
||||
- the cgi-script will send back at least one header field followed by an empty line
|
||||
@@ -125,13 +101,18 @@
|
||||
- "Status"
|
||||
- the cgi-script may send back more header fields
|
||||
- the server must check and modify few things :
|
||||
- there is no duplicate in headers fields (if there is, resolve conflict)
|
||||
- there is no space between the field name and the ":"
|
||||
- the newlines are of form "\r\n", and not "\n" only
|
||||
- if the location field is not present, then if the status field is not present either, then the status code is 200
|
||||
- the cgi-script can return a location field, of two types :
|
||||
- local redirection : start with a "/", the server must answer as if this was the request uri
|
||||
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
|
||||
- there is no field duplicate (resolve conflicts)
|
||||
- there is no space between filed name and ":"
|
||||
- change all the '\n' by '\r\n'
|
||||
- if no Location field && no Status field -> status code = 200
|
||||
- handle Location field, either :
|
||||
- local : start with '/' --> rerun the request with new uri
|
||||
- client : start with '<scheme>:' --> send back status code 302
|
||||
- there is at least one header field followed by '\r\n\r\n' :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
- if status field, change server status for this one
|
||||
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||
|
||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||
@@ -232,6 +213,37 @@
|
||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||
|
||||
|
||||
---
|
||||
## cgi env variables
|
||||
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||
[wikipedia variables environnements cgi](https://fr.wikipedia.org/wiki/Variables_d%27environnement_CGI)
|
||||
[cgi server variables on adobe](https://helpx.adobe.com/coldfusion/cfml-reference/reserved-words-and-variables/cgi-environment-cgi-scope-variables/cgi-server-variables.html)
|
||||
|
||||
```None
|
||||
AUTH_TYPE : if the srcipt is protected, the authentification method used to validate the user
|
||||
CONTENT_LENGTH : length of the request body-message
|
||||
CONTENT_TYPE : (Content-Type field) if there is attached information, as with method POST or PUT, this is the content type of the data (e.g. "text/plain", it is set by the attribute "enctype" in html <form> as three values : "application/x-www-form-urlencoded", "multipart/form-data", "text/plain")
|
||||
GATEWAY_INTERFACE : CGI version (e.g. CGI/1.1)
|
||||
PATH_INFO : if any, path of the resquest in addition to the cgi script path (e.g. for cgi script path = "/usr/web/cgi-bin/script.cgi", and the url = "http://server.org/cgi-bin/script.cgi/house", the PATH-INFO would be "house")
|
||||
PATH_TRANSLATED : full path of the request, like path-to-cgi/PATH_INFO, null if PATH_INFO is null (e.g. for "http://server.org/cgi-bin/prog/the/path", PATH_INFO would be : "/the/path" and PATH_TRANSLATED would be : "/usr/web/cgi-bin/prog/the/path")
|
||||
QUERY_STRING : everything following the ? in the url sent by client (e.g. for url "http://server.org/query?var1=val2&var2=val2", it would be : "var1=val2&var2=val2")
|
||||
REMOTE_ADDR : ip address of the client
|
||||
REMOTE_HOST : host name of the client, empty if not known, or equal to REMOTE_ADDR
|
||||
REMOTE_IDENT : if known, username of the client, otherwise empty, use for logging only
|
||||
REMOTE_USER : username of client, if script is protected and the server support user authentification
|
||||
REQUEST_METHOD : method used for the request (for http, usually POST or GET)
|
||||
SCRIPT_NAME : path to the cgi, relative to the root, used for self-referencing URLs (e.g. "/cgi-bin/script.cgi")
|
||||
SERVER_NAME : name of the server, as hostname, IP address, or DNS (e.g. dns : "www.server.org")
|
||||
SERVER_PORT : the port number your server is listening on (e.g. 80)
|
||||
SERVER_PROTOCOL : protocol used for the request (e.g. HTTP/1.1)
|
||||
SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
|
||||
```
|
||||
[redirect status for php-cgi](https://woozle.org/papers/php-cgi.html)
|
||||
```None
|
||||
REDIRECT_STATUS : for exemple, 200
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
## http errors
|
||||
|
||||
@@ -271,36 +283,6 @@
|
||||
- 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)
|
||||
[wikipedia variables environnements cgi](https://fr.wikipedia.org/wiki/Variables_d%27environnement_CGI)
|
||||
[cgi server variables on adobe](https://helpx.adobe.com/coldfusion/cfml-reference/reserved-words-and-variables/cgi-environment-cgi-scope-variables/cgi-server-variables.html)
|
||||
|
||||
```None
|
||||
AUTH_TYPE : if the srcipt is protected, the authentification method used to validate the user
|
||||
CONTENT_LENGTH : length of the request body-message
|
||||
CONTENT_TYPE : (Content-Type field) if there is attached information, as with method POST or PUT, this is the content type of the data (e.g. "text/plain", it is set by the attribute "enctype" in html <form> as three values : "application/x-www-form-urlencoded", "multipart/form-data", "text/plain")
|
||||
GATEWAY_INTERFACE : CGI version (e.g. CGI/1.1)
|
||||
PATH_INFO : if any, path of the resquest in addition to the cgi script path (e.g. for cgi script path = "/usr/web/cgi-bin/script.cgi", and the url = "http://server.org/cgi-bin/script.cgi/house", the PATH-INFO would be "house")
|
||||
PATH_TRANSLATED : full path of the request, like path-to-cgi/PATH_INFO, null if PATH_INFO is null (e.g. for "http://server.org/cgi-bin/prog/the/path", PATH_INFO would be : "/the/path" and PATH_TRANSLATED would be : "/usr/web/cgi-bin/prog/the/path")
|
||||
QUERY_STRING : everything following the ? in the url sent by client (e.g. for url "http://server.org/query?var1=val2&var2=val2", it would be : "var1=val2&var2=val2")
|
||||
REMOTE_ADDR : ip address of the client
|
||||
REMOTE_HOST : host name of the client, empty if not known, or equal to REMOTE_ADDR
|
||||
REMOTE_IDENT : if known, username of the client, otherwise empty, use for logging only
|
||||
REMOTE_USER : username of client, if script is protected and the server support user authentification
|
||||
REQUEST_METHOD : method used for the request (for http, usually POST or GET)
|
||||
SCRIPT_NAME : path to the cgi, relative to the root, used for self-referencing URLs (e.g. "/cgi-bin/script.cgi")
|
||||
SERVER_NAME : name of the server, as hostname, IP address, or DNS (e.g. dns : "www.server.org")
|
||||
SERVER_PORT : the port number your server is listening on (e.g. 80)
|
||||
SERVER_PROTOCOL : protocol used for the request (e.g. HTTP/1.1)
|
||||
SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
|
||||
```
|
||||
[redirect status for php-cgi](https://woozle.org/papers/php-cgi.html)
|
||||
```None
|
||||
REDIRECT_STATUS : for exemple, 200
|
||||
```
|
||||
|
||||
---
|
||||
## ressources
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ server {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location /cgi-bin {
|
||||
root ./srcs/cgi-bin/;
|
||||
cgi_ext out php sh;
|
||||
}
|
||||
|
||||
location /redirect {
|
||||
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
||||
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
||||
@@ -26,9 +31,11 @@ server {
|
||||
|
||||
location /test {
|
||||
index index1.html subdex.html;
|
||||
root ./www/test/;
|
||||
}
|
||||
|
||||
location /test/index1.html {
|
||||
root ./www/test/;
|
||||
index index1.html subdex.html;
|
||||
}
|
||||
|
||||
@@ -36,25 +43,24 @@ server {
|
||||
redirect 301 https://berniesanders.com/404/;
|
||||
}
|
||||
|
||||
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
|
||||
location /stylesheet/style.css {
|
||||
location /stylesheet/ {
|
||||
# root ./www/../;
|
||||
root ./;
|
||||
root ./styelsheet/;
|
||||
}
|
||||
|
||||
location /test/something.html {
|
||||
# allow_methods DELETE;
|
||||
root ./www/test/;
|
||||
}
|
||||
|
||||
# location /something/long/here {
|
||||
# }
|
||||
|
||||
location /test/test_deeper/ {
|
||||
# allow_methods
|
||||
autoindex on;
|
||||
root ./www/test/test_deeper/;
|
||||
}
|
||||
|
||||
location /test/test_deeper/super_deep {
|
||||
root ./www/test/test_deeper/super_deep/;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
@@ -63,10 +69,4 @@ server {
|
||||
# }
|
||||
|
||||
|
||||
# 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;
|
||||
# }
|
||||
|
||||
}
|
||||
|
||||
23
memo.txt
23
memo.txt
@@ -1,18 +1,19 @@
|
||||
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)
|
||||
- CGI (TODO HUGO)
|
||||
- chunked request (WIP, a bit difficult)
|
||||
|
||||
- Need to test normal body parsing
|
||||
- basic html upload page for testing request of web browser
|
||||
- upload files with config "upload_dir"
|
||||
|
||||
- Ecrire des tests !
|
||||
- handle redirection (Work, but weird behavior need deeper test)
|
||||
- _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
|
||||
Si ce n'est pas deja fait :
|
||||
- dans config, check erreur si port > 16bits
|
||||
(peut-être check si ip > 32bits)
|
||||
-----------------------------
|
||||
- gerer le champ "Accept" du client
|
||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||
|
||||
175
srcs/Client.cpp
175
srcs/Client.cpp
@@ -8,6 +8,8 @@
|
||||
Client::Client()
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
@@ -22,6 +24,8 @@ Client::Client()
|
||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
@@ -77,13 +81,19 @@ Client & Client::operator=( Client const & rhs )
|
||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
{
|
||||
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||
return ;
|
||||
header_complete = true;
|
||||
clear_request(); // not mandatory
|
||||
|
||||
_parse_request_line();
|
||||
if (status)
|
||||
return;
|
||||
_parse_request_fields();
|
||||
|
||||
// DEBUG
|
||||
print_client("headers");
|
||||
// print_client("headers");
|
||||
|
||||
if (status)
|
||||
return;
|
||||
assigned_server = ::_determine_process_server(this, servers);
|
||||
@@ -93,6 +103,9 @@ print_client("headers");
|
||||
return;
|
||||
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
||||
|
||||
// DEBUG
|
||||
// std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n";
|
||||
|
||||
// dont clear raw_request, we need it for future reparsing of body
|
||||
// see call of parse_request() in _read_request()
|
||||
// raw_request.clear();
|
||||
@@ -100,38 +113,121 @@ print_client("headers");
|
||||
|
||||
void Client::parse_request_body()
|
||||
{
|
||||
std::cerr << "parse_request_body()\n";
|
||||
size_t pos;
|
||||
|
||||
pos = raw_request.find(CRLF CRLF);
|
||||
pos += std::string(CRLF CRLF).size();
|
||||
_request.body = raw_request.substr(pos);
|
||||
if (pos == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body() bad call, header incomplete\n";
|
||||
// QUESTION from hugo : don't we change the status here ?
|
||||
return;
|
||||
}
|
||||
pos += CRLF_SIZE*2;
|
||||
|
||||
// Chunked decoding WIP. Dont work.
|
||||
if (!get_rq_headers("Transfer-Encoding").empty()
|
||||
&& get_rq_headers("Transfer-Encoding") == "chunked")
|
||||
{
|
||||
size_t chunk_size = 1;
|
||||
size_t chunk_field_end = 0;
|
||||
char *endptr = NULL;
|
||||
char *endptr_copy = NULL;
|
||||
/* TODO: verify if last chunk in raw_request (to avoid multiples complete parsing)
|
||||
but how ? with "raw_request.rfind("0" CRLF CRLF)", there no confirmation
|
||||
that we have found the last last-chunk OR just some data */
|
||||
|
||||
_request.body = raw_request.substr(pos);
|
||||
|
||||
std::cerr << "______Chunked\n" << _request.body << "\n______\n";
|
||||
pos = 0;
|
||||
while (chunk_size != 0)
|
||||
{
|
||||
/* if (pos > _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), pos > size()\n";
|
||||
// status = 400;
|
||||
return;
|
||||
} */
|
||||
/*
|
||||
if (pos == _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), will reread till last chunk\n";
|
||||
return;
|
||||
} */
|
||||
|
||||
/* endptr_copy = endptr; */
|
||||
(void)endptr_copy;
|
||||
chunk_size = std::strtoul(&_request.body[pos], &endptr, 16);
|
||||
/* if (chunk_size == LONG_MAX && errno == ERANGE)
|
||||
status = 413; */
|
||||
/* if (endptr == endptr_copy)
|
||||
{
|
||||
std::cerr << "parse_request_body(), no conversion possible\n";
|
||||
return;
|
||||
} */
|
||||
|
||||
|
||||
chunk_field_end = _request.body.find(CRLF, pos);
|
||||
if (chunk_field_end == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body(), chunk_field no CRLF\n";
|
||||
// status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
chunk_field_end += CRLF_SIZE;
|
||||
_request.body.erase(pos, chunk_field_end);
|
||||
pos += chunk_size + CRLF_SIZE;
|
||||
}
|
||||
|
||||
_request.headers.erase("Transfer-Encoding");
|
||||
body_complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Content-Length = " << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
||||
std::cerr << "raw_request.size() - pos = " << raw_request.size() - pos << "\n";
|
||||
_request.body = raw_request.substr(pos);
|
||||
std::cerr << "_request.body.size() = " << _request.body.size() << "\n";
|
||||
|
||||
if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
{
|
||||
_request.body = raw_request.substr(pos);
|
||||
body_complete = true;
|
||||
}
|
||||
|
||||
/* Should be equivalent */
|
||||
// _request.body = raw_request.substr(pos);
|
||||
// if (_request.body.size() >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
// body_complete = true;
|
||||
}
|
||||
|
||||
///////////////
|
||||
// Body checks
|
||||
if (_request.body.size() > assigned_server->client_body_limit)
|
||||
status = 413; // HTTP Client Errors
|
||||
}
|
||||
|
||||
bool Client::fill_script_path(std::string script)
|
||||
void Client::fill_script_path(std::string &path, size_t pos)
|
||||
{
|
||||
size_t pos;
|
||||
int len = script.size();
|
||||
std::string path = this->get_rq_abs_path();
|
||||
std::string tmp;
|
||||
|
||||
pos = path.find(script);
|
||||
if (pos == 0)
|
||||
if (path[0] == '.')
|
||||
{
|
||||
tmp = path.substr(0, pos + len);
|
||||
_request.script.path = "./srcs" + tmp; // TODO: root path ?
|
||||
_request.script.info = path.substr(pos + len);
|
||||
return true;
|
||||
path.erase(0, 1);
|
||||
pos--;
|
||||
}
|
||||
return false;
|
||||
_request.script.path = path.substr(0, pos);
|
||||
_request.script.info = path.substr(pos);
|
||||
}
|
||||
|
||||
void Client::clear()
|
||||
{
|
||||
clear_request();
|
||||
header_complete = false;
|
||||
body_complete = false;
|
||||
request_complete = false;
|
||||
read_body_size = 0;
|
||||
assigned_server = NULL;
|
||||
assigned_location = NULL;
|
||||
@@ -144,7 +240,7 @@ void Client::clear_request()
|
||||
{
|
||||
clear_script();
|
||||
_request.method = UNKNOWN;
|
||||
_request.uri.clear();
|
||||
_request.target.clear();
|
||||
_request.version.clear();
|
||||
_request.headers.clear();
|
||||
_request.body.clear();
|
||||
@@ -173,7 +269,7 @@ void Client::print_client(std::string message)
|
||||
<< "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_target() : [" << get_rq_target() << "]\n"
|
||||
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
|
||||
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
||||
<< "get_rq_version() : [" << get_rq_version() << "]\n"
|
||||
@@ -202,7 +298,7 @@ const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
|
||||
http_method Client::get_rq_method() const { return _request.method; }
|
||||
std::string Client::get_rq_method_str() const
|
||||
{ return ::http_methods_to_str(_request.method); }
|
||||
std::string Client::get_rq_uri() const { return _request.uri; }
|
||||
std::string Client::get_rq_target() const { return _request.target; }
|
||||
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
||||
std::string Client::get_rq_query() const { return _request.query; }
|
||||
std::string Client::get_rq_version() const { return _request.version; }
|
||||
@@ -211,6 +307,7 @@ std::string Client::get_rq_port() const { return _request.port; }
|
||||
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
||||
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
||||
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
||||
|
||||
std::string Client::get_rq_headers(const std::string & key) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it;
|
||||
@@ -241,22 +338,22 @@ void Client::_parse_request_line()
|
||||
else
|
||||
{
|
||||
_request.method = str_to_http_method(line[0]);
|
||||
_request.uri = line[1];
|
||||
_parse_request_uri(line[1]);
|
||||
_request.target = line[1];
|
||||
_parse_request_target(line[1]);
|
||||
_request.version = line[2];
|
||||
}
|
||||
}
|
||||
|
||||
void Client::_parse_request_uri( std::string uri )
|
||||
void Client::_parse_request_target( std::string target )
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
pos = uri.find("?");
|
||||
if (pos != std::string::npos)
|
||||
_request.query = uri.substr(pos + 1);
|
||||
pos = target.find("?");
|
||||
if (pos != NPOS)
|
||||
_request.query = target.substr(pos + 1);
|
||||
else
|
||||
_request.query = "";
|
||||
_request.abs_path = uri.substr(0, pos);
|
||||
_request.abs_path = target.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_fields()
|
||||
@@ -268,11 +365,11 @@ void Client::_parse_request_fields()
|
||||
headers = raw_request;
|
||||
// delete first line
|
||||
pos = headers.find(CRLF);
|
||||
if (pos != std::string::npos)
|
||||
if (pos != NPOS)
|
||||
headers.erase(0, pos + std::string(CRLF).size());
|
||||
// delete body part
|
||||
pos = headers.find(CRLF CRLF);
|
||||
if (pos != std::string::npos)
|
||||
if (pos != NPOS)
|
||||
headers.erase(pos);
|
||||
else {
|
||||
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
||||
@@ -296,7 +393,7 @@ void Client::_parse_port_hostname(std::string host)
|
||||
|
||||
pos = host.find(':');
|
||||
// port :
|
||||
if (pos == std::string::npos)
|
||||
if (pos == NPOS)
|
||||
_request.port = "4040"; // TODO: make equal to default port in config
|
||||
else
|
||||
_request.port = host.substr(pos);
|
||||
@@ -308,7 +405,7 @@ void Client::_parse_port_hostname(std::string host)
|
||||
|
||||
void Client::_check_request_errors()
|
||||
{
|
||||
//////////////////////
|
||||
///////////////////////
|
||||
// Request line checks
|
||||
if (_request.method == UNKNOWN)
|
||||
status = 501; // HTTP Client Errors
|
||||
@@ -330,15 +427,21 @@ void Client::_check_request_errors()
|
||||
response.append(CRLF CRLF);
|
||||
}
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
/////////////////
|
||||
//////////////////
|
||||
// Headers checks
|
||||
if (!this->get_rq_headers("Content-Length").empty()
|
||||
&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
|
||||
status = 413; // HTTP Client Errors
|
||||
|
||||
else if (!this->get_rq_headers("Content-Length").empty()
|
||||
&& std::strtoul(this->get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
|
||||
status = 413;
|
||||
else if (!this->get_rq_headers("Transfer-Encoding").empty()
|
||||
&& this->get_rq_headers("Transfer-Encoding") != "chunked" )
|
||||
status = 501;
|
||||
else if (!this->get_rq_headers("Content-Encoding").empty())
|
||||
{
|
||||
status = 415;
|
||||
response.append("Accept-Encoding:"); // empty, no encoding accepted
|
||||
response.append(CRLF);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# include "utils.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "colors.h"
|
||||
|
||||
struct Script
|
||||
{
|
||||
@@ -21,7 +22,7 @@ struct Script
|
||||
struct Request
|
||||
{
|
||||
http_method method;
|
||||
std::string uri;
|
||||
std::string target;
|
||||
std::string abs_path;
|
||||
std::string query;
|
||||
std::string version;
|
||||
@@ -45,7 +46,9 @@ class Client
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
bool header_complete;
|
||||
size_t read_body_size;
|
||||
bool body_complete;
|
||||
bool request_complete;
|
||||
size_t read_body_size; // unused for now
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *assigned_location;
|
||||
|
||||
@@ -58,7 +61,7 @@ class Client
|
||||
// requests getters
|
||||
http_method get_rq_method() const;
|
||||
std::string get_rq_method_str() const;
|
||||
std::string get_rq_uri() const;
|
||||
std::string get_rq_target() const;
|
||||
std::string get_rq_abs_path() const;
|
||||
std::string get_rq_query() const;
|
||||
std::string get_rq_version() const;
|
||||
@@ -74,7 +77,7 @@ class Client
|
||||
void clear();
|
||||
void clear_request();
|
||||
void clear_script();
|
||||
bool fill_script_path(std::string script);
|
||||
void fill_script_path(std::string &path, size_t pos);
|
||||
// DEBUG
|
||||
void print_client(std::string message = "");
|
||||
|
||||
@@ -87,7 +90,7 @@ class Client
|
||||
|
||||
void _parse_request_line();
|
||||
void _parse_request_fields();
|
||||
void _parse_request_uri( std::string uri );
|
||||
void _parse_request_target( std::string target );
|
||||
void _parse_port_hostname(std::string host);
|
||||
|
||||
void _check_request_errors();
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
|
||||
int main (int ac, char **av) {
|
||||
std::string to_send;
|
||||
std::string header;
|
||||
std::string end_header = "\r\n\r\n";
|
||||
std::string response;
|
||||
std::stringstream strs;
|
||||
|
||||
header = "HTTP/1.1 200 OK\n";
|
||||
header += "Content-Type: text/html; charset=UTF-8\n";
|
||||
header += "Content-Length: ";
|
||||
|
||||
response = "<!DOCTYPE html>\n";
|
||||
response += "<html>\n";
|
||||
response += "<head>\n";
|
||||
response += "<title>CGI</title>\n";
|
||||
response += "</head>\n";
|
||||
response += "<body>\n";
|
||||
response += "<h2>CGI request :</h2>\n";
|
||||
for (int i = 1; i < ac; i++)
|
||||
{
|
||||
response += "<p>";
|
||||
response += av[i];
|
||||
response += "</p>\n";
|
||||
}
|
||||
response += "</body>\n";
|
||||
response += "</html>\n";
|
||||
|
||||
strs << response.size();
|
||||
header += strs.str();
|
||||
header += end_header;
|
||||
to_send = header;
|
||||
to_send += response;
|
||||
|
||||
std::cout << to_send;
|
||||
|
||||
return 0;
|
||||
}
|
||||
4
srcs/cgi-bin/cgi.sh
Executable file
4
srcs/cgi-bin/cgi.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#! /bin/bash
|
||||
echo "status: 100\r\n"
|
||||
echo "\r\n\r\n"
|
||||
echo "hiii"
|
||||
Binary file not shown.
114
srcs/cgi-bin/cgi_cpp.cpp
Normal file
114
srcs/cgi-bin/cgi_cpp.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <vector>
|
||||
# include <stdlib.h> // getenv
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
# define NPOS std::string::npos
|
||||
|
||||
std::string trim(std::string str, char del)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
// delete leadings del
|
||||
pos = str.find_first_not_of(del);
|
||||
if (pos == NPOS)
|
||||
pos = str.size();
|
||||
str = str.substr(pos);
|
||||
|
||||
// delete trailing del
|
||||
pos = str.find_last_not_of(del);
|
||||
if (pos != NPOS)
|
||||
str = str.substr(0, pos + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
split(const std::string & input, std::string delim, char ctrim = '\0')
|
||||
{
|
||||
std::vector<std::string> split_str;
|
||||
std::string tmp;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while (end != NPOS)
|
||||
{
|
||||
end = input.find(delim, start);
|
||||
len = end - start;
|
||||
if (end == 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;
|
||||
}
|
||||
|
||||
int main (int ac, char **av, char **en)
|
||||
{
|
||||
std::vector<std::string> split_str;
|
||||
std::vector<std::string> sub_split_str;
|
||||
std::vector<std::string>::const_iterator it;
|
||||
char * tmp;
|
||||
std::string input;
|
||||
std::string http_header;
|
||||
std::string http_body;
|
||||
std::ostringstream strs;
|
||||
size_t pos;
|
||||
|
||||
std::cin >> input;
|
||||
|
||||
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||
http_header += "Content-Length: ";
|
||||
|
||||
http_body = "\
|
||||
<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<title>CGI</title>\
|
||||
</head>\
|
||||
<body>\
|
||||
<h2>cgi</h2>\
|
||||
";
|
||||
|
||||
http_body += "<h3>";
|
||||
tmp = getenv("REQUEST_METHOD");
|
||||
if (tmp != NULL)
|
||||
http_body += tmp;
|
||||
else
|
||||
http_body = "method not foud";
|
||||
http_body += "</h3>";
|
||||
|
||||
split_str = split(input, "&");
|
||||
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||
{
|
||||
sub_split_str = split(*it, "=");
|
||||
http_body += "<h3>";
|
||||
http_body += sub_split_str[0];
|
||||
http_body += "</h3>";
|
||||
http_body += "<p>";
|
||||
http_body += sub_split_str[1];
|
||||
http_body += "</p>";
|
||||
}
|
||||
|
||||
http_body += "\
|
||||
</body>\
|
||||
</html>\
|
||||
";
|
||||
|
||||
strs << http_body.size();
|
||||
http_header += strs.str();
|
||||
http_header += CRLF CRLF;
|
||||
|
||||
std::cout << http_header << CRLF CRLF << http_body;
|
||||
return 0;
|
||||
}
|
||||
|
||||
133
srcs/cgi-bin/cgi_cpp_content_length.cpp
Normal file
133
srcs/cgi-bin/cgi_cpp_content_length.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <vector>
|
||||
# include <stdlib.h> // getenv
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
# define NPOS std::string::npos
|
||||
|
||||
std::string trim(std::string str, char del)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
// delete leadings del
|
||||
pos = str.find_first_not_of(del);
|
||||
if (pos == NPOS)
|
||||
pos = str.size();
|
||||
str = str.substr(pos);
|
||||
|
||||
// delete trailing del
|
||||
pos = str.find_last_not_of(del);
|
||||
if (pos != NPOS)
|
||||
str = str.substr(0, pos + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
split(const std::string & input, std::string delim, char ctrim = '\0')
|
||||
{
|
||||
std::vector<std::string> split_str;
|
||||
std::string tmp;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while (end != NPOS)
|
||||
{
|
||||
end = input.find(delim, start);
|
||||
len = end - start;
|
||||
if (end == 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;
|
||||
}
|
||||
|
||||
int main (int ac, char **av, char **en) {
|
||||
std::vector<std::string> split_str;
|
||||
std::vector<std::string> sub_split_str;
|
||||
std::vector<std::string>::const_iterator it;
|
||||
char * tmp;
|
||||
std::string output;
|
||||
std::ostringstream strs;
|
||||
size_t pos;
|
||||
|
||||
std::cout << "Content-Type: text/html; charset=UTF-8" << CRLF CRLF;
|
||||
|
||||
std::cout
|
||||
<< "<!DOCTYPE html>"
|
||||
<< "<html>"
|
||||
<< "<head>"
|
||||
<< " <title>CGI</title>"
|
||||
<< "</head>"
|
||||
<< "<body>"
|
||||
<< " <h2>cgi</h2>"
|
||||
<< " <h3>";
|
||||
|
||||
tmp = getenv("REQUEST_METHOD");
|
||||
if (tmp != NULL)
|
||||
output = tmp;
|
||||
else
|
||||
output = "method not foud";
|
||||
|
||||
std::cout
|
||||
<< output
|
||||
<< " </h3>"
|
||||
<< " <h3>http-request-body-message content :</h3>";
|
||||
|
||||
|
||||
std::cin >> output;
|
||||
split_str = split(output, "&");
|
||||
output.clear();
|
||||
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||
{
|
||||
sub_split_str = split(*it, "=");
|
||||
|
||||
std::cout
|
||||
<< "<p>"
|
||||
<< sub_split_str[0]
|
||||
<< " : "
|
||||
<< sub_split_str[1]
|
||||
<< "</p>";
|
||||
}
|
||||
|
||||
tmp = getenv("QUERY_STRING");
|
||||
if (tmp == NULL)
|
||||
std::cout << "query not foud";
|
||||
|
||||
std::cout
|
||||
<< " <h3>http-uri-query content :</h3>";
|
||||
|
||||
output = tmp;
|
||||
split_str = split(output, "&");
|
||||
output.clear();
|
||||
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||
{
|
||||
sub_split_str = split(*it, "=");
|
||||
|
||||
std::cout
|
||||
<< "<h3>"
|
||||
<< sub_split_str[0]
|
||||
<< "</h3>"
|
||||
<< "<p>"
|
||||
<< sub_split_str[1]
|
||||
<< "</p>";
|
||||
}
|
||||
|
||||
|
||||
std::cout
|
||||
<< "</body>"
|
||||
<< "</html>";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
29
srcs/cgi-bin/cgi_second/cgi.php
Executable file
29
srcs/cgi-bin/cgi_second/cgi.php
Executable file
@@ -0,0 +1,29 @@
|
||||
#! /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";
|
||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
||||
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
|
||||
echo "\nGATEWAY_INTERFACE: " . getenv("GATEWAY_INTERFACE");
|
||||
echo "\nPATH_INFO: " . getenv("PATH_INFO");
|
||||
echo "\nPATH_TRANSLATED: " . getenv("PATH_TRANSLATED");
|
||||
echo "\nQUERY_STRING: " . getenv("QUERY_STRING");
|
||||
echo "\nREMOTE_ADDR: " . getenv("REMOTE_ADDR");
|
||||
echo "\nREMOTE_HOST: " . getenv("REMOTE_HOST");
|
||||
echo "\nREMOTE_IDENT: " . getenv("REMOTE_IDENT");
|
||||
echo "\nREMOTE_USER: " . getenv("REMOTE_USER");
|
||||
echo "\nREQUEST_METHOD: " . getenv("REQUEST_METHOD");
|
||||
echo "\nSCRIPT_NAME: " . getenv("SCRIPT_NAME");
|
||||
echo "\nSERVER_NAME: " . getenv("SERVER_NAME");
|
||||
echo "\nSERVER_PORT: " . getenv("SERVER_PORT");
|
||||
echo "\nSERVER_PROTOCOL: " . getenv("SERVER_PROTOCOL");
|
||||
echo "\nSERVER_SOFTWARE: " . getenv("SERVER_SOFTWARE");
|
||||
echo "\nREDIRECT_STATUS: " . getenv("REDIRECT_STATUS");
|
||||
|
||||
// echo $_POST['REQUEST_METHOD'];
|
||||
echo "\n\n-----------\nEND PHP-CGI\n\n";
|
||||
?>
|
||||
@@ -1,14 +1,3 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
|
||||
/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef CONFIGPARSER_HPP
|
||||
# define CONFIGPARSER_HPP
|
||||
@@ -22,7 +11,7 @@
|
||||
# include <exception> // exception, what
|
||||
# include <stdexcept> // runtime_error, invalid_argument
|
||||
# include <string> // string
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // strtol, stroul
|
||||
# include <iostream> // cout, cin
|
||||
# include <fstream> // ifstream
|
||||
//# include <unistd.h> // access()
|
||||
@@ -34,7 +23,7 @@ class ConfigParser {
|
||||
|
||||
public:
|
||||
|
||||
// canonical
|
||||
// canonical?
|
||||
|
||||
ConfigParser(const char* path); // a string?
|
||||
~ConfigParser();
|
||||
@@ -42,23 +31,15 @@ public:
|
||||
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
|
||||
// ConfigParser & operator=(const ConfigParser& rhs);
|
||||
|
||||
// void parse(); // return void cuz throw exceptions.
|
||||
std::vector<ServerConfig> * parse(); // const?
|
||||
// std::vector<ServerConfig> parse(); // const?
|
||||
|
||||
// other parses?
|
||||
|
||||
// i thought if it were an instance of this class you could call
|
||||
// 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;
|
||||
|
||||
@@ -83,38 +64,10 @@ private:
|
||||
std::string _get_rest_of_line(size_t *curr); // const?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// some sort of post processing...
|
||||
|
||||
/* Post Processing */
|
||||
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
|
||||
{
|
||||
MyException(const std::string str)
|
||||
: std::invalid_argument(str) {}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */
|
||||
/* Updated: 2022/08/12 18:12:23 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
# include <iostream>
|
||||
# include <sys/stat.h> // stat()
|
||||
|
||||
# include <stdio.h> // printf(), gotta go
|
||||
|
||||
|
||||
# include "utils.hpp"
|
||||
|
||||
// again, struct instead?
|
||||
@@ -31,7 +28,7 @@ public:
|
||||
// canonic stuff?
|
||||
|
||||
std::string path; // /path and /path/ are fine
|
||||
// i add trailing / if a dir
|
||||
// i remove trailing / systematically
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
unsigned int allow_methods;
|
||||
@@ -42,10 +39,8 @@ public:
|
||||
|
||||
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()
|
||||
void print_all() // const?
|
||||
{
|
||||
std::cout << "\nPRINTING A LOCATION\n";
|
||||
|
||||
@@ -70,7 +65,7 @@ public:
|
||||
int comp_rhs = 0;
|
||||
size_t tmp = 0;
|
||||
|
||||
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
|
||||
while ((tmp = this->path.find_first_of("/", tmp)) != NPOS)
|
||||
{
|
||||
++tmp;
|
||||
++comp_lhs;
|
||||
@@ -78,7 +73,7 @@ public:
|
||||
if (path[path.find_last_of("/") + 1] != '\0')
|
||||
++comp_lhs;
|
||||
tmp = 0;
|
||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != NPOS)
|
||||
{
|
||||
++tmp;
|
||||
++comp_rhs;
|
||||
|
||||
@@ -20,12 +20,14 @@ public:
|
||||
// we could shove default in here if we wanted to...
|
||||
|
||||
std::string host;
|
||||
std::string port; // port needs to be something else... not quite an int
|
||||
std::string port;
|
||||
|
||||
std::string root; // ./www/ or www work www/ and www work
|
||||
// i do remove trailing / tho
|
||||
|
||||
unsigned int client_body_limit; // set to default max if none set
|
||||
size_t client_body_limit; // set to default max if none set
|
||||
// 413 (Request Entity Too Large) if exceeded
|
||||
// default is 1m 1 000 000 ?
|
||||
|
||||
std::vector<std::string> index;
|
||||
std::map<int, std::string> error_pages;
|
||||
@@ -33,7 +35,7 @@ public:
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
|
||||
void print_all()
|
||||
void print_all() // const?
|
||||
{
|
||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
|
||||
// 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 <= ?
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
|
||||
|
||||
// should i be sending & references?
|
||||
// const?
|
||||
std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
const std::string value)
|
||||
{
|
||||
@@ -13,22 +13,18 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
// check values for ; at end and right number of words depending on key
|
||||
|
||||
// std::cout << "pre check\n";
|
||||
if (key.find_first_of(";") != std::string::npos)
|
||||
if (key.find_first_of(";") != NPOS)
|
||||
throw std::invalid_argument("bad config file arguments 2");
|
||||
|
||||
// there shouldn't be any tabs, right? not between values...
|
||||
if (value.find_first_of("\t") != std::string::npos)
|
||||
{
|
||||
// std::cout << value << "\n";
|
||||
if (value.find_first_of("\t") != NPOS)
|
||||
throw std::invalid_argument("why would you put tabs between values");
|
||||
}
|
||||
|
||||
size_t i = value.find_first_of(";");
|
||||
// so you can't have no ;
|
||||
// you can't have just ;
|
||||
// and you can't have a ; not at the end or several ;
|
||||
// in theory value_find_last_of should find the only ;
|
||||
if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \
|
||||
if (i == NPOS || (value.find_last_not_of(" \n")) != i \
|
||||
|| value.compare(";") == 0)
|
||||
throw std::invalid_argument("bad config file arguments 4");
|
||||
|
||||
@@ -36,34 +32,30 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
return (value.substr(0, i));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// const?
|
||||
// assumes curr is on a space or \t or \n
|
||||
// get first word? next word? word?
|
||||
std::string ConfigParser::_get_first_word(size_t *curr)
|
||||
{
|
||||
size_t start;
|
||||
|
||||
// are these checks excessive?
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
||||
if ((*curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
std::string key = _content.substr(start, *curr - start);
|
||||
|
||||
return (key);
|
||||
}
|
||||
|
||||
// const?
|
||||
// also assumes curr is on a space \t or \n
|
||||
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
{
|
||||
size_t start;
|
||||
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
|
||||
if ((*curr = _content.find_first_of("\n", start)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
std::string values = _content.substr(start, *curr - start);
|
||||
@@ -71,8 +63,6 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_print_content() const
|
||||
{
|
||||
std::cout << _content;
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.cpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/13 22:11:17 by me #+# #+# */
|
||||
/* Updated: 2022/08/03 17:51:35 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
@@ -21,7 +10,7 @@ ConfigParser::ConfigParser()
|
||||
|
||||
ConfigParser::ConfigParser(const char* path)
|
||||
{
|
||||
std::cout << "Param Constructor\n";
|
||||
// std::cout << "Param Constructor\n";
|
||||
|
||||
std::ifstream file;
|
||||
std::string buf;
|
||||
@@ -35,10 +24,10 @@ ConfigParser::ConfigParser(const char* path)
|
||||
{
|
||||
getline(file, buf);
|
||||
// remove # comments here.
|
||||
if ((comment = buf.find_first_of("#")) == std::string::npos)
|
||||
if ((comment = buf.find_first_of("#")) == NPOS)
|
||||
{
|
||||
// remove empty lines, i think...
|
||||
if ((buf.find_first_not_of(" \t")) != std::string::npos)
|
||||
if ((buf.find_first_not_of(" \t")) != NPOS)
|
||||
_content.append(buf + '\n');
|
||||
}
|
||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||
@@ -70,23 +59,22 @@ ConfigParser & ConfigParser::operator=(const ConfigParser& rhs)
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// const?
|
||||
std::vector<ServerConfig> * ConfigParser::parse()
|
||||
{
|
||||
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
||||
// std::vector<ServerConfig> ret;
|
||||
|
||||
size_t start = 0;
|
||||
size_t curr = _content.find_first_not_of(" \t\n", 0);
|
||||
|
||||
if (curr == std::string::npos)
|
||||
if (curr == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
while (curr != std::string::npos)
|
||||
while (curr != NPOS)
|
||||
{
|
||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos)
|
||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
||||
if ((curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
std::string key = _content.substr(start, curr - start);
|
||||
if (key != "server")
|
||||
@@ -103,13 +91,13 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
|
||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
if (curr == NPOS || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 1");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
// are there other things to check for?
|
||||
while (curr != std::string::npos) // here curr == { + 1
|
||||
while (curr != NPOS) // here curr == { + 1
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
@@ -152,12 +140,12 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
|
||||
curr = _content.find_first_not_of(" \t\n", curr);
|
||||
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
if (curr == NPOS || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 2");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
while (curr != std::string::npos)
|
||||
while (curr != NPOS)
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
@@ -204,7 +192,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
else if (key == "listen" && size == 1 && server->host == "" \
|
||||
&& server->port == "")
|
||||
{
|
||||
if (tmp_val[0].find_first_of(":") == std::string::npos)
|
||||
if (tmp_val[0].find_first_of(":") == NPOS)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
@@ -234,6 +222,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
// tmp_val[0].push_back('/');
|
||||
server->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
@@ -241,7 +230,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
server->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
@@ -255,7 +244,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||
throw std::invalid_argument("invalid error code");
|
||||
int status_code = atoi(tmp_val[i].c_str());
|
||||
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
|
||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||
throw std::invalid_argument("redeclaring error page");
|
||||
server->error_pages[status_code] = path;
|
||||
@@ -283,6 +272,7 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
// tmp_val[0].push_back('/');
|
||||
location->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
@@ -320,12 +310,12 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
&& 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';
|
||||
// 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");
|
||||
|
||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
||||
location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
||||
|
||||
@@ -52,14 +52,17 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
if (it_l->allow_methods == UNKNOWN)
|
||||
it_l->allow_methods = ANY_METHODS;
|
||||
|
||||
if (it_l->index.empty())
|
||||
if (it_l->index.empty() && it_l->autoindex == false)
|
||||
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('/');
|
||||
// if (eval_file_type(it_l->root) == IS_DIR
|
||||
// && it_l->path[it_l->path.size() - 1] != '/')
|
||||
// it_l->path.push_back('/');
|
||||
if (it_l->path[it_l->path.size() - 1] == '/'
|
||||
&& it_l->path.size() > 1)
|
||||
it_l->path.erase(it_l->path.size() - 1);
|
||||
|
||||
++it_l;
|
||||
}
|
||||
@@ -70,17 +73,15 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
}
|
||||
}
|
||||
|
||||
// const?
|
||||
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
|
||||
{
|
||||
std::vector<LocationConfig>::iterator it = locations.begin();
|
||||
std::vector<LocationConfig>::const_iterator it = locations.begin();
|
||||
|
||||
while (it != locations.end())
|
||||
{
|
||||
if (it->path.compare("/") == 0)
|
||||
{
|
||||
// std::cout << "in compare: " << it->path << " -- ";
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -19,7 +19,7 @@ int main(int ac, char **av)
|
||||
|
||||
ConfigParser configParser(config.c_str());
|
||||
|
||||
configParser._print_content();
|
||||
// configParser._print_content();
|
||||
|
||||
// i don't love that servers has to be a pointer...
|
||||
std::vector<ServerConfig>* servers = configParser.parse();
|
||||
@@ -27,8 +27,9 @@ int main(int ac, char **av)
|
||||
// use an iterator you moron
|
||||
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
||||
{
|
||||
(void)0;
|
||||
// std::cout << it->server_name << " ";
|
||||
it->print_all();
|
||||
// it->print_all();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,11 +38,11 @@ std::vector<std::string>
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while (end != std::string::npos)
|
||||
while (end != NPOS)
|
||||
{
|
||||
end = input.find(delim, start);
|
||||
len = end - start;
|
||||
if (end == std::string::npos)
|
||||
if (end == NPOS)
|
||||
len = end;
|
||||
tmp = input.substr(start, len);
|
||||
if (ctrim != '\0')
|
||||
@@ -60,13 +60,13 @@ std::string trim(std::string str, char del)
|
||||
|
||||
// delete leadings del
|
||||
pos = str.find_first_not_of(del);
|
||||
if (pos == std::string::npos)
|
||||
if (pos == NPOS)
|
||||
pos = str.size();
|
||||
str = str.substr(pos);
|
||||
|
||||
// delete trailing del
|
||||
pos = str.find_last_not_of(del);
|
||||
if (pos != std::string::npos)
|
||||
if (pos != NPOS)
|
||||
str = str.substr(0, pos + 1);
|
||||
|
||||
return str;
|
||||
@@ -110,7 +110,7 @@ bool isNumeric_btw(int low, int high, std::string str)
|
||||
if (std::isdigit(str[i]) == false)
|
||||
return false;
|
||||
}
|
||||
int n = std::atoi(str.c_str());
|
||||
int n = std::strtol(str.c_str(), NULL, 10);
|
||||
if (n < low || n > high)
|
||||
return false;
|
||||
return true;
|
||||
@@ -151,29 +151,41 @@ std::string http_methods_to_str(unsigned int methods)
|
||||
|
||||
# include <iostream>
|
||||
|
||||
// you could make this &path...
|
||||
int path_is_valid(std::string path)
|
||||
file_type eval_file_type(const std::string &path)
|
||||
{
|
||||
const char *tmp_path = path.c_str();
|
||||
struct stat s;
|
||||
|
||||
if (stat(tmp_path, &s) == 0)
|
||||
if (stat(tmp_path, &s) != -1)
|
||||
{
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a file\n";
|
||||
return (2);
|
||||
}
|
||||
return (IS_FILE);
|
||||
else if (S_ISDIR(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a Dir\n";
|
||||
return (1);
|
||||
}
|
||||
return (IS_DIR);
|
||||
}
|
||||
// std::cout << "path is neither dir nor file\n";
|
||||
return (0);
|
||||
else
|
||||
{
|
||||
std::perror("err stat()");
|
||||
}
|
||||
|
||||
return (IS_OTHER);
|
||||
}
|
||||
|
||||
size_t eval_file_mode(std::string path, int mode)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
return 404; // NOT_FOUND, file doesn't exist
|
||||
}
|
||||
|
||||
if (access(path.c_str(), mode) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
return 403; // FORBIDDEN, file doesn't have execution permission
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
replace_all_substr(
|
||||
@@ -187,7 +199,7 @@ void
|
||||
while (1)
|
||||
{
|
||||
pos = str.find(ori_substr, pos);
|
||||
if (pos == std::string::npos)
|
||||
if (pos == NPOS)
|
||||
break;
|
||||
str.replace(pos, ori_substr.size(), new_substr);
|
||||
pos += new_substr.size();
|
||||
@@ -201,8 +213,8 @@ std::string str_tolower(std::string str)
|
||||
}
|
||||
|
||||
// identify a line in a string, by delim (ex. '\n')
|
||||
// delete this line from the string
|
||||
// and return the deleted line
|
||||
// delete this line from the string (and the following nl sequence characters)
|
||||
// and return the deleted line (without the followinf nl sequence characters)
|
||||
std::string
|
||||
extract_line(std::string & str, size_t pos, std::string delim)
|
||||
{
|
||||
@@ -212,18 +224,18 @@ std::string
|
||||
size_t len;
|
||||
|
||||
begin = str.rfind(delim, pos);
|
||||
if (begin == std::string::npos)
|
||||
if (begin == NPOS)
|
||||
begin = 0;
|
||||
else
|
||||
else if (begin < pos)
|
||||
begin += delim.size();
|
||||
|
||||
end = str.find(delim, pos);
|
||||
len = end;
|
||||
if (end != std::string::npos)
|
||||
if (end != NPOS)
|
||||
len = end - begin;
|
||||
|
||||
del_str = str.substr(begin, len);
|
||||
str.erase(begin, len);
|
||||
str.erase(begin, len + delim.size());
|
||||
return del_str;
|
||||
}
|
||||
|
||||
@@ -256,13 +268,13 @@ size_t
|
||||
for (it = list.begin(); it != it_end; it++)
|
||||
{
|
||||
pos = (*it).find(':');
|
||||
if (pos == std::string::npos)
|
||||
if (pos == NPOS)
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
}
|
||||
key = (*it).substr(0, pos);
|
||||
if ( key.find(' ') != std::string::npos )
|
||||
if ( key.find(' ') != NPOS )
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
|
||||
@@ -6,16 +6,34 @@
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <climits> // LONG_MAX
|
||||
# include <cerrno> // errno
|
||||
# include <sys/stat.h> // stat()
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
# include <cstdio> // fflush
|
||||
# include "colors.h"
|
||||
# include <cstdio> // perror, fflush
|
||||
# include <unistd.h> // close, access
|
||||
# include "colors.h" // for debug print_special
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
# define CRLF_SIZE 2
|
||||
# define NPOS std::string::npos
|
||||
|
||||
/* Equivalent for end of http header size :
|
||||
** std::string(CRLF CRLF).size();
|
||||
** sizeof(CRLF CRLF) - 1;
|
||||
** CRLF_SIZE*2
|
||||
*/
|
||||
|
||||
enum file_type
|
||||
{
|
||||
IS_OTHER,
|
||||
IS_FILE,
|
||||
IS_DIR
|
||||
};
|
||||
|
||||
enum http_method
|
||||
{
|
||||
@@ -45,7 +63,8 @@ std::string itos(int n);
|
||||
std::string trim(std::string str, char del);
|
||||
http_method str_to_http_method(std::string &str);
|
||||
std::string http_methods_to_str(unsigned int methods);
|
||||
int path_is_valid(std::string path);
|
||||
file_type eval_file_type(const std::string &path);
|
||||
size_t eval_file_mode(std::string path, int mode);
|
||||
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");
|
||||
|
||||
@@ -23,14 +23,16 @@
|
||||
# include <algorithm> // find
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <dirent.h> // opendir()
|
||||
# include <locale> // isalpha, local
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
# include "http_status.hpp"
|
||||
# include "autoindex.hpp"
|
||||
# include "colors.h"
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
@@ -76,29 +78,34 @@ class Webserv
|
||||
void _request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
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, std::string &path);
|
||||
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);
|
||||
|
||||
// move later
|
||||
std::string _replace_url_root(Client *client, std::string path);
|
||||
|
||||
void _get(Client *client, std::string &path);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
void _autoindex(Client *client, std::string &path);
|
||||
void _autoindex(Client *client, const std::string &path);
|
||||
// method_post.cpp
|
||||
void _post(Client *client);
|
||||
void _post(Client *client, const std::string &path);
|
||||
void _post_file(Client *client, const std::string &path);
|
||||
// method_delete.cpp
|
||||
void _delete(Client *client);
|
||||
void _delete(Client *client, const std::string &path);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client);
|
||||
bool _is_cgi(Client *client, std::string path);
|
||||
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
|
||||
std::string _exec_cgi(Client *client);
|
||||
char** _set_env(Client *client);
|
||||
char* _dup_env(std::string var, std::string val);
|
||||
@@ -107,6 +114,8 @@ class Webserv
|
||||
void _check_script_output(Client *client, std::string & output);
|
||||
void _check_script_status(Client *client, std::string & output);
|
||||
void _check_script_fields(Client *client, std::string & output);
|
||||
void _add_script_body_length_header(std::string & output);
|
||||
void _remove_body_leading_empty_lines(std::string & output);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
@@ -116,12 +125,16 @@ class Webserv
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
void _close_all_listen_sockets();
|
||||
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_client(uint32_t events, int fd);
|
||||
// init.cpp
|
||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||
void _listen(int socket_fd, unsigned int max_connections);
|
||||
void _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
|
||||
// timeout.cpp
|
||||
void _timeout();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#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>"\
|
||||
@@ -27,6 +25,4 @@
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,16 +1,67 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
bool Webserv::_is_cgi(Client *client)
|
||||
bool Webserv::_is_cgi(Client *client, std::string path)
|
||||
{
|
||||
// TODO see how it works with config
|
||||
if (client->fill_script_path("/cgi-bin/php-cgi"))
|
||||
return true;
|
||||
if (client->fill_script_path("/cgi-bin/cgi_cpp.cgi"))
|
||||
return true;
|
||||
std::string script_path;
|
||||
size_t file_type;
|
||||
size_t file_mode;
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos != NPOS)
|
||||
{
|
||||
pos = _cgi_pos(client, path, pos);
|
||||
if (pos == NPOS)
|
||||
break;
|
||||
client->fill_script_path(path, pos);
|
||||
script_path = "." + client->get_rq_script_path();
|
||||
file_type = ::eval_file_type(script_path);
|
||||
if (file_type == IS_DIR) // but what if it's a symlink ?
|
||||
continue;
|
||||
if (file_type == IS_FILE)
|
||||
{
|
||||
file_mode = ::eval_file_mode( script_path, X_OK );
|
||||
if (!file_mode)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
client->clear_script();
|
||||
client->status = file_mode; // 404 not_found OR 403 forbidden
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
|
||||
{
|
||||
std::vector<std::string> v_ext;
|
||||
std::vector<std::string>::const_iterator it;
|
||||
std::vector<std::string>::const_iterator it_end;
|
||||
size_t len;
|
||||
std::locale loc; // for isalpha()
|
||||
|
||||
v_ext = client->assigned_location->cgi_ext;
|
||||
if (v_ext.empty())
|
||||
return NPOS;
|
||||
it_end = client->assigned_location->cgi_ext.end();
|
||||
while (pos < path.size())
|
||||
{
|
||||
if (path.compare(pos, 2, "./") == 0)
|
||||
pos += 2;
|
||||
pos = path.find('.', pos);
|
||||
if (pos == NPOS)
|
||||
return pos;
|
||||
it = client->assigned_location->cgi_ext.begin();
|
||||
for ( ; it != it_end; ++it)
|
||||
{
|
||||
len = (*it).size();
|
||||
if (path.compare(pos + 1, len, *it) == 0)
|
||||
if ( !std::isalpha(path[pos + 1 + len], loc) )
|
||||
return pos + 1 + len;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return NPOS;
|
||||
}
|
||||
|
||||
std::string Webserv::_exec_cgi(Client *client)
|
||||
{
|
||||
char** env;
|
||||
@@ -89,8 +140,7 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
||||
std::string body = client->get_rq_body();
|
||||
int fd_in[2];
|
||||
int fd_out[2];
|
||||
int save_in = dup(STDIN_FILENO);
|
||||
int save_out = dup(STDOUT_FILENO);
|
||||
std::string path;
|
||||
|
||||
pipe(fd_in);
|
||||
pipe(fd_out);
|
||||
@@ -98,20 +148,22 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
std::cerr << "fork crashed" << std::endl;
|
||||
else if (pid == 0)
|
||||
else if (pid == 0) // child
|
||||
{
|
||||
close(FD_WR_TO_CHLD);
|
||||
close(FD_RD_FR_CHLD);
|
||||
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
||||
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
||||
// DEBUG
|
||||
std::cerr << "execve:\n";
|
||||
execve(client->get_rq_script_path().c_str(), nll, env);
|
||||
path = "." + client->get_rq_script_path();
|
||||
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
|
||||
execve(path.c_str(), nll, env);
|
||||
// for tests execve crash :
|
||||
//execve("wrong", nll, env);
|
||||
std::cerr << "execve crashed.\n";
|
||||
|
||||
// TODO HUGO : check errno
|
||||
}
|
||||
else
|
||||
else //parent
|
||||
{
|
||||
close(FD_RD_FR_PRNT);
|
||||
close(FD_WR_TO_PRNT);
|
||||
@@ -129,9 +181,7 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
||||
}
|
||||
if (script_output.empty())
|
||||
script_output = "Status: 500\r\n\r\n";
|
||||
|
||||
dup2(save_in, STDIN_FILENO);
|
||||
dup2(save_out, STDOUT_FILENO);
|
||||
|
||||
return script_output;
|
||||
}
|
||||
|
||||
@@ -139,6 +189,8 @@ void Webserv::_check_script_output(Client *client, std::string & output)
|
||||
{
|
||||
_check_script_status(client, output);
|
||||
_check_script_fields(client, output);
|
||||
_add_script_body_length_header(output);
|
||||
_remove_body_leading_empty_lines(output);
|
||||
// _check_script_empty_lines(client, output);
|
||||
// _check_script_space_colons(client, output);
|
||||
// _check_script_new_lines(client, output);
|
||||
@@ -150,10 +202,10 @@ void Webserv::_check_script_status(Client *client, std::string & output)
|
||||
int status_pos;
|
||||
|
||||
pos = output.find("Status:");
|
||||
if (pos != std::string::npos)
|
||||
if (pos != NPOS)
|
||||
{
|
||||
status_pos = pos + std::string("Status:").size();
|
||||
client->status = atoi(output.c_str() + status_pos);
|
||||
client->status = std::strtoul(output.c_str() + status_pos, NULL, 10);
|
||||
::extract_line(output, pos, CRLF);
|
||||
}
|
||||
else
|
||||
@@ -172,13 +224,13 @@ void Webserv::_check_script_fields(Client *client, std::string & output)
|
||||
// put server headers in map
|
||||
tmp = client->response;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != std::string::npos)
|
||||
if (pos != 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)
|
||||
if (pos != NPOS)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, scr_fld);
|
||||
// compare both map to supress duplicates
|
||||
@@ -195,3 +247,58 @@ void Webserv::_check_script_fields(Client *client, std::string & output)
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_remove_body_leading_empty_lines(std::string & output)
|
||||
{
|
||||
size_t pos;
|
||||
size_t pos_empty;
|
||||
|
||||
pos = output.find(CRLF CRLF);
|
||||
if (pos == NPOS)
|
||||
return;
|
||||
pos += CRLF_SIZE * 2;
|
||||
pos_empty = pos;
|
||||
while (pos_empty == pos)
|
||||
{
|
||||
pos = output.find(CRLF, pos);
|
||||
if (pos == pos_empty)
|
||||
extract_line(output, pos, CRLF);
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_add_script_body_length_header(std::string & output)
|
||||
{
|
||||
std::map<std::string, std::string> field;
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
std::stringstream str_len;
|
||||
std::string tmp;
|
||||
size_t pos;
|
||||
size_t len;
|
||||
|
||||
pos = output.find(CRLF CRLF);
|
||||
if (pos != NPOS)
|
||||
tmp = output.substr(pos + CRLF_SIZE);
|
||||
len = tmp.size();
|
||||
str_len << len;
|
||||
|
||||
// put script headers in map
|
||||
tmp = output;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != NPOS)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, field);
|
||||
// case insensitive search in map for "Content-Length"
|
||||
tmp = "Content-Length";
|
||||
for (it = field.begin(); it != field.end(); ++it)
|
||||
{
|
||||
if (str_tolower(it->first) == str_tolower(tmp))
|
||||
{
|
||||
pos = output.find(it->first);
|
||||
::extract_line(output, pos, CRLF);
|
||||
}
|
||||
}
|
||||
tmp += ": ";
|
||||
tmp += str_len.str();
|
||||
tmp += CRLF;
|
||||
output.insert(0, tmp);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,3 +42,71 @@ void Webserv::_close_all_listen_sockets()
|
||||
_listen_sockets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
|
||||
{
|
||||
/*
|
||||
** Many common code with init_virtual_servers(). Could refactor it.
|
||||
*/
|
||||
int ret;
|
||||
|
||||
std::cerr << "close lsocket " << it->fd << "\n";
|
||||
if (::close(it->fd) == -1)
|
||||
std::perror("err close()");
|
||||
std::cerr << "try to reopen lsocket\n";
|
||||
ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ?
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err socket()");
|
||||
_listen_sockets.erase(it);
|
||||
return;
|
||||
}
|
||||
it->fd = ret;
|
||||
|
||||
// HUGO ADD
|
||||
// allow socket descriptor to be reuseable
|
||||
// I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
|
||||
int on = 1;
|
||||
if (setsockopt(it->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
||||
{
|
||||
::perror("err setsockopt()");
|
||||
_listen_sockets.erase(it);
|
||||
return;
|
||||
}
|
||||
// HUGO ADD END
|
||||
|
||||
try {
|
||||
_bind(it->fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||
_listen(it->fd, 42); // 42 arbitrary
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
_listen_sockets.erase(it);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_epoll_update(it->fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
{
|
||||
_listen_sockets.erase(it);
|
||||
return;
|
||||
}
|
||||
std::cerr << "reopen success\n";
|
||||
}
|
||||
|
||||
void Webserv::_handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it)
|
||||
{
|
||||
if (events & EPOLLERR)
|
||||
std::cerr << "EPOLLERR on lsocket fd " << it->fd << "\n"; // DEBUG
|
||||
if (events & EPOLLHUP)
|
||||
std::cerr << "EPOLLHUP on lsocket fd " << it->fd << "\n"; // DEBUG
|
||||
_reopen_lsocket(it);
|
||||
}
|
||||
|
||||
void Webserv::_handle_epoll_error_client(uint32_t events, int fd)
|
||||
{
|
||||
if (events & EPOLLERR)
|
||||
std::cerr << "EPOLLERR on client fd " << fd << "\n"; // DEBUG
|
||||
if (events & EPOLLHUP)
|
||||
std::cerr << "EPOLLHUP on client fd " << fd << "\n"; // DEBUG
|
||||
_close_client(fd);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@
|
||||
# define S403 "403 Forbidden"
|
||||
# define S404 "404 Not Found"
|
||||
# define S405 "405 Method Not Allowed"
|
||||
# define S408 "408 Request Timeout"
|
||||
# define S413 "413 Content Too Large"
|
||||
# define S415 "415 Unsupported Media Type"
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
|
||||
@@ -48,8 +48,8 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
//
|
||||
// HUGO ADD END
|
||||
|
||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
||||
_listen(new_socket.fd, 512); // 512 arbitrary
|
||||
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||
_listen(new_socket.fd, 42); // 42 arbitrary
|
||||
|
||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
throw std::runtime_error("Socket init");
|
||||
@@ -89,7 +89,7 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
|
||||
void Webserv::_init_http_status_map()
|
||||
{
|
||||
/* "map.insert()" over "map.operator[]" :
|
||||
/* "map.insert()" more appropriate than "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;
|
||||
@@ -110,6 +110,7 @@ void Webserv::_init_http_status_map()
|
||||
_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(408, S408));
|
||||
_http_status.insert(status_pair(413, S413));
|
||||
|
||||
_http_status.insert(status_pair(500, S500));
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_delete(Client *client)
|
||||
void Webserv::_delete(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +1,42 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_get(Client *client)
|
||||
std::string Webserv::_replace_url_root(Client *client, std::string path)
|
||||
{
|
||||
/* RULES **
|
||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||
std::cerr << "path before = " << path << "\n"; // DEBUG
|
||||
if (client->assigned_location->path == "/")
|
||||
path.insert(0, client->assigned_location->root);
|
||||
else
|
||||
path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
|
||||
std::cerr << "path after = " << path << "\n"; // DEBUG
|
||||
return path;
|
||||
}
|
||||
|
||||
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 ???
|
||||
// const?
|
||||
void Webserv::_get(Client *client, std::string &path)
|
||||
{
|
||||
|
||||
|
||||
*/
|
||||
std::string path = client->get_rq_abs_path();
|
||||
|
||||
// this might not be the best thing, a voir
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
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)
|
||||
// Index/Autoindex block
|
||||
if (eval_file_type(path) == IS_DIR)
|
||||
{
|
||||
// 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)
|
||||
if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE)
|
||||
{
|
||||
// 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);
|
||||
else
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
@@ -82,7 +50,7 @@ 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() ?
|
||||
std::stringstream buf;
|
||||
|
||||
std::cout << "made it to get_file\n";
|
||||
std::cout << "_get_file()\n";
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
@@ -132,36 +100,33 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// const?
|
||||
void Webserv::_autoindex(Client *client, const std::string &path)
|
||||
{
|
||||
// std::cout << "made it to _autoindex\n";
|
||||
std::cout << "_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';
|
||||
// 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)
|
||||
// std::cout << "Path in auto is: " << path << '\n';
|
||||
if ( (dir = opendir(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(path);
|
||||
dir_list.append(AUTOINDEX_MID1);
|
||||
dir_list.append(client->assigned_location->path);
|
||||
dir_list.append(path);
|
||||
dir_list.append(AUTOINDEX_MID2);
|
||||
/* print all the files and directories within directory */
|
||||
while ((ent = readdir (dir)) != NULL)
|
||||
{
|
||||
// std::cout << "ent: " << ent->d_name << '\n';
|
||||
if (strcmp(".", ent->d_name) == 0)
|
||||
continue ;
|
||||
dir_list.append("<a href=\"");
|
||||
dir_list.append(client->assigned_location->path.c_str());
|
||||
dir_list.append(client->get_rq_abs_path() + "/");
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("\">");
|
||||
dir_list.append(ent->d_name);
|
||||
@@ -183,9 +148,8 @@ void Webserv::_autoindex(Client *client, std::string &path)
|
||||
else
|
||||
{
|
||||
// in theory not possible cuz we already checked...
|
||||
/* could not open directory */
|
||||
// perror ("");
|
||||
std::cout << "could not open dir\n";
|
||||
std::cerr << "could not open dir\n";
|
||||
// throw?
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
#include "Webserv.hpp"
|
||||
|
||||
|
||||
void Webserv::_post(Client *client)
|
||||
void Webserv::_post(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
#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
|
||||
|
||||
@@ -25,23 +25,23 @@ void Webserv::_request(Client *client)
|
||||
}
|
||||
else if (ret == READ_COMPLETE)
|
||||
{
|
||||
if (client->body_complete)
|
||||
std::cerr << "______BODY\n" << client->get_rq_body() << "\n______\n"; // DEBUG
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
client->request_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
int Webserv::_read_request(Client *client)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
ssize_t ret;
|
||||
|
||||
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.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
if (ret == 0)
|
||||
@@ -49,41 +49,34 @@ int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
|
||||
client->raw_request.append(buf, ret);
|
||||
// ::print_special(client->raw_request);
|
||||
// std::cerr << "__raw_request__\n" << client->raw_request << "\n______\n"; // DEBUG
|
||||
|
||||
if (!client->header_complete)
|
||||
{
|
||||
if (client->raw_request.find(CRLF CRLF) != std::string::npos)
|
||||
client->parse_request_headers(_servers);
|
||||
if (client->status)
|
||||
return READ_COMPLETE;
|
||||
if (client->header_complete)
|
||||
{
|
||||
client->header_complete = true;
|
||||
client->parse_request_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;
|
||||
|
||||
if (client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty()) // No body case
|
||||
return READ_COMPLETE;
|
||||
if (client->get_rq_headers("Content-Type").empty()
|
||||
&& client->get_rq_headers("Content-Length").empty()
|
||||
&& client->get_rq_headers("Transfer-Encoding").empty())
|
||||
return READ_COMPLETE; // No body case
|
||||
}
|
||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
||||
{
|
||||
// 413 or 400 ? 413 seems common among http server, but don't fit perfectly.
|
||||
{ // 413 or 400 ? 413 seems common among http server, but don't fit perfectly.
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
else if (client->header_complete)
|
||||
if (client->header_complete)
|
||||
{
|
||||
client->read_body_size += ret;
|
||||
if (client->read_body_size > client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
// client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now
|
||||
client->parse_request_body();
|
||||
if (client->status || client->body_complete)
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
if ((int)client->read_body_size >= ::atoi(client->get_rq_headers("Content-Length").c_str()))
|
||||
{
|
||||
client->parse_request_body();
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
return READ_IN_PROGRESS;
|
||||
|
||||
@@ -21,7 +21,7 @@ void Webserv::_response(Client *client)
|
||||
}
|
||||
else if (ret == SEND_COMPLETE)
|
||||
{
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
if (client->get_rq_headers("Connection") == "close" || client->status == 408)
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
@@ -44,6 +44,8 @@ int Webserv::_send_response(Client *client)
|
||||
if (client->status >= 400)
|
||||
_error_html_response(client);
|
||||
|
||||
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||
|
||||
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)
|
||||
@@ -67,26 +69,42 @@ void Webserv::_append_base_headers(Client *client)
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
}
|
||||
|
||||
// TODO HUGO : wip
|
||||
void Webserv::_construct_response(Client *client)
|
||||
{
|
||||
/* Switch between normal behavior or CGI here ?
|
||||
maybe better than in _get(), _post(), ...*/
|
||||
_process_method(client);
|
||||
std::string path;
|
||||
std::string script_output;
|
||||
|
||||
path = _replace_url_root(client, client->get_rq_abs_path());
|
||||
if (_is_cgi(client, path))
|
||||
{
|
||||
script_output = _exec_cgi(client);
|
||||
|
||||
/*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(script_output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||
|
||||
_check_script_output(client, script_output);
|
||||
client->response += script_output;
|
||||
|
||||
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||
|
||||
return;
|
||||
}
|
||||
_process_method(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_process_method(Client *client)
|
||||
void Webserv::_process_method(Client *client, std::string &path)
|
||||
{
|
||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||
std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
||||
std::cerr << "allow_methods = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug
|
||||
|
||||
switch (client->get_rq_method())
|
||||
{
|
||||
case (GET):
|
||||
_get(client); break;
|
||||
_get(client, path); break;
|
||||
case (POST):
|
||||
_post(client); break;
|
||||
_post(client, path); break;
|
||||
case (DELETE):
|
||||
_delete(client); break;
|
||||
_delete(client, path); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -124,7 +142,7 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s
|
||||
else
|
||||
{
|
||||
client->response.append(mime_type);
|
||||
if (mime_type.find("text/") != std::string::npos)
|
||||
if (mime_type.find("text/") != NPOS)
|
||||
client->response.append("; charset=UTF-8");
|
||||
}
|
||||
client->response.append(CRLF);
|
||||
@@ -169,67 +187,10 @@ ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
|
||||
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.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));
|
||||
else
|
||||
return (&(server.locations.back()));
|
||||
}
|
||||
|
||||
// const?
|
||||
// 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
|
||||
@@ -237,69 +198,35 @@ 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
|
||||
|
||||
New Rule for location paths, they never end in /
|
||||
Sooo
|
||||
If we get a url that ends in / ignore the last /
|
||||
|
||||
|
||||
*/
|
||||
|
||||
std::string uri = path;
|
||||
if (uri[uri.size() - 1] == '/')
|
||||
uri.erase(uri.size() - 1);
|
||||
|
||||
|
||||
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";
|
||||
if (it->path.size() > uri.size())
|
||||
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 (uri.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (it->path.size() == uri.size())
|
||||
return (&(*it));
|
||||
else if (uri[it->path.size()] == '/')
|
||||
return (&(*it));
|
||||
// this works cuz only ever looking for a / burried in a longer path
|
||||
}
|
||||
}
|
||||
return (&(server.locations.back()));
|
||||
|
||||
|
||||
// /test/mdr
|
||||
// /test/mdr/
|
||||
// /test/mdrBST
|
||||
@@ -313,81 +240,20 @@ if no path coresponds then use the most correct one
|
||||
/test/test_deepei
|
||||
/test/test_deepei/
|
||||
/test/test_deeperi
|
||||
|
||||
/test/test_deeper/super_deep/
|
||||
/test/aaaaaaaaaaa/super_deep/
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
// 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())
|
||||
if (dot_pos != NPOS && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
@@ -20,36 +20,41 @@ void Webserv::run()
|
||||
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
||||
if (nfds == -1)
|
||||
{
|
||||
int errno_copy = errno;
|
||||
std::perror("err epoll_wait()");
|
||||
throw std::runtime_error("Epoll wait");
|
||||
}
|
||||
else if (nfds == 0)
|
||||
{
|
||||
if (!_clients.empty())
|
||||
{
|
||||
std::cerr << "Timeout " << TIMEOUT << "ms\n";
|
||||
_close_all_clients();
|
||||
}
|
||||
if (errno_copy == EINTR)
|
||||
g_run = false;
|
||||
else
|
||||
throw std::runtime_error("Epoll wait");
|
||||
}
|
||||
else if (nfds == 0 && !_clients.empty())
|
||||
_timeout();
|
||||
i = 0;
|
||||
while (i < nfds)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
try {
|
||||
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)) );
|
||||
if (it_socket != _listen_sockets.end())
|
||||
{
|
||||
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||
_handle_epoll_error_lsocket(events[i].events, it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||
_handle_epoll_error_client(events[i].events, events[i].data.fd);
|
||||
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)
|
||||
{
|
||||
catch (const std::bad_alloc& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
_close_all_clients();
|
||||
/* Swap to free the memory
|
||||
@@ -57,8 +62,7 @@ void Webserv::run()
|
||||
std::vector<Client>().swap(_clients);
|
||||
break;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
++i;
|
||||
}
|
||||
|
||||
18
srcs/webserv/timeout.cpp
Normal file
18
srcs/webserv/timeout.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_timeout()
|
||||
{
|
||||
std::cerr << "_timeout()\n";
|
||||
std::vector<Client>::iterator it = _clients.begin();
|
||||
while (it != _clients.end())
|
||||
{
|
||||
if (!it->request_complete)
|
||||
{
|
||||
std::cerr << "timeout request fd " << it->get_cl_fd() << "\n";
|
||||
it->status = 408;
|
||||
_epoll_update(it->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
15
test_chunk.txt
Normal file
15
test_chunk.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
|
||||
|
||||
GET / HTTP/1.1
|
||||
Host: localhost:4040
|
||||
Accept: */*
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
7
|
||||
Mozilla
|
||||
9
|
||||
Developer
|
||||
7
|
||||
Network
|
||||
0
|
||||
4
urls.txt
Normal file
4
urls.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
http://localhost:4040/test
|
||||
http://localhost:4040/test/test_deeper/
|
||||
http://localhost:4040/test/test_deeper/super_deep/
|
||||
http://localhost:4040/test/index1.html
|
||||
@@ -3,11 +3,82 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: auto;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid red;
|
||||
margin: 20px auto;
|
||||
padding: 5px;
|
||||
}
|
||||
form > * {
|
||||
display: flex;
|
||||
margin: 5px auto 5px 5px;
|
||||
}
|
||||
mark {
|
||||
margin: 0px 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form method="get">
|
||||
|
||||
<form method="get" action="/cgi-bin/cgi_cpp.out">
|
||||
|
||||
<p><mark>get</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="post" action="/cgi-bin/cgi_cpp.out">
|
||||
|
||||
<p><mark>post</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="get" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||
|
||||
<p><mark>get</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="post" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||
|
||||
<p><mark>post</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post">
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
www/upload_form.html
Normal file
10
www/upload_form.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!--
|
||||
https://www.w3schools.com/howto/howto_html_file_upload_button.asp
|
||||
https://www.filestack.com/fileschool/html/html-file-upload-tutorial-example/
|
||||
https://www.rfc-editor.org/rfc/rfc9110#name-multipart-types
|
||||
https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
|
||||
-->
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<input type="file" id="myFile" name="upload_file">
|
||||
<input type="submit">
|
||||
</form>
|
||||
Reference in New Issue
Block a user