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
|
NAME = webserv
|
||||||
CXX = clang++
|
CXX = c++
|
||||||
|
|
||||||
CXXFLAGS = -Wall -Wextra #-Werror
|
CXXFLAGS = -Wall -Wextra #-Werror
|
||||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||||
@@ -23,7 +23,7 @@ SRCS = main.cpp \
|
|||||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||||
accept.cpp request.cpp response.cpp \
|
accept.cpp request.cpp response.cpp \
|
||||||
method_get.cpp method_post.cpp method_delete.cpp \
|
method_get.cpp method_post.cpp method_delete.cpp \
|
||||||
run_loop.cpp \
|
run_loop.cpp timeout.cpp \
|
||||||
parser.cpp \
|
parser.cpp \
|
||||||
extraConfig.cpp \
|
extraConfig.cpp \
|
||||||
postProcessing.cpp \
|
postProcessing.cpp \
|
||||||
@@ -43,12 +43,14 @@ all: $(NAME)
|
|||||||
|
|
||||||
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
|
||||||
|
|
||||||
$(OBJS_D):
|
$(OBJS_D):
|
||||||
mkdir $@
|
mkdir $@
|
||||||
|
|
||||||
$(NAME): $(OBJS)
|
$(NAME): $(OBJS)
|
||||||
$(CXX) $^ -o $(NAME)
|
$(CXX) $^ -o $(NAME)
|
||||||
|
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJS_D)
|
rm -rf $(OBJS_D)
|
||||||
@@ -62,3 +64,24 @@ re: fclean all
|
|||||||
|
|
||||||
-include $(DEPS) # header dependencie
|
-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
|
## work together
|
||||||
|
|
||||||
#### TODO hugo
|
#### next commit
|
||||||
- `_is_cgi()` and `_fill_cgi_path()`
|
|
||||||
- two cgi tests :
|
|
||||||
? - a basic form with "name" and "something", that return a html page with that
|
|
||||||
? - for GET and POST
|
|
||||||
? - a script called by a file extension in URI
|
|
||||||
|
|
||||||
#### questions
|
#### questions
|
||||||
- in client, fd is in privat, so accesible by getter, is it ok ?
|
- how should we handle a wrong url like `http://localhost/cgi-bin/wrong.phpp/good.php` ?
|
||||||
- in client.cpp i fill the port, is there a default one in case it's not in the request ?
|
- do we serve `./srcs/cgi-bin/good.php` ?
|
||||||
- timeout server but still works ?
|
- or do we return 404 "not found" ?
|
||||||
- path contains double "//" from `Webserv::_get()` in response.cpp
|
-> - for now, execve would crash, but that doesn't produce a 404 error, rather a 500, is it bad ?
|
||||||
- cgi path ? defined in config ? and root path ? :
|
- 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"
|
||||||
- `Client.cpp : fill_script_path()`
|
- if a url has a file with extension, but it's not a cgi extension, is it necessary to look further ?
|
||||||
- `cgi.cpp : is_cgi()`
|
- ex. `http://localhost/file.php/file.py` for `cgi_ext py;` ?
|
||||||
- `cgi.cpp : set_env()`
|
|
||||||
- what if the uri contains a php file, and the config said php must be handled by cgi, but the path to this php in the uri is wrong ?
|
|
||||||
- is it ok ? `http://my_site.com/cgi-bin/php-cgi` (real path)
|
|
||||||
- is it ok ? `http://my_site.com/php-cgi` (reconstruct path ?)
|
|
||||||
- is it ok ? `http://my_site.com/something/php-cgi` (what about 'something' ?)
|
|
||||||
- is it ok ? `http://my_site.com/something/cgi-bin/php-cgi` (real path with 'something' before ? )
|
|
||||||
- I don't save the STDIN and STDOUT before dup2 in child process, is it wrong ?
|
|
||||||
- the response page is received long after the cgi-script is done, why ?
|
- 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)
|
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||||
|
|
||||||
#### output cgi script :
|
#### 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 :
|
#### summary :
|
||||||
- the cgi-script will send back at least one header field followed by an empty line
|
- the cgi-script will send back at least one header field followed by an empty line
|
||||||
@@ -125,13 +101,18 @@
|
|||||||
- "Status"
|
- "Status"
|
||||||
- the cgi-script may send back more header fields
|
- the cgi-script may send back more header fields
|
||||||
- the server must check and modify few things :
|
- the server must check and modify few things :
|
||||||
- there is no duplicate in headers fields (if there is, resolve conflict)
|
- there is no field duplicate (resolve conflicts)
|
||||||
- there is no space between the field name and the ":"
|
- there is no space between filed name and ":"
|
||||||
- the newlines are of form "\r\n", and not "\n" only
|
- change all the '\n' by '\r\n'
|
||||||
- if the location field is not present, then if the status field is not present either, then the status code is 200
|
- if no Location field && no Status field -> status code = 200
|
||||||
- the cgi-script can return a location field, of two types :
|
- handle Location field, either :
|
||||||
- local redirection : start with a "/", the server must answer as if this was the request uri
|
- local : start with '/' --> rerun the request with new uri
|
||||||
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
|
- 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
|
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||||
|
|
||||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||||
@@ -232,6 +213,37 @@
|
|||||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
## 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
|
## 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.
|
- 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
|
## ressources
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ server {
|
|||||||
autoindex on;
|
autoindex on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /cgi-bin {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
cgi_ext out php sh;
|
||||||
|
}
|
||||||
|
|
||||||
location /redirect {
|
location /redirect {
|
||||||
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
||||||
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
||||||
@@ -26,9 +31,11 @@ server {
|
|||||||
|
|
||||||
location /test {
|
location /test {
|
||||||
index index1.html subdex.html;
|
index index1.html subdex.html;
|
||||||
|
root ./www/test/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /test/index1.html {
|
location /test/index1.html {
|
||||||
|
root ./www/test/;
|
||||||
index index1.html subdex.html;
|
index index1.html subdex.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,25 +43,24 @@ server {
|
|||||||
redirect 301 https://berniesanders.com/404/;
|
redirect 301 https://berniesanders.com/404/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
|
location /stylesheet/ {
|
||||||
location /stylesheet/style.css {
|
|
||||||
# root ./www/../;
|
# root ./www/../;
|
||||||
root ./;
|
root ./styelsheet/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /test/something.html {
|
location /test/something.html {
|
||||||
# allow_methods DELETE;
|
# allow_methods DELETE;
|
||||||
|
root ./www/test/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# location /something/long/here {
|
|
||||||
# }
|
|
||||||
|
|
||||||
location /test/test_deeper/ {
|
location /test/test_deeper/ {
|
||||||
# allow_methods
|
# allow_methods
|
||||||
autoindex on;
|
autoindex on;
|
||||||
|
root ./www/test/test_deeper/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /test/test_deeper/super_deep {
|
location /test/test_deeper/super_deep {
|
||||||
|
root ./www/test/test_deeper/super_deep/;
|
||||||
autoindex on;
|
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 :
|
IN 42 SUBJECT AND/OR PRIORITY :
|
||||||
- CGI
|
- CGI (TODO HUGO)
|
||||||
- chunked request (response not mandatory it seems)
|
- chunked request (WIP, a bit difficult)
|
||||||
- fix need for index and autoindex config
|
|
||||||
- Ecrire des tests !
|
- Need to test normal body parsing
|
||||||
- "root" need to replace "location->path" part of "client.path"
|
- basic html upload page for testing request of web browser
|
||||||
replace up to the last "/" of the "location->path" part
|
|
||||||
(if its a folder this will be in fact the entire path)
|
|
||||||
- handle redirection (Work, but weird behavior need deeper test)
|
|
||||||
- upload files with config "upload_dir"
|
- 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)
|
- _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 le champ "Accept" du client
|
||||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
- 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)
|
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||||
|
|||||||
173
srcs/Client.cpp
173
srcs/Client.cpp
@@ -8,6 +8,8 @@
|
|||||||
Client::Client()
|
Client::Client()
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
|
body_complete(false),
|
||||||
|
request_complete(false),
|
||||||
read_body_size(0),
|
read_body_size(0),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
@@ -22,6 +24,8 @@ Client::Client()
|
|||||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
|
body_complete(false),
|
||||||
|
request_complete(false),
|
||||||
read_body_size(0),
|
read_body_size(0),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
@@ -77,13 +81,19 @@ Client & Client::operator=( Client const & rhs )
|
|||||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||||
{
|
{
|
||||||
|
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||||
|
return ;
|
||||||
|
header_complete = true;
|
||||||
clear_request(); // not mandatory
|
clear_request(); // not mandatory
|
||||||
|
|
||||||
_parse_request_line();
|
_parse_request_line();
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
_parse_request_fields();
|
_parse_request_fields();
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
print_client("headers");
|
// print_client("headers");
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
assigned_server = ::_determine_process_server(this, servers);
|
assigned_server = ::_determine_process_server(this, servers);
|
||||||
@@ -93,6 +103,9 @@ print_client("headers");
|
|||||||
return;
|
return;
|
||||||
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
_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
|
// dont clear raw_request, we need it for future reparsing of body
|
||||||
// see call of parse_request() in _read_request()
|
// see call of parse_request() in _read_request()
|
||||||
// raw_request.clear();
|
// raw_request.clear();
|
||||||
@@ -100,38 +113,121 @@ print_client("headers");
|
|||||||
|
|
||||||
void Client::parse_request_body()
|
void Client::parse_request_body()
|
||||||
{
|
{
|
||||||
|
std::cerr << "parse_request_body()\n";
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
pos = raw_request.find(CRLF CRLF);
|
pos = raw_request.find(CRLF CRLF);
|
||||||
pos += std::string(CRLF CRLF).size();
|
if (pos == NPOS)
|
||||||
_request.body = raw_request.substr(pos);
|
{
|
||||||
|
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)
|
if (_request.body.size() > assigned_server->client_body_limit)
|
||||||
status = 413; // HTTP Client Errors
|
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;
|
std::string tmp;
|
||||||
|
|
||||||
pos = path.find(script);
|
if (path[0] == '.')
|
||||||
if (pos == 0)
|
|
||||||
{
|
{
|
||||||
tmp = path.substr(0, pos + len);
|
path.erase(0, 1);
|
||||||
_request.script.path = "./srcs" + tmp; // TODO: root path ?
|
pos--;
|
||||||
_request.script.info = path.substr(pos + len);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
_request.script.path = path.substr(0, pos);
|
||||||
|
_request.script.info = path.substr(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clear()
|
void Client::clear()
|
||||||
{
|
{
|
||||||
clear_request();
|
clear_request();
|
||||||
header_complete = false;
|
header_complete = false;
|
||||||
|
body_complete = false;
|
||||||
|
request_complete = false;
|
||||||
read_body_size = 0;
|
read_body_size = 0;
|
||||||
assigned_server = NULL;
|
assigned_server = NULL;
|
||||||
assigned_location = NULL;
|
assigned_location = NULL;
|
||||||
@@ -144,7 +240,7 @@ void Client::clear_request()
|
|||||||
{
|
{
|
||||||
clear_script();
|
clear_script();
|
||||||
_request.method = UNKNOWN;
|
_request.method = UNKNOWN;
|
||||||
_request.uri.clear();
|
_request.target.clear();
|
||||||
_request.version.clear();
|
_request.version.clear();
|
||||||
_request.headers.clear();
|
_request.headers.clear();
|
||||||
_request.body.clear();
|
_request.body.clear();
|
||||||
@@ -173,7 +269,7 @@ void Client::print_client(std::string message)
|
|||||||
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
||||||
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
||||||
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\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_abs_path() : [" << get_rq_abs_path() << "]\n"
|
||||||
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
||||||
<< "get_rq_version() : [" << get_rq_version() << "]\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; }
|
http_method Client::get_rq_method() const { return _request.method; }
|
||||||
std::string Client::get_rq_method_str() const
|
std::string Client::get_rq_method_str() const
|
||||||
{ return ::http_methods_to_str(_request.method); }
|
{ 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_abs_path() const { return _request.abs_path; }
|
||||||
std::string Client::get_rq_query() const { return _request.query; }
|
std::string Client::get_rq_query() const { return _request.query; }
|
||||||
std::string Client::get_rq_version() const { return _request.version; }
|
std::string Client::get_rq_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_hostname() const { return _request.hostname; }
|
||||||
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
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_script_info()const { return _request.script.info; }
|
||||||
|
|
||||||
std::string Client::get_rq_headers(const std::string & key) const
|
std::string Client::get_rq_headers(const std::string & key) const
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string>::const_iterator it;
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
@@ -241,22 +338,22 @@ void Client::_parse_request_line()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_request.method = str_to_http_method(line[0]);
|
_request.method = str_to_http_method(line[0]);
|
||||||
_request.uri = line[1];
|
_request.target = line[1];
|
||||||
_parse_request_uri(line[1]);
|
_parse_request_target(line[1]);
|
||||||
_request.version = line[2];
|
_request.version = line[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::_parse_request_uri( std::string uri )
|
void Client::_parse_request_target( std::string target )
|
||||||
{
|
{
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
pos = uri.find("?");
|
pos = target.find("?");
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
_request.query = uri.substr(pos + 1);
|
_request.query = target.substr(pos + 1);
|
||||||
else
|
else
|
||||||
_request.query = "";
|
_request.query = "";
|
||||||
_request.abs_path = uri.substr(0, pos);
|
_request.abs_path = target.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::_parse_request_fields()
|
void Client::_parse_request_fields()
|
||||||
@@ -268,11 +365,11 @@ void Client::_parse_request_fields()
|
|||||||
headers = raw_request;
|
headers = raw_request;
|
||||||
// delete first line
|
// delete first line
|
||||||
pos = headers.find(CRLF);
|
pos = headers.find(CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
headers.erase(0, pos + std::string(CRLF).size());
|
headers.erase(0, pos + std::string(CRLF).size());
|
||||||
// delete body part
|
// delete body part
|
||||||
pos = headers.find(CRLF CRLF);
|
pos = headers.find(CRLF CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
headers.erase(pos);
|
headers.erase(pos);
|
||||||
else {
|
else {
|
||||||
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
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(':');
|
pos = host.find(':');
|
||||||
// port :
|
// port :
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
_request.port = "4040"; // TODO: make equal to default port in config
|
_request.port = "4040"; // TODO: make equal to default port in config
|
||||||
else
|
else
|
||||||
_request.port = host.substr(pos);
|
_request.port = host.substr(pos);
|
||||||
@@ -308,7 +405,7 @@ void Client::_parse_port_hostname(std::string host)
|
|||||||
|
|
||||||
void Client::_check_request_errors()
|
void Client::_check_request_errors()
|
||||||
{
|
{
|
||||||
//////////////////////
|
///////////////////////
|
||||||
// Request line checks
|
// Request line checks
|
||||||
if (_request.method == UNKNOWN)
|
if (_request.method == UNKNOWN)
|
||||||
status = 501; // HTTP Client Errors
|
status = 501; // HTTP Client Errors
|
||||||
@@ -330,14 +427,20 @@ void Client::_check_request_errors()
|
|||||||
response.append(CRLF CRLF);
|
response.append(CRLF CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status)
|
//////////////////
|
||||||
return;
|
|
||||||
|
|
||||||
/////////////////
|
|
||||||
// Headers checks
|
// Headers checks
|
||||||
if (!this->get_rq_headers("Content-Length").empty()
|
else if (!this->get_rq_headers("Content-Length").empty()
|
||||||
&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
|
&& std::strtoul(this->get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
|
||||||
status = 413; // HTTP Client Errors
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
# include "ServerConfig.hpp"
|
# include "ServerConfig.hpp"
|
||||||
|
# include "colors.h"
|
||||||
|
|
||||||
struct Script
|
struct Script
|
||||||
{
|
{
|
||||||
@@ -21,7 +22,7 @@ struct Script
|
|||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
http_method method;
|
http_method method;
|
||||||
std::string uri;
|
std::string target;
|
||||||
std::string abs_path;
|
std::string abs_path;
|
||||||
std::string query;
|
std::string query;
|
||||||
std::string version;
|
std::string version;
|
||||||
@@ -45,7 +46,9 @@ class Client
|
|||||||
std::string response;
|
std::string response;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
bool header_complete;
|
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[]
|
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||||
const LocationConfig *assigned_location;
|
const LocationConfig *assigned_location;
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ class Client
|
|||||||
// requests getters
|
// requests getters
|
||||||
http_method get_rq_method() const;
|
http_method get_rq_method() const;
|
||||||
std::string get_rq_method_str() 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_abs_path() const;
|
||||||
std::string get_rq_query() const;
|
std::string get_rq_query() const;
|
||||||
std::string get_rq_version() const;
|
std::string get_rq_version() const;
|
||||||
@@ -74,7 +77,7 @@ class Client
|
|||||||
void clear();
|
void clear();
|
||||||
void clear_request();
|
void clear_request();
|
||||||
void clear_script();
|
void clear_script();
|
||||||
bool fill_script_path(std::string script);
|
void fill_script_path(std::string &path, size_t pos);
|
||||||
// DEBUG
|
// DEBUG
|
||||||
void print_client(std::string message = "");
|
void print_client(std::string message = "");
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ class Client
|
|||||||
|
|
||||||
void _parse_request_line();
|
void _parse_request_line();
|
||||||
void _parse_request_fields();
|
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 _parse_port_hostname(std::string host);
|
||||||
|
|
||||||
void _check_request_errors();
|
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
|
#ifndef CONFIGPARSER_HPP
|
||||||
# define CONFIGPARSER_HPP
|
# define CONFIGPARSER_HPP
|
||||||
@@ -22,7 +11,7 @@
|
|||||||
# include <exception> // exception, what
|
# include <exception> // exception, what
|
||||||
# include <stdexcept> // runtime_error, invalid_argument
|
# include <stdexcept> // runtime_error, invalid_argument
|
||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
# include <cstdlib> // strtol, stroul
|
||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
# include <fstream> // ifstream
|
# include <fstream> // ifstream
|
||||||
//# include <unistd.h> // access()
|
//# include <unistd.h> // access()
|
||||||
@@ -34,7 +23,7 @@ class ConfigParser {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// canonical
|
// canonical?
|
||||||
|
|
||||||
ConfigParser(const char* path); // a string?
|
ConfigParser(const char* path); // a string?
|
||||||
~ConfigParser();
|
~ConfigParser();
|
||||||
@@ -42,23 +31,15 @@ public:
|
|||||||
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
|
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
|
||||||
// ConfigParser & operator=(const ConfigParser& rhs);
|
// ConfigParser & operator=(const ConfigParser& rhs);
|
||||||
|
|
||||||
// void parse(); // return void cuz throw exceptions.
|
|
||||||
std::vector<ServerConfig> * parse(); // const?
|
std::vector<ServerConfig> * parse(); // const?
|
||||||
// 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
|
// i thought if it were an instance of this class you could call
|
||||||
// private member functions from anywhere...
|
// private member functions from anywhere...
|
||||||
void _print_content() const;
|
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:
|
private:
|
||||||
std::string _content;
|
std::string _content;
|
||||||
|
|
||||||
@@ -83,38 +64,10 @@ private:
|
|||||||
std::string _get_rest_of_line(size_t *curr); // const?
|
std::string _get_rest_of_line(size_t *curr); // const?
|
||||||
|
|
||||||
|
|
||||||
|
/* Post Processing */
|
||||||
|
|
||||||
|
|
||||||
// some sort of post processing...
|
|
||||||
|
|
||||||
void _post_processing(std::vector<ServerConfig> *servers);
|
void _post_processing(std::vector<ServerConfig> *servers);
|
||||||
|
|
||||||
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
|
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> +#+ +:+ +#+ */
|
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
/* 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 <iostream>
|
||||||
# include <sys/stat.h> // stat()
|
# include <sys/stat.h> // stat()
|
||||||
|
|
||||||
# include <stdio.h> // printf(), gotta go
|
|
||||||
|
|
||||||
|
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
|
|
||||||
// again, struct instead?
|
// again, struct instead?
|
||||||
@@ -31,7 +28,7 @@ public:
|
|||||||
// canonic stuff?
|
// canonic stuff?
|
||||||
|
|
||||||
std::string path; // /path and /path/ are fine
|
std::string path; // /path and /path/ are fine
|
||||||
// i add trailing / if a dir
|
// i remove trailing / systematically
|
||||||
std::string root;
|
std::string root;
|
||||||
std::vector<std::string> index;
|
std::vector<std::string> index;
|
||||||
unsigned int allow_methods;
|
unsigned int allow_methods;
|
||||||
@@ -42,10 +39,8 @@ public:
|
|||||||
|
|
||||||
int redirect_status; // only in location
|
int redirect_status; // only in location
|
||||||
std::string redirect_uri; // only 1 per 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";
|
std::cout << "\nPRINTING A LOCATION\n";
|
||||||
|
|
||||||
@@ -70,7 +65,7 @@ public:
|
|||||||
int comp_rhs = 0;
|
int comp_rhs = 0;
|
||||||
size_t tmp = 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;
|
++tmp;
|
||||||
++comp_lhs;
|
++comp_lhs;
|
||||||
@@ -78,7 +73,7 @@ public:
|
|||||||
if (path[path.find_last_of("/") + 1] != '\0')
|
if (path[path.find_last_of("/") + 1] != '\0')
|
||||||
++comp_lhs;
|
++comp_lhs;
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
|
while ((tmp = rhs.path.find_first_of("/", tmp)) != NPOS)
|
||||||
{
|
{
|
||||||
++tmp;
|
++tmp;
|
||||||
++comp_rhs;
|
++comp_rhs;
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ public:
|
|||||||
// we could shove default in here if we wanted to...
|
// we could shove default in here if we wanted to...
|
||||||
|
|
||||||
std::string host;
|
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
|
std::string root; // ./www/ or www work www/ and www work
|
||||||
// i do remove trailing / tho
|
// 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::vector<std::string> index;
|
||||||
std::map<int, std::string> error_pages;
|
std::map<int, std::string> error_pages;
|
||||||
@@ -33,7 +35,7 @@ public:
|
|||||||
std::vector<LocationConfig> locations;
|
std::vector<LocationConfig> locations;
|
||||||
|
|
||||||
|
|
||||||
void print_all()
|
void print_all() // const?
|
||||||
{
|
{
|
||||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
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"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
|
// should i be sending & references?
|
||||||
|
// const?
|
||||||
std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||||
const std::string value)
|
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
|
// check values for ; at end and right number of words depending on key
|
||||||
|
|
||||||
// std::cout << "pre check\n";
|
// 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");
|
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") != NPOS)
|
||||||
if (value.find_first_of("\t") != std::string::npos)
|
|
||||||
{
|
|
||||||
// std::cout << value << "\n";
|
|
||||||
throw std::invalid_argument("why would you put tabs between values");
|
throw std::invalid_argument("why would you put tabs between values");
|
||||||
}
|
|
||||||
|
|
||||||
size_t i = value.find_first_of(";");
|
size_t i = value.find_first_of(";");
|
||||||
// so you can't have no ;
|
// so you can't have no ;
|
||||||
// you can't have just ;
|
// you can't have just ;
|
||||||
// and you can't have a ; not at the end or several ;
|
// and you can't have a ; not at the end or several ;
|
||||||
// in theory value_find_last_of should find the only ;
|
// 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)
|
|| value.compare(";") == 0)
|
||||||
throw std::invalid_argument("bad config file arguments 4");
|
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));
|
return (value.substr(0, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const?
|
||||||
|
|
||||||
// assumes curr is on a space or \t or \n
|
// 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)
|
std::string ConfigParser::_get_first_word(size_t *curr)
|
||||||
{
|
{
|
||||||
size_t start;
|
size_t start;
|
||||||
|
|
||||||
// are these checks excessive?
|
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
|
||||||
throw std::invalid_argument("bad config file arguments");
|
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");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
std::string key = _content.substr(start, *curr - start);
|
std::string key = _content.substr(start, *curr - start);
|
||||||
|
|
||||||
return (key);
|
return (key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const?
|
||||||
// also assumes curr is on a space \t or \n
|
// also assumes curr is on a space \t or \n
|
||||||
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||||
{
|
{
|
||||||
size_t start;
|
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");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
if ((*curr = _content.find_first_of("\n", start)) == NPOS)
|
||||||
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
|
|
||||||
throw std::invalid_argument("bad config file arguments");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
std::string values = _content.substr(start, *curr - start);
|
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
|
void ConfigParser::_print_content() const
|
||||||
{
|
{
|
||||||
std::cout << _content;
|
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"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
@@ -21,7 +10,7 @@ ConfigParser::ConfigParser()
|
|||||||
|
|
||||||
ConfigParser::ConfigParser(const char* path)
|
ConfigParser::ConfigParser(const char* path)
|
||||||
{
|
{
|
||||||
std::cout << "Param Constructor\n";
|
// std::cout << "Param Constructor\n";
|
||||||
|
|
||||||
std::ifstream file;
|
std::ifstream file;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
@@ -35,10 +24,10 @@ ConfigParser::ConfigParser(const char* path)
|
|||||||
{
|
{
|
||||||
getline(file, buf);
|
getline(file, buf);
|
||||||
// remove # comments here.
|
// remove # comments here.
|
||||||
if ((comment = buf.find_first_of("#")) == std::string::npos)
|
if ((comment = buf.find_first_of("#")) == NPOS)
|
||||||
{
|
{
|
||||||
// remove empty lines, i think...
|
// 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');
|
_content.append(buf + '\n');
|
||||||
}
|
}
|
||||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
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> * ConfigParser::parse()
|
||||||
{
|
{
|
||||||
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
||||||
// std::vector<ServerConfig> ret;
|
|
||||||
|
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
size_t curr = _content.find_first_not_of(" \t\n", 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");
|
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");
|
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");
|
throw std::invalid_argument("empty config file");
|
||||||
std::string key = _content.substr(start, curr - start);
|
std::string key = _content.substr(start, curr - start);
|
||||||
if (key != "server")
|
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);
|
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||||
|
|
||||||
ret.client_body_limit = 0;
|
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");
|
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");
|
throw std::invalid_argument("bad config file syntax");
|
||||||
// are there other things to check for?
|
// 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...
|
// so this moves curr to past the word...
|
||||||
std::string key = _get_first_word(&curr);
|
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);
|
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");
|
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");
|
throw std::invalid_argument("bad config file syntax");
|
||||||
while (curr != std::string::npos)
|
while (curr != NPOS)
|
||||||
{
|
{
|
||||||
// so this moves curr to past the word...
|
// so this moves curr to past the word...
|
||||||
std::string key = _get_first_word(&curr);
|
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 == "" \
|
else if (key == "listen" && size == 1 && server->host == "" \
|
||||||
&& server->port == "")
|
&& 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]))
|
if (!::isNumeric(tmp_val[0]))
|
||||||
throw std::invalid_argument("bad port number");
|
throw std::invalid_argument("bad port number");
|
||||||
@@ -234,6 +222,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
// remove trailing /
|
// remove trailing /
|
||||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||||
|
// tmp_val[0].push_back('/');
|
||||||
server->root = tmp_val[0];
|
server->root = tmp_val[0];
|
||||||
}
|
}
|
||||||
else if (key == "client_body_limit" && size == 1 \
|
else if (key == "client_body_limit" && size == 1 \
|
||||||
@@ -241,7 +230,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
{
|
{
|
||||||
if (!::isNumeric(tmp_val[0]))
|
if (!::isNumeric(tmp_val[0]))
|
||||||
throw std::invalid_argument("client_body_limit not a number");
|
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")
|
else if (key == "index")
|
||||||
{
|
{
|
||||||
@@ -255,7 +244,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
{
|
{
|
||||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||||
throw std::invalid_argument("invalid error code");
|
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())
|
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||||
throw std::invalid_argument("redeclaring error page");
|
throw std::invalid_argument("redeclaring error page");
|
||||||
server->error_pages[status_code] = path;
|
server->error_pages[status_code] = path;
|
||||||
@@ -283,6 +272,7 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
|||||||
// remove trailing /
|
// remove trailing /
|
||||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||||
|
// tmp_val[0].push_back('/');
|
||||||
location->root = tmp_val[0];
|
location->root = tmp_val[0];
|
||||||
}
|
}
|
||||||
else if (key == "autoindex" && size == 1)
|
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] != "303" && tmp_val[0] != "307"
|
||||||
&& tmp_val[0] != "308")
|
&& tmp_val[0] != "308")
|
||||||
throw std::invalid_argument("bad redirect status");
|
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://")
|
if (tmp_val[1].compare(0, 7, "http://")
|
||||||
&& tmp_val[1].compare(0, 8, "https://"))
|
&& tmp_val[1].compare(0, 8, "https://"))
|
||||||
throw std::invalid_argument("bad redirect uri");
|
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];
|
location->redirect_uri = tmp_val[1];
|
||||||
}
|
}
|
||||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
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)
|
if (it_l->allow_methods == UNKNOWN)
|
||||||
it_l->allow_methods = ANY_METHODS;
|
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;
|
it_l->index = it->index;
|
||||||
|
|
||||||
// nothing to be done for cgi_ext, error_pages, redirect
|
// nothing to be done for cgi_ext, error_pages, redirect
|
||||||
|
|
||||||
if (path_is_valid(it_l->root + it_l->path) == 1 \
|
// if (eval_file_type(it_l->root) == IS_DIR
|
||||||
&& it_l->path[it_l->path.size() - 1] != '/')
|
// && it_l->path[it_l->path.size() - 1] != '/')
|
||||||
it_l->path.push_back('/');
|
// 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;
|
++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)
|
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())
|
while (it != locations.end())
|
||||||
{
|
{
|
||||||
if (it->path.compare("/") == 0)
|
if (it->path.compare("/") == 0)
|
||||||
{
|
|
||||||
// std::cout << "in compare: " << it->path << " -- ";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ int main(int ac, char **av)
|
|||||||
|
|
||||||
ConfigParser configParser(config.c_str());
|
ConfigParser configParser(config.c_str());
|
||||||
|
|
||||||
configParser._print_content();
|
// configParser._print_content();
|
||||||
|
|
||||||
// i don't love that servers has to be a pointer...
|
// i don't love that servers has to be a pointer...
|
||||||
std::vector<ServerConfig>* servers = configParser.parse();
|
std::vector<ServerConfig>* servers = configParser.parse();
|
||||||
@@ -27,8 +27,9 @@ int main(int ac, char **av)
|
|||||||
// use an iterator you moron
|
// use an iterator you moron
|
||||||
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
||||||
{
|
{
|
||||||
|
(void)0;
|
||||||
// std::cout << it->server_name << " ";
|
// 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 end = 0;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
while (end != std::string::npos)
|
while (end != NPOS)
|
||||||
{
|
{
|
||||||
end = input.find(delim, start);
|
end = input.find(delim, start);
|
||||||
len = end - start;
|
len = end - start;
|
||||||
if (end == std::string::npos)
|
if (end == NPOS)
|
||||||
len = end;
|
len = end;
|
||||||
tmp = input.substr(start, len);
|
tmp = input.substr(start, len);
|
||||||
if (ctrim != '\0')
|
if (ctrim != '\0')
|
||||||
@@ -60,13 +60,13 @@ std::string trim(std::string str, char del)
|
|||||||
|
|
||||||
// delete leadings del
|
// delete leadings del
|
||||||
pos = str.find_first_not_of(del);
|
pos = str.find_first_not_of(del);
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
pos = str.size();
|
pos = str.size();
|
||||||
str = str.substr(pos);
|
str = str.substr(pos);
|
||||||
|
|
||||||
// delete trailing del
|
// delete trailing del
|
||||||
pos = str.find_last_not_of(del);
|
pos = str.find_last_not_of(del);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
str = str.substr(0, pos + 1);
|
str = str.substr(0, pos + 1);
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
@@ -110,7 +110,7 @@ bool isNumeric_btw(int low, int high, std::string str)
|
|||||||
if (std::isdigit(str[i]) == false)
|
if (std::isdigit(str[i]) == false)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int n = std::atoi(str.c_str());
|
int n = std::strtol(str.c_str(), NULL, 10);
|
||||||
if (n < low || n > high)
|
if (n < low || n > high)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -151,29 +151,41 @@ std::string http_methods_to_str(unsigned int methods)
|
|||||||
|
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
|
|
||||||
// you could make this &path...
|
file_type eval_file_type(const std::string &path)
|
||||||
int path_is_valid(std::string path)
|
|
||||||
{
|
{
|
||||||
const char *tmp_path = path.c_str();
|
const char *tmp_path = path.c_str();
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
if (stat(tmp_path, &s) == 0)
|
if (stat(tmp_path, &s) != -1)
|
||||||
{
|
{
|
||||||
if (S_ISREG(s.st_mode))
|
if (S_ISREG(s.st_mode))
|
||||||
{
|
return (IS_FILE);
|
||||||
// std::cout << "is a file\n";
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
else if (S_ISDIR(s.st_mode))
|
else if (S_ISDIR(s.st_mode))
|
||||||
{
|
return (IS_DIR);
|
||||||
// std::cout << "is a Dir\n";
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// std::cout << "path is neither dir nor file\n";
|
else
|
||||||
return (0);
|
{
|
||||||
|
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
|
void
|
||||||
replace_all_substr(
|
replace_all_substr(
|
||||||
@@ -187,7 +199,7 @@ void
|
|||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
pos = str.find(ori_substr, pos);
|
pos = str.find(ori_substr, pos);
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
break;
|
break;
|
||||||
str.replace(pos, ori_substr.size(), new_substr);
|
str.replace(pos, ori_substr.size(), new_substr);
|
||||||
pos += new_substr.size();
|
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')
|
// identify a line in a string, by delim (ex. '\n')
|
||||||
// delete this line from the string
|
// delete this line from the string (and the following nl sequence characters)
|
||||||
// and return the deleted line
|
// and return the deleted line (without the followinf nl sequence characters)
|
||||||
std::string
|
std::string
|
||||||
extract_line(std::string & str, size_t pos, std::string delim)
|
extract_line(std::string & str, size_t pos, std::string delim)
|
||||||
{
|
{
|
||||||
@@ -212,18 +224,18 @@ std::string
|
|||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
begin = str.rfind(delim, pos);
|
begin = str.rfind(delim, pos);
|
||||||
if (begin == std::string::npos)
|
if (begin == NPOS)
|
||||||
begin = 0;
|
begin = 0;
|
||||||
else
|
else if (begin < pos)
|
||||||
begin += delim.size();
|
begin += delim.size();
|
||||||
|
|
||||||
end = str.find(delim, pos);
|
end = str.find(delim, pos);
|
||||||
len = end;
|
len = end;
|
||||||
if (end != std::string::npos)
|
if (end != NPOS)
|
||||||
len = end - begin;
|
len = end - begin;
|
||||||
|
|
||||||
del_str = str.substr(begin, len);
|
del_str = str.substr(begin, len);
|
||||||
str.erase(begin, len);
|
str.erase(begin, len + delim.size());
|
||||||
return del_str;
|
return del_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,13 +268,13 @@ size_t
|
|||||||
for (it = list.begin(); it != it_end; it++)
|
for (it = list.begin(); it != it_end; it++)
|
||||||
{
|
{
|
||||||
pos = (*it).find(':');
|
pos = (*it).find(':');
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
{
|
{
|
||||||
err++;
|
err++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
key = (*it).substr(0, pos);
|
key = (*it).substr(0, pos);
|
||||||
if ( key.find(' ') != std::string::npos )
|
if ( key.find(' ') != NPOS )
|
||||||
{
|
{
|
||||||
err++;
|
err++;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -6,16 +6,34 @@
|
|||||||
# include <map>
|
# include <map>
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <sstream>
|
# include <sstream>
|
||||||
# include <cstdlib> // atoi
|
# include <cstdlib> // strtol, strtoul
|
||||||
|
# include <climits> // LONG_MAX
|
||||||
|
# include <cerrno> // errno
|
||||||
# include <sys/stat.h> // stat()
|
# include <sys/stat.h> // stat()
|
||||||
# include <cctype> // tolower
|
# include <cctype> // tolower
|
||||||
# include <algorithm> // transform
|
# include <algorithm> // transform
|
||||||
# include <cstdio> // fflush
|
# include <cstdio> // perror, fflush
|
||||||
# include "colors.h"
|
# include <unistd.h> // close, access
|
||||||
|
# include "colors.h" // for debug print_special
|
||||||
|
|
||||||
# define CR "\r"
|
# define CR "\r"
|
||||||
# define LF "\n"
|
# define LF "\n"
|
||||||
# define CRLF CR LF
|
# 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
|
enum http_method
|
||||||
{
|
{
|
||||||
@@ -45,7 +63,8 @@ std::string itos(int n);
|
|||||||
std::string trim(std::string str, char del);
|
std::string trim(std::string str, char del);
|
||||||
http_method str_to_http_method(std::string &str);
|
http_method str_to_http_method(std::string &str);
|
||||||
std::string http_methods_to_str(unsigned int methods);
|
std::string http_methods_to_str(unsigned int methods);
|
||||||
int path_is_valid(std::string path);
|
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);
|
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 str_tolower(std::string str);
|
||||||
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
||||||
|
|||||||
@@ -23,14 +23,16 @@
|
|||||||
# include <algorithm> // find
|
# include <algorithm> // find
|
||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <cstdio> // perror, remove
|
# include <cstdio> // perror, remove
|
||||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
# include <cstdlib> // strtol, strtoul
|
||||||
# include <dirent.h> // opendir()
|
# include <dirent.h> // opendir()
|
||||||
|
# include <locale> // isalpha, local
|
||||||
|
|
||||||
# include "Client.hpp"
|
# include "Client.hpp"
|
||||||
# include "ServerConfig.hpp"
|
# include "ServerConfig.hpp"
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
# include "http_status.hpp"
|
# include "http_status.hpp"
|
||||||
# include "autoindex.hpp"
|
# include "autoindex.hpp"
|
||||||
|
# include "colors.h"
|
||||||
|
|
||||||
extern bool g_run;
|
extern bool g_run;
|
||||||
extern int g_last_signal;
|
extern int g_last_signal;
|
||||||
@@ -76,29 +78,34 @@ class Webserv
|
|||||||
void _request(Client *client);
|
void _request(Client *client);
|
||||||
int _read_request(Client *client);
|
int _read_request(Client *client);
|
||||||
// response.cpp
|
// response.cpp
|
||||||
void _response(Client *client);
|
void _response(Client *client);
|
||||||
int _send_response(Client *client);
|
int _send_response(Client *client);
|
||||||
void _append_base_headers(Client *client);
|
void _append_base_headers(Client *client);
|
||||||
void _construct_response(Client *client);
|
void _construct_response(Client *client);
|
||||||
void _process_method(Client *client);
|
void _process_method(Client *client, std::string &path);
|
||||||
void _insert_status_line(Client *client);
|
void _insert_status_line(Client *client);
|
||||||
void _error_html_response(Client *client);
|
void _error_html_response(Client *client);
|
||||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
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[]
|
// 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;
|
// const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||||
std::string _determine_file_extension(const std::string &path) const;
|
std::string _determine_file_extension(const std::string &path) const;
|
||||||
// method_get.cpp
|
// 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 _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
|
// method_post.cpp
|
||||||
void _post(Client *client);
|
void _post(Client *client, const std::string &path);
|
||||||
void _post_file(Client *client, const std::string &path);
|
void _post_file(Client *client, const std::string &path);
|
||||||
// method_delete.cpp
|
// method_delete.cpp
|
||||||
void _delete(Client *client);
|
void _delete(Client *client, const std::string &path);
|
||||||
void _delete_file(Client *client, const std::string &path);
|
void _delete_file(Client *client, const std::string &path);
|
||||||
// cgi_script.cpp
|
// cgi_script.cpp
|
||||||
bool _is_cgi(Client *client);
|
bool _is_cgi(Client *client, std::string path);
|
||||||
|
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
|
||||||
std::string _exec_cgi(Client *client);
|
std::string _exec_cgi(Client *client);
|
||||||
char** _set_env(Client *client);
|
char** _set_env(Client *client);
|
||||||
char* _dup_env(std::string var, std::string val);
|
char* _dup_env(std::string var, std::string val);
|
||||||
@@ -107,6 +114,8 @@ class Webserv
|
|||||||
void _check_script_output(Client *client, std::string & output);
|
void _check_script_output(Client *client, std::string & output);
|
||||||
void _check_script_status(Client *client, std::string & output);
|
void _check_script_status(Client *client, std::string & output);
|
||||||
void _check_script_fields(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
|
// epoll_update.cpp
|
||||||
int _epoll_update(int fd, uint32_t events, int op);
|
int _epoll_update(int fd, uint32_t events, int op);
|
||||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||||
@@ -116,12 +125,16 @@ class Webserv
|
|||||||
void _close_client(int fd);
|
void _close_client(int fd);
|
||||||
void _close_all_clients();
|
void _close_all_clients();
|
||||||
void _close_all_listen_sockets();
|
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
|
// init.cpp
|
||||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||||
void _listen(int socket_fd, unsigned int max_connections);
|
void _listen(int socket_fd, unsigned int max_connections);
|
||||||
void _init_http_status_map();
|
void _init_http_status_map();
|
||||||
void _init_mime_types_map();
|
void _init_mime_types_map();
|
||||||
|
// timeout.cpp
|
||||||
|
void _timeout();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
#ifndef AUTOINDEX_HPP
|
#ifndef AUTOINDEX_HPP
|
||||||
# define 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 \
|
# define AUTOINDEX_START \
|
||||||
"<!DOCTYPE html>"\
|
"<!DOCTYPE html>"\
|
||||||
"<html>"\
|
"<html>"\
|
||||||
@@ -27,6 +25,4 @@
|
|||||||
"</body>"\
|
"</body>"\
|
||||||
"</html>"
|
"</html>"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,16 +1,67 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
#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
|
std::string script_path;
|
||||||
if (client->fill_script_path("/cgi-bin/php-cgi"))
|
size_t file_type;
|
||||||
return true;
|
size_t file_mode;
|
||||||
if (client->fill_script_path("/cgi-bin/cgi_cpp.cgi"))
|
size_t pos = 0;
|
||||||
return true;
|
|
||||||
|
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;
|
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)
|
std::string Webserv::_exec_cgi(Client *client)
|
||||||
{
|
{
|
||||||
char** env;
|
char** env;
|
||||||
@@ -89,8 +140,7 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
std::string body = client->get_rq_body();
|
std::string body = client->get_rq_body();
|
||||||
int fd_in[2];
|
int fd_in[2];
|
||||||
int fd_out[2];
|
int fd_out[2];
|
||||||
int save_in = dup(STDIN_FILENO);
|
std::string path;
|
||||||
int save_out = dup(STDOUT_FILENO);
|
|
||||||
|
|
||||||
pipe(fd_in);
|
pipe(fd_in);
|
||||||
pipe(fd_out);
|
pipe(fd_out);
|
||||||
@@ -98,20 +148,22 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
std::cerr << "fork crashed" << std::endl;
|
std::cerr << "fork crashed" << std::endl;
|
||||||
else if (pid == 0)
|
else if (pid == 0) // child
|
||||||
{
|
{
|
||||||
close(FD_WR_TO_CHLD);
|
close(FD_WR_TO_CHLD);
|
||||||
close(FD_RD_FR_CHLD);
|
close(FD_RD_FR_CHLD);
|
||||||
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
||||||
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
||||||
// DEBUG
|
path = "." + client->get_rq_script_path();
|
||||||
std::cerr << "execve:\n";
|
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
|
||||||
execve(client->get_rq_script_path().c_str(), nll, env);
|
execve(path.c_str(), nll, env);
|
||||||
// for tests execve crash :
|
// for tests execve crash :
|
||||||
//execve("wrong", nll, env);
|
//execve("wrong", nll, env);
|
||||||
std::cerr << "execve crashed.\n";
|
std::cerr << "execve crashed.\n";
|
||||||
|
|
||||||
|
// TODO HUGO : check errno
|
||||||
}
|
}
|
||||||
else
|
else //parent
|
||||||
{
|
{
|
||||||
close(FD_RD_FR_PRNT);
|
close(FD_RD_FR_PRNT);
|
||||||
close(FD_WR_TO_PRNT);
|
close(FD_WR_TO_PRNT);
|
||||||
@@ -130,8 +182,6 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
if (script_output.empty())
|
if (script_output.empty())
|
||||||
script_output = "Status: 500\r\n\r\n";
|
script_output = "Status: 500\r\n\r\n";
|
||||||
|
|
||||||
dup2(save_in, STDIN_FILENO);
|
|
||||||
dup2(save_out, STDOUT_FILENO);
|
|
||||||
return script_output;
|
return script_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +189,8 @@ void Webserv::_check_script_output(Client *client, std::string & output)
|
|||||||
{
|
{
|
||||||
_check_script_status(client, output);
|
_check_script_status(client, output);
|
||||||
_check_script_fields(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_empty_lines(client, output);
|
||||||
// _check_script_space_colons(client, output);
|
// _check_script_space_colons(client, output);
|
||||||
// _check_script_new_lines(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;
|
int status_pos;
|
||||||
|
|
||||||
pos = output.find("Status:");
|
pos = output.find("Status:");
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
{
|
{
|
||||||
status_pos = pos + std::string("Status:").size();
|
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);
|
::extract_line(output, pos, CRLF);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -172,13 +224,13 @@ void Webserv::_check_script_fields(Client *client, std::string & output)
|
|||||||
// put server headers in map
|
// put server headers in map
|
||||||
tmp = client->response;
|
tmp = client->response;
|
||||||
pos = tmp.find(CRLF CRLF);
|
pos = tmp.find(CRLF CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
tmp.erase(pos);
|
tmp.erase(pos);
|
||||||
::parse_http_headers(tmp, srv_fld);
|
::parse_http_headers(tmp, srv_fld);
|
||||||
// put script headers in map
|
// put script headers in map
|
||||||
tmp = output;
|
tmp = output;
|
||||||
pos = tmp.find(CRLF CRLF);
|
pos = tmp.find(CRLF CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
tmp.erase(pos);
|
tmp.erase(pos);
|
||||||
::parse_http_headers(tmp, scr_fld);
|
::parse_http_headers(tmp, scr_fld);
|
||||||
// compare both map to supress duplicates
|
// 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();
|
_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 S403 "403 Forbidden"
|
||||||
# define S404 "404 Not Found"
|
# define S404 "404 Not Found"
|
||||||
# define S405 "405 Method Not Allowed"
|
# define S405 "405 Method Not Allowed"
|
||||||
|
# define S408 "408 Request Timeout"
|
||||||
# define S413 "413 Content Too Large"
|
# define S413 "413 Content Too Large"
|
||||||
|
# define S415 "415 Unsupported Media Type"
|
||||||
|
|
||||||
# define S500 "500 Internal Server Error"
|
# define S500 "500 Internal Server Error"
|
||||||
# define S501 "501 Not Implemented"
|
# define S501 "501 Not Implemented"
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
|||||||
//
|
//
|
||||||
// HUGO ADD END
|
// HUGO ADD END
|
||||||
|
|
||||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||||
_listen(new_socket.fd, 512); // 512 arbitrary
|
_listen(new_socket.fd, 42); // 42 arbitrary
|
||||||
|
|
||||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||||
throw std::runtime_error("Socket init");
|
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()
|
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
|
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||||
*/
|
*/
|
||||||
typedef std::map<int, std::string>::value_type status_pair;
|
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(403, S403));
|
||||||
_http_status.insert(status_pair(404, S404));
|
_http_status.insert(status_pair(404, S404));
|
||||||
_http_status.insert(status_pair(405, S405));
|
_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(413, S413));
|
||||||
|
|
||||||
_http_status.insert(status_pair(500, S500));
|
_http_status.insert(status_pair(500, S500));
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
#include "Webserv.hpp"
|
||||||
|
|
||||||
void Webserv::_delete(Client *client)
|
void Webserv::_delete(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
WIP
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_rq_abs_path();
|
|
||||||
path.insert(0, client->assigned_location->root);
|
|
||||||
|
|
||||||
/* CGI Here ? */
|
|
||||||
|
|
||||||
_delete_file(client, path);
|
_delete_file(client, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,42 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
#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
|
// const?
|
||||||
if no index and autoindex, server that
|
void Webserv::_get(Client *client, std::string &path)
|
||||||
if file, server that!
|
{
|
||||||
|
|
||||||
Where does cgi fit in in all this ???
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
// Index/Autoindex block
|
||||||
std::string path = client->get_rq_abs_path();
|
if (eval_file_type(path) == IS_DIR)
|
||||||
|
|
||||||
// this might not be the best thing, a voir
|
|
||||||
path.insert(0, client->assigned_location->root);
|
|
||||||
|
|
||||||
std::cerr << "path = " << path << "\n";
|
|
||||||
|
|
||||||
// path = root + location.path
|
|
||||||
// we will tack on an index if there is a valid one
|
|
||||||
// or autoindex if allowed
|
|
||||||
// or let _get_file sort out the error otherwise.
|
|
||||||
|
|
||||||
if (path_is_valid(path) == 1)
|
|
||||||
{
|
{
|
||||||
// std::cout << "path is valid\n";
|
|
||||||
if (path[path.size() - 1] != '/')
|
if (path[path.size() - 1] != '/')
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||||
{
|
{
|
||||||
// std::cout << "location path: " << client->assigned_location->path << '\n';
|
if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE)
|
||||||
// std::cout << "location index: " << client->assigned_location->index[i] << '\n';
|
|
||||||
// std::cout << "path with index: " << path + assigned_location->index[i] << '\n';
|
|
||||||
if (path_is_valid(path + client->assigned_location->index[i]) == 2)
|
|
||||||
{
|
{
|
||||||
// std::cout << "found a valid index\n";
|
|
||||||
path.append(client->assigned_location->index[i]);
|
path.append(client->assigned_location->index[i]);
|
||||||
_get_file(client, path);
|
_get_file(client, path);
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (client->assigned_location->autoindex == true)
|
if (client->assigned_location->autoindex == true)
|
||||||
{
|
|
||||||
_autoindex(client, path);
|
_autoindex(client, path);
|
||||||
return ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// else
|
else
|
||||||
// _get_file(client, path);
|
_get_file(client, path);
|
||||||
// what about cgi ???
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TMP HUGO
|
|
||||||
//
|
|
||||||
std::string script_output;
|
|
||||||
if (_is_cgi(client))
|
|
||||||
{
|
|
||||||
script_output = _exec_cgi(client);
|
|
||||||
_check_script_output(client, script_output);
|
|
||||||
client->response += script_output;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// END TMP HUGO
|
|
||||||
|
|
||||||
_get_file(client, path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
# 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::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
|
|
||||||
std::cout << "made it to get_file\n";
|
std::cout << "_get_file()\n";
|
||||||
|
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
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...
|
// const?
|
||||||
// def can improve but works for now...
|
void Webserv::_autoindex(Client *client, const std::string &path)
|
||||||
//void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &path)
|
|
||||||
void Webserv::_autoindex(Client *client, std::string &path)
|
|
||||||
{
|
{
|
||||||
// std::cout << "made it to _autoindex\n";
|
std::cout << "_autoindex()\n";
|
||||||
|
|
||||||
(void)path;
|
|
||||||
std::string dir_list;
|
std::string dir_list;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
|
|
||||||
// std::cout << "location root: " << client->assigned_location->root << " location path: "
|
// std::cout << "location root: " << client->assigned_location->root << " location path: " << client->assigned_location->path << '\n';
|
||||||
// << client->assigned_location->path << '\n';
|
|
||||||
|
|
||||||
// if ((dir = opendir (path.c_str())) != NULL)
|
// std::cout << "Path in auto is: " << path << '\n';
|
||||||
if ((dir = opendir ((client->assigned_location->root + client->assigned_location->path).c_str())) != NULL)
|
if ( (dir = opendir(path.c_str()) ) != NULL)
|
||||||
{
|
{
|
||||||
/* print all the files and directories within directory */
|
|
||||||
dir_list.append(AUTOINDEX_START);
|
dir_list.append(AUTOINDEX_START);
|
||||||
dir_list.append(client->assigned_location->path);
|
dir_list.append(path);
|
||||||
dir_list.append(AUTOINDEX_MID1);
|
dir_list.append(AUTOINDEX_MID1);
|
||||||
dir_list.append(client->assigned_location->path);
|
dir_list.append(path);
|
||||||
dir_list.append(AUTOINDEX_MID2);
|
dir_list.append(AUTOINDEX_MID2);
|
||||||
|
/* print all the files and directories within directory */
|
||||||
while ((ent = readdir (dir)) != NULL)
|
while ((ent = readdir (dir)) != NULL)
|
||||||
{
|
{
|
||||||
|
// std::cout << "ent: " << ent->d_name << '\n';
|
||||||
if (strcmp(".", ent->d_name) == 0)
|
if (strcmp(".", ent->d_name) == 0)
|
||||||
continue ;
|
continue ;
|
||||||
dir_list.append("<a href=\"");
|
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(ent->d_name);
|
||||||
dir_list.append("\">");
|
dir_list.append("\">");
|
||||||
dir_list.append(ent->d_name);
|
dir_list.append(ent->d_name);
|
||||||
@@ -183,9 +148,8 @@ void Webserv::_autoindex(Client *client, std::string &path)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// in theory not possible cuz we already checked...
|
// in theory not possible cuz we already checked...
|
||||||
/* could not open directory */
|
std::cerr << "could not open dir\n";
|
||||||
// perror ("");
|
// throw?
|
||||||
std::cout << "could not open dir\n";
|
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,12 @@
|
|||||||
#include "Webserv.hpp"
|
#include "Webserv.hpp"
|
||||||
|
|
||||||
|
|
||||||
void Webserv::_post(Client *client)
|
void Webserv::_post(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
WIP
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_rq_abs_path();
|
|
||||||
path.insert(0, client->assigned_location->root);
|
|
||||||
|
|
||||||
/* CGI Here ? */
|
|
||||||
|
|
||||||
_post_file(client, path);
|
_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)
|
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);
|
_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];
|
char buf[BUFSIZE];
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
std::cerr << "call recv()" << "\n" ;
|
|
||||||
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
||||||
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
std::perror("err recv()");
|
std::perror("err recv()");
|
||||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
|
||||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
|
||||||
return READ_CLOSE;
|
return READ_CLOSE;
|
||||||
}
|
}
|
||||||
if (ret == 0)
|
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
|
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||||
return READ_CLOSE;
|
return READ_CLOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->raw_request.append(buf, ret);
|
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->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;
|
if (client->get_rq_headers("Content-Type").empty()
|
||||||
client->parse_request_headers(_servers);
|
&& client->get_rq_headers("Content-Length").empty()
|
||||||
std::cerr << client->get_rq_method_str() << " " << client->get_rq_uri() << " " << client->get_rq_version() << "\n"; // DEBUG
|
&& client->get_rq_headers("Transfer-Encoding").empty())
|
||||||
if (client->status)
|
return READ_COMPLETE; // No body case
|
||||||
return READ_COMPLETE;
|
|
||||||
|
|
||||||
if (client->get_rq_headers("Content-Type").empty() && client->get_rq_headers("Content-Length").empty()) // No body case
|
|
||||||
return READ_COMPLETE;
|
|
||||||
}
|
}
|
||||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
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;
|
client->status = 413;
|
||||||
return READ_COMPLETE;
|
return READ_COMPLETE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (client->header_complete)
|
if (client->header_complete)
|
||||||
{
|
{
|
||||||
client->read_body_size += ret;
|
// client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now
|
||||||
if (client->read_body_size > client->assigned_server->client_body_limit)
|
client->parse_request_body();
|
||||||
{
|
if (client->status || client->body_complete)
|
||||||
client->status = 413;
|
|
||||||
return READ_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;
|
return READ_IN_PROGRESS;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void Webserv::_response(Client *client)
|
|||||||
}
|
}
|
||||||
else if (ret == SEND_COMPLETE)
|
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());
|
_close_client(client->get_cl_fd());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -44,6 +44,8 @@ int Webserv::_send_response(Client *client)
|
|||||||
if (client->status >= 400)
|
if (client->status >= 400)
|
||||||
_error_html_response(client);
|
_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
|
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
||||||
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
@@ -67,26 +69,42 @@ void Webserv::_append_base_headers(Client *client)
|
|||||||
client->response.append("Connection: keep-alive" CRLF);
|
client->response.append("Connection: keep-alive" CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO HUGO : wip
|
||||||
void Webserv::_construct_response(Client *client)
|
void Webserv::_construct_response(Client *client)
|
||||||
{
|
{
|
||||||
/* Switch between normal behavior or CGI here ?
|
std::string path;
|
||||||
maybe better than in _get(), _post(), ...*/
|
std::string script_output;
|
||||||
_process_method(client);
|
|
||||||
|
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 = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug
|
||||||
std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
|
||||||
|
|
||||||
switch (client->get_rq_method())
|
switch (client->get_rq_method())
|
||||||
{
|
{
|
||||||
case (GET):
|
case (GET):
|
||||||
_get(client); break;
|
_get(client, path); break;
|
||||||
case (POST):
|
case (POST):
|
||||||
_post(client); break;
|
_post(client, path); break;
|
||||||
case (DELETE):
|
case (DELETE):
|
||||||
_delete(client); break;
|
_delete(client, path); break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -124,7 +142,7 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
client->response.append(mime_type);
|
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("; charset=UTF-8");
|
||||||
}
|
}
|
||||||
client->response.append(CRLF);
|
client->response.append(CRLF);
|
||||||
@@ -169,67 +187,10 @@ ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig
|
|||||||
return (&(*default_server));
|
return (&(*default_server));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const?
|
||||||
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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary Global Scope. Probably move to Client in the future.
|
// Temporary Global Scope. Probably move to Client in the future.
|
||||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path)
|
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 ***
|
/* RULES ***
|
||||||
|
|
||||||
If a path coresponds exactly to a location, use that one
|
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
|
most correct means the most precise branch that is still above
|
||||||
the point we are aiming for
|
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++)
|
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||||
{
|
{
|
||||||
std::cout << it->path << " -- ";
|
if (it->path.size() > uri.size())
|
||||||
// std::cout << it->path[it->path.size() - 1] << " ";
|
|
||||||
// it->path.size() -1 only when path ends in / because
|
|
||||||
// if path doesn't end in / then we are looking for a file
|
|
||||||
// meaning all it->paths that end in / are wrong if they >=
|
|
||||||
// if (it->path[it->path.size() - 1] == '/' ? it->path.size() - 1 > path.size() : it->path.size() > path.size())
|
|
||||||
if (path[path.size() - 1] == '/' ? it->path.size() > path.size() : it->path.size() - 1 > path.size())
|
|
||||||
{
|
|
||||||
std::cout << "skipping this one\n";
|
|
||||||
continue ;
|
continue ;
|
||||||
}
|
|
||||||
|
|
||||||
// if (it->path.size() > path.size()) // Warning : il faut aussi prendre en compte l'éventuel "/" final
|
if (uri.compare(0, it->path.size(), it->path) == 0)
|
||||||
// continue;
|
|
||||||
|
|
||||||
|
|
||||||
// IS THERE A WAY TO SIMPLIFY THIS LOGIC ???
|
|
||||||
|
|
||||||
|
|
||||||
// if (it->path[it->path.size() - 1] == '/')
|
|
||||||
if (path[path.size() - 1] == '/')
|
|
||||||
{
|
{
|
||||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
if (it->path.size() == uri.size())
|
||||||
{
|
return (&(*it));
|
||||||
std::cout << "checking with last /\n";
|
else if (uri[it->path.size()] == '/')
|
||||||
if (it->path.size() == path.size())
|
return (&(*it));
|
||||||
{
|
// this works cuz only ever looking for a / burried in a longer path
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return (&(server.locations.back()));
|
||||||
|
|
||||||
|
|
||||||
// /test/mdr
|
// /test/mdr
|
||||||
// /test/mdr/
|
// /test/mdr/
|
||||||
// /test/mdrBST
|
// /test/mdrBST
|
||||||
@@ -313,81 +240,20 @@ if no path coresponds then use the most correct one
|
|||||||
/test/test_deepei
|
/test/test_deepei
|
||||||
/test/test_deepei/
|
/test/test_deepei/
|
||||||
/test/test_deeperi
|
/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
|
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||||
{
|
{
|
||||||
size_t dot_pos = path.rfind(".");
|
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 ( path.substr(dot_pos + 1) );
|
||||||
return (std::string(""));
|
return (std::string(""));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,36 +20,41 @@ void Webserv::run()
|
|||||||
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
||||||
if (nfds == -1)
|
if (nfds == -1)
|
||||||
{
|
{
|
||||||
|
int errno_copy = errno;
|
||||||
std::perror("err epoll_wait()");
|
std::perror("err epoll_wait()");
|
||||||
throw std::runtime_error("Epoll wait");
|
if (errno_copy == EINTR)
|
||||||
}
|
g_run = false;
|
||||||
else if (nfds == 0)
|
else
|
||||||
{
|
throw std::runtime_error("Epoll wait");
|
||||||
if (!_clients.empty())
|
|
||||||
{
|
|
||||||
std::cerr << "Timeout " << TIMEOUT << "ms\n";
|
|
||||||
_close_all_clients();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (nfds == 0 && !_clients.empty())
|
||||||
|
_timeout();
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < nfds)
|
while (i < nfds)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
// TODO : handle EPOLLERR and EPOLLHUP
|
|
||||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
if (it_socket != _listen_sockets.end())
|
||||||
_accept_connection(*it_socket);
|
{
|
||||||
else if (events[i].events & EPOLLIN)
|
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
_handle_epoll_error_lsocket(events[i].events, it_socket);
|
||||||
else if (events[i].events & EPOLLOUT)
|
else if (events[i].events & EPOLLIN)
|
||||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
_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;
|
++i;
|
||||||
if (!g_run)
|
if (!g_run)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (const std::bad_alloc& e)
|
catch (const std::bad_alloc& e) {
|
||||||
{
|
|
||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
_close_all_clients();
|
_close_all_clients();
|
||||||
/* Swap to free the memory
|
/* Swap to free the memory
|
||||||
@@ -57,8 +62,7 @@ void Webserv::run()
|
|||||||
std::vector<Client>().swap(_clients);
|
std::vector<Client>().swap(_clients);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e) {
|
||||||
{
|
|
||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
++i;
|
++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>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title></title>
|
<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>
|
</head>
|
||||||
<body>
|
<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">
|
<input type="submit" value="submit">
|
||||||
</form>
|
</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>
|
</body>
|
||||||
</html>
|
</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