Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6f63931ad | ||
|
|
d663a4c7e6 | ||
|
|
be499328f6 | ||
|
|
41db4fc12b | ||
|
|
a20a5eff27 | ||
|
|
db4c7468cc | ||
|
|
b0949615c8 | ||
|
|
285f2d049f | ||
|
|
defb2ada61 | ||
|
|
058701f657 | ||
|
|
f17bc9fa58 | ||
|
|
c7bbf29a1b | ||
|
|
b44acafefe | ||
|
|
cade79c37f | ||
|
|
52824f30bd | ||
|
|
2a70c6b26d | ||
|
|
71c07140e2 | ||
|
|
de09c2357c | ||
|
|
c225063361 | ||
|
|
400efbe720 | ||
|
|
f7c0ff1a8a | ||
|
|
3a58b5d921 | ||
|
|
a1fff0f8c2 | ||
|
|
ad2b5a629a | ||
|
|
ff77dfd298 | ||
|
|
ab0bc2c4c0 | ||
|
|
08f6929db9 | ||
|
|
c32fc2c8a2 | ||
|
|
27b4f96618 | ||
|
|
0b51d13f13 | ||
|
|
1d67e6988d | ||
|
|
11f71ea74f | ||
|
|
9a379c835d | ||
|
|
360c27df5f | ||
|
|
c7905ebd19 | ||
|
|
17230ccc42 | ||
|
|
69c1a6f6bf | ||
|
|
86f7740984 | ||
|
|
58f67bf42e | ||
|
|
f7f3e42b15 | ||
|
|
8e90221058 | ||
|
|
f393aa46bf | ||
|
|
1eb989a3fd | ||
|
|
56f4cf7e15 | ||
|
|
482a38952e | ||
|
|
ef034a356b | ||
|
|
53a548e314 | ||
|
|
3dad938e3c | ||
|
|
1d3cdef7a6 | ||
|
|
97c90236b9 | ||
|
|
643b09c4f7 | ||
|
|
a44b9b493a | ||
|
|
ae9a9b37f1 | ||
|
|
e1a68bc3e4 | ||
|
|
7fdc81f5f4 | ||
|
|
f10931042f | ||
|
|
972f52ebc8 | ||
|
|
0a72778c8c | ||
|
|
da1f4b6e37 | ||
|
|
78c3ffa456 | ||
|
|
988e15c738 | ||
|
|
02cfdf43bc | ||
|
|
f87024c32f | ||
|
|
9ee7205b95 | ||
|
|
cf69168a84 | ||
|
|
63365a9067 | ||
|
|
94852babc6 | ||
|
|
f777441edf | ||
|
|
1ccf61bc68 | ||
|
|
7ecfc22c7b | ||
|
|
e0fd743b5b | ||
|
|
4ab099ee4d | ||
|
|
4870b2c05d | ||
|
|
8ec1353723 | ||
|
|
4b1baca126 | ||
|
|
63ff42b57d | ||
|
|
08928c46fc | ||
|
|
6f171ec07b | ||
|
|
b0df37df60 | ||
|
|
05c7faf1f3 | ||
|
|
79bdc1ecbf | ||
|
|
6f0c3b4d6d | ||
|
|
f7dc5ccde4 | ||
|
|
914d4cb0c0 | ||
|
|
e596d8f093 | ||
|
|
9ac14aa1aa | ||
|
|
3102253092 | ||
|
|
f7e6b61811 | ||
|
|
fce1bcbece | ||
|
|
a9ada4cb28 | ||
|
|
0026106bf6 | ||
|
|
ca8427262d | ||
|
|
3d46505411 | ||
|
|
b0c524a8bd | ||
|
|
fef26aee5b | ||
|
|
6f5b28dd93 | ||
|
|
164f717e00 | ||
|
|
83536ee8ad | ||
|
|
6791b13a52 | ||
|
|
21283e2493 | ||
|
|
6c0dd771c3 | ||
|
|
c331b69600 | ||
|
|
41b5e57800 | ||
|
|
d79281751a | ||
|
|
8f167d85f3 | ||
|
|
b30a3a1407 | ||
|
|
5aabeb6b46 | ||
|
|
f252887d53 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,3 +20,5 @@ ubuntu_cgi_tester
|
||||
webserv
|
||||
!**/webserv/
|
||||
*.log
|
||||
|
||||
large.jpg
|
||||
|
||||
42
Makefile
42
Makefile
@@ -5,23 +5,28 @@ CXX = c++
|
||||
CXXFLAGS = -Wall -Wextra #-Werror
|
||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||
CXXFLAGS += -std=c++98
|
||||
CXXFLAGS += -g
|
||||
CXXFLAGS += -g3
|
||||
CXXFLAGS += -MMD -MP #header dependencie
|
||||
#CXXFLAGS += -O3
|
||||
|
||||
VPATH = $(SRCS_D)
|
||||
|
||||
HEADERS_D = srcs
|
||||
HEADERS_D = srcs \
|
||||
srcs/webserv \
|
||||
srcs/config
|
||||
|
||||
SRCS_D = srcs \
|
||||
srcs/webserv
|
||||
srcs/webserv \
|
||||
srcs/config
|
||||
|
||||
SRCS = main.cpp \
|
||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||
accept.cpp request.cpp response.cpp \
|
||||
run_loop.cpp \
|
||||
ConfigParser.cpp \
|
||||
ConfigParserUtils.cpp \
|
||||
ConfigParserPost.cpp \
|
||||
method_get.cpp method_post.cpp method_delete.cpp \
|
||||
run_loop.cpp timeout.cpp \
|
||||
parser.cpp \
|
||||
extraConfig.cpp \
|
||||
postProcessing.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
@@ -38,12 +43,14 @@ all: $(NAME)
|
||||
|
||||
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
|
||||
|
||||
$(OBJS_D):
|
||||
mkdir $@
|
||||
|
||||
$(NAME): $(OBJS)
|
||||
$(CXX) $^ -o $(NAME)
|
||||
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJS_D)
|
||||
@@ -57,3 +64,24 @@ re: fclean all
|
||||
|
||||
-include $(DEPS) # header dependencie
|
||||
|
||||
.SILENT:
|
||||
|
||||
# ------------------
|
||||
# ----- COLORS -----
|
||||
# ------------------
|
||||
|
||||
_GREY=$ \033[30m
|
||||
_RED=$ \033[31m
|
||||
_GREEN=$ \033[32m
|
||||
_YELLOW=$ \033[33m
|
||||
_BLUE=$ \033[34m
|
||||
_PURPLE=$ \033[35m
|
||||
_CYAN=$ \033[36m
|
||||
_WHITE=$ \033[37m
|
||||
_END=$ \033[0m
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
203
README.md
203
README.md
@@ -1,18 +1,17 @@
|
||||
|
||||
---
|
||||
## questions
|
||||
- mettre les fonctions specifiques a la requete, dans la class client ?
|
||||
- où est-ce que j'inclus le cgi ?
|
||||
- est-ce que le cgi est appellé par `/cgi-bin` ?
|
||||
- non
|
||||
- g rajouté `char ** env` dans client.cpp
|
||||
- non
|
||||
- ajouter un champ "message body" dans client ?
|
||||
- non
|
||||
- comment organiser la creation du message reponse (cgi ou pas) et des headers ?
|
||||
- comment je gere le path `/cgi-bin/` avec la suite ?
|
||||
- qu'est-ce que le cgi renvoit comme headers ? comment c'est géré ?
|
||||
- https://www.rfc-editor.org/rfc/rfc3875
|
||||
## work together
|
||||
|
||||
#### next commit
|
||||
|
||||
#### questions
|
||||
- how should we handle a wrong url like `http://localhost/cgi-bin/wrong.phpp/good.php` ?
|
||||
- do we serve `./srcs/cgi-bin/good.php` ?
|
||||
- or do we return 404 "not found" ?
|
||||
-> - for now, execve would crash, but that doesn't produce a 404 error, rather a 500, is it bad ?
|
||||
- could we use errno after execve to choose an appropriate http error ? subject says : "Checking the value of errno is strictly forbidden after a read or a write operation"
|
||||
- if a url has a file with extension, but it's not a cgi extension, is it necessary to look further ?
|
||||
- ex. `http://localhost/file.php/file.py` for `cgi_ext py;` ?
|
||||
- the response page is received long after the cgi-script is done, why ?
|
||||
|
||||
---
|
||||
## man
|
||||
@@ -88,16 +87,142 @@
|
||||
- [ ] Your server must be able to listen to multiple ports (see Configuration file)
|
||||
- [ ] Your server should never die.
|
||||
|
||||
---
|
||||
## cgi rfc
|
||||
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||
|
||||
#### output cgi script :
|
||||
|
||||
#### summary :
|
||||
- the cgi-script will send back at least one header field followed by an empty line
|
||||
- this header field will be one of three :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
- the cgi-script may send back more header fields
|
||||
- the server must check and modify few things :
|
||||
- there is no field duplicate (resolve conflicts)
|
||||
- there is no space between filed name and ":"
|
||||
- change all the '\n' by '\r\n'
|
||||
- if no Location field && no Status field -> status code = 200
|
||||
- handle Location field, either :
|
||||
- local : start with '/' --> rerun the request with new uri
|
||||
- client : start with '<scheme>:' --> send back status code 302
|
||||
- there is at least one header field followed by '\r\n\r\n' :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
- if status field, change server status for this one
|
||||
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||
|
||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||
|
||||
- The server [...] receives the request from the client
|
||||
- selects a CGI script to handle the request
|
||||
- converts the client request to a CGI request
|
||||
- executes the script and converts the CGI response into a response for the client
|
||||
|
||||
[3.3: script uri](https://www.rfc-editor.org/rfc/rfc3875#section-3.3)
|
||||
|
||||
- the 'Script-URI' [...] MUST have the property that if the client had accessed this URI instead, then the script would have been executed
|
||||
|
||||
[4: how the server prepare the cgi requests](https://www.rfc-editor.org/rfc/rfc3875#section-4)
|
||||
|
||||
- the cgi receives 2 differents set of informations :
|
||||
- the request meta-variables (in UNIX, by env variables)
|
||||
- and the message-body
|
||||
|
||||
[4.1: request meta-variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||
|
||||
- a header field that spans multiple lines MUST be merged onto a single line
|
||||
|
||||
[4.2: request message-body](https://www.rfc-editor.org/rfc/rfc3875#section-4.2)
|
||||
|
||||
- unless defined otherwise, the script access request data by reading stdin
|
||||
|
||||
[6: how the response from the script is returned to the server](https://www.rfc-editor.org/rfc/rfc3875#section-6)
|
||||
|
||||
- The response comprises 2 parts, separated by a blank line :
|
||||
- a message-header
|
||||
- and a message-body
|
||||
- The message-header contains one or more header fields
|
||||
- The body may be NULL
|
||||
|
||||
[6.2: responses types](https://www.rfc-editor.org/rfc/rfc3875#section-6.2)
|
||||
|
||||
- four types of responses :
|
||||
- document response
|
||||
- local redirect response
|
||||
- client redirect response
|
||||
- client redirect response with document
|
||||
- document response :
|
||||
- it must return a Content-Type header field
|
||||
- a Status-Header field is optional (200 is assumed if omited)
|
||||
- the server must check the cgi-script output, and modifie it to comply with the protocol version
|
||||
- local redirect response :
|
||||
- it must return only a Location field
|
||||
- it contains a local path URI and query string ('local-pathquery')
|
||||
- the local path URI must start with a "/"
|
||||
- the server must generate the response for this local-pathquery
|
||||
- client redirect response :
|
||||
- it must return only a Location field
|
||||
- it contains an absolute URI path, to indicate the client that it should reprocess the request with this URI
|
||||
- the absolute URI always start with the name of scheme followed by ":"
|
||||
- the http server must generate a 302 'Found' message
|
||||
- client redirect response with document
|
||||
- it must return a Location field with an absolute URI path
|
||||
- it must return the Status header field, with a value of 302 'Found'
|
||||
- the server must check the cgi-script output, and modifie it to comply with the protocol version
|
||||
|
||||
[6.3: cgi header fields](https://www.rfc-editor.org/rfc/rfc3875#section-6.3)
|
||||
|
||||
- whitespace is permitted between the ":" and the field-value
|
||||
- but not between the field-name and the ":"
|
||||
- the CGI script can set three differents fields :
|
||||
- Content-Type
|
||||
- Location
|
||||
- Status
|
||||
- Content-Type :
|
||||
- if there is a body in the response, a Content-Type field must be present
|
||||
- if there is no Content-Type, the server must not attempt to determine one
|
||||
- Location :
|
||||
- the local URI path must be an absolut path, not a relative path, nor NULL
|
||||
- the local URI path must, then, start with "/"
|
||||
- the absolut URI start with "<name-of-scheme>:"
|
||||
- Status :
|
||||
- a 3-digit integer code
|
||||
- 4 standards :
|
||||
- 200 'OK' indicates success, it's the default value
|
||||
- 302 'Found' with Location header and response message-body
|
||||
- 400 'Bad Request' an unknown request format, like missing CONTENT-TYPE
|
||||
- 501 'Not Implemented' the script received unsupported REQUEST-METHOD
|
||||
- construction: `Status:400 "explication of the error"\n`
|
||||
- the cgi-script can return other header fields, concerning the response message
|
||||
- the server must translate cgi-headers syntax into http-header syntax
|
||||
- for exemple, newline can be encoded in different ways
|
||||
- the cgi-script must not return header fields concerning client-side communication
|
||||
- the server can remove such fields
|
||||
- (not sure : https://www.rfc-editor.org/rfc/rfc3875#section-6.3.4)
|
||||
- the server must resolve conflicts between script-header fields and themselves
|
||||
|
||||
[6.3: cgi message body](https://www.rfc-editor.org/rfc/rfc3875#section-6.4)
|
||||
|
||||
- the server must read it untill EOF
|
||||
- the server must not modify it, except to convert charset if needed
|
||||
|
||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||
|
||||
|
||||
---
|
||||
## cgi env variables
|
||||
[cgi env variables](http://www.faqs.org/rfcs/rfc3875.html)
|
||||
[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 content
|
||||
CONTENT_TYPE : 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")
|
||||
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")
|
||||
@@ -114,10 +239,50 @@ 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 Client Errors
|
||||
- 400 Bad Request This error code indicates that the request cannot be processed because of wrong syntax usage by the client.
|
||||
- 401 Unauthorized This error code indicates that the client is not authorized to receive the requested data, without authentication. A login name and password based authentication might be required to access the requested data.
|
||||
- 403 Forbidden There is no way you can access the requested data. A 403 error announces that the data is off limits.
|
||||
- 404 Not Found This error indicates that the resources requested by the client are currently unavailable.
|
||||
- 405 Method Not Allowed This error indicates wrong usage of request method. Depending on the kind of data requested, the appropriate request method must be chosen.
|
||||
- 406 Not Acceptable When the data provided by a web server does not match the specifications made in ‘Accept’ header of the client HTTP request, this error is the result.
|
||||
- 407 Proxy Authentication Required This error clearly indicates that an authentication from the proxy server is required to gain access to requested resources.
|
||||
- 408 Request Timeout This type of error indicates that the client was delayed in making a request, within the specified time allocated to it, by the server.
|
||||
- 409 Conflict This error code is displayed when the server perceives a conflict between two requests made simultaneously by different clients, for the same resource.
|
||||
- 410 Gone This error code indicates that the requested data is no longer hosted on the server and therefore further requests made for it, would be futile.
|
||||
- 411 Length Required If the request made by the client does not include information about the length of the requested data or resource, this error code is displayed.
|
||||
- 412 Precondition Failed Some requests made by clients come attached with conditions that need to be satisfied by the server, before data transaction may happen. If these conditions are not met, error 412 results.
|
||||
- 413 Request Entity Too Large When a client makes a request which is too overwhelming for the server’s resources to handle, it presents this error code.
|
||||
- 414 Requested URI Too Long A Uniform Resource Identifier (URI) is a character string used to describe a data stream or resource on a server. Error 414 occurs when the server is unable to process the URI, because of limited resources and long string length.
|
||||
- 415 Unsupported Media Type A server may be designed to allow only certain formats for media files. When error 415 is displayed, it indicates that the format of file being uploaded through a client request, does not match the requisite format.
|
||||
- 416 Request Range Not Satisfiable Sometimes, a client may request for only a small part of a file, instead of asking for the entire file. If this request is not specified properly and the part of the file requested does not exist, this error is displayed.
|
||||
- 417 Expectation Failed This error code is displayed when the server cannot meet the specifications provided in the request.
|
||||
- 422 Unprocessable Entity This error is displayed when the request made, cannot be processed due to an error in semantic structure.
|
||||
- 423 Locked This error is displayed when a requested piece of data or resource has been locked, making it inaccessible for a server.
|
||||
- 424 Failed Dependency A server may process a succession of requests from a client with the fulfillment of each, dependent on the one made before. This error is displayed when a request made before is not fulfilled, due to which the current request cannot be processed.
|
||||
- 426 Upgrade Required This error signifies that the client may need to switch over to a secure protocol like TLS to get the request processed.
|
||||
- 444 No Response This error signifies that the server has simply rejected the client request and terminated connection.
|
||||
- 449 Retry With This is a request made by the server to the client, to make the request again after executing certain actions or making specific changes in request. This is an error code introduced by Microsoft.
|
||||
- 499 Client Closed Request When client terminates a connection made with the server, while its processing the associated request, this error code is displayed.
|
||||
- 450 Blocked By Windows Parental Controls Another error code introduced by Microsoft, this one is displayed when a URL is blocked by parental control settings on the web browsers.
|
||||
|
||||
#### HTTP Server Errors
|
||||
- 500 Internal Server Error A generic message displayed by the server, when the problem with the request cannot be specified by any other appropriate code.
|
||||
- 501 Not Implemented This error indicates the inability of the server to process a request, as it hasn’t been configured to respond to the request method used.
|
||||
- 502 Bad Gateway Sometimes, hosted pages on web servers are transmitted to client via proxy servers. If the proxy server (to which a client has sent a request), fails connecting with the web server (known as the upstream server), error 502 results.
|
||||
- 503 Service Unavailable When the server is already overloaded with multiple requests, it will temporarily stop entertaining new requests, by displaying a 503 error code.
|
||||
- 504 Gateway Timeout When the request made by a proxy server to the web server hosting a resource times out, error 504 is reported.
|
||||
- 505 HTTP Version Not Supported An error code seen rarely, it is displayed when the web server does not support the protocol version of the client request.
|
||||
|
||||
|
||||
---
|
||||
## ressources
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/Yeah/not_happy.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/nop/other.pouic
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/nop/youpi.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/youpi.bad_extension
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
inside YoupiBanane/youpi.bla
|
||||
|
||||
@@ -16,6 +16,7 @@ server {
|
||||
location /board {
|
||||
allow_methods GET;
|
||||
root ./www/html;
|
||||
cgi_ext php cgi
|
||||
}
|
||||
|
||||
location /board/content {
|
||||
|
||||
@@ -10,8 +10,63 @@ server {
|
||||
# client_body_limit 400;
|
||||
|
||||
index index.html; # this is another comment
|
||||
|
||||
root ./www/;
|
||||
|
||||
allow_methods GET;
|
||||
}
|
||||
error_page 404 ./www/error_pages/error_404.html;
|
||||
|
||||
location /list {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location /cgi-bin {
|
||||
root ./srcs/cgi-bin/;
|
||||
cgi_ext out php sh;
|
||||
}
|
||||
|
||||
location /redirect {
|
||||
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
||||
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
||||
}
|
||||
|
||||
location /test {
|
||||
index index1.html subdex.html;
|
||||
root ./www/test/;
|
||||
}
|
||||
|
||||
location /test/index1.html {
|
||||
root ./www/test/;
|
||||
index index1.html subdex.html;
|
||||
}
|
||||
|
||||
location /hilarious_404/ {
|
||||
redirect 301 https://berniesanders.com/404/;
|
||||
}
|
||||
|
||||
location /stylesheet/ {
|
||||
# root ./www/../;
|
||||
root ./styelsheet/;
|
||||
}
|
||||
|
||||
location /test/something.html {
|
||||
# allow_methods DELETE;
|
||||
root ./www/test/;
|
||||
}
|
||||
|
||||
location /test/test_deeper/ {
|
||||
# allow_methods
|
||||
autoindex on;
|
||||
root ./www/test/test_deeper/;
|
||||
}
|
||||
|
||||
location /test/test_deeper/super_deep {
|
||||
root ./www/test/test_deeper/super_deep/;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
# location /test/test_deeper/something.html {
|
||||
# allow_methods DELETE;
|
||||
# }
|
||||
|
||||
|
||||
}
|
||||
|
||||
33
memo.txt
33
memo.txt
@@ -1,7 +1,34 @@
|
||||
- un thread par serveur présent dans le fichier de config ?
|
||||
IN 42 SUBJECT AND/OR PRIORITY :
|
||||
- CGI (TODO HUGO)
|
||||
- chunked request (WIP, a bit difficult)
|
||||
|
||||
----------------
|
||||
- Need to test normal body parsing
|
||||
- basic html upload page for testing request of web browser
|
||||
- upload files with config "upload_dir"
|
||||
|
||||
----------------
|
||||
- Ecrire des tests !
|
||||
- handle redirection (Work, but weird behavior need deeper test)
|
||||
- _determine_location() review (New version to complete and test)
|
||||
-----------------------------
|
||||
Si ce n'est pas deja fait :
|
||||
- dans config, check erreur si port > 16bits
|
||||
(peut-être check si ip > 32bits)
|
||||
-----------------------------
|
||||
- gerer le champ "Accept" du client
|
||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||
- maybe add a "last_action_time" in Client for timeout handling
|
||||
little global timeout on epoll, like 100ms, then find client that actualy need to timeout
|
||||
if (actual_time - client.last_action_time > 10000ms){timeout(client)}
|
||||
- add headers "Date" and "Last-Modified" to response
|
||||
- change "std::string" to reference "std::string &" in most functions
|
||||
and add "const" if apropriate.
|
||||
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
|
||||
|
||||
|
||||
__________________________
|
||||
--------------------------
|
||||
|
||||
----Discord 42------------
|
||||
Un truc cool et surtout bien utile ici c'est d'utiliser un proxy entre ton navigateur et ton serveur pour vérifier ce qui est envoyé en raw. Les navigateurs peuvent avoir des comportements différents.
|
||||
Vous avez des modules sur vos navigateur ou des logiciels externe. C'est assez rapide et gratuit.
|
||||
478
srcs/Client.cpp
478
srcs/Client.cpp
@@ -5,7 +5,35 @@
|
||||
* COPLIENS
|
||||
*********************************************/
|
||||
|
||||
Client::Client( ) {
|
||||
Client::Client()
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
_fd(0),
|
||||
_port(""),
|
||||
_ip(""),
|
||||
_lsocket(NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||
: status(0),
|
||||
header_complete(false),
|
||||
body_complete(false),
|
||||
request_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL),
|
||||
_fd(afd),
|
||||
_port(aport),
|
||||
_ip(aip),
|
||||
_lsocket(lsocket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,13 +41,35 @@ Client::~Client() {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy constructor :
|
||||
// Client::Client( Client const & src ) {}
|
||||
|
||||
// assignement operator :
|
||||
// Client & Client::operator=( Client const & rhs ) {}
|
||||
|
||||
/* // WIP not sure fo what is more logic here
|
||||
Client::Client( Client const & src )
|
||||
: status ( src.status ),
|
||||
header_complete ( src.header_complete ),
|
||||
read_body_size ( src.read_body_size ),
|
||||
assigned_server ( src.assigned_server ),
|
||||
assigned_location ( src.assigned_location ),
|
||||
_fd ( src._fd ),
|
||||
_port ( src._port ),
|
||||
_ip ( src._ip ),
|
||||
_lsocket ( src._lsocket )
|
||||
{
|
||||
raw_request = src.raw_request;
|
||||
response = src.response;
|
||||
// buf = strdup(src.buf); // TODO: this doesn't work
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
/* // WIP placeholder because of const values
|
||||
Client & Client::operator=( Client const & rhs )
|
||||
{
|
||||
if ( this != &rhs )
|
||||
{
|
||||
// stuff
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
*/
|
||||
|
||||
/*********************************************
|
||||
* PUBLIC MEMBER FUNCTIONS
|
||||
@@ -29,88 +79,380 @@ Client::~Client() {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||
void Client::parse_request()
|
||||
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||
{
|
||||
std::string sub;
|
||||
std::vector<std::string> list;
|
||||
size_t pos;
|
||||
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||
return ;
|
||||
header_complete = true;
|
||||
clear_request(); // not mandatory
|
||||
|
||||
pos = (raw_request).find("\r\n\r\n");
|
||||
sub = (raw_request).substr(0, pos);
|
||||
list = split(sub, '\n');
|
||||
// request_line
|
||||
_parse_request_line(*list.begin());
|
||||
list.erase(list.begin());
|
||||
// headers
|
||||
_parse_request_headers(list);
|
||||
//body- message
|
||||
_parse_request_body(pos + 4);
|
||||
_parse_request_line();
|
||||
if (status)
|
||||
return;
|
||||
_parse_request_fields();
|
||||
|
||||
// DEBUG
|
||||
// print_client("headers");
|
||||
|
||||
if (status)
|
||||
return;
|
||||
assigned_server = ::_determine_process_server(this, servers);
|
||||
assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
|
||||
_check_request_errors();
|
||||
if (status)
|
||||
return;
|
||||
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
||||
|
||||
// DEBUG
|
||||
// std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n";
|
||||
|
||||
// dont clear raw_request, we need it for future reparsing of body
|
||||
// see call of parse_request() in _read_request()
|
||||
// raw_request.clear();
|
||||
}
|
||||
|
||||
void Client::parse_request_body()
|
||||
{
|
||||
std::cerr << "parse_request_body()\n";
|
||||
size_t pos;
|
||||
|
||||
pos = raw_request.find(CRLF CRLF);
|
||||
if (pos == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body() bad call, header incomplete\n";
|
||||
// QUESTION from hugo : don't we change the status here ?
|
||||
return;
|
||||
}
|
||||
pos += CRLF_SIZE*2;
|
||||
|
||||
// Chunked decoding WIP. Dont work.
|
||||
if (!get_rq_headers("Transfer-Encoding").empty()
|
||||
&& get_rq_headers("Transfer-Encoding") == "chunked")
|
||||
{
|
||||
size_t chunk_size = 1;
|
||||
size_t chunk_field_end = 0;
|
||||
char *endptr = NULL;
|
||||
char *endptr_copy = NULL;
|
||||
/* TODO: verify if last chunk in raw_request (to avoid multiples complete parsing)
|
||||
but how ? with "raw_request.rfind("0" CRLF CRLF)", there no confirmation
|
||||
that we have found the last last-chunk OR just some data */
|
||||
|
||||
_request.body = raw_request.substr(pos);
|
||||
|
||||
std::cerr << "______Chunked\n" << _request.body << "\n______\n";
|
||||
pos = 0;
|
||||
while (chunk_size != 0)
|
||||
{
|
||||
/* if (pos > _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), pos > size()\n";
|
||||
// status = 400;
|
||||
return;
|
||||
} */
|
||||
/*
|
||||
if (pos == _request.body.size())
|
||||
{
|
||||
std::cerr << "parse_request_body(), will reread till last chunk\n";
|
||||
return;
|
||||
} */
|
||||
|
||||
/* endptr_copy = endptr; */
|
||||
(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;
|
||||
} */
|
||||
|
||||
|
||||
std::string Client::get_method() { return _request.method; }
|
||||
std::string Client::get_path() { return _request.path; }
|
||||
std::string Client::get_version() { return _request.version; }
|
||||
std::string Client::get_body() { return _request.body; }
|
||||
std::string Client::get_headers(std::string key) { return _request.headers[key]; }
|
||||
chunk_field_end = _request.body.find(CRLF, pos);
|
||||
if (chunk_field_end == NPOS)
|
||||
{
|
||||
std::cerr << "parse_request_body(), chunk_field no CRLF\n";
|
||||
// status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
chunk_field_end += CRLF_SIZE;
|
||||
_request.body.erase(pos, chunk_field_end);
|
||||
pos += chunk_size + CRLF_SIZE;
|
||||
}
|
||||
|
||||
_request.headers.erase("Transfer-Encoding");
|
||||
body_complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Content-Length = " << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
||||
std::cerr << "raw_request.size() - pos = " << raw_request.size() - pos << "\n";
|
||||
_request.body = raw_request.substr(pos);
|
||||
std::cerr << "_request.body.size() = " << _request.body.size() << "\n";
|
||||
|
||||
if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
{
|
||||
_request.body = raw_request.substr(pos);
|
||||
body_complete = true;
|
||||
}
|
||||
|
||||
/* Should be equivalent */
|
||||
// _request.body = raw_request.substr(pos);
|
||||
// if (_request.body.size() >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||
// body_complete = true;
|
||||
}
|
||||
|
||||
///////////////
|
||||
// Body checks
|
||||
if (_request.body.size() > assigned_server->client_body_limit)
|
||||
status = 413; // HTTP Client Errors
|
||||
}
|
||||
|
||||
void Client::fill_script_path(std::string &path, size_t pos)
|
||||
{
|
||||
std::string tmp;
|
||||
|
||||
if (path[0] == '.')
|
||||
{
|
||||
path.erase(0, 1);
|
||||
pos--;
|
||||
}
|
||||
_request.script.path = path.substr(0, pos);
|
||||
_request.script.info = path.substr(pos);
|
||||
}
|
||||
|
||||
void Client::clear()
|
||||
{
|
||||
clear_request();
|
||||
header_complete = false;
|
||||
body_complete = false;
|
||||
request_complete = false;
|
||||
read_body_size = 0;
|
||||
assigned_server = NULL;
|
||||
assigned_location = NULL;
|
||||
raw_request.clear();
|
||||
response.clear();
|
||||
status = 0;
|
||||
}
|
||||
|
||||
void Client::clear_request()
|
||||
{
|
||||
clear_script();
|
||||
_request.method = UNKNOWN;
|
||||
_request.target.clear();
|
||||
_request.version.clear();
|
||||
_request.headers.clear();
|
||||
_request.body.clear();
|
||||
_request.abs_path.clear();
|
||||
_request.query.clear();
|
||||
_request.port.clear();
|
||||
_request.hostname.clear();
|
||||
}
|
||||
|
||||
void Client::clear_script()
|
||||
{
|
||||
_request.script.path.clear();
|
||||
_request.script.info.clear();
|
||||
}
|
||||
|
||||
// debug
|
||||
void Client::print_client(std::string message)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
|
||||
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n";
|
||||
::print_special(raw_request);
|
||||
std::cout << "\n__\n"
|
||||
<< "get_cl_fd() : [" << get_cl_fd() << "]\n"
|
||||
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
||||
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
||||
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\n"
|
||||
<< "get_rq_target() : [" << get_rq_target() << "]\n"
|
||||
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
|
||||
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
||||
<< "get_rq_version() : [" << get_rq_version() << "]\n"
|
||||
<< "get_rq_body() : [" << get_rq_body() << "]\n"
|
||||
<< "get_rq_port() : [" << get_rq_port() << "]\n"
|
||||
<< "get_rq_hostname() : [" << get_rq_hostname() << "]\n"
|
||||
<< "get_rq_script_path() : [" << get_rq_script_path() << "]\n"
|
||||
<< "get_rq_script_info() : [" << get_rq_script_info() << "]\n"
|
||||
<< "headers :\n";
|
||||
for (it = _request.headers.begin(); it != _request.headers.end(); it++)
|
||||
std::cout << " " << it->first << ": [" << it->second << "]\n";
|
||||
std::cout << "\n=== END PRINT CLIENT ===\n\n";
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* GETTERS
|
||||
*********************************************/
|
||||
|
||||
// client side
|
||||
int Client::get_cl_fd() const { return _fd; }
|
||||
const std::string & Client::get_cl_ip() const { return _ip; }
|
||||
const std::string & Client::get_cl_port() const { return _port; }
|
||||
const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
|
||||
|
||||
// requette
|
||||
http_method Client::get_rq_method() const { return _request.method; }
|
||||
std::string Client::get_rq_method_str() const
|
||||
{ return ::http_methods_to_str(_request.method); }
|
||||
std::string Client::get_rq_target() const { return _request.target; }
|
||||
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
||||
std::string Client::get_rq_query() const { return _request.query; }
|
||||
std::string Client::get_rq_version() const { return _request.version; }
|
||||
std::string Client::get_rq_body() const { return _request.body; }
|
||||
std::string Client::get_rq_port() const { return _request.port; }
|
||||
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
||||
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
||||
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
||||
|
||||
std::string Client::get_rq_headers(const std::string & key) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it;
|
||||
|
||||
it = _request.headers.find(::str_tolower(key));
|
||||
if (it == _request.headers.end())
|
||||
return "";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************
|
||||
* PRIVATE MEMBER FUNCTIONS
|
||||
*********************************************/
|
||||
|
||||
void Client::_parse_request_line( std::string rline )
|
||||
void Client::_parse_request_line()
|
||||
{
|
||||
std::vector<std::string> sline;
|
||||
std::string tmp;
|
||||
std::vector<std::string> line;
|
||||
std::string raw_line;
|
||||
|
||||
sline = split(rline, ' ');
|
||||
if (sline.size() != 3)
|
||||
raw_line = ::get_line(raw_request, 0, CRLF);
|
||||
line = ::split_trim(raw_line, " ");
|
||||
if (line.size() != 3)
|
||||
{
|
||||
std::cerr << "err _parse_request_line(): ";
|
||||
throw std::runtime_error("bad request-line header");
|
||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
// method
|
||||
tmp = ::trim(sline[0], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.method = tmp;
|
||||
// TODO uri in request_line
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.3
|
||||
// https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs
|
||||
tmp = ::trim(sline[1], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.path = tmp;
|
||||
// http version
|
||||
tmp = ::trim(sline[2], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
_request.version = tmp;
|
||||
}
|
||||
|
||||
void Client::_parse_request_headers( std::vector<std::string> list )
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
std::vector<std::string>::iterator it;
|
||||
size_t pos;
|
||||
|
||||
for (it = list.begin(); it != list.end(); it++)
|
||||
else
|
||||
{
|
||||
pos = (*it).find(':');
|
||||
key = (*it).substr( 0, pos );
|
||||
key = ::trim(key, ' ');
|
||||
key = ::trim(key, '\r');
|
||||
val = (*it).substr( pos + 1 );
|
||||
val = ::trim(val, ' ');
|
||||
val = ::trim(val, '\r');
|
||||
_request.headers.insert( std::pair<std::string, std::string>(key, val) );
|
||||
_request.method = str_to_http_method(line[0]);
|
||||
_request.target = line[1];
|
||||
_parse_request_target(line[1]);
|
||||
_request.version = line[2];
|
||||
}
|
||||
}
|
||||
|
||||
void Client::_parse_request_body( size_t pos )
|
||||
void Client::_parse_request_target( std::string target )
|
||||
{
|
||||
std::string body = &raw_request[pos];
|
||||
size_t pos;
|
||||
|
||||
_request.body = body;
|
||||
pos = target.find("?");
|
||||
if (pos != NPOS)
|
||||
_request.query = target.substr(pos + 1);
|
||||
else
|
||||
_request.query = "";
|
||||
_request.abs_path = target.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_fields()
|
||||
{
|
||||
std::string headers;
|
||||
size_t pos;
|
||||
int ret;
|
||||
|
||||
headers = raw_request;
|
||||
// delete first line
|
||||
pos = headers.find(CRLF);
|
||||
if (pos != NPOS)
|
||||
headers.erase(0, pos + std::string(CRLF).size());
|
||||
// delete body part
|
||||
pos = headers.find(CRLF CRLF);
|
||||
if (pos != NPOS)
|
||||
headers.erase(pos);
|
||||
else {
|
||||
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
// copy result of parser into headers
|
||||
ret = ::parse_http_headers(headers, _request.headers);
|
||||
if (ret > 0) {
|
||||
std::cerr << "err _parse_request_fields(): " << ret << " fields are bad formated\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
::str_map_key_tolower(_request.headers);
|
||||
}
|
||||
|
||||
void Client::_parse_port_hostname(std::string host)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
if (host == "")
|
||||
std::cerr << "no host\n";
|
||||
|
||||
pos = host.find(':');
|
||||
// port :
|
||||
if (pos == NPOS)
|
||||
_request.port = "4040"; // TODO: make equal to default port in config
|
||||
else
|
||||
_request.port = host.substr(pos);
|
||||
if (_request.port == ":")
|
||||
_request.port = "";
|
||||
// hostname :
|
||||
_request.hostname = host.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_check_request_errors()
|
||||
{
|
||||
///////////////////////
|
||||
// Request line checks
|
||||
if (_request.method == UNKNOWN)
|
||||
status = 501; // HTTP Client Errors
|
||||
else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
status = 505; // HTTP Client Errors
|
||||
else if (!(assigned_location->allow_methods & _request.method))
|
||||
{
|
||||
status = 405; // HTTP Client Errors
|
||||
response.append("Allow: ");
|
||||
response.append(::http_methods_to_str(assigned_location->allow_methods));
|
||||
response.append(CRLF);
|
||||
}
|
||||
else if (assigned_location->redirect_status)
|
||||
{ // Weird behavior. Sometimes, the web browser seems to wait for a complete response until timeout.
|
||||
// (for codes 301, 302, 303, 307, and 308)
|
||||
status = assigned_location->redirect_status;
|
||||
response.append("Location: ");
|
||||
response.append(assigned_location->redirect_uri);
|
||||
response.append(CRLF CRLF);
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// Headers checks
|
||||
else if (!this->get_rq_headers("Content-Length").empty()
|
||||
&& std::strtoul(this->get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
|
||||
status = 413;
|
||||
else if (!this->get_rq_headers("Transfer-Encoding").empty()
|
||||
&& this->get_rq_headers("Transfer-Encoding") != "chunked" )
|
||||
status = 501;
|
||||
else if (!this->get_rq_headers("Content-Encoding").empty())
|
||||
{
|
||||
status = 415;
|
||||
response.append("Accept-Encoding:"); // empty, no encoding accepted
|
||||
response.append(CRLF);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* OVERLOAD
|
||||
*********************************************/
|
||||
|
||||
bool operator==(const Client& lhs, const Client& rhs)
|
||||
{ return lhs.get_cl_fd() == rhs.get_cl_fd(); }
|
||||
bool operator==(const Client& lhs, int fd)
|
||||
{ return lhs.get_cl_fd() == fd; }
|
||||
bool operator==(int fd, const Client& rhs)
|
||||
{ return fd == rhs.get_cl_fd(); }
|
||||
|
||||
|
||||
@@ -6,49 +6,103 @@
|
||||
# include <string>
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string.h> // strdup
|
||||
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# include "utils.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "colors.h"
|
||||
|
||||
struct Script
|
||||
{
|
||||
std::string path;
|
||||
std::string info;
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string method;
|
||||
std::string path;
|
||||
http_method method;
|
||||
std::string target;
|
||||
std::string abs_path;
|
||||
std::string query;
|
||||
std::string version;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
std::string port;
|
||||
std::string hostname;
|
||||
struct Script script;
|
||||
};
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
Client(int afd, listen_socket *lsocket, std::string aport, std::string aip);
|
||||
~Client();
|
||||
//Client(Client const &src);
|
||||
//Client &operator=(Client const &rhs);
|
||||
// Client(Client const &src);
|
||||
// Client &operator=(Client const &rhs);
|
||||
|
||||
int fd;
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
unsigned int status;
|
||||
bool header_complete;
|
||||
bool body_complete;
|
||||
bool request_complete;
|
||||
size_t read_body_size; // unused for now
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *assigned_location;
|
||||
|
||||
std::string get_method();
|
||||
std::string get_path();
|
||||
std::string get_version();
|
||||
std::string get_body();
|
||||
std::string get_headers(std::string key);
|
||||
// getters
|
||||
int get_cl_fd() const;
|
||||
const std::string & get_cl_port() const;
|
||||
const std::string & get_cl_ip() const;
|
||||
const listen_socket * get_cl_lsocket() const;
|
||||
|
||||
void parse_request();
|
||||
// requests getters
|
||||
http_method get_rq_method() const;
|
||||
std::string get_rq_method_str() const;
|
||||
std::string get_rq_target() const;
|
||||
std::string get_rq_abs_path() const;
|
||||
std::string get_rq_query() const;
|
||||
std::string get_rq_version() const;
|
||||
std::string get_rq_body() const;
|
||||
std::string get_rq_port() const;
|
||||
std::string get_rq_hostname() const;
|
||||
std::string get_rq_script_path() const;
|
||||
std::string get_rq_script_info() const;
|
||||
std::string get_rq_headers(const std::string & key) const;
|
||||
|
||||
void parse_request_headers(std::vector<ServerConfig> &servers);
|
||||
void parse_request_body();
|
||||
void clear();
|
||||
void clear_request();
|
||||
void clear_script();
|
||||
void fill_script_path(std::string &path, size_t pos);
|
||||
// DEBUG
|
||||
void print_client(std::string message = "");
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
std::string _port;
|
||||
std::string _ip;
|
||||
listen_socket * _lsocket;
|
||||
struct Request _request;
|
||||
|
||||
void _parse_request_line( std::string rline );
|
||||
void _parse_request_headers( std::vector<std::string> list );
|
||||
void _parse_request_body( size_t pos );
|
||||
void _parse_request_line();
|
||||
void _parse_request_fields();
|
||||
void _parse_request_target( std::string target );
|
||||
void _parse_port_hostname(std::string host);
|
||||
|
||||
void _check_request_errors();
|
||||
};
|
||||
|
||||
bool operator==(const Client& lhs, const Client& rhs);
|
||||
bool operator==(const Client& lhs, int fd);
|
||||
bool operator==(int fd, const Client& rhs);
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,400 +0,0 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.cpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/13 22:11:17 by me #+# #+# */
|
||||
/* Updated: 2022/07/31 13:18:14 by simplonco ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
// Default
|
||||
ConfigParser::ConfigParser()
|
||||
{
|
||||
std::cout << "Default Constructor\n";
|
||||
// don't use yet, you have no idea what the defaults are
|
||||
}
|
||||
|
||||
ConfigParser::ConfigParser(const char* path)
|
||||
{
|
||||
std::cout << "Param Constructor\n";
|
||||
|
||||
std::ifstream file;
|
||||
std::string buf;
|
||||
size_t comment;
|
||||
|
||||
_content.clear();
|
||||
file.open(path);
|
||||
if (file.is_open())
|
||||
{
|
||||
// are there more throws i need to add in case of errors, what would
|
||||
// those errors be?
|
||||
while (!file.eof())
|
||||
{
|
||||
getline(file, buf);
|
||||
// remove # comments here.
|
||||
if ((comment = buf.find_first_of("#")) == std::string::npos)
|
||||
{
|
||||
// remove empty lines, i think...
|
||||
if ((buf.find_first_not_of(" \t")) != std::string::npos)
|
||||
_content.append(buf + '\n');
|
||||
}
|
||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||
{
|
||||
// is there a comment at the end of the line
|
||||
std::string tmp = buf.substr(0, comment - 1);
|
||||
_content.append(tmp + '\n');
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("open config");
|
||||
}
|
||||
|
||||
ConfigParser::~ConfigParser()
|
||||
{
|
||||
// do i need to destroy anything, won't it handle itself?
|
||||
}
|
||||
|
||||
/*
|
||||
ConfigParser & ConfigParser::operator=(const ConfigParser& rhs)
|
||||
{
|
||||
if (this == rhs) // * & ?
|
||||
return (*this); // * ?
|
||||
|
||||
// make some stuff equal
|
||||
return (*this);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
std::vector<ServerConfig> * ConfigParser::parse()
|
||||
{
|
||||
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
||||
// std::vector<ServerConfig> ret;
|
||||
|
||||
size_t start = 0;
|
||||
size_t curr = _content.find_first_not_of(" \t\n", 0);
|
||||
|
||||
if (curr == std::string::npos)
|
||||
throw std::invalid_argument("empty config file");
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// why no checks here
|
||||
// if not here do i need them elsewhere?
|
||||
start = _content.find_first_not_of(" \t\n", curr);
|
||||
curr = _content.find_first_of(" \t\n", start);
|
||||
std::string key = _content.substr(start, curr - start);
|
||||
if (key != "server")
|
||||
throw std::invalid_argument("bad config file arguments 1");
|
||||
ret->push_back(_parse_server(&curr));
|
||||
}
|
||||
_post_processing(ret);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
ServerConfig ConfigParser::_parse_server(size_t *start)
|
||||
{
|
||||
ServerConfig ret;
|
||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.autoindex = false;
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 1");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
while (curr != std::string::npos) // here curr == { + 1
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
// now curr is on space after 1st word.
|
||||
if (key == "}")
|
||||
{
|
||||
// why +1 curr is already after it no?
|
||||
*start = _content.find_first_not_of(" \t\n", curr + 1);
|
||||
break ;
|
||||
}
|
||||
else if (key == "location")
|
||||
ret.locations.push_back(_parse_location(&curr));
|
||||
else
|
||||
{
|
||||
std::string values = _get_rest_of_line(&curr);
|
||||
// curr now should be \n
|
||||
_set_server_values(&ret, key, values);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
{
|
||||
LocationConfig ret;
|
||||
size_t curr = *start;
|
||||
// start is after the 1st word aka "location"
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.redirect_status = 0;
|
||||
ret.path = _get_first_word(&curr);
|
||||
// in theory now curr should be right after the "path"
|
||||
|
||||
curr = _content.find_first_not_of(" \t\n", curr);
|
||||
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 2");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
// now curr is on space after 1st word.
|
||||
if (key == "}")
|
||||
{
|
||||
*start = curr;
|
||||
break ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string values = _get_rest_of_line(&curr);
|
||||
// curr now should be \n
|
||||
_set_location_values(&ret, key, values);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
size_t size = tmp_val.size();
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "server_name" && size == 1)
|
||||
{
|
||||
for (size_t i = 0; i < server->server_name.size(); i++)
|
||||
{
|
||||
if (server->server_name[i].compare(tmp_val[0]) == 0)
|
||||
throw std::invalid_argument("server_name already exists");
|
||||
}
|
||||
server->server_name.push_back(tmp_val[0]);
|
||||
}
|
||||
else if (key == "listen" && size == 1 && server->host == "" \
|
||||
&& server->port == "")
|
||||
{
|
||||
if (tmp_val[0].find_first_of(":") == std::string::npos)
|
||||
{
|
||||
// should i limit which ports can be used?
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
server->host = "0.0.0.0";
|
||||
server->port = tmp_val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> tmp2 = ::split(tmp_val[0], ':');
|
||||
|
||||
std::vector<std::string> ip = ::split(tmp2[0], '.');
|
||||
if (ip.size() != 4)
|
||||
throw std::invalid_argument("bad host ip");
|
||||
for (size_t i = 0; i < ip.size(); i++)
|
||||
{
|
||||
if (!::isNumeric_btw(0, 255, ip[i]))
|
||||
throw std::invalid_argument("bad host ip");
|
||||
}
|
||||
if (!::isNumeric(tmp2[1]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
server->host = tmp2[0];
|
||||
server->port = tmp2[1];
|
||||
}
|
||||
}
|
||||
else if (key == "root" && size == 1 && server->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
server->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
{
|
||||
// autoindex is a bool, there's no good way for me to see if it has
|
||||
// bet set already
|
||||
server->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& server->client_body_limit == 0)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
server->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
// i think you can call index several times...
|
||||
// should i be doing an access?
|
||||
// since index is at the root, but root might not yet be defined
|
||||
// will check index later in post
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
server->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && server->allow_methods.empty())
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
MethodType m = _str_to_method_type(tmp_val[i]);
|
||||
if (m == 3)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
server->allow_methods.push_back(m);
|
||||
}
|
||||
}
|
||||
else if (key == "error_page")
|
||||
{
|
||||
|
||||
// so it can either be just a /here/is/the/repo
|
||||
// or it can be http://some_domain.com/here
|
||||
// wtf... how should we handle...
|
||||
|
||||
|
||||
|
||||
// you can definitely call Error_pages several times, i think
|
||||
std::string path = tmp_val[tmp_val.size() - 1];
|
||||
for (unsigned long i = 0; i != tmp_val.size() - 1; i++)
|
||||
{
|
||||
// what are the bounds for Error codes?
|
||||
if (!(isNumeric_btw(0, 600, tmp_val[i])))
|
||||
throw std::invalid_argument("value not a valid number");
|
||||
int status_code = atoi(tmp_val[i].c_str());
|
||||
|
||||
// yea cuz here we continue.. why suddenly flexible not throw ?
|
||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||
continue ;
|
||||
server->error_pages[status_code] = path;
|
||||
}
|
||||
}
|
||||
/* else if (key == "recv_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
// what is tv_sec and do i need it?
|
||||
// ok so i don't fully understand this part but ok, keep for now...
|
||||
server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "send_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
server->send_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
*/ else
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
size_t size = tmp_val.size();
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "root" && size == 1 && location->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
location->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& location->client_body_limit == 0)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
location->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
// you can definitely call Index several times, i think
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
location->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && location->allow_methods.empty())
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
MethodType m = _str_to_method_type(tmp_val[i]);
|
||||
if (m == 3)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
location->allow_methods.push_back(m);
|
||||
}
|
||||
}
|
||||
else if (key == "cgi_info")
|
||||
{
|
||||
// you can call cgi_info several times i think.
|
||||
// ok wtf is all this even doing, figure that out
|
||||
unsigned long i = value.find_first_of(" ");
|
||||
if (i == std::string::npos)
|
||||
throw std::invalid_argument("bad config file arguments 8");
|
||||
// ok why an int now, we gotta be more consistent!
|
||||
int j = value.find_first_not_of(" ", i);
|
||||
location->cgi_info[value.substr(0, i)] = value.substr(j, value.length());
|
||||
}
|
||||
else if (key == "return" && location->redirect_status == 0 \
|
||||
&& location->redirect_uri == "")
|
||||
{
|
||||
// actually i think there can only be one per location...
|
||||
// you can definitely call return several times, i think
|
||||
if (tmp_val.size() != 2)
|
||||
throw std::invalid_argument("wrong number of values");
|
||||
// and tmp_val[0] should be a number and tmp_val[1] a string?
|
||||
if (!(::isNumeric(tmp_val[0])))
|
||||
throw std::invalid_argument("value not a number");
|
||||
|
||||
// somehow check that tmp_val[1] is a string? or valid? how?
|
||||
// something about using access() to see if
|
||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
|
||||
// make certain servers default
|
||||
// fill out empty settings
|
||||
// if special settings are empty throw
|
||||
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port should already be set
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// is that a good default?
|
||||
if (it->root == "")
|
||||
it->root = "/";
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
// autoindex should already be false by default right?
|
||||
|
||||
// what do we do if Allow methods is left empty?
|
||||
// all ?
|
||||
if (it->allow_methods.empty())
|
||||
throw std::invalid_argument("No methods specified");
|
||||
|
||||
|
||||
// what to do if index is left empty? index.html?
|
||||
// ok but i still need to check index, no idea how...
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
// check that path is feasible...
|
||||
// opendir?
|
||||
DIR* dir = opendir(it_l->path.c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("location dir could not be opened");
|
||||
|
||||
if (it_l->client_body_limit == 0)
|
||||
it_l->client_body_limit = 5000; // what is the recomended size?
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
// fill out allow methods from server?
|
||||
if (it_l->allow_methods.empty())
|
||||
it_l->allow_methods = it->allow_methods;
|
||||
|
||||
// fill out index from Server?
|
||||
// or do a bunch of checks on what is in there...
|
||||
|
||||
// same for redirect status i think
|
||||
|
||||
// maybe do something for Cgi_info?
|
||||
|
||||
++it_l;
|
||||
}
|
||||
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
// do the defaults at the end?
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* LocationConfig.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/07/25 20:09:48 by me ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef LOCATIONCONFIG_HPP
|
||||
# define LOCATIONCONFIG_HPP
|
||||
|
||||
# include "MethodType.hpp"
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string>
|
||||
|
||||
|
||||
// again, struct instead?
|
||||
class LocationConfig
|
||||
{
|
||||
public:
|
||||
// canonic stuff?
|
||||
|
||||
std::string path;
|
||||
|
||||
int client_body_limit;
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
std::vector<MethodType> allow_methods;
|
||||
std::map<std::string, std::string> cgi_info;
|
||||
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// wait no there can only be 1 and i think it might have to be in
|
||||
// location only...
|
||||
int redirect_status;
|
||||
std::string redirect_uri;
|
||||
// au pire you do location / { return 301 http://location; }
|
||||
// and that's how you get the redirect from the root.
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
|
||||
#ifndef METHODTYPE_HPP
|
||||
# define METHODTYPE_HPP
|
||||
|
||||
enum MethodType
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
DELETE,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
#ifndef SERVER_HPP
|
||||
# define SERVER_HPP
|
||||
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
// Server(Placeholder);
|
||||
// Server();
|
||||
// Server(Server const &src);
|
||||
// ~Server();
|
||||
// Server &operator=(Server const &rhs);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,93 +0,0 @@
|
||||
|
||||
#ifndef SERVERCONFIG_HPP
|
||||
# define SERVERCONFIG_HPP
|
||||
|
||||
# include "MethodType.hpp"
|
||||
# include "LocationConfig.hpp"
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string> // string
|
||||
# include <iostream> // cout, cin
|
||||
|
||||
// a class that's all public? just so we have options?
|
||||
class ServerConfig
|
||||
{
|
||||
public:
|
||||
|
||||
// do i need some canonic stuff?
|
||||
|
||||
|
||||
// there can be several
|
||||
std::vector<std::string> server_name;
|
||||
// we could shove default in here if we wanted to...
|
||||
|
||||
// there can only be 1 per server...
|
||||
std::string host;
|
||||
std::string port; // port needs to be something else... not quite an int
|
||||
// should a Server be able to handle several?
|
||||
|
||||
// there can only be one.
|
||||
std::string root;
|
||||
|
||||
int client_body_limit; // set to default max if none set
|
||||
|
||||
// might be the only one we let slide if bad input...
|
||||
bool autoindex;
|
||||
|
||||
// we will check the index in the post processing with access() ?
|
||||
std::vector<std::string> index;
|
||||
std::map<int, std::string> error_pages;
|
||||
|
||||
// i'm tempted to do something diff for storing method types...
|
||||
// fuck it, you can only call allow_methods once in Server
|
||||
// once more in each location.
|
||||
std::vector<MethodType> allow_methods;
|
||||
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
// not convinced we need these...
|
||||
// struct timeval send_timeout;
|
||||
// struct timeval recv_timeout;
|
||||
|
||||
|
||||
// fuck maybe i do need return here...
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// i think actually there can only be 1 and it can only be in a location?
|
||||
// int redirect_status;
|
||||
// std::string redirect_uri;
|
||||
|
||||
void print_all()
|
||||
{
|
||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
||||
|
||||
for (size_t i = 0; i < server_name.size(); i++)
|
||||
std::cout << server_name[i] << " ";
|
||||
std::cout << "root: " << root << '\n';
|
||||
std::cout << "index: ";
|
||||
for (size_t i = 0; i < index.size(); i++)
|
||||
std::cout << index[i] << " ";
|
||||
std::cout << "\nerror_pages: ";
|
||||
for(std::map<int, std::string>::iterator it = error_pages.begin(); \
|
||||
it != error_pages.end(); it++)
|
||||
std::cout << it->first << "--" << it->second << " ";
|
||||
// for (size_t i = 0; i < error_pages.size(); i++)
|
||||
// std::cout << error_pages->first << "--" << error_pages->second << " ";
|
||||
std::cout << "\nallow_methods: ";
|
||||
for (size_t i = 0; i < allow_methods.size(); i++)
|
||||
std::cout << allow_methods[i] << " ";
|
||||
std::cout << "\nskiping Locations for now...\n";
|
||||
std::cout << "also skiping send_timeout and recv\n";
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
||||
// std::cout << "redirect_status: " << redirect_status << '\n';
|
||||
// std::cout << "redirect_uri: " << redirect_uri << '\n';
|
||||
std::cout << "host: " << host << '\n';
|
||||
std::cout << "port: " << port << '\n';
|
||||
|
||||
std::cout << "\n----------\n";
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
115
srcs/Webserv.hpp
115
srcs/Webserv.hpp
@@ -1,115 +0,0 @@
|
||||
|
||||
#ifndef WEBSERV_HPP
|
||||
# define WEBSERV_HPP
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <exception> // exception, what
|
||||
# include <stdexcept> // runtime_error, invalid_argument
|
||||
# include <sys/epoll.h> // epoll
|
||||
# include <fcntl.h> // fcntl
|
||||
# include <sys/wait.h> // waitpid
|
||||
# include <csignal> // signal
|
||||
# include <fstream> // ifstream
|
||||
# include <sstream> // stringstream
|
||||
# include <cerrno> // errno
|
||||
# include <unistd.h> // close, access
|
||||
# include <iostream> // cout, cin
|
||||
# include <cstring> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
|
||||
# include <algorithm> // find
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
|
||||
// TODO: A virer
|
||||
//# include "ConfigParser.hpp"
|
||||
//# include "LocationConfig.hpp"
|
||||
//# include "MethodType.hpp"
|
||||
//# include "utils.hpp"
|
||||
// TODO: A virer
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
void signal_handler(int signum);
|
||||
|
||||
/* enum // WIP test
|
||||
{
|
||||
SERVER_FD = 1,
|
||||
CLIENT_FD
|
||||
};
|
||||
|
||||
struct s // WIP test
|
||||
{
|
||||
int fd;
|
||||
Client *ptr;
|
||||
};
|
||||
*/
|
||||
|
||||
// these might only be TMP
|
||||
# define FAILURE -1
|
||||
# define SUCCESS 1
|
||||
|
||||
class Webserv
|
||||
{
|
||||
public:
|
||||
// base.cpp
|
||||
Webserv();
|
||||
// Webserv(Webserv const &src);
|
||||
|
||||
// what should it take as arg, *, &, ?
|
||||
// Webserv(std::vector<ServerConfig>& servers);
|
||||
|
||||
~Webserv();
|
||||
// Webserv &operator=(Webserv const &rhs);
|
||||
|
||||
// init.cpp
|
||||
void init_virtual_servers(std::vector<ServerConfig>* servers);
|
||||
// run_loop.cpp
|
||||
void run();
|
||||
|
||||
private:
|
||||
int _epfd;
|
||||
std::vector<int> _listen_sockets;
|
||||
std::vector<ServerConfig> _servers;
|
||||
std::vector<Client> _clients;
|
||||
|
||||
// accept.cpp
|
||||
void _accept_connection(int fd);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
void _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
void _send_response(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _insert_status_line(Client *client);
|
||||
void _get_ressource(Client *client);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client);
|
||||
void _exec_cgi(Client *client);
|
||||
void _construct_client(Client *client);
|
||||
char** _set_env(Client *client);
|
||||
char* _dup_env(std::string var, std::string val);
|
||||
void _exec_script(Client *client, char **env);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
// signal.cpp
|
||||
void _handle_last_signal();
|
||||
// close.cpp
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
// init.cpp
|
||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||
void _listen(int socket_fd, unsigned int max_connections);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,447 +0,0 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
int g_last_signal;
|
||||
bool g_run;
|
||||
void signal_handler(int signum)
|
||||
{
|
||||
g_last_signal = signum;
|
||||
}
|
||||
|
||||
Webserv::Webserv()
|
||||
{
|
||||
std::cerr << "Server init\n";
|
||||
|
||||
_epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ?
|
||||
if (_epfd == -1)
|
||||
{
|
||||
std::perror("err epoll_create1()");
|
||||
throw std::runtime_error("Epoll init");
|
||||
}
|
||||
|
||||
std::signal(SIGPIPE, signal_handler);
|
||||
std::signal(SIGINT, signal_handler);
|
||||
}
|
||||
|
||||
/* Webserv::Webserv(Webserv const &src)
|
||||
{
|
||||
|
||||
} */
|
||||
|
||||
Webserv::~Webserv()
|
||||
{
|
||||
close(_socket_fd);
|
||||
close(_epfd);
|
||||
_close_all_clients();
|
||||
std::cerr << "Server destroyed\n";
|
||||
}
|
||||
|
||||
/* Webserv & Webserv::operator=(Webserv const &rhs)
|
||||
{
|
||||
|
||||
} */
|
||||
|
||||
///////////////
|
||||
// Functions //
|
||||
|
||||
void Webserv::init_virtual_servers() // ADD config param
|
||||
{
|
||||
_socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ?
|
||||
if (_socket_fd == -1)
|
||||
{
|
||||
std::perror("err socket()");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
// HUGO ADD
|
||||
// allow socket descriptor to be reuseable
|
||||
int on = 1;
|
||||
if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
{
|
||||
::perror("setsockopt() failed");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
// HUGO ADD END
|
||||
|
||||
_bind(_socket_fd, 4040);
|
||||
_listen(_socket_fd, 512); // 512 arbitrary
|
||||
|
||||
if (_epoll_update(_socket_fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
|
||||
void Webserv::start()
|
||||
{
|
||||
std::cerr << "Server started\n";
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
int nfds;
|
||||
int i;
|
||||
int count_loop = 0;
|
||||
|
||||
g_run = true;
|
||||
while (g_run)
|
||||
{
|
||||
std::cerr << ++count_loop << "----loop epoll()\n";
|
||||
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
||||
if (nfds == -1)
|
||||
{
|
||||
std::perror("err epoll_wait(): ");
|
||||
throw std::runtime_error("Epoll wait");
|
||||
}
|
||||
else if (nfds == 0)
|
||||
{
|
||||
if (!_clients.empty())
|
||||
{
|
||||
std::cerr << "Timeout " << TIMEOUT << "ms\n";
|
||||
_close_all_clients();
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
while (i < nfds)
|
||||
{
|
||||
// if ((events[i].data.u32 == SERVER_FD) && (events[i].events & EPOLLIN)) // Dont work, see "SERVER_FD" define
|
||||
if ((events[i].data.fd == _socket_fd) && (events[i].events & EPOLLIN))
|
||||
_accept_connection(events[i].data.fd);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_read_request(static_cast<Client*>(events[i].data.ptr));
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_send_response(static_cast<Client*>(events[i].data.ptr));
|
||||
++i;
|
||||
_actual_client = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Private Functions //
|
||||
|
||||
void Webserv::_accept_connection(int fd)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
|
||||
std::cerr << "accept()\n";
|
||||
addr_len = sizeof addr;
|
||||
accepted_fd = ::accept(fd, (sockaddr*)&addr, &addr_len);
|
||||
if (accepted_fd == -1)
|
||||
{
|
||||
std::perror("err accept(): ");
|
||||
return ;
|
||||
}
|
||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
_clients.push_back(Client());
|
||||
_clients.back().fd = accepted_fd;
|
||||
|
||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD, &_clients.back());
|
||||
}
|
||||
|
||||
//////////
|
||||
// READ //
|
||||
void Webserv::_read_request(Client *client)
|
||||
{
|
||||
char buf[BUFSIZE+1];
|
||||
ssize_t ret;
|
||||
_actual_client = client;
|
||||
|
||||
std::cerr << "recv()\n";
|
||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err recv(): ");
|
||||
// if (g_last_signal)
|
||||
// _handle_last_signal();
|
||||
// else
|
||||
// _close_client(client->fd);
|
||||
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
return ;
|
||||
}
|
||||
/*
|
||||
if (ret == BUFSIZE)
|
||||
// send error like "request too long" to client
|
||||
*/
|
||||
|
||||
buf[ret] = '\0';
|
||||
client->raw_request.append(buf);
|
||||
|
||||
// HUGO TMP
|
||||
//
|
||||
_parse_request(client);
|
||||
//
|
||||
// HUGO TMP END
|
||||
|
||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD, client);
|
||||
}
|
||||
|
||||
///////////
|
||||
// WRITE //
|
||||
void Webserv::_send_response(Client *client)
|
||||
{
|
||||
ssize_t ret;
|
||||
_actual_client = client;
|
||||
|
||||
std::cerr << "send()\n";
|
||||
std::cerr << "RAW_REQUEST\n|\n" << client->raw_request << "|\n";
|
||||
|
||||
|
||||
// TMP HUGO test cgi
|
||||
//
|
||||
// if "POST" found in _buf, execve a cgi
|
||||
if (client->raw_request.find("POST") != std::string::npos)
|
||||
_exec_cgi_script(client);
|
||||
// if "index.html" found in _buf, send the page
|
||||
if (client->raw_request.find("index.html") != std::string::npos)
|
||||
_serve_file(client, "index.html");
|
||||
//
|
||||
// TMP HUGO end test cgi
|
||||
|
||||
|
||||
ret = ::send(client->fd, MSG_TEST, sizeof MSG_TEST - 1, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send(): ");
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
// else
|
||||
// _close_client(client->fd);
|
||||
return ;
|
||||
}
|
||||
|
||||
_close_client(client->fd);
|
||||
// if (client->raw_request.find("Connection: keep-alive") == std::string::npos)
|
||||
// _close_client(client->fd);
|
||||
// else
|
||||
// _epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD, client);
|
||||
|
||||
client->raw_request.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Misc functions //
|
||||
|
||||
int Webserv::_epoll_update(int fd, uint32_t events, int op)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
std::memset(&ev, 0, sizeof ev);
|
||||
ev.events = events;
|
||||
ev.data.fd = fd;
|
||||
if (::epoll_ctl(_epfd, op, fd, &ev) == -1)
|
||||
{
|
||||
std::perror("err _epoll_update(): ");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
void _parse_request(Client *client)
|
||||
{
|
||||
std::string request = client->raw_request;
|
||||
std::map<std::string, std::string> field = client->request;
|
||||
// size_t size = request.size();
|
||||
size_t begin = 0;
|
||||
size_t end;
|
||||
size_t len;
|
||||
|
||||
// std::string str ("test un: deux\ntest deux: trois quatre\ntest :trois quatre cinq");
|
||||
// std::string sub;
|
||||
|
||||
std::cout << str << "\n\n";
|
||||
|
||||
int i = 0;
|
||||
while (end != std::string::npos)
|
||||
{
|
||||
// find first portion, before ':'
|
||||
end = str.find(':', begin);
|
||||
len = end - begin;
|
||||
if (end == std::string::npos)
|
||||
len = end;
|
||||
sub = str.substr(begin, len);
|
||||
std::cout << i << "|" << sub << "\n";
|
||||
// std::cout << "[begin:" << begin << " - end:" << end << " - len:" << len << "] " << sub << "\n";
|
||||
begin = end + 1;
|
||||
|
||||
// find second portion, until '\n'
|
||||
end = str.find('\n', begin);
|
||||
len = end - begin;
|
||||
if (end == std::string::npos)
|
||||
len = end;
|
||||
sub = str.substr(begin, len);
|
||||
std::cout << i << "|" << sub << "\n";
|
||||
begin = end + 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
// for (size_t i = 0; i < size; i++)
|
||||
// {
|
||||
// field.insert(request);
|
||||
// }
|
||||
|
||||
// GET /home.html HTTP/1.1
|
||||
// Host: developer.mozilla.org
|
||||
// User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
|
||||
// Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
// Accept-Language: en-US,en;q=0.5
|
||||
// Accept-Encoding: gzip, deflate, br
|
||||
// Referer: https://developer.mozilla.org/testpage.html
|
||||
// Connection: keep-alive
|
||||
// Upgrade-Insecure-Requests: 1
|
||||
// If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT
|
||||
// If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a"
|
||||
// Cache-Control: max-age=0
|
||||
|
||||
}
|
||||
void Webserv::_serve_file(Client *client, std::string page)
|
||||
{
|
||||
int page_fd;
|
||||
std::string to_send;
|
||||
std::string end_header = "\r\n\r\n";
|
||||
std::string body;
|
||||
std::stringstream strs;
|
||||
char buffer[1];
|
||||
|
||||
to_send = "HTTP/1.1 200 OK\n";
|
||||
to_send += "Content-Type: text/html;\n";
|
||||
to_send += "Content-Length: ";
|
||||
page_fd = open(page.c_str(), O_RDONLY);
|
||||
for (int ret = 1; ret > 0;)
|
||||
{
|
||||
ret = read(page_fd, buffer, 1);
|
||||
body += buffer;
|
||||
}
|
||||
strs << body.size();
|
||||
to_send += strs.str();
|
||||
to_send += end_header;
|
||||
to_send += body;
|
||||
if (::send(client->fd, to_send.c_str(), to_send.size(), 0) == -1)
|
||||
std::perror("err send()");
|
||||
}
|
||||
void Webserv::_exec_cgi_script(Client *client)
|
||||
{
|
||||
int save_stdout;
|
||||
char** env = new char*[5];
|
||||
char * const * nll = NULL;
|
||||
|
||||
// set env
|
||||
env[0] = strdup("PATH_INFO=/no");
|
||||
env[1] = strdup("REQUEST_METHOD=POST");
|
||||
env[2] = strdup("SERVER_PROTOCOL=HTTP/1.1");
|
||||
env[3] = strdup("CONTENT_LENGTH=665");
|
||||
env[4] = NULL;
|
||||
// save STDOUT
|
||||
save_stdout = dup(STDOUT_FILENO);
|
||||
// inside child process
|
||||
if (fork() == 0)
|
||||
{
|
||||
dup2(client->fd, STDOUT_FILENO);
|
||||
// execve("./srcs/cgi-bin/cgi_cpp.cgi", nll, env);
|
||||
execve("./srcs/cgi-bin/php-cgi", nll, env);
|
||||
}
|
||||
// inside parent process
|
||||
else
|
||||
waitpid(-1, NULL, 0);
|
||||
// restore stdout
|
||||
dup2(save_stdout, STDOUT_FILENO);
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
int Webserv::_epoll_update(int fd, uint32_t events, int op, void *ptr)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
std::memset(&ev, 0, sizeof ev);
|
||||
ev.events = events;
|
||||
ev.data.ptr = ptr;
|
||||
if (::epoll_ctl(_epfd, op, fd, &ev) == -1)
|
||||
{
|
||||
std::perror("err _epoll_update(): ");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void Webserv::_handle_last_signal()
|
||||
{
|
||||
if (g_last_signal == SIGPIPE)
|
||||
{
|
||||
std::cerr << "SIGPIPE\n";
|
||||
if (_actual_client)
|
||||
{
|
||||
_close_client(_actual_client->fd);
|
||||
_actual_client = NULL;
|
||||
}
|
||||
}
|
||||
else if (g_last_signal == SIGINT)
|
||||
{
|
||||
g_run = false;
|
||||
}
|
||||
g_last_signal = 0;
|
||||
}
|
||||
|
||||
void Webserv::_close_client(int fd)
|
||||
{
|
||||
std::vector<Client>::iterator it = _clients.begin();
|
||||
while (it != _clients.end())
|
||||
{
|
||||
if (it->fd == fd)
|
||||
{
|
||||
// _epoll_update(fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(fd) == -1)
|
||||
std::perror("err close(): ");
|
||||
else
|
||||
std::cerr << "close fd " << fd << "\n";
|
||||
_clients.erase(it);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_close_all_clients()
|
||||
{
|
||||
while (!_clients.empty())
|
||||
{
|
||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(_clients.back().fd) == -1)
|
||||
std::perror("err close(): ");
|
||||
else
|
||||
std::cerr << "close fd " << _clients.back().fd << "\n";
|
||||
_clients.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// Init functions //
|
||||
|
||||
void Webserv::_bind(int socket_fd, in_port_t port)
|
||||
{
|
||||
// cast invalid ? how to ?
|
||||
// const struct sockaddr* cast_test = static_cast<const struct sockaddr*>(addr);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
std::memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = ::htons(port);
|
||||
addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ?
|
||||
|
||||
if (::bind(socket_fd, (const sockaddr*)&addr, sizeof addr) == -1)
|
||||
{
|
||||
std::perror("err bind(): ");
|
||||
throw std::runtime_error("Socket bind");
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
{
|
||||
if (::listen(socket_fd, max_connections) == -1)
|
||||
{
|
||||
std::perror("err listen(): ");
|
||||
throw std::runtime_error("Socket listen");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
#! /usr/bin/php
|
||||
|
||||
<?php
|
||||
echo "false: 300\r\n";
|
||||
echo "server: Webserv/0.2\r\n";
|
||||
echo "Status: 300\r\n";
|
||||
echo "\r\n";
|
||||
echo "BEGIN PHP-CGI\n-----------\n\n";
|
||||
//phpinfo();
|
||||
|
||||
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
|
||||
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
|
||||
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
|
||||
@@ -26,4 +27,3 @@
|
||||
// echo $_POST['REQUEST_METHOD'];
|
||||
echo "\n\n-----------\nEND PHP-CGI\n\n";
|
||||
?>
|
||||
|
||||
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";
|
||||
?>
|
||||
25
srcs/colors.h
Normal file
25
srcs/colors.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef COLORS_H
|
||||
# define COLORS_H
|
||||
|
||||
# define GRAY "\e[0;30m"
|
||||
# define RED "\e[0;31m"
|
||||
# define GREEN "\e[0;32m"
|
||||
# define YELLOW "\e[0;33m"
|
||||
# define BLUE "\e[0;34m"
|
||||
# define PURPLE "\e[0;35m"
|
||||
# define CYAN "\e[0;36m"
|
||||
# define WHITE "\e[0;37m"
|
||||
|
||||
# define B_GRAY "\e[1;30m"
|
||||
# define B_RED "\e[1;31m"
|
||||
# define B_GREEN "\e[1;32m"
|
||||
# define B_YELLOW "\e[1;33m"
|
||||
# define B_BLUE "\e[1;34m"
|
||||
# define B_PURPLE "\e[1;35m"
|
||||
# define B_CYAN "\e[1;36m"
|
||||
# define B_WHITE "\e[1;37m"
|
||||
|
||||
# define RESET "\e[0m"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
|
||||
/* Updated: 2022/07/27 19:27:57 by me ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef CONFIGPARSER_HPP
|
||||
# define CONFIGPARSER_HPP
|
||||
|
||||
# include "ServerConfig.hpp"
|
||||
# include "LocationConfig.hpp"
|
||||
# include "MethodType.hpp"
|
||||
# include "utils.hpp"
|
||||
|
||||
# include <map>
|
||||
@@ -23,18 +11,19 @@
|
||||
# include <exception> // exception, what
|
||||
# include <stdexcept> // runtime_error, invalid_argument
|
||||
# include <string> // string
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // strtol, stroul
|
||||
# include <iostream> // cout, cin
|
||||
# include <fstream> // ifstream
|
||||
//# include <unistd.h> // access()
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include <dirent.h> // opendir(), doesn't work...
|
||||
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
||||
# include <algorithm> // sort() in Post
|
||||
|
||||
class ConfigParser {
|
||||
|
||||
public:
|
||||
|
||||
// canonical
|
||||
// canonical?
|
||||
|
||||
ConfigParser(const char* path); // a string?
|
||||
~ConfigParser();
|
||||
@@ -42,16 +31,15 @@ public:
|
||||
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
|
||||
// ConfigParser & operator=(const ConfigParser& rhs);
|
||||
|
||||
// void parse(); // return void cuz throw exceptions.
|
||||
std::vector<ServerConfig> * parse(); // const?
|
||||
// std::vector<ServerConfig> parse(); // const?
|
||||
|
||||
// other parses?
|
||||
|
||||
// i thought if it were an instance of this class you could call
|
||||
// private member functions from anywhere...
|
||||
void _print_content() const;
|
||||
|
||||
|
||||
private:
|
||||
std::string _content;
|
||||
|
||||
@@ -76,31 +64,10 @@ private:
|
||||
std::string _get_rest_of_line(size_t *curr); // const?
|
||||
|
||||
|
||||
// why static? it's an enum...
|
||||
static MethodType _str_to_method_type(std::string str);
|
||||
|
||||
|
||||
|
||||
// some sort of post processing...
|
||||
|
||||
/* Post Processing */
|
||||
void _post_processing(std::vector<ServerConfig> *servers);
|
||||
|
||||
|
||||
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
|
||||
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
99
srcs/config/LocationConfig.hpp
Normal file
99
srcs/config/LocationConfig.hpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* LocationConfig.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/08/12 18:12:23 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef LOCATIONCONFIG_HPP
|
||||
# define LOCATIONCONFIG_HPP
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string>
|
||||
# include <iostream>
|
||||
# include <sys/stat.h> // stat()
|
||||
|
||||
# include "utils.hpp"
|
||||
|
||||
// again, struct instead?
|
||||
class LocationConfig
|
||||
{
|
||||
public:
|
||||
// canonic stuff?
|
||||
|
||||
std::string path; // /path and /path/ are fine
|
||||
// i remove trailing / systematically
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
unsigned int allow_methods;
|
||||
std::vector<std::string> cgi_ext; // php not .php
|
||||
bool autoindex;
|
||||
|
||||
std::string upload_dir;
|
||||
|
||||
int redirect_status; // only in location
|
||||
std::string redirect_uri; // only 1 per location
|
||||
|
||||
void print_all() // const?
|
||||
{
|
||||
std::cout << "\nPRINTING A LOCATION\n";
|
||||
|
||||
std::cout << "Path: " << path << '\n';
|
||||
std::cout << "root: " << root << '\n';
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
|
||||
std::cout << "Skipping index...\n";
|
||||
|
||||
std::cout << "Location allow_methods: ";
|
||||
std::cout << ::http_methods_to_str(allow_methods) << "\n";
|
||||
|
||||
std::cout << "Skipping redirect status etc...\n";
|
||||
|
||||
std::cout << "------\n";
|
||||
}
|
||||
|
||||
// works a lot better than using a compare function...
|
||||
bool operator<(const LocationConfig& rhs) const
|
||||
{
|
||||
int comp_lhs = 0;
|
||||
int comp_rhs = 0;
|
||||
size_t tmp = 0;
|
||||
|
||||
while ((tmp = this->path.find_first_of("/", tmp)) != NPOS)
|
||||
{
|
||||
++tmp;
|
||||
++comp_lhs;
|
||||
}
|
||||
if (path[path.find_last_of("/") + 1] != '\0')
|
||||
++comp_lhs;
|
||||
tmp = 0;
|
||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != NPOS)
|
||||
{
|
||||
++tmp;
|
||||
++comp_rhs;
|
||||
}
|
||||
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
|
||||
++comp_rhs;
|
||||
|
||||
return (comp_lhs < comp_rhs); // right comparison ? not <= ?
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
68
srcs/config/ServerConfig.hpp
Normal file
68
srcs/config/ServerConfig.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
#ifndef SERVERCONFIG_HPP
|
||||
# define SERVERCONFIG_HPP
|
||||
|
||||
# include "utils.hpp"
|
||||
# include "LocationConfig.hpp"
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string> // string
|
||||
# include <iostream> // cout, cin
|
||||
|
||||
// a class that's all public? just so we have options?
|
||||
class ServerConfig
|
||||
{
|
||||
public:
|
||||
// do i need some canonic stuff?
|
||||
|
||||
std::vector<std::string> server_name;
|
||||
// we could shove default in here if we wanted to...
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
|
||||
std::string root; // ./www/ or www work www/ and www work
|
||||
// i do remove trailing / tho
|
||||
|
||||
size_t client_body_limit; // set to default max if none set
|
||||
// 413 (Request Entity Too Large) if exceeded
|
||||
// default is 1m 1 000 000 ?
|
||||
|
||||
std::vector<std::string> index;
|
||||
std::map<int, std::string> error_pages;
|
||||
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
|
||||
void print_all() // const?
|
||||
{
|
||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
||||
|
||||
for (size_t i = 0; i < server_name.size(); i++)
|
||||
std::cout << server_name[i] << " ";
|
||||
std::cout << "root: " << root << '\n';
|
||||
std::cout << "index: ";
|
||||
for (size_t i = 0; i < index.size(); i++)
|
||||
std::cout << index[i] << " ";
|
||||
std::cout << "\nerror_pages: ";
|
||||
for(std::map<int, std::string>::iterator it = error_pages.begin(); \
|
||||
it != error_pages.end(); it++)
|
||||
std::cout << it->first << "--" << it->second << " ";
|
||||
// for (size_t i = 0; i < error_pages.size(); i++)
|
||||
// std::cout << error_pages->first << "--" << error_pages->second << " ";
|
||||
|
||||
// std::cout << "skiping Locations for now...\n";
|
||||
for (std::vector<LocationConfig>::iterator it = locations.begin(); it < locations.end(); it++)
|
||||
it->print_all();
|
||||
|
||||
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
||||
std::cout << "host: " << host << '\n';
|
||||
std::cout << "port: " << port << '\n';
|
||||
|
||||
std::cout << "\n----------\n";
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
|
||||
|
||||
// should i be sending & references?
|
||||
// const?
|
||||
std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
const std::string value)
|
||||
{
|
||||
@@ -13,92 +13,58 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
||||
// check values for ; at end and right number of words depending on key
|
||||
|
||||
// std::cout << "pre check\n";
|
||||
if (key.find_first_of(";") != std::string::npos)
|
||||
if (key.find_first_of(";") != NPOS)
|
||||
throw std::invalid_argument("bad config file arguments 2");
|
||||
|
||||
// there shouldn't be any tabs, right? not between values...
|
||||
if (value.find_first_of("\t") != std::string::npos)
|
||||
{
|
||||
std::cout << value << "\n";
|
||||
throw std::invalid_argument("bad config file arguments 3");
|
||||
}
|
||||
if (value.find_first_of("\t") != NPOS)
|
||||
throw std::invalid_argument("why would you put tabs between values");
|
||||
|
||||
size_t i = value.find_first_of(";");
|
||||
// so you can't have no ;
|
||||
// you can't have just ;
|
||||
// and you can't have a ; not at the end or several ;
|
||||
// in theory value_find_last_of should find the only ;
|
||||
if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \
|
||||
if (i == NPOS || (value.find_last_not_of(" \n")) != i \
|
||||
|| value.compare(";") == 0)
|
||||
throw std::invalid_argument("bad config file arguments 4");
|
||||
|
||||
|
||||
// we Trim value.
|
||||
// is this valid?
|
||||
// would it be better to shove the result directly in tmp_val?
|
||||
// like call substr in split?
|
||||
//value = value.substr(0, i - 1);
|
||||
return (value.substr(0, i));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// const?
|
||||
// assumes curr is on a space or \t or \n
|
||||
// get first word? next word? word?
|
||||
std::string ConfigParser::_get_first_word(size_t *curr)
|
||||
{
|
||||
size_t start;
|
||||
|
||||
// are these checks excessive?
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
||||
if ((*curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
std::string key = _content.substr(start, *curr - start);
|
||||
|
||||
return (key);
|
||||
}
|
||||
|
||||
// const?
|
||||
// also assumes curr is on a space \t or \n
|
||||
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
{
|
||||
size_t start;
|
||||
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
// std::cout << "start + 4 = " << _content.substr(start, 4) << "\n";
|
||||
// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n";
|
||||
|
||||
|
||||
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
|
||||
if ((*curr = _content.find_first_of("\n", start)) == NPOS)
|
||||
throw std::invalid_argument("bad config file arguments");
|
||||
|
||||
std::string values = _content.substr(start, *curr - start);
|
||||
|
||||
// std::cout << "rest of Line values: " << values << "\n";
|
||||
|
||||
return (values);
|
||||
}
|
||||
|
||||
|
||||
MethodType ConfigParser::_str_to_method_type(std::string str)
|
||||
{
|
||||
if (str == "GET")
|
||||
return GET;
|
||||
else if (str == "POST")
|
||||
return POST;
|
||||
else if (str == "DELETE")
|
||||
return DELETE;
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_print_content() const
|
||||
{
|
||||
std::cout << _content;
|
||||
}
|
||||
|
||||
// I might need to make my own Exceptions to throw...
|
||||
342
srcs/config/parser.cpp
Normal file
342
srcs/config/parser.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
// Default
|
||||
ConfigParser::ConfigParser()
|
||||
{
|
||||
std::cout << "Default Constructor\n";
|
||||
// don't use yet, you have no idea what the defaults are
|
||||
}
|
||||
|
||||
ConfigParser::ConfigParser(const char* path)
|
||||
{
|
||||
// std::cout << "Param Constructor\n";
|
||||
|
||||
std::ifstream file;
|
||||
std::string buf;
|
||||
size_t comment;
|
||||
|
||||
_content.clear();
|
||||
file.open(path);
|
||||
if (file.is_open())
|
||||
{
|
||||
while (!file.eof())
|
||||
{
|
||||
getline(file, buf);
|
||||
// remove # comments here.
|
||||
if ((comment = buf.find_first_of("#")) == NPOS)
|
||||
{
|
||||
// remove empty lines, i think...
|
||||
if ((buf.find_first_not_of(" \t")) != NPOS)
|
||||
_content.append(buf + '\n');
|
||||
}
|
||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||
{
|
||||
// check for comment at the end of the line
|
||||
std::string tmp = buf.substr(0, comment - 1);
|
||||
_content.append(tmp + '\n');
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("failed to open config");
|
||||
}
|
||||
|
||||
ConfigParser::~ConfigParser()
|
||||
{
|
||||
// do i need to destroy anything, won't it handle itself?
|
||||
}
|
||||
|
||||
/*
|
||||
ConfigParser & ConfigParser::operator=(const ConfigParser& rhs)
|
||||
{
|
||||
if (this == rhs) // * & ?
|
||||
return (*this); // * ?
|
||||
|
||||
// make some stuff equal
|
||||
return (*this);
|
||||
}
|
||||
*/
|
||||
|
||||
// const?
|
||||
std::vector<ServerConfig> * ConfigParser::parse()
|
||||
{
|
||||
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
||||
|
||||
size_t start = 0;
|
||||
size_t curr = _content.find_first_not_of(" \t\n", 0);
|
||||
|
||||
if (curr == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
while (curr != NPOS)
|
||||
{
|
||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||
throw std::invalid_argument("empty config file");
|
||||
std::string key = _content.substr(start, curr - start);
|
||||
if (key != "server")
|
||||
throw std::invalid_argument("bad config file arguments 1");
|
||||
ret->push_back(_parse_server(&curr));
|
||||
}
|
||||
_post_processing(ret);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
ServerConfig ConfigParser::_parse_server(size_t *start)
|
||||
{
|
||||
ServerConfig ret;
|
||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
if (curr == NPOS || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 1");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
// are there other things to check for?
|
||||
while (curr != NPOS) // here curr == { + 1
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
// now curr is on space after 1st word.
|
||||
if (key == "}")
|
||||
{
|
||||
// why +1 curr is already after it no?
|
||||
*start = _content.find_first_not_of(" \t\n", curr + 1);
|
||||
break ;
|
||||
}
|
||||
else if (key == "location")
|
||||
ret.locations.push_back(_parse_location(&curr));
|
||||
else
|
||||
{
|
||||
std::string values = _get_rest_of_line(&curr);
|
||||
// curr now should be \n
|
||||
_set_server_values(&ret, key, values);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
{
|
||||
LocationConfig ret;
|
||||
size_t curr = *start;
|
||||
// start is after the 1st word aka "location"
|
||||
|
||||
ret.autoindex = false;
|
||||
ret.redirect_status = 0;
|
||||
ret.allow_methods = 0;
|
||||
|
||||
ret.path = _get_first_word(&curr);
|
||||
if (ret.path[0] != '/')
|
||||
throw std::invalid_argument("Location path require a leading /");
|
||||
// ret.path.insert(0, "/");
|
||||
// in theory now curr should be right after the "path"
|
||||
|
||||
curr = _content.find_first_not_of(" \t\n", curr);
|
||||
|
||||
if (curr == NPOS || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 2");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
while (curr != NPOS)
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
std::string key = _get_first_word(&curr);
|
||||
// now curr is on space after 1st word.
|
||||
if (key == "}")
|
||||
{
|
||||
*start = curr;
|
||||
break ;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string values = _get_rest_of_line(&curr);
|
||||
// curr now should be \n
|
||||
_set_location_values(&ret, key, values);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// should i be sending pointers or references?
|
||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
// should i be sending pointers or references?
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
size_t size = tmp_val.size();
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "server_name" && server->server_name.empty())
|
||||
{
|
||||
for (std::vector<std::string>::iterator it = server->server_name.begin(); \
|
||||
it < server->server_name.end(); it++)
|
||||
{
|
||||
if (it->compare(tmp_val[0]) == 0)
|
||||
throw std::invalid_argument("server_name already exists");
|
||||
}
|
||||
server->server_name.push_back(tmp_val[0]);
|
||||
}
|
||||
else if (key == "listen" && size == 1 && server->host == "" \
|
||||
&& server->port == "")
|
||||
{
|
||||
if (tmp_val[0].find_first_of(":") == NPOS)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
server->host = "0.0.0.0";
|
||||
server->port = tmp_val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> tmp2 = ::split(tmp_val[0], ':');
|
||||
|
||||
std::vector<std::string> ip = ::split(tmp2[0], '.');
|
||||
if (ip.size() != 4)
|
||||
throw std::invalid_argument("bad host ip");
|
||||
for (size_t i = 0; i < ip.size(); i++)
|
||||
{
|
||||
if (!::isNumeric_btw(0, 255, ip[i]))
|
||||
throw std::invalid_argument("bad host ip");
|
||||
}
|
||||
if (!::isNumeric(tmp2[1]))
|
||||
throw std::invalid_argument("bad port number");
|
||||
server->host = tmp2[0];
|
||||
server->port = tmp2[1];
|
||||
}
|
||||
}
|
||||
else if (key == "root" && size == 1 && server->root == "")
|
||||
{
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
// tmp_val[0].push_back('/');
|
||||
server->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& server->client_body_limit == 0)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
server->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "error_page")
|
||||
{
|
||||
std::string path = tmp_val[tmp_val.size() - 1];
|
||||
for (unsigned long i = 0; i != tmp_val.size() - 1; i++)
|
||||
{
|
||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||
throw std::invalid_argument("invalid error code");
|
||||
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
|
||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||
throw std::invalid_argument("redeclaring error page");
|
||||
server->error_pages[status_code] = path;
|
||||
}
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
|
||||
|
||||
// should i be sending pointers or references?
|
||||
void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
// should i be sending pointers or references?
|
||||
value = _pre_set_val_check(key, value);
|
||||
|
||||
std::vector<std::string> tmp_val = ::split(value, ' ');
|
||||
size_t size = tmp_val.size();
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "root" && size == 1 && location->root == "")
|
||||
{
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
// tmp_val[0].push_back('/');
|
||||
location->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
location->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
else if (key == "index")
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
location->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && location->allow_methods == 0)
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
location->allow_methods |= m;
|
||||
}
|
||||
}
|
||||
else if (key == "cgi_ext")
|
||||
{
|
||||
for (size_t i = 0; i < tmp_val.size(); i++)
|
||||
{
|
||||
if (tmp_val[i][0] == '.')
|
||||
throw std::invalid_argument("cgi_ext should not have a leading '.'");
|
||||
location->cgi_ext.push_back(tmp_val[i]);
|
||||
}
|
||||
}
|
||||
else if (key == "redirect" && location->redirect_status == 0 \
|
||||
&& location->redirect_uri == "")
|
||||
{
|
||||
if (tmp_val.size() != 2)
|
||||
throw std::invalid_argument("wrong number of values");
|
||||
if (tmp_val[0] != "301" && tmp_val[0] != "302"
|
||||
&& tmp_val[0] != "303" && tmp_val[0] != "307"
|
||||
&& tmp_val[0] != "308")
|
||||
throw std::invalid_argument("bad redirect status");
|
||||
// std::cout << tmp_val[1] << '\n';
|
||||
if (tmp_val[1].compare(0, 7, "http://")
|
||||
&& tmp_val[1].compare(0, 8, "https://"))
|
||||
throw std::invalid_argument("bad redirect uri");
|
||||
|
||||
location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
||||
{
|
||||
// what checks to do?
|
||||
// add trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] != '/')
|
||||
tmp_val[0].push_back('/');
|
||||
location->upload_dir = tmp_val[0];
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
89
srcs/config/postProcessing.cpp
Normal file
89
srcs/config/postProcessing.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port are Mandatory
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// root is mandatory
|
||||
if (it->root == "")
|
||||
throw std::invalid_argument("Config file needs a root");
|
||||
|
||||
// index is mandatory in Server
|
||||
if (it->index.empty())
|
||||
throw std::invalid_argument("Config file needs an Index");
|
||||
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
|
||||
if (!_find_root_path_location(it->locations))
|
||||
{
|
||||
LocationConfig tmp;
|
||||
|
||||
tmp.path = "/";
|
||||
tmp.root = it->root;
|
||||
tmp.index = it->index;
|
||||
tmp.allow_methods = ANY_METHODS;
|
||||
tmp.autoindex = false;
|
||||
tmp.redirect_status = 0;
|
||||
it->locations.push_back(tmp);
|
||||
}
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
if (it_l->allow_methods == UNKNOWN)
|
||||
it_l->allow_methods = ANY_METHODS;
|
||||
|
||||
if (it_l->index.empty() && it_l->autoindex == false)
|
||||
it_l->index = it->index;
|
||||
|
||||
// nothing to be done for cgi_ext, error_pages, redirect
|
||||
|
||||
// if (eval_file_type(it_l->root) == IS_DIR
|
||||
// && it_l->path[it_l->path.size() - 1] != '/')
|
||||
// it_l->path.push_back('/');
|
||||
if (it_l->path[it_l->path.size() - 1] == '/'
|
||||
&& it_l->path.size() > 1)
|
||||
it_l->path.erase(it_l->path.size() - 1);
|
||||
|
||||
++it_l;
|
||||
}
|
||||
std::sort(it->locations.begin(), it->locations.end());
|
||||
std::reverse(it->locations.begin(), it->locations.end());
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// const?
|
||||
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
|
||||
{
|
||||
std::vector<LocationConfig>::const_iterator it = locations.begin();
|
||||
|
||||
while (it != locations.end())
|
||||
{
|
||||
if (it->path.compare("/") == 0)
|
||||
return true;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
# include <sstream>
|
||||
# include <string.h>
|
||||
|
||||
char* itoa(int n)
|
||||
{
|
||||
std::stringstream strs;
|
||||
char * str;
|
||||
|
||||
strs << n;
|
||||
str = (char*)(strs.str().c_str());
|
||||
return (str);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ int main(int ac, char **av)
|
||||
|
||||
ConfigParser configParser(config.c_str());
|
||||
|
||||
configParser._print_content();
|
||||
// configParser._print_content();
|
||||
|
||||
// i don't love that servers has to be a pointer...
|
||||
std::vector<ServerConfig>* servers = configParser.parse();
|
||||
@@ -27,8 +27,9 @@ int main(int ac, char **av)
|
||||
// use an iterator you moron
|
||||
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
||||
{
|
||||
(void)0;
|
||||
// std::cout << it->server_name << " ";
|
||||
it->print_all();
|
||||
// it->print_all();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +45,7 @@ int main(int ac, char **av)
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
285
srcs/utils.cpp
285
srcs/utils.cpp
@@ -1,6 +1,22 @@
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
void throw_test()
|
||||
{
|
||||
static int i = 0;
|
||||
++i;
|
||||
if (i % 8 == 0)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
// notice : the use of getline make it such as
|
||||
// it doesn't identify multiple delim as one :
|
||||
// " something \n else " -> 1 - something
|
||||
// 2 - else
|
||||
// is not the same as :
|
||||
// " something \n\n else " -> 1 - something
|
||||
// 2 -
|
||||
// 3 - else
|
||||
std::vector<std::string> split(std::string input, char delimiter)
|
||||
{
|
||||
std::vector<std::string> answer;
|
||||
@@ -13,14 +29,62 @@ std::vector<std::string> split(std::string input, char delimiter)
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string trim(std::string str, char c)
|
||||
std::vector<std::string>
|
||||
split_trim(std::string input, std::string delim, char ctrim)
|
||||
{
|
||||
str = str.substr(str.find_first_not_of(c));
|
||||
str = str.substr(0, str.find_last_not_of(c) + 1);
|
||||
std::vector<std::string> split_str;
|
||||
std::string tmp;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while (end != 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//// trim a set of char
|
||||
//std::string trim(std::string str, std::string del)
|
||||
//{
|
||||
// std::string new_str;
|
||||
//
|
||||
// while (new_str.compare(str) != 0)
|
||||
// {
|
||||
// for (size_t i = 0; i < del.size(); i++)
|
||||
// trim(str, del[i]);
|
||||
// }
|
||||
// return str;
|
||||
//}
|
||||
|
||||
std::string itos(int n)
|
||||
{
|
||||
std::stringstream strs;
|
||||
@@ -46,9 +110,222 @@ bool isNumeric_btw(int low, int high, std::string str)
|
||||
if (std::isdigit(str[i]) == false)
|
||||
return false;
|
||||
}
|
||||
int n = std::atoi(str.c_str());
|
||||
int n = std::strtol(str.c_str(), NULL, 10);
|
||||
if (n < low || n > high)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
http_method str_to_http_method(std::string &str)
|
||||
{
|
||||
if (str == "GET")
|
||||
return GET;
|
||||
else if (str == "POST")
|
||||
return POST;
|
||||
else if (str == "DELETE")
|
||||
return DELETE;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
std::string http_methods_to_str(unsigned int methods)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
if (methods & GET)
|
||||
str.append("GET");
|
||||
if (methods & POST)
|
||||
{
|
||||
if (!str.empty())
|
||||
str.append(", ");
|
||||
str.append("POST");
|
||||
}
|
||||
if (methods & DELETE)
|
||||
{
|
||||
if (!str.empty())
|
||||
str.append(", ");
|
||||
str.append("DELETE");
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
# include <iostream>
|
||||
|
||||
file_type eval_file_type(const std::string &path)
|
||||
{
|
||||
const char *tmp_path = path.c_str();
|
||||
struct stat s;
|
||||
|
||||
if (stat(tmp_path, &s) != -1)
|
||||
{
|
||||
if (S_ISREG(s.st_mode))
|
||||
return (IS_FILE);
|
||||
else if (S_ISDIR(s.st_mode))
|
||||
return (IS_DIR);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::perror("err stat()");
|
||||
}
|
||||
|
||||
return (IS_OTHER);
|
||||
}
|
||||
|
||||
size_t eval_file_mode(std::string path, int mode)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
return 404; // NOT_FOUND, file doesn't exist
|
||||
}
|
||||
|
||||
if (access(path.c_str(), mode) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
return 403; // FORBIDDEN, file doesn't have execution permission
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
replace_all_substr(
|
||||
std::string &str,
|
||||
const std::string &ori_substr,
|
||||
const std::string &new_substr)
|
||||
{
|
||||
if (ori_substr.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while (1)
|
||||
{
|
||||
pos = str.find(ori_substr, pos);
|
||||
if (pos == NPOS)
|
||||
break;
|
||||
str.replace(pos, ori_substr.size(), new_substr);
|
||||
pos += new_substr.size();
|
||||
}
|
||||
}
|
||||
|
||||
std::string str_tolower(std::string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
// identify a line in a string, by delim (ex. '\n')
|
||||
// delete this line from the string (and the following nl sequence characters)
|
||||
// and return the deleted line (without the followinf nl sequence characters)
|
||||
std::string
|
||||
extract_line(std::string & str, size_t pos, std::string delim)
|
||||
{
|
||||
std::string del_str;
|
||||
size_t begin;
|
||||
size_t end;
|
||||
size_t len;
|
||||
|
||||
begin = str.rfind(delim, pos);
|
||||
if (begin == NPOS)
|
||||
begin = 0;
|
||||
else if (begin < pos)
|
||||
begin += delim.size();
|
||||
|
||||
end = str.find(delim, pos);
|
||||
len = end;
|
||||
if (end != NPOS)
|
||||
len = end - begin;
|
||||
|
||||
del_str = str.substr(begin, len);
|
||||
str.erase(begin, len + delim.size());
|
||||
return del_str;
|
||||
}
|
||||
|
||||
// get a line in a string, by delim
|
||||
// same as extract, except it doesn't delete it
|
||||
std::string get_line(std::string str, size_t pos, std::string delim)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
ret = ::extract_line(str, pos, delim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
parse_http_headers (
|
||||
std::string headers,
|
||||
std::map<std::string, std::string> & fields )
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
std::vector<std::string>::iterator it;
|
||||
std::vector<std::string>::iterator it_end;
|
||||
size_t err = 0;
|
||||
size_t pos;
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
||||
list = ::split_trim(headers, CRLF, ' ');
|
||||
|
||||
it_end = list.end();
|
||||
for (it = list.begin(); it != it_end; it++)
|
||||
{
|
||||
pos = (*it).find(':');
|
||||
if (pos == NPOS)
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
}
|
||||
key = (*it).substr(0, pos);
|
||||
if ( key.find(' ') != NPOS )
|
||||
{
|
||||
err++;
|
||||
continue;
|
||||
}
|
||||
// bad idea, in cgi we need to have the original value
|
||||
// key = ::str_tolower(key); // to make "key" case_insensitive
|
||||
val = (*it).substr(pos + 1);
|
||||
val = ::trim(val, ' ');
|
||||
fields.insert( std::pair<std::string, std::string>(key, val) );
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void str_map_key_tolower(std::map<std::string, std::string> & mp)
|
||||
{
|
||||
std::map<std::string, std::string> new_mp;
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
for (it = mp.begin(); it != mp.end(); it++)
|
||||
{
|
||||
key = it->first;
|
||||
value = it->second;
|
||||
key = ::str_tolower(key);
|
||||
new_mp.insert( std::pair<std::string, std::string>(key, value) );
|
||||
}
|
||||
mp.swap(new_mp);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
void print_special(std::string str)
|
||||
{
|
||||
char c;
|
||||
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
{
|
||||
c = str[i];
|
||||
if (c == '\r')
|
||||
std::cout << YELLOW << "\\r" << RESET;
|
||||
else if (c == '\n')
|
||||
std::cout << YELLOW << "\\n" << RESET << "\n";
|
||||
else
|
||||
std::cout << c;
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const listen_socket& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
bool operator==(int fd, const listen_socket& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
|
||||
@@ -3,14 +3,76 @@
|
||||
# define UTILS_HPP
|
||||
|
||||
# include <vector>
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <climits> // LONG_MAX
|
||||
# include <cerrno> // errno
|
||||
# include <sys/stat.h> // stat()
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
# include <cstdio> // perror, fflush
|
||||
# include <unistd.h> // close, access
|
||||
# include "colors.h" // for debug print_special
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter);
|
||||
bool isNumeric(std::string str);
|
||||
bool isNumeric_btw(int low, int high, std::string str);
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
# define CRLF_SIZE 2
|
||||
# define NPOS std::string::npos
|
||||
|
||||
/* Equivalent for end of http header size :
|
||||
** std::string(CRLF CRLF).size();
|
||||
** sizeof(CRLF CRLF) - 1;
|
||||
** CRLF_SIZE*2
|
||||
*/
|
||||
|
||||
enum file_type
|
||||
{
|
||||
IS_OTHER,
|
||||
IS_FILE,
|
||||
IS_DIR
|
||||
};
|
||||
|
||||
enum http_method
|
||||
{
|
||||
UNKNOWN = 0b0,
|
||||
GET = 1 << 0,
|
||||
POST = 1 << 1,
|
||||
DELETE = 1 << 2,
|
||||
ANY_METHODS = 0b11111111,
|
||||
// ALL_METHODS = 0b11111111,
|
||||
// i would prefer this...
|
||||
};
|
||||
|
||||
struct listen_socket
|
||||
{
|
||||
int fd;
|
||||
std::string host;
|
||||
std::string port;
|
||||
};
|
||||
bool operator==(const listen_socket& lhs, int fd);
|
||||
bool operator==(int fd, const listen_socket& rhs);
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter);
|
||||
std::vector<std::string> split_trim(std::string input, std::string delim = "\n", char ctrim = '\0');
|
||||
bool isNumeric(std::string str);
|
||||
bool isNumeric_btw(int low, int high, std::string str);
|
||||
std::string itos(int n);
|
||||
std::string trim(std::string str, char c);
|
||||
std::string trim(std::string str, char del);
|
||||
http_method str_to_http_method(std::string &str);
|
||||
std::string http_methods_to_str(unsigned int methods);
|
||||
file_type eval_file_type(const std::string &path);
|
||||
size_t eval_file_mode(std::string path, int mode);
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||
std::string str_tolower(std::string str);
|
||||
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
||||
std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n");
|
||||
size_t parse_http_headers (std::string headers, std::map<std::string, std::string> & fields );
|
||||
void str_map_key_tolower(std::map<std::string, std::string> & mp);
|
||||
void throw_test();
|
||||
// debug
|
||||
void print_special(std::string str);
|
||||
|
||||
#endif
|
||||
|
||||
141
srcs/webserv/Webserv.hpp
Normal file
141
srcs/webserv/Webserv.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
#ifndef WEBSERV_HPP
|
||||
# define WEBSERV_HPP
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <exception> // exception, what
|
||||
# include <stdexcept> // runtime_error, invalid_argument
|
||||
# include <sys/epoll.h> // epoll
|
||||
# include <fcntl.h> // fcntl
|
||||
# include <sys/wait.h> // waitpid
|
||||
# include <csignal> // signal
|
||||
# include <fstream> // ifstream
|
||||
# include <sstream> // stringstream
|
||||
# include <cerrno> // errno
|
||||
# include <unistd.h> // close, access
|
||||
# include <iostream> // cout, cin
|
||||
# include <cstring> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
|
||||
# include <algorithm> // find
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // strtol, strtoul
|
||||
# include <dirent.h> // opendir()
|
||||
# include <locale> // isalpha, local
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
# include "http_status.hpp"
|
||||
# include "autoindex.hpp"
|
||||
# include "colors.h"
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
void signal_handler(int signum);
|
||||
|
||||
// these might only be TMP
|
||||
# define FAILURE -1
|
||||
# define SUCCESS 1
|
||||
|
||||
# define MIME_TYPE_DEFAULT "application/octet-stream"
|
||||
|
||||
class Webserv
|
||||
{
|
||||
public:
|
||||
// base.cpp
|
||||
Webserv();
|
||||
// Webserv(Webserv const &src);
|
||||
|
||||
// what should it take as arg, *, &, ?
|
||||
// Webserv(std::vector<ServerConfig>& servers);
|
||||
|
||||
~Webserv();
|
||||
// Webserv &operator=(Webserv const &rhs);
|
||||
|
||||
// init.cpp
|
||||
void init_virtual_servers(std::vector<ServerConfig>* servers);
|
||||
// run_loop.cpp
|
||||
void run();
|
||||
|
||||
private:
|
||||
int _epfd;
|
||||
std::vector<listen_socket> _listen_sockets;
|
||||
std::vector<ServerConfig> _servers;
|
||||
std::vector<Client> _clients;
|
||||
std::map<int, std::string> _http_status;
|
||||
std::map<std::string, std::string> _mime_types;
|
||||
|
||||
// accept.cpp
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
std::map<std::string, std::string>
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client, std::string &path);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
// ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
||||
// const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
// method_get.cpp
|
||||
|
||||
// move later
|
||||
std::string _replace_url_root(Client *client, std::string path);
|
||||
|
||||
void _get(Client *client, std::string &path);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
void _autoindex(Client *client, const std::string &path);
|
||||
// method_post.cpp
|
||||
void _post(Client *client, const std::string &path);
|
||||
void _post_file(Client *client, const std::string &path);
|
||||
// method_delete.cpp
|
||||
void _delete(Client *client, const std::string &path);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client, std::string path);
|
||||
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
|
||||
std::string _exec_cgi(Client *client);
|
||||
char** _set_env(Client *client);
|
||||
char* _dup_env(std::string var, std::string val);
|
||||
char* _dup_env(std::string var, int i);
|
||||
std::string _exec_script(Client *client, char **env);
|
||||
void _check_script_output(Client *client, std::string & output);
|
||||
void _check_script_status(Client *client, std::string & output);
|
||||
void _check_script_fields(Client *client, std::string & output);
|
||||
void _add_script_body_length_header(std::string & output);
|
||||
void _remove_body_leading_empty_lines(std::string & output);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
// signal.cpp
|
||||
void _handle_last_signal();
|
||||
// close.cpp
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
void _close_all_listen_sockets();
|
||||
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
|
||||
void _handle_epoll_error_client(uint32_t events, int fd);
|
||||
// init.cpp
|
||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
||||
void _listen(int socket_fd, unsigned int max_connections);
|
||||
void _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
// timeout.cpp
|
||||
void _timeout();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_accept_connection(int fd)
|
||||
void Webserv::_accept_connection(listen_socket &lsocket)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
std::map<std::string, std::string> infos;
|
||||
|
||||
std::cerr << "accept()\n";
|
||||
addr_len = sizeof addr;
|
||||
accepted_fd = ::accept(fd, (sockaddr*)&addr, &addr_len);
|
||||
accepted_fd = ::accept(lsocket.fd, (sockaddr*)&addr, &addr_len);
|
||||
if (accepted_fd == -1)
|
||||
{
|
||||
std::perror("err accept()");
|
||||
@@ -19,8 +20,22 @@ void Webserv::_accept_connection(int fd)
|
||||
}
|
||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
_clients.push_back(Client());
|
||||
_clients.back().fd = accepted_fd;
|
||||
|
||||
infos = _extract_infos(addr);
|
||||
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||
_clients.push_back(client);
|
||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>
|
||||
Webserv::_extract_infos(struct sockaddr_in addr)
|
||||
{
|
||||
struct in_addr ip_conversion;
|
||||
std::map<std::string, std::string> infos;
|
||||
|
||||
infos["port"] = ::itos( addr.sin_port );
|
||||
ip_conversion.s_addr = addr.sin_addr.s_addr;
|
||||
infos["ip"] = inet_ntoa( ip_conversion );
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
28
srcs/webserv/autoindex.hpp
Normal file
28
srcs/webserv/autoindex.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#ifndef AUTOINDEX_HPP
|
||||
# define AUTOINDEX_HPP
|
||||
|
||||
# define AUTOINDEX_START \
|
||||
"<!DOCTYPE html>"\
|
||||
"<html>"\
|
||||
"<head>"\
|
||||
"<title>Index of "
|
||||
|
||||
# define AUTOINDEX_MID1 \
|
||||
"</title>"\
|
||||
"</head>"\
|
||||
"<body>" \
|
||||
"<h1>Index of "
|
||||
|
||||
# define AUTOINDEX_MID2 \
|
||||
"</h1>"\
|
||||
"<hr>"\
|
||||
"<pre>"
|
||||
|
||||
# define AUTOINDEX_END \
|
||||
"</pre>"\
|
||||
"<hr>"\
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,9 @@ Webserv::Webserv()
|
||||
throw std::runtime_error("Epoll init");
|
||||
}
|
||||
|
||||
_init_http_status_map();
|
||||
_init_mime_types_map();
|
||||
|
||||
std::signal(SIGPIPE, signal_handler);
|
||||
std::signal(SIGINT, signal_handler);
|
||||
}
|
||||
@@ -42,10 +45,9 @@ Webserv::Webserv(std::vector<ServerConfig>* servers)
|
||||
|
||||
Webserv::~Webserv()
|
||||
{
|
||||
//close(_socket_fd);
|
||||
// TODO : CLOSE ALL _listen_sockets
|
||||
close(_epfd);
|
||||
_close_all_clients();
|
||||
_close_all_listen_sockets();
|
||||
std::cerr << "Server destroyed\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,80 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
bool Webserv::_is_cgi(Client *client)
|
||||
bool Webserv::_is_cgi(Client *client, std::string path)
|
||||
{
|
||||
if (client->get_path().find("/cgi-bin/") != std::string::npos)
|
||||
return true;
|
||||
std::string script_path;
|
||||
size_t file_type;
|
||||
size_t file_mode;
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos != NPOS)
|
||||
{
|
||||
pos = _cgi_pos(client, path, pos);
|
||||
if (pos == NPOS)
|
||||
break;
|
||||
client->fill_script_path(path, pos);
|
||||
script_path = "." + client->get_rq_script_path();
|
||||
file_type = ::eval_file_type(script_path);
|
||||
if (file_type == IS_DIR) // but what if it's a symlink ?
|
||||
continue;
|
||||
if (file_type == IS_FILE)
|
||||
{
|
||||
file_mode = ::eval_file_mode( script_path, X_OK );
|
||||
if (!file_mode)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
client->clear_script();
|
||||
client->status = file_mode; // 404 not_found OR 403 forbidden
|
||||
return false;
|
||||
}
|
||||
|
||||
void Webserv::_exec_cgi(Client *client)
|
||||
size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
|
||||
{
|
||||
char** env;
|
||||
std::vector<std::string> v_ext;
|
||||
std::vector<std::string>::const_iterator it;
|
||||
std::vector<std::string>::const_iterator it_end;
|
||||
size_t len;
|
||||
std::locale loc; // for isalpha()
|
||||
|
||||
v_ext = client->assigned_location->cgi_ext;
|
||||
if (v_ext.empty())
|
||||
return NPOS;
|
||||
it_end = client->assigned_location->cgi_ext.end();
|
||||
while (pos < path.size())
|
||||
{
|
||||
if (path.compare(pos, 2, "./") == 0)
|
||||
pos += 2;
|
||||
pos = path.find('.', pos);
|
||||
if (pos == NPOS)
|
||||
return pos;
|
||||
it = client->assigned_location->cgi_ext.begin();
|
||||
for ( ; it != it_end; ++it)
|
||||
{
|
||||
len = (*it).size();
|
||||
if (path.compare(pos + 1, len, *it) == 0)
|
||||
if ( !std::isalpha(path[pos + 1 + len], loc) )
|
||||
return pos + 1 + len;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return NPOS;
|
||||
}
|
||||
|
||||
std::string Webserv::_exec_cgi(Client *client)
|
||||
{
|
||||
char** env;
|
||||
std::string script_output;
|
||||
|
||||
env = _set_env(client);
|
||||
_exec_script(client, env);
|
||||
// _construct_response(client);
|
||||
script_output = _exec_script(client, env);
|
||||
|
||||
for (int i = 0; env[i]; i++)
|
||||
delete[] env[i];
|
||||
delete[] env;
|
||||
|
||||
return script_output;
|
||||
}
|
||||
|
||||
char* Webserv::_dup_env(std::string var, std::string val = "")
|
||||
@@ -25,56 +85,220 @@ char* Webserv::_dup_env(std::string var, std::string val = "")
|
||||
return ( strdup(str.c_str()) );
|
||||
}
|
||||
|
||||
char* Webserv::_dup_env(std::string var, int i)
|
||||
{
|
||||
std::string str;
|
||||
std::string val;
|
||||
|
||||
val = ::itos(i);
|
||||
str = var + "=" + val;
|
||||
// TODO change strdup for something with new
|
||||
return ( strdup(str.c_str()) );
|
||||
}
|
||||
|
||||
char** Webserv::_set_env(Client *client)
|
||||
{
|
||||
char** env = new char*[19];
|
||||
|
||||
env[0] = _dup_env("AUTH_TYPE");
|
||||
env[1] = _dup_env("CONTENT_LENGTH", "665");
|
||||
env[2] = _dup_env("CONTENT_TYPE");
|
||||
env[3] = _dup_env("GATEWAY_INTERFACE");
|
||||
env[4] = _dup_env("PATH_INFO");
|
||||
env[5] = _dup_env("PATH_TRANSLATED");
|
||||
env[6] = _dup_env("QUERY_STRING");
|
||||
env[7] = _dup_env("REMOTE_ADDR");
|
||||
env[8] = _dup_env("REMOTE_HOST", client->get_headers("Host")); // just test
|
||||
env[9] = _dup_env("REMOTE_IDENT");
|
||||
env[10] = _dup_env("REMOTE_USER");
|
||||
env[11] = _dup_env("REQUEST_METHOD", client->get_method());
|
||||
env[12] = _dup_env("SCRIPT_NAME");
|
||||
env[13] = _dup_env("SERVER_NAME");
|
||||
env[14] = _dup_env("SERVER_PORT");
|
||||
env[15] = _dup_env("SERVER_PROTOCOL", client->get_version());
|
||||
env[16] = _dup_env("SERVER_SOFTWARE");
|
||||
env[17] = _dup_env("REDIRECT_STATUS");
|
||||
env[0] = _dup_env("AUTH_TYPE"); // authentification not supported
|
||||
env[1] = _dup_env("CONTENT_LENGTH" , client->get_rq_body().size());
|
||||
env[2] = _dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type"));
|
||||
env[3] = _dup_env("GATEWAY_INTERFACE" , "CGI/1.1"); // https://www.rfc-editor.org/rfc/rfc3875
|
||||
env[4] = _dup_env("PATH_INFO" , client->get_rq_script_info());
|
||||
env[5] = _dup_env("PATH_TRANSLATED"); // not supported
|
||||
env[6] = _dup_env("QUERY_STRING" , client->get_rq_query());
|
||||
env[7] = _dup_env("REMOTE_ADDR" , client->get_cl_ip());
|
||||
env[8] = _dup_env("REMOTE_HOST" , client->get_rq_headers("Host")); // just test
|
||||
env[9] = _dup_env("REMOTE_IDENT"); // authentification not supported
|
||||
env[10] = _dup_env("REMOTE_USER"); // authentification not supported
|
||||
env[11] = _dup_env("REQUEST_METHOD" , client->get_rq_method_str());
|
||||
env[12] = _dup_env("SCRIPT_NAME" , client->get_rq_script_path());
|
||||
env[13] = _dup_env("SERVER_NAME" , client->get_rq_hostname());
|
||||
env[14] = _dup_env("SERVER_PORT" , client->get_rq_port());
|
||||
env[15] = _dup_env("SERVER_PROTOCOL" , client->get_rq_version());
|
||||
env[16] = _dup_env("SERVER_SOFTWARE" , "webser/1.0");
|
||||
env[17] = _dup_env("REDIRECT_STATUS" , "200");
|
||||
env[18] = NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
void Webserv::_exec_script(Client *client, char **env)
|
||||
std::string Webserv::_exec_script(Client *client, char **env)
|
||||
{
|
||||
int save_stdout;
|
||||
char * const * nll = NULL;
|
||||
#define RD 0
|
||||
#define WR 1
|
||||
#define CGI_BUF_SIZE 10
|
||||
#define FD_WR_TO_CHLD fd_in[WR]
|
||||
#define FD_WR_TO_PRNT fd_out[WR]
|
||||
#define FD_RD_FR_CHLD fd_out[RD]
|
||||
#define FD_RD_FR_PRNT fd_in[RD]
|
||||
|
||||
// save STDOUT
|
||||
save_stdout = dup(STDOUT_FILENO);
|
||||
// inside child process
|
||||
if (fork() == 0)
|
||||
{
|
||||
dup2(client->fd, STDOUT_FILENO);
|
||||
// execve("./srcs/cgi-bin/cgi_cpp.cgi", nll, client->env);
|
||||
execve("./srcs/cgi-bin/php-cgi", nll, env);
|
||||
pid_t pid;
|
||||
char buf[CGI_BUF_SIZE]; // WIP define buffer
|
||||
char * const * nll = NULL;
|
||||
std::string script_output;
|
||||
std::string body = client->get_rq_body();
|
||||
int fd_in[2];
|
||||
int fd_out[2];
|
||||
std::string path;
|
||||
|
||||
pipe(fd_in);
|
||||
pipe(fd_out);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
std::cerr << "fork crashed" << std::endl;
|
||||
else if (pid == 0) // child
|
||||
{
|
||||
close(FD_WR_TO_CHLD);
|
||||
close(FD_RD_FR_CHLD);
|
||||
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
||||
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
||||
path = "." + client->get_rq_script_path();
|
||||
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
|
||||
execve(path.c_str(), nll, env);
|
||||
// for tests execve crash :
|
||||
//execve("wrong", nll, env);
|
||||
std::cerr << "execve crashed.\n";
|
||||
|
||||
// TODO HUGO : check errno
|
||||
}
|
||||
// inside parent process
|
||||
else
|
||||
else //parent
|
||||
{
|
||||
close(FD_RD_FR_PRNT);
|
||||
close(FD_WR_TO_PRNT);
|
||||
write(FD_WR_TO_CHLD, body.c_str(), body.size());
|
||||
close(FD_WR_TO_CHLD);
|
||||
waitpid(-1, NULL, 0);
|
||||
// restore stdout
|
||||
dup2(save_stdout, STDOUT_FILENO);
|
||||
|
||||
memset(buf, '\0', CGI_BUF_SIZE);
|
||||
while (read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE - 1) > 0)
|
||||
{
|
||||
script_output += buf;
|
||||
memset(buf, '\0', CGI_BUF_SIZE);
|
||||
}
|
||||
close(FD_RD_FR_CHLD);
|
||||
}
|
||||
if (script_output.empty())
|
||||
script_output = "Status: 500\r\n\r\n";
|
||||
|
||||
return script_output;
|
||||
}
|
||||
|
||||
void Webserv::_construct_client(Client *client)
|
||||
void Webserv::_check_script_output(Client *client, std::string & output)
|
||||
{
|
||||
(void)client;
|
||||
_check_script_status(client, output);
|
||||
_check_script_fields(client, output);
|
||||
_add_script_body_length_header(output);
|
||||
_remove_body_leading_empty_lines(output);
|
||||
// _check_script_empty_lines(client, output);
|
||||
// _check_script_space_colons(client, output);
|
||||
// _check_script_new_lines(client, output);
|
||||
}
|
||||
|
||||
void Webserv::_check_script_status(Client *client, std::string & output)
|
||||
{
|
||||
size_t pos;
|
||||
int status_pos;
|
||||
|
||||
pos = output.find("Status:");
|
||||
if (pos != NPOS)
|
||||
{
|
||||
status_pos = pos + std::string("Status:").size();
|
||||
client->status = std::strtoul(output.c_str() + status_pos, NULL, 10);
|
||||
::extract_line(output, pos, CRLF);
|
||||
}
|
||||
else
|
||||
client->status = 200;
|
||||
}
|
||||
|
||||
void Webserv::_check_script_fields(Client *client, std::string & output)
|
||||
{
|
||||
std::map<std::string, std::string> srv_fld; // server_field
|
||||
std::map<std::string, std::string> scr_fld; // script_field
|
||||
std::map<std::string, std::string>::iterator it_srv;
|
||||
std::map<std::string, std::string>::iterator it_scr;
|
||||
std::string tmp;
|
||||
size_t pos;
|
||||
|
||||
// put server headers in map
|
||||
tmp = client->response;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != NPOS)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, srv_fld);
|
||||
// put script headers in map
|
||||
tmp = output;
|
||||
pos = tmp.find(CRLF CRLF);
|
||||
if (pos != NPOS)
|
||||
tmp.erase(pos);
|
||||
::parse_http_headers(tmp, scr_fld);
|
||||
// compare both map to supress duplicates
|
||||
for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++)
|
||||
{
|
||||
for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++)
|
||||
{
|
||||
if (str_tolower(it_srv->first) == str_tolower(it_scr->first))
|
||||
{
|
||||
pos = client->response.find(it_srv->first);
|
||||
::extract_line(client->response, pos, CRLF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,12 @@ void Webserv::_close_client(int fd)
|
||||
std::vector<Client>::iterator it = _clients.begin();
|
||||
while (it != _clients.end())
|
||||
{
|
||||
if (it->fd == fd)
|
||||
if (*it == fd)
|
||||
{
|
||||
// _epoll_update(fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
std::cerr << "close fd " << fd << "\n";
|
||||
if (::close(fd) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << fd << "\n";
|
||||
_clients.erase(it);
|
||||
break;
|
||||
}
|
||||
@@ -25,10 +24,89 @@ void Webserv::_close_all_clients()
|
||||
while (!_clients.empty())
|
||||
{
|
||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(_clients.back().fd) == -1)
|
||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||
if (::close(_clients.back().get_cl_fd()) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << _clients.back().fd << "\n";
|
||||
_clients.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_close_all_listen_sockets()
|
||||
{
|
||||
while (!_listen_sockets.empty())
|
||||
{
|
||||
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
std::cerr << "close fd " << _listen_sockets.back().fd << "\n";
|
||||
if (::close(_listen_sockets.back().fd) == -1)
|
||||
std::perror("err close()");
|
||||
_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);
|
||||
}
|
||||
|
||||
52
srcs/webserv/http_status.hpp
Normal file
52
srcs/webserv/http_status.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
#ifndef HTTP_STATUS_HPP
|
||||
# define HTTP_STATUS_HPP
|
||||
|
||||
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
|
||||
/*
|
||||
First version of macro HTML_ERROR(STATUS) dont work with call like this :
|
||||
client->response.append( HTML_ERROR( _http_status[client->status].c_str() ) );
|
||||
so I made the other version with dynamic replacement of STATUS .
|
||||
*/
|
||||
// # 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 STATUS_PLACEHOLDER "$STATUS"
|
||||
# define HTML_ERROR \
|
||||
"<!DOCTYPE html>"\
|
||||
"<html>"\
|
||||
"<head>"\
|
||||
"<title>" STATUS_PLACEHOLDER "</title>"\
|
||||
"</head>"\
|
||||
"<body>"\
|
||||
"<h1 style=\"text-align:center\">" STATUS_PLACEHOLDER "</h1>"\
|
||||
"<hr>"\
|
||||
"<p style=\"text-align:center\">Le Webserv/0.1</p>"\
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
// When new status added, need to update _init_http_status_map()
|
||||
# define S200 "200 OK"
|
||||
# define S201 "201 Created"
|
||||
# define S204 "204 No Content"
|
||||
|
||||
# define S301 "301 Moved Permanently"
|
||||
# define S302 "302 Found"
|
||||
# define S303 "303 See Other"
|
||||
# define S304 "304 Not Modified" // unused
|
||||
# define S307 "307 Temporary Redirect"
|
||||
# define S308 "308 Permanent Redirect"
|
||||
|
||||
# define S400 "400 Bad Request"
|
||||
# define S403 "403 Forbidden"
|
||||
# define S404 "404 Not Found"
|
||||
# define S405 "405 Method Not Allowed"
|
||||
# define S408 "408 Request Timeout"
|
||||
# define S413 "413 Content Too Large"
|
||||
# define S415 "415 Unsupported Media Type"
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
# define S505 "505 HTTP Version Not Supported"
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,9 @@
|
||||
void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
{
|
||||
int ret;
|
||||
std::vector<int> _open_ports;
|
||||
std::vector<std::string> open_sockets;
|
||||
struct listen_socket new_socket;
|
||||
std::string host_port;
|
||||
|
||||
_servers = *servers;
|
||||
_listen_sockets.clear();
|
||||
@@ -12,7 +14,11 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
|
||||
while (it != _servers.end())
|
||||
{
|
||||
if ( std::find(_open_ports.begin(), _open_ports.end(), std::atoi(it->port.data()) ) != _open_ports.end() )
|
||||
host_port.clear();
|
||||
host_port.append(it->host);
|
||||
host_port.append(":");
|
||||
host_port.append(it->port);
|
||||
if ( std::find(open_sockets.begin(), open_sockets.end(), host_port) != open_sockets.end() )
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
@@ -24,27 +30,31 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
std::perror("err socket()");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
new_socket.fd = ret;
|
||||
new_socket.host = it->host;
|
||||
new_socket.port = it->port;
|
||||
_listen_sockets.push_back(new_socket);
|
||||
|
||||
// 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(ret, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
if (setsockopt(new_socket.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
||||
{
|
||||
::perror("err setsockopt()");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
//
|
||||
// HUGO ADD END
|
||||
_listen_sockets.push_back(ret);
|
||||
|
||||
_bind(_listen_sockets.back(), std::atoi(it->port.data()), it->host);
|
||||
_listen(_listen_sockets.back(), 512); // 512 arbitrary
|
||||
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||
_listen(new_socket.fd, 42); // 42 arbitrary
|
||||
|
||||
if (_epoll_update(_listen_sockets.back(), EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
throw std::runtime_error("Socket init");
|
||||
|
||||
_open_ports.push_back(std::atoi(it->port.data()));
|
||||
open_sockets.push_back(host_port);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -76,3 +86,156 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
throw std::runtime_error("Socket listen");
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_init_http_status_map()
|
||||
{
|
||||
/* "map.insert()" more appropriate than "map.operator[]" :
|
||||
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||
*/
|
||||
typedef std::map<int, std::string>::value_type status_pair;
|
||||
|
||||
// _http_status.insert(std::make_pair(200, S200)); // equivalent
|
||||
_http_status.insert(status_pair(200, S200));
|
||||
_http_status.insert(status_pair(201, S201));
|
||||
_http_status.insert(status_pair(204, S204));
|
||||
|
||||
_http_status.insert(status_pair(301, S301));
|
||||
_http_status.insert(status_pair(302, S302));
|
||||
_http_status.insert(status_pair(303, S303));
|
||||
_http_status.insert(status_pair(304, S304));
|
||||
_http_status.insert(status_pair(307, S307));
|
||||
_http_status.insert(status_pair(308, S308));
|
||||
|
||||
_http_status.insert(status_pair(400, S400));
|
||||
_http_status.insert(status_pair(403, S403));
|
||||
_http_status.insert(status_pair(404, S404));
|
||||
_http_status.insert(status_pair(405, S405));
|
||||
_http_status.insert(status_pair(408, S408));
|
||||
_http_status.insert(status_pair(413, S413));
|
||||
|
||||
_http_status.insert(status_pair(500, S500));
|
||||
_http_status.insert(status_pair(501, S501));
|
||||
}
|
||||
|
||||
void Webserv::_init_mime_types_map()
|
||||
{
|
||||
/* From :
|
||||
** http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
*/
|
||||
typedef std::map<std::string, std::string>::value_type mime_pair;
|
||||
|
||||
_mime_types.insert(mime_pair("", MIME_TYPE_DEFAULT));
|
||||
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("htm", "text/html"));
|
||||
_mime_types.insert(mime_pair("shtml", "text/html"));
|
||||
_mime_types.insert(mime_pair("css", "text/css"));
|
||||
_mime_types.insert(mime_pair("xml", "text/xml"));
|
||||
_mime_types.insert(mime_pair("gif", "image/gif"));
|
||||
_mime_types.insert(mime_pair("jpeg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("jpg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("js", "application/javascript"));
|
||||
_mime_types.insert(mime_pair("atom", "application/atom+xml"));
|
||||
_mime_types.insert(mime_pair("rss", "application/rss+xml"));
|
||||
|
||||
_mime_types.insert(mime_pair("mml", "text/mathml"));
|
||||
_mime_types.insert(mime_pair("txt", "text/plain"));
|
||||
_mime_types.insert(mime_pair("jad", "text/vnd.sun.j2me.app-descriptor"));
|
||||
_mime_types.insert(mime_pair("wml", "text/vnd.wap.wml"));
|
||||
_mime_types.insert(mime_pair("htc", "text/x-component"));
|
||||
|
||||
_mime_types.insert(mime_pair("png", "image/png"));
|
||||
_mime_types.insert(mime_pair("tif", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("tiff", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("wbmp", "image/vnd.wap.wbmp"));
|
||||
_mime_types.insert(mime_pair("ico", "image/x-icon"));
|
||||
_mime_types.insert(mime_pair("jng", "image/x-jng"));
|
||||
_mime_types.insert(mime_pair("bmp", "image/x-ms-bmp"));
|
||||
_mime_types.insert(mime_pair("svg", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("svgz", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("webp", "image/webp"));
|
||||
|
||||
_mime_types.insert(mime_pair("woff", "application/font-woff"));
|
||||
_mime_types.insert(mime_pair("jar", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("war", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("ear", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("json", "application/json"));
|
||||
_mime_types.insert(mime_pair("hqx", "application/mac-binhex40"));
|
||||
_mime_types.insert(mime_pair("doc", "application/msword"));
|
||||
_mime_types.insert(mime_pair("pdf", "application/pdf"));
|
||||
_mime_types.insert(mime_pair("ps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("eps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("ai", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("rtf", "application/rtf"));
|
||||
_mime_types.insert(mime_pair("m3u8", "application/vnd.apple.mpegurl"));
|
||||
_mime_types.insert(mime_pair("xls", "application/vnd.ms-excel"));
|
||||
_mime_types.insert(mime_pair("eot", "application/vnd.ms-fontobject"));
|
||||
_mime_types.insert(mime_pair("ppt", "application/vnd.ms-powerpoint"));
|
||||
_mime_types.insert(mime_pair("wmlc", "application/vnd.wap.wmlc"));
|
||||
_mime_types.insert(mime_pair("kml", "application/vnd.google-earth.kml+xml"));
|
||||
_mime_types.insert(mime_pair("kmz", "application/vnd.google-earth.kmz"));
|
||||
_mime_types.insert(mime_pair("7z", "application/x-7z-compressed"));
|
||||
_mime_types.insert(mime_pair("cco", "application/x-cocoa"));
|
||||
_mime_types.insert(mime_pair("jardiff", "application/x-java-archive-diff"));
|
||||
_mime_types.insert(mime_pair("jnlp", "application/x-java-jnlp-file"));
|
||||
_mime_types.insert(mime_pair("run", "application/x-makeself"));
|
||||
_mime_types.insert(mime_pair("pl", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("pm", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("prc", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("pdb", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("rar", "application/x-rar-compressed"));
|
||||
_mime_types.insert(mime_pair("rpm", "application/x-redhat-package-manager"));
|
||||
_mime_types.insert(mime_pair("sea", "application/x-sea"));
|
||||
_mime_types.insert(mime_pair("swf", "application/x-shockwave-flash"));
|
||||
_mime_types.insert(mime_pair("sit", "application/x-stuffit"));
|
||||
_mime_types.insert(mime_pair("tcl", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("tk", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("der", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("pem", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("crt", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("xpi", "application/x-xpinstall"));
|
||||
_mime_types.insert(mime_pair("xhtml", "application/xhtml+xml"));
|
||||
_mime_types.insert(mime_pair("xspf", "application/xspf+xml"));
|
||||
_mime_types.insert(mime_pair("zip", "application/zip"));
|
||||
|
||||
_mime_types.insert(mime_pair("bin", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("exe", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dll", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("deb", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dmg", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("iso", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("img", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msi", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msp", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msm", "application/octet-stream"));
|
||||
|
||||
_mime_types.insert(mime_pair("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
|
||||
_mime_types.insert(mime_pair("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||
_mime_types.insert(mime_pair("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"));
|
||||
|
||||
_mime_types.insert(mime_pair("mid", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("midi", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("kar", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("mp3", "audio/mpeg"));
|
||||
_mime_types.insert(mime_pair("ogg", "audio/ogg"));
|
||||
_mime_types.insert(mime_pair("m4a", "audio/x-m4a"));
|
||||
_mime_types.insert(mime_pair("ra", "audio/x-realaudio"));
|
||||
|
||||
_mime_types.insert(mime_pair("3gpp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("3gp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("ts", "video/mp2t"));
|
||||
_mime_types.insert(mime_pair("mp4", "video/mp4"));
|
||||
_mime_types.insert(mime_pair("mpeg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mpg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mov", "video/quicktime"));
|
||||
_mime_types.insert(mime_pair("webm", "video/webm"));
|
||||
_mime_types.insert(mime_pair("flv", "video/x-flv"));
|
||||
_mime_types.insert(mime_pair("m4v", "video/x-m4v"));
|
||||
_mime_types.insert(mime_pair("mng", "video/x-mng"));
|
||||
_mime_types.insert(mime_pair("asx", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("asf", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
|
||||
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
|
||||
}
|
||||
|
||||
|
||||
35
srcs/webserv/method_delete.cpp
Normal file
35
srcs/webserv/method_delete.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_delete(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
155
srcs/webserv/method_get.cpp
Normal file
155
srcs/webserv/method_get.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
std::string Webserv::_replace_url_root(Client *client, std::string path)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// const?
|
||||
void Webserv::_get(Client *client, std::string &path)
|
||||
{
|
||||
|
||||
|
||||
// Index/Autoindex block
|
||||
if (eval_file_type(path) == IS_DIR)
|
||||
{
|
||||
if (path[path.size() - 1] != '/')
|
||||
path.push_back('/');
|
||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||
{
|
||||
if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE)
|
||||
{
|
||||
path.append(client->assigned_location->index[i]);
|
||||
_get_file(client, path);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (client->assigned_location->autoindex == true)
|
||||
_autoindex(client, path);
|
||||
}
|
||||
else
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
std::ios::binary
|
||||
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.binary
|
||||
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
|
||||
*/
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
std::stringstream buf;
|
||||
|
||||
std::cout << "_get_file()\n";
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::ate);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* std::streampos size = ifd.tellg();
|
||||
// WIP Low priority : Chunk or not chunk (if filesize too big)
|
||||
if (size > MAX_FILESIZE)
|
||||
{
|
||||
// Then chunk
|
||||
client->status = 500; // WIP temp
|
||||
std::cerr << "File too large for non chunk body\n";
|
||||
return ;
|
||||
} */
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
buf << ifd.rdbuf();
|
||||
if (!ifd || !buf)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
std::string file_ext = _determine_file_extension(path);
|
||||
_append_body(client, buf.str(), file_ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const?
|
||||
void Webserv::_autoindex(Client *client, const std::string &path)
|
||||
{
|
||||
std::cout << "_autoindex()\n";
|
||||
|
||||
std::string dir_list;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
// std::cout << "location root: " << client->assigned_location->root << " location path: " << client->assigned_location->path << '\n';
|
||||
|
||||
// std::cout << "Path in auto is: " << path << '\n';
|
||||
if ( (dir = opendir(path.c_str()) ) != NULL)
|
||||
{
|
||||
dir_list.append(AUTOINDEX_START);
|
||||
dir_list.append(path);
|
||||
dir_list.append(AUTOINDEX_MID1);
|
||||
dir_list.append(path);
|
||||
dir_list.append(AUTOINDEX_MID2);
|
||||
/* print all the files and directories within directory */
|
||||
while ((ent = readdir (dir)) != NULL)
|
||||
{
|
||||
// std::cout << "ent: " << ent->d_name << '\n';
|
||||
if (strcmp(".", ent->d_name) == 0)
|
||||
continue ;
|
||||
dir_list.append("<a href=\"");
|
||||
dir_list.append(client->get_rq_abs_path() + "/");
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("\">");
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("</a>");
|
||||
dir_list.append("\r\n"); // is this right?
|
||||
}
|
||||
|
||||
// <a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
// <a href="/test/test_deeper/index1.html">index1.html</a>
|
||||
|
||||
// apparently this is more than good enough!
|
||||
// <a href="/test/test_deeper/..">..</a>
|
||||
|
||||
dir_list.append(AUTOINDEX_END);
|
||||
// std::cout << "\n\n" << dir_list << '\n';
|
||||
closedir (dir);
|
||||
_append_body(client, dir_list, "html");
|
||||
}
|
||||
else
|
||||
{
|
||||
// in theory not possible cuz we already checked...
|
||||
std::cerr << "could not open dir\n";
|
||||
// throw?
|
||||
return ;
|
||||
}
|
||||
}
|
||||
58
srcs/webserv/method_post.cpp
Normal file
58
srcs/webserv/method_post.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
|
||||
void Webserv::_post(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Content-Length useless at this point ?
|
||||
ofd << client->get_rq_body();
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
}
|
||||
}
|
||||
2
srcs/webserv/parsing_request.cpp
Normal file
2
srcs/webserv/parsing_request.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "parsing_request.hpp"
|
||||
|
||||
@@ -1,44 +1,83 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
#define BUFSIZE 8192
|
||||
// Arbitrary values
|
||||
#define BUFSIZE 8192 // (8Ko)
|
||||
#define MAX_HEADER_SIZE 16384 // (16Ko)
|
||||
|
||||
enum read_return
|
||||
{
|
||||
READ_IN_PROGRESS,
|
||||
READ_COMPLETE,
|
||||
READ_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_request(Client *client)
|
||||
{
|
||||
_read_request(client);
|
||||
int ret = _read_request(client);
|
||||
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == READ_CLOSE)
|
||||
{
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
else if (ret == READ_COMPLETE)
|
||||
{
|
||||
if (client->body_complete)
|
||||
std::cerr << "______BODY\n" << client->get_rq_body() << "\n______\n"; // DEBUG
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
client->request_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_read_request(Client *client)
|
||||
int Webserv::_read_request(Client *client)
|
||||
{
|
||||
char buf[BUFSIZE+1];
|
||||
char buf[BUFSIZE];
|
||||
ssize_t ret;
|
||||
|
||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
|
||||
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err recv()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
return READ_CLOSE;
|
||||
}
|
||||
if (ret == 0) // Not sure what to do in case of 0. Just close ?
|
||||
if (ret == 0)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
/*
|
||||
if (ret == BUFSIZE)
|
||||
// send error like "request too long" to client
|
||||
*/
|
||||
client->raw_request.append(buf, ret);
|
||||
// ::print_special(client->raw_request);
|
||||
// std::cerr << "__raw_request__\n" << client->raw_request << "\n______\n"; // DEBUG
|
||||
|
||||
buf[ret] = '\0';
|
||||
client->raw_request.append(buf);
|
||||
client->parse_request();
|
||||
if (!client->header_complete)
|
||||
{
|
||||
client->parse_request_headers(_servers);
|
||||
if (client->status)
|
||||
return READ_COMPLETE;
|
||||
if (client->header_complete)
|
||||
{
|
||||
if (client->get_rq_headers("Content-Type").empty()
|
||||
&& client->get_rq_headers("Content-Length").empty()
|
||||
&& client->get_rq_headers("Transfer-Encoding").empty())
|
||||
return READ_COMPLETE; // No body case
|
||||
}
|
||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
||||
{ // 413 or 400 ? 413 seems common among http server, but don't fit perfectly.
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
if (client->header_complete)
|
||||
{
|
||||
// client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now
|
||||
client->parse_request_body();
|
||||
if (client->status || client->body_complete)
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
|
||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
|
||||
return READ_IN_PROGRESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,169 +1,259 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
enum send_return
|
||||
{
|
||||
SEND_IN_PROGRESS, // unused
|
||||
SEND_COMPLETE,
|
||||
SEND_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_response(Client *client)
|
||||
{
|
||||
_send_response(client);
|
||||
int ret = _send_response(client);
|
||||
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == SEND_CLOSE)
|
||||
{
|
||||
_close_client(client->get_cl_fd());
|
||||
}
|
||||
else if (ret == SEND_COMPLETE)
|
||||
{
|
||||
if (client->get_rq_headers("Connection") == "close" || client->status == 408)
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_send_response(Client *client)
|
||||
int Webserv::_send_response(Client *client)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "send()\n";
|
||||
std::cerr << "RAW_REQUEST\n|\n" << client->raw_request << "|\n"; // DEBUG
|
||||
|
||||
_construct_response(client);
|
||||
_append_base_headers(client);
|
||||
if (!client->status)
|
||||
_construct_response(client);
|
||||
_insert_status_line(client);
|
||||
if (client->status >= 400)
|
||||
_error_html_response(client);
|
||||
|
||||
ret = ::send(client->fd, client->response.data(), client->response.size(), 0);
|
||||
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||
|
||||
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
||||
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
return SEND_CLOSE;
|
||||
}
|
||||
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
||||
|
||||
if (client->raw_request.find("Connection: close") != std::string::npos)
|
||||
_close_client(client->fd);
|
||||
else
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->raw_request.clear();
|
||||
client->response.clear();
|
||||
}
|
||||
return SEND_COMPLETE;
|
||||
}
|
||||
|
||||
void Webserv::_append_base_headers(Client *client)
|
||||
{
|
||||
client->response.append("Server: Webserv/0.1" CRLF);
|
||||
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
client->response.append("Connection: close" CRLF);
|
||||
else
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
}
|
||||
|
||||
// TODO HUGO : wip
|
||||
void Webserv::_construct_response(Client *client)
|
||||
{
|
||||
client->status = 200;
|
||||
client->response.append("Server: Webserv/0.1\r\n");
|
||||
std::string path;
|
||||
std::string script_output;
|
||||
|
||||
path = _replace_url_root(client, client->get_rq_abs_path());
|
||||
if (_is_cgi(client, path))
|
||||
{
|
||||
script_output = _exec_cgi(client);
|
||||
|
||||
if (client->raw_request.find("Connection: close") != std::string::npos)
|
||||
client->response.append("Connection: close\r\n");
|
||||
else
|
||||
client->response.append("Connection: keep-alive\r\n");
|
||||
/*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";
|
||||
|
||||
_get_ressource(client);
|
||||
_check_script_output(client, script_output);
|
||||
client->response += script_output;
|
||||
|
||||
_insert_status_line(client);
|
||||
/*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, std::string &path)
|
||||
{
|
||||
std::cerr << "allow_methods = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug
|
||||
|
||||
switch (client->get_rq_method())
|
||||
{
|
||||
case (GET):
|
||||
_get(client, path); break;
|
||||
case (POST):
|
||||
_post(client, path); break;
|
||||
case (DELETE):
|
||||
_delete(client, path); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define E400 "\r\n<!DOCTYPE html><html><head><title>400 Bad Request</title></head><body><h1 style=\"text-align:center\">400 Bad Request</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
#define E404 "\r\n<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1 style=\"text-align:center\">404 Not Found</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
#define E500 "\r\n<!DOCTYPE html><html><head><title>500 Internal Server Error</title></head><body><h1 style=\"text-align:center\">500 Internal Server Error</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
void Webserv::_insert_status_line(Client *client)
|
||||
{
|
||||
std::string status_line;
|
||||
|
||||
status_line.append("HTTP/1.1 ");
|
||||
// WIP, maybe make a map for status response
|
||||
switch (client->status)
|
||||
{
|
||||
case (200):
|
||||
status_line.append("200 OK");
|
||||
break;
|
||||
case (400):
|
||||
status_line.append("400 Not Found");
|
||||
client->response.append(E400);
|
||||
break;
|
||||
case (404):
|
||||
status_line.append("404 Not Found");
|
||||
client->response.append(E404);
|
||||
break;
|
||||
case (500):
|
||||
status_line.append("500 Internal Server Error");
|
||||
client->response.append(E500);
|
||||
break;
|
||||
}
|
||||
status_line.append("\r\n");
|
||||
|
||||
status_line.append(_http_status[client->status]);
|
||||
status_line.append(CRLF);
|
||||
client->response.insert(0, status_line);
|
||||
}
|
||||
|
||||
#define ROOT "website"
|
||||
#define INDEX "index.html"
|
||||
#define MAX_FILESIZE 1000000 // (1Mo)
|
||||
void Webserv::_get_ressource(Client *client)
|
||||
void Webserv::_error_html_response(Client *client)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
char buf[MAX_FILESIZE+1];
|
||||
std::string tmp;
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
if (_is_cgi(client))
|
||||
if (!client->assigned_server || client->assigned_server->error_pages[client->status].empty())
|
||||
{
|
||||
_exec_cgi(client);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
// Mini parsing à l'arrache du PATH
|
||||
std::string path;
|
||||
try
|
||||
{
|
||||
path = client->raw_request.substr(0, client->raw_request.find("\r\n"));
|
||||
path = path.substr(0, path.rfind(" "));
|
||||
path = path.substr(path.find("/"));
|
||||
if (path == "/")
|
||||
path.append(INDEX);
|
||||
path.insert(0, ROOT);
|
||||
}
|
||||
catch (std::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
client->status = 400;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.data(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.data(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?)
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": open fail" << '\n';
|
||||
client->status = 500;
|
||||
std::string html_page = HTML_ERROR;
|
||||
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
|
||||
_append_body(client, html_page, "html");
|
||||
}
|
||||
else
|
||||
{
|
||||
// WIP : Chunk or not chunk (if filesize too big)
|
||||
std::streampos size = ifd.tellg();
|
||||
if (size > MAX_FILESIZE)
|
||||
{
|
||||
// Then chunk
|
||||
client->status = 500; // WIP temp
|
||||
std::cerr << "File too large for non chunk body\n";
|
||||
ifd.close();
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
ifd.read(buf, size);
|
||||
buf[ifd.gcount()] = '\0';
|
||||
|
||||
client->response.append("Content-Type: text/html; charset=UTF-8\r\n");
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
|
||||
tmp = ::itos(ifd.gcount());
|
||||
client->response.append(tmp.c_str());
|
||||
client->response.append("\r\n");
|
||||
|
||||
// Body
|
||||
client->response.append("\r\n");
|
||||
client->response.append(buf);
|
||||
|
||||
ifd.close();
|
||||
}
|
||||
_get_file(client, client->assigned_server->error_pages[client->status]);
|
||||
}
|
||||
|
||||
void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
|
||||
{
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
|
||||
client->response.append("Content-Type: ");
|
||||
if (mime_type.empty())
|
||||
client->response.append(MIME_TYPE_DEFAULT);
|
||||
else
|
||||
{
|
||||
client->response.append(mime_type);
|
||||
if (mime_type.find("text/") != NPOS)
|
||||
client->response.append("; charset=UTF-8");
|
||||
}
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body.size());
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
}
|
||||
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||
{
|
||||
/*
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
_determine_process_server() should be complete.
|
||||
TODO : test it
|
||||
*/
|
||||
|
||||
std::string const &server_name = client->get_rq_headers("Host");
|
||||
std::vector<ServerConfig>::iterator it = servers.begin();
|
||||
std::vector<ServerConfig>::iterator default_server = servers.end();
|
||||
|
||||
while (it != servers.end())
|
||||
{
|
||||
if (it->host == client->get_cl_lsocket()->host
|
||||
&& it->port == client->get_cl_lsocket()->port)
|
||||
{
|
||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||
break;
|
||||
else if (default_server == servers.end())
|
||||
default_server = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != servers.end())
|
||||
return (&(*it));
|
||||
else
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
// const?
|
||||
// Temporary Global Scope. Probably move to Client in the future.
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path)
|
||||
{
|
||||
/* RULES ***
|
||||
|
||||
If a path coresponds exactly to a location, use that one
|
||||
if no path coresponds then use the most correct one
|
||||
most correct means the most precise branch that is still above
|
||||
the point we are aiming for
|
||||
|
||||
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++)
|
||||
{
|
||||
if (it->path.size() > uri.size())
|
||||
continue ;
|
||||
|
||||
if (uri.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
if (it->path.size() == uri.size())
|
||||
return (&(*it));
|
||||
else if (uri[it->path.size()] == '/')
|
||||
return (&(*it));
|
||||
// this works cuz only ever looking for a / burried in a longer path
|
||||
}
|
||||
}
|
||||
return (&(server.locations.back()));
|
||||
|
||||
|
||||
// /test/mdr
|
||||
// /test/mdr/
|
||||
// /test/mdrBST
|
||||
|
||||
/* More stuff to check this still works with ***
|
||||
|
||||
/test/test_
|
||||
/test/test_/
|
||||
/test/test_deeper
|
||||
/test/test_deeper/
|
||||
/test/test_deepei
|
||||
/test/test_deepei/
|
||||
/test/test_deeperi
|
||||
/test/test_deeper/super_deep/
|
||||
/test/aaaaaaaaaaa/super_deep/
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||
{
|
||||
size_t dot_pos = path.rfind(".");
|
||||
if (dot_pos != NPOS && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
@@ -4,14 +4,6 @@
|
||||
#define MAX_EVENTS 42 // arbitrary
|
||||
#define TIMEOUT 3000
|
||||
|
||||
// Temp. To move in other file
|
||||
bool operator==(const Client& lhs, const Client& rhs)
|
||||
{ return lhs.fd == rhs.fd; }
|
||||
bool operator==(const Client& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
bool operator==(int fd, const Client& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
void Webserv::run()
|
||||
{
|
||||
std::cerr << "Server started\n";
|
||||
@@ -19,6 +11,7 @@ void Webserv::run()
|
||||
int nfds;
|
||||
int i;
|
||||
int count_loop = 0;
|
||||
std::vector<listen_socket>::iterator it_socket;
|
||||
|
||||
g_run = true;
|
||||
while (g_run)
|
||||
@@ -27,31 +20,52 @@ void Webserv::run()
|
||||
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
|
||||
if (nfds == -1)
|
||||
{
|
||||
int errno_copy = errno;
|
||||
std::perror("err epoll_wait()");
|
||||
throw std::runtime_error("Epoll wait");
|
||||
}
|
||||
else if (nfds == 0)
|
||||
{
|
||||
if (!_clients.empty())
|
||||
{
|
||||
std::cerr << "Timeout " << TIMEOUT << "ms\n";
|
||||
_close_all_clients();
|
||||
}
|
||||
if (errno_copy == EINTR)
|
||||
g_run = false;
|
||||
else
|
||||
throw std::runtime_error("Epoll wait");
|
||||
}
|
||||
else if (nfds == 0 && !_clients.empty())
|
||||
_timeout();
|
||||
i = 0;
|
||||
while (i < nfds)
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
if ((std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd) != _listen_sockets.end())
|
||||
&& (events[i].events & EPOLLIN))
|
||||
_accept_connection(events[i].data.fd);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
++i;
|
||||
if (!g_run)
|
||||
try {
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end())
|
||||
{
|
||||
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||
_handle_epoll_error_lsocket(events[i].events, it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
|
||||
_handle_epoll_error_client(events[i].events, events[i].data.fd);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
}
|
||||
++i;
|
||||
if (!g_run)
|
||||
break;
|
||||
}
|
||||
catch (const std::bad_alloc& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
_close_all_clients();
|
||||
/* Swap to free the memory
|
||||
From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */
|
||||
std::vector<Client>().swap(_clients);
|
||||
break;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
4
stylesheet/style.css
Normal file
4
stylesheet/style.css
Normal file
@@ -0,0 +1,4 @@
|
||||
h1 {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
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
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Le Webserv</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,300 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- saved from url=(0057)https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://dublincore.org/documents/2008/08/04/dc-html/"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>rfc2119</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="Verified-headnote-styling">
|
||||
<span style="font-weight: bold;">This is a purely informative rendering of an RFC that includes verified errata. This rendering may not be used as a reference.</span>
|
||||
<br>
|
||||
<br>
|
||||
The following 'Verified' errata have been incorporated in this document:
|
||||
<a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid493">EID 493</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 494</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_495">EID 495</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 496</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid498">EID 498</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_499">EID 499</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid500">EID 500</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_5101">EID 5101</a>
|
||||
</div>
|
||||
|
||||
<pre>Network Working Group S. Bradner
|
||||
Request for Comments: 2119 Harvard University
|
||||
BCP: 14 March 1997
|
||||
Category: Best Current Practice
|
||||
|
||||
|
||||
Key words for use in RFCs to Indicate Requirement Levels
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This document specifies an Internet Best Current Practices for the
|
||||
Internet Community, and requests discussion and suggestions for
|
||||
improvements. Distribution of this memo is unlimited.
|
||||
|
||||
Abstract
|
||||
|
||||
In many standards track documents several words are used to signify
|
||||
the requirements in the specification. These words are often
|
||||
capitalized. This document defines these words as they should be
|
||||
interpreted in IETF documents. Authors who follow these guidelines
|
||||
should incorporate this phrase near the beginning of their document:
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-499"> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL <button id="btn_499" target="expand_499" onclick="hideFunction("expand_499")">Expand</button>
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
|
||||
RECOMMENDED", "MAY", and "OPTIONAL" in this document are to
|
||||
be interpreted as described in RFC 2119.</span>
|
||||
<div class="nodeCloseClass" id="expand_499"><div class="Verified-endnote-styling" id="eid499">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid499">EID 499</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> Abstract
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
|
||||
"OPTIONAL" in this document are to be interpreted as described in
|
||||
RFC 2119.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
|
||||
RECOMMENDED", "MAY", and "OPTIONAL" in this document are to
|
||||
be interpreted as described in RFC 2119.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
The phrase "NOT RECOMMENDED" is missing from this sentence.
|
||||
</div>
|
||||
</div>
|
||||
Note that the force of these words is modified by the requirement
|
||||
level of the document in which they are used.
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-495">1. MUST This word, or the terms "REQUIRED" or "SHALL", means that the <button id="btn_495" target="expand_495" onclick="hideFunction("expand_495")">Expand</button>
|
||||
definition is an absolute requirement of the specification.
|
||||
<div class="Verified-endnote-styling" id="eid493">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid493">EID 493</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", mean that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", means that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid498">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid498">EID 498</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED", means that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid500">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid500">EID 500</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", means that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
<div class="nodeCloseClass" id="expand_495"><div class="Verified-endnote-styling" id="eid495">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid495">EID 495</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
1. MUST This word, or the terms "REQUIRED" or "SHALL", mean that the
|
||||
definition is an absolute requirement of the specification.
|
||||
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
1. MUST This word, or the terms "REQUIRED" or "SHALL", means that the
|
||||
definition is an absolute requirement of the specification.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", mean that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-5101">5. MAY This word, or the adjective "OPTIONAL", mean that an item is <button id="btn_5101" target="expand_5101" onclick="hideFunction("expand_5101")">Expand</button>
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides).</span>
|
||||
<div class="nodeCloseClass" id="expand_5101"><div class="Verified-endnote-styling" id="eid5101">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid5101">EID 5101</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 5
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
5. MAY This word, or the adjective "OPTIONAL", mean that an item is
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides.)
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
5. MAY This word, or the adjective "OPTIONAL", mean that an item is
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides).
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
Full stop should appear outside the parentheses in the last sentence.
|
||||
</div>
|
||||
</div>
|
||||
6. Guidance in the use of these Imperatives
|
||||
|
||||
Imperatives of the type defined in this memo must be used with care
|
||||
and sparingly. In particular, they MUST only be used where it is
|
||||
actually required for interoperation or to limit behavior which has
|
||||
potential for causing harm <span class="Verified-inline-styling" id="inline-494">(e.g., limiting retransmissions)</span> For <button id="btn_494" target="expand_494" onclick="hideFunction("expand_494")">Expand Multiple</button>
|
||||
<div class="nodeCloseClass" id="expand_494"><div class="Verified-endnote-styling" id="eid494">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid494">EID 494</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 6
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
(e.g., limiting retransmisssions)
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
(e.g., limiting retransmissions)
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid496">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid496">EID 496</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 6
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
In particular, they MUST only be used where it is actually required
|
||||
for interoperation or to limit behavior which has potential for
|
||||
causing harm (e.g., limiting retransmisssions) For example, they
|
||||
must not be used to try to impose a particular method on
|
||||
implementors where the method is not required for interoperability.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
In particular, they MUST only be used where it is actually required
|
||||
for interoperation or to limit behavior which has potential for
|
||||
causing harm (e.g., limiting retransmissions). For example, they
|
||||
must not be used to try to impose a particular method on
|
||||
implementors where the method is not required for interoperability.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</div> example, they must not be used to try to impose a particular method
|
||||
on implementors where the method is not required for
|
||||
interoperability.
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
These terms are frequently used to specify behavior with security
|
||||
implications. The effects on security of not implementing a MUST or
|
||||
SHOULD, or doing something the specification says MUST NOT or SHOULD
|
||||
NOT be done may be very subtle. Document authors should take the time
|
||||
to elaborate the security implications of not following
|
||||
recommendations or requirements as most implementors will not have
|
||||
had the benefit of the experience and discussion that produced the
|
||||
specification.
|
||||
|
||||
8. Acknowledgments
|
||||
|
||||
The definitions of these terms are an amalgam of definitions taken
|
||||
from a number of RFCs. In addition, suggestions have been
|
||||
incorporated from a number of people including Robert Ullmann, Thomas
|
||||
Narten, Neal McBurnett, and Robert Elz.
|
||||
|
||||
9. Author's Address
|
||||
|
||||
Scott Bradner
|
||||
Harvard University
|
||||
1350 Mass. Ave.
|
||||
Cambridge, MA 02138
|
||||
|
||||
phone - +1 617 495 3864
|
||||
|
||||
email - sob@harvard.edu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</pre></body></html>
|
||||
BIN
www/Van_Eyck_Portrait_Arnolfini.jpg
Normal file
BIN
www/Van_Eyck_Portrait_Arnolfini.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
BIN
www/drill.jpg
Normal file
BIN
www/drill.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
11
www/error_pages/error_404.html
Normal file
11
www/error_pages/error_404.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>404 Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
|
||||
<hr>
|
||||
<p style=\"text-align:center\">Le Webserv/0.1</p>
|
||||
</body>
|
||||
</html>
|
||||
BIN
www/favicon.ico
Normal file
BIN
www/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
84
www/form_get.html
Normal file
84
www/form_get.html
Normal file
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: auto;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid red;
|
||||
margin: 20px auto;
|
||||
padding: 5px;
|
||||
}
|
||||
form > * {
|
||||
display: flex;
|
||||
margin: 5px auto 5px 5px;
|
||||
}
|
||||
mark {
|
||||
margin: 0px 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<form method="get" action="/cgi-bin/cgi_cpp.out">
|
||||
|
||||
<p><mark>get</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="post" action="/cgi-bin/cgi_cpp.out">
|
||||
|
||||
<p><mark>post</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="get" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||
|
||||
<p><mark>get</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
<form method="post" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||
|
||||
<p><mark>post</mark> form</p>
|
||||
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||
<label for="fname">First name:</label><br>
|
||||
<input type="text" id="fname" name="fname" value="John"><br>
|
||||
<label for="lname">Last name:</label><br>
|
||||
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
<br>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- <link href="styles/style.css" rel="stylesheet"> -->
|
||||
<title>Le Webserv</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
BIN
www/kermit.ico
Normal file
BIN
www/kermit.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
www/punpun.png
Normal file
BIN
www/punpun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 310 KiB |
@@ -13,7 +13,7 @@
|
||||
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-monochrome.css" title="Monochrome">
|
||||
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-printer.css" title="Printer">
|
||||
|
||||
<script src="./rfc2119_files/errata.js.téléchargement"></script>
|
||||
<script src="./rfc2119_files/errata.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="Verified-headnote-styling">
|
||||
@@ -309,4 +309,4 @@ Full stop should appear outside the parentheses in the last sentence.
|
||||
|
||||
|
||||
|
||||
</pre></body></html>
|
||||
</pre></body></html>
|
||||
139
www/rfc2119_files/errata-base.css
Normal file
139
www/rfc2119_files/errata-base.css
Normal file
@@ -0,0 +1,139 @@
|
||||
<style type="text/css">
|
||||
@media only screen
|
||||
and (min-width: 992px)
|
||||
and (max-width: 1199px) {
|
||||
body { font-size: 14pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-width: 768px)
|
||||
and (max-width: 991px) {
|
||||
body { font-size: 14pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-width: 480px)
|
||||
and (max-width: 767px) {
|
||||
body { font-size: 11pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (max-width: 479px) {
|
||||
body { font-size: 8pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-device-width : 375px)
|
||||
and (max-device-width : 667px) {
|
||||
body { font-size: 9.5pt; }
|
||||
div.content { width: 96ex; margin: 0; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-device-width: 1200px) {
|
||||
body { font-size: 10pt; margin: 0 4em; }
|
||||
div.content { width: 96ex; margin: 0; }
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
font-weight: bold;
|
||||
line-height: 0pt;
|
||||
display: inline;
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre {
|
||||
font-size: 1em;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.pre {
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
}
|
||||
.header{
|
||||
font-weight: bold;
|
||||
}
|
||||
.newpage {
|
||||
page-break-before: always;
|
||||
}
|
||||
.invisible {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
a.selflink {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
font-family: monospace;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.noprint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen {
|
||||
.grey, .grey a:link, .grey a:visited {
|
||||
color: #777;
|
||||
}
|
||||
.docinfo {
|
||||
background-color: #EEE;
|
||||
}
|
||||
.top {
|
||||
border-top: 7px solid #EEE;
|
||||
}
|
||||
.bgwhite { background-color: white; }
|
||||
.bgred { background-color: #F44; }
|
||||
.bggrey { background-color: #666; }
|
||||
.bgbrown { background-color: #840; }
|
||||
.bgorange { background-color: #FA0; }
|
||||
.bgyellow { background-color: #EE0; }
|
||||
.bgmagenta{ background-color: #F4F; }
|
||||
.bgblue { background-color: #66F; }
|
||||
.bgcyan { background-color: #4DD; }
|
||||
.bggreen { background-color: #4F4; }
|
||||
|
||||
.legend { font-size: 90%; }
|
||||
.cplate { font-size: 70%; border: solid grey 1px; }
|
||||
}
|
||||
|
||||
|
||||
.Verified-headnote-styling, .Held-headnote-styling, .Reported-headnote-styling, .Rejected-headnote-styling {
|
||||
border:dashed;
|
||||
margin:8px;
|
||||
padding;24px;
|
||||
overflow-wrap: normal;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.Verified-endnote-styling, .Held-endnote-styling, .Reported-endnote-styling, .Rejected-endnote-styling {
|
||||
border:dashed;
|
||||
margin:8px;
|
||||
padding:24px;
|
||||
overflow:auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.Verified-ineline-styling, .Held-ineline-styling, .Reported-ineline-styling, .Rejected-ineline-styling {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.nodeCloseClass {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.nodeOpenClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
</style>
|
||||
43
www/rfc2119_files/errata-color.css
Normal file
43
www/rfc2119_files/errata-color.css
Normal file
@@ -0,0 +1,43 @@
|
||||
<style type="text/css">
|
||||
.verified {color: green}
|
||||
.supplementary-styling {
|
||||
background-color: yellow
|
||||
}
|
||||
.old-text {color: red}
|
||||
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
background-color: LightGreen;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Verified-inline-styling {
|
||||
color: Green;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
background-color: LightBlue;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
color: Blue;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color:GoldenRod;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
|
||||
</style>
|
||||
34
www/rfc2119_files/errata-monochrome.css
Normal file
34
www/rfc2119_files/errata-monochrome.css
Normal file
@@ -0,0 +1,34 @@
|
||||
<style type="text/css">
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
font-wieght: bold;
|
||||
}
|
||||
.Verified-inline-styling {
|
||||
font-wieght: bold;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
font-style: italic;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color: Yellow;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
43
www/rfc2119_files/errata-printer.css
Normal file
43
www/rfc2119_files/errata-printer.css
Normal file
@@ -0,0 +1,43 @@
|
||||
<style type="text/css">
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
font-wieght: bold;
|
||||
}
|
||||
.Verified-inline-styling {
|
||||
font-wieght: bold;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
font-style: italic;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color: Yellow;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.nodeCloseClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
.nodeOpenClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
</style>
|
||||
4
www/rfc2119_files/errata.js
Normal file
4
www/rfc2119_files/errata.js
Normal file
@@ -0,0 +1,4 @@
|
||||
function hideFunction(nodeId) {
|
||||
var ul = document.getElementById(nodeId)
|
||||
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
|
||||
}
|
||||
4
www/rfc2119_files/errata.js.téléchargement
Normal file
4
www/rfc2119_files/errata.js.téléchargement
Normal file
@@ -0,0 +1,4 @@
|
||||
function hideFunction(nodeId) {
|
||||
var ul = document.getElementById(nodeId)
|
||||
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
|
||||
}
|
||||
BIN
www/root.png
Normal file
BIN
www/root.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 446 KiB |
14
www/test/index1.html
Normal file
14
www/test/index1.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test index</title>
|
||||
<!-- <link rel="stylesheet" href="stylesheet/style.css"> -->
|
||||
<link rel="stylesheet" href="/stylesheet/style.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>Webserv Test Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/something.html
Normal file
11
www/test/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/index1.html
Normal file
11
www/test/test_deeper/index1.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv Test Deeper Index</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/something.html
Normal file
11
www/test/test_deeper/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/super_deep/something.html
Normal file
11
www/test/test_deeper/super_deep/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Super Deep Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
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