Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72b12132d4 | ||
|
|
da5d1f38b0 | ||
|
|
875810c992 | ||
|
|
c6ebb95dc5 | ||
|
|
4fdbb1e0eb | ||
|
|
b45ed52a2a | ||
|
|
1a99c6cc84 | ||
|
|
9f3903d497 | ||
|
|
ccc542f52b | ||
|
|
19a9d3b464 | ||
|
|
4dc70373f8 | ||
|
|
1b7d388231 | ||
|
|
517f5dfc8a | ||
|
|
2c6bc096cc | ||
|
|
5c5f298493 | ||
|
|
1ed4128afc | ||
|
|
ff443c80b1 | ||
|
|
4602844f5a | ||
|
|
2a1aec8f1d | ||
|
|
4a0c0ee238 | ||
|
|
5627b6d1a2 | ||
|
|
5e403fc71c | ||
|
|
f8c6923c6d | ||
|
|
8c2aff6c6b | ||
|
|
e691c517e8 | ||
|
|
973388bf97 | ||
|
|
48af92b3bc | ||
|
|
c05536ca01 | ||
|
|
008b2a635f | ||
|
|
54b6d1f6d6 | ||
|
|
c9c8946e70 | ||
|
|
19da59ecf8 | ||
|
|
fe89be65f6 | ||
|
|
1c13e254d5 | ||
|
|
3fe37ea451 | ||
|
|
6ad6ec7d63 | ||
|
|
036256522a | ||
|
|
a284e400c1 | ||
|
|
9ac84f706d | ||
|
|
b9d4317f51 | ||
|
|
aa03880601 | ||
|
|
a6bfea3b56 | ||
|
|
f6f63931ad | ||
|
|
8000668b36 | ||
|
|
21efbef86a | ||
|
|
076f46fb85 | ||
|
|
683dbadb91 | ||
|
|
8d4961c9b5 | ||
|
|
d663a4c7e6 | ||
|
|
37c5be9ffc | ||
|
|
ce0a004d28 | ||
|
|
84babec82b | ||
|
|
ddafa229c6 | ||
|
|
be499328f6 | ||
|
|
41db4fc12b | ||
|
|
a20a5eff27 | ||
|
|
db4c7468cc | ||
|
|
dda32c759a | ||
|
|
b0949615c8 | ||
|
|
285f2d049f | ||
|
|
defb2ada61 | ||
|
|
ebd0fda52c | ||
|
|
c3240b8618 | ||
|
|
3495ff19a8 | ||
|
|
058701f657 | ||
|
|
a69273b88e | ||
|
|
f17bc9fa58 | ||
|
|
4d395088d0 | ||
|
|
c7bbf29a1b | ||
|
|
b44acafefe | ||
|
|
cade79c37f | ||
|
|
52824f30bd | ||
|
|
2a70c6b26d | ||
|
|
400efbe720 | ||
|
|
f7c0ff1a8a | ||
|
|
ab0bc2c4c0 | ||
|
|
08f6929db9 | ||
|
|
c32fc2c8a2 | ||
|
|
0b51d13f13 | ||
|
|
360c27df5f | ||
|
|
69c1a6f6bf |
2
.gitignore
vendored
@@ -22,3 +22,5 @@ webserv
|
|||||||
*.log
|
*.log
|
||||||
|
|
||||||
large.jpg
|
large.jpg
|
||||||
|
webserv_tester
|
||||||
|
webserv_tester2
|
||||||
|
|||||||
23
42.config
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
server {
|
||||||
|
|
||||||
|
|
||||||
|
server_name server1;
|
||||||
|
listen 0.0.0.0:4040;
|
||||||
|
client_body_limit 1000;
|
||||||
|
|
||||||
|
index youpi.bla; # this is another comment
|
||||||
|
|
||||||
|
root ./YoupiBanane/;
|
||||||
|
|
||||||
|
error_page 404 ./www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /directory {
|
||||||
|
index youpi.bad_extention;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
Makefile
@@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
NAME = webserv
|
NAME = webserv
|
||||||
CXX = clang++
|
CXX = c++
|
||||||
|
|
||||||
CXXFLAGS = -Wall -Wextra #-Werror
|
CXXFLAGS = -Wall -Wextra #-Werror
|
||||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||||
CXXFLAGS += -std=c++98
|
CXXFLAGS += -std=c++98
|
||||||
CXXFLAGS += -g3
|
CXXFLAGS += -g
|
||||||
|
#CXXFLAGS += -fno-limit-debug-info
|
||||||
CXXFLAGS += -MMD -MP #header dependencie
|
CXXFLAGS += -MMD -MP #header dependencie
|
||||||
#CXXFLAGS += -O3
|
#CXXFLAGS += -O3
|
||||||
|
|
||||||
@@ -17,44 +18,61 @@ HEADERS_D = srcs \
|
|||||||
|
|
||||||
SRCS_D = srcs \
|
SRCS_D = srcs \
|
||||||
srcs/webserv \
|
srcs/webserv \
|
||||||
srcs/config
|
srcs/config \
|
||||||
|
|
||||||
SRCS = main.cpp \
|
SRCS = main.cpp \
|
||||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||||
accept.cpp request.cpp response.cpp \
|
accept.cpp request.cpp response.cpp \
|
||||||
method_get.cpp method_post.cpp method_delete.cpp \
|
method_get.cpp method_post.cpp method_delete.cpp \
|
||||||
run_loop.cpp \
|
run_loop.cpp timeout.cpp \
|
||||||
parser.cpp \
|
parser.cpp \
|
||||||
extraConfig.cpp \
|
extraConfig.cpp \
|
||||||
postProcessing.cpp \
|
postProcessing.cpp \
|
||||||
utils.cpp \
|
utils.cpp \
|
||||||
cgi_script.cpp \
|
cgi.cpp \
|
||||||
Client.cpp \
|
Client.cpp Client_multipart_body.cpp \
|
||||||
|
|
||||||
OBJS_D = builds
|
OBJS_D = builds
|
||||||
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
||||||
DEPS = $(OBJS:.o=.d) #header dependencie
|
DEPS = $(OBJS:.o=.d) #header dependencie
|
||||||
|
|
||||||
# --------------------
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
# ------ RULES -------
|
# . target: prerequisites . $@ : target #
|
||||||
# --------------------
|
# RULES . recipe . $< : 1st prerequisite #
|
||||||
|
# . @recipe (silent) . $^ : all prerequisites #
|
||||||
|
# . target: VAR = assignment . | : order-only prereq. #
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
|
|
||||||
all: $(NAME)
|
all: $(NAME)
|
||||||
|
|
||||||
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
|
||||||
|
|
||||||
$(OBJS_D):
|
$(OBJS_D):
|
||||||
mkdir $@
|
mkdir $@
|
||||||
|
|
||||||
$(NAME): $(OBJS)
|
$(NAME): $(OBJS)
|
||||||
$(CXX) $^ -o $(NAME)
|
$(CXX) $^ -o $(NAME)
|
||||||
|
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
|
||||||
|
|
||||||
|
# CGI
|
||||||
|
cgi:
|
||||||
|
make -C srcs/cgi-bin
|
||||||
|
cgiclean:
|
||||||
|
make clean -C srcs/cgi-bin
|
||||||
|
cgifclean:
|
||||||
|
make fclean -C srcs/cgi-bin
|
||||||
|
cgire:
|
||||||
|
make re -C srcs/cgi-bin
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJS_D)
|
rm -rf $(OBJS_D)
|
||||||
|
echo "$(_RED).o Files Deleted 😱$(_END)"
|
||||||
|
|
||||||
fclean: clean
|
fclean: clean
|
||||||
rm -f $(NAME)
|
rm -f $(NAME)
|
||||||
|
echo "$(_RED)$(NAME) Deleted 😱$(_END)"
|
||||||
|
|
||||||
re: fclean all
|
re: fclean all
|
||||||
|
|
||||||
@@ -62,3 +80,24 @@ re: fclean all
|
|||||||
|
|
||||||
-include $(DEPS) # header dependencie
|
-include $(DEPS) # header dependencie
|
||||||
|
|
||||||
|
.SILENT:
|
||||||
|
|
||||||
|
# ------------------
|
||||||
|
# ----- COLORS -----
|
||||||
|
# ------------------
|
||||||
|
|
||||||
|
_GREY=$ \033[30m
|
||||||
|
_RED=$ \033[31m
|
||||||
|
_GREEN=$ \033[32m
|
||||||
|
_YELLOW=$ \033[33m
|
||||||
|
_BLUE=$ \033[34m
|
||||||
|
_PURPLE=$ \033[35m
|
||||||
|
_CYAN=$ \033[36m
|
||||||
|
_WHITE=$ \033[37m
|
||||||
|
_END=$ \033[0m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
149
README.md
@@ -1,29 +1,6 @@
|
|||||||
|
|
||||||
## work together
|
## work together
|
||||||
|
|
||||||
#### TODO hugo
|
|
||||||
- `_is_cgi()` and `_fill_cgi_path()`
|
|
||||||
- two cgi tests :
|
|
||||||
? - a basic form with "name" and "something", that return a html page with that
|
|
||||||
? - for GET and POST
|
|
||||||
? - a script called by a file extension in URI
|
|
||||||
|
|
||||||
#### questions
|
|
||||||
- in client, fd is in privat, so accesible by getter, is it ok ?
|
|
||||||
- in client.cpp i fill the port, is there a default one in case it's not in the request ?
|
|
||||||
- timeout server but still works ?
|
|
||||||
- path contains double "//" from `Webserv::_get()` in response.cpp
|
|
||||||
- cgi path ? defined in config ? and root path ? :
|
|
||||||
- `Client.cpp : fill_script_path()`
|
|
||||||
- `cgi.cpp : is_cgi()`
|
|
||||||
- `cgi.cpp : set_env()`
|
|
||||||
- what if the uri contains a php file, and the config said php must be handled by cgi, but the path to this php in the uri is wrong ?
|
|
||||||
- is it ok ? `http://my_site.com/cgi-bin/php-cgi` (real path)
|
|
||||||
- is it ok ? `http://my_site.com/php-cgi` (reconstruct path ?)
|
|
||||||
- is it ok ? `http://my_site.com/something/php-cgi` (what about 'something' ?)
|
|
||||||
- is it ok ? `http://my_site.com/something/cgi-bin/php-cgi` (real path with 'something' before ? )
|
|
||||||
- I don't save the STDIN and STDOUT before dup2 in child process, is it wrong ?
|
|
||||||
- the response page is received long after the cgi-script is done, why ?
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## man
|
## man
|
||||||
@@ -104,18 +81,6 @@
|
|||||||
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||||
|
|
||||||
#### output cgi script :
|
#### output cgi script :
|
||||||
! TODO : change all the '\n' by '\r\n'
|
|
||||||
! TODO : there is at least one header field followed by '\r\n\r\n' :
|
|
||||||
- "Content-Type"
|
|
||||||
- "Location"
|
|
||||||
- "Status"
|
|
||||||
! TODO : there is no space between filed name and ":"
|
|
||||||
! TODO?: handle Location field, either :
|
|
||||||
- local : start with '/' --> rerun the request with new uri
|
|
||||||
- client : start with '<scheme>:' --> send back status code 302
|
|
||||||
-> TODO : there is no field duplicate (resolve conflicts)
|
|
||||||
-> TODO : if status field, change server status for this one
|
|
||||||
-> TODO : if no Location field && no Status field -> status code = 200
|
|
||||||
|
|
||||||
#### summary :
|
#### summary :
|
||||||
- the cgi-script will send back at least one header field followed by an empty line
|
- the cgi-script will send back at least one header field followed by an empty line
|
||||||
@@ -125,13 +90,18 @@
|
|||||||
- "Status"
|
- "Status"
|
||||||
- the cgi-script may send back more header fields
|
- the cgi-script may send back more header fields
|
||||||
- the server must check and modify few things :
|
- the server must check and modify few things :
|
||||||
- there is no duplicate in headers fields (if there is, resolve conflict)
|
- there is no field duplicate (resolve conflicts)
|
||||||
- there is no space between the field name and the ":"
|
- there is no space between filed name and ":"
|
||||||
- the newlines are of form "\r\n", and not "\n" only
|
- change all the '\n' by '\r\n'
|
||||||
- if the location field is not present, then if the status field is not present either, then the status code is 200
|
- if no Location field && no Status field -> status code = 200
|
||||||
- the cgi-script can return a location field, of two types :
|
- handle Location field, either :
|
||||||
- local redirection : start with a "/", the server must answer as if this was the request uri
|
- local : start with '/' --> rerun the request with new uri
|
||||||
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
|
- client : start with '<scheme>:' --> send back status code 302
|
||||||
|
- there is at least one header field followed by '\r\n\r\n' :
|
||||||
|
- "Content-Type"
|
||||||
|
- "Location"
|
||||||
|
- "Status"
|
||||||
|
- if status field, change server status for this one
|
||||||
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||||
|
|
||||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||||
@@ -232,45 +202,6 @@
|
|||||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
## http errors
|
|
||||||
|
|
||||||
#### HTTP Client Errors
|
|
||||||
- 400 Bad Request This error code indicates that the request cannot be processed because of wrong syntax usage by the client.
|
|
||||||
- 401 Unauthorized This error code indicates that the client is not authorized to receive the requested data, without authentication. A login name and password based authentication might be required to access the requested data.
|
|
||||||
- 403 Forbidden There is no way you can access the requested data. A 403 error announces that the data is off limits.
|
|
||||||
- 404 Not Found This error indicates that the resources requested by the client are currently unavailable.
|
|
||||||
- 405 Method Not Allowed This error indicates wrong usage of request method. Depending on the kind of data requested, the appropriate request method must be chosen.
|
|
||||||
- 406 Not Acceptable When the data provided by a web server does not match the specifications made in ‘Accept’ header of the client HTTP request, this error is the result.
|
|
||||||
- 407 Proxy Authentication Required This error clearly indicates that an authentication from the proxy server is required to gain access to requested resources.
|
|
||||||
- 408 Request Timeout This type of error indicates that the client was delayed in making a request, within the specified time allocated to it, by the server.
|
|
||||||
- 409 Conflict This error code is displayed when the server perceives a conflict between two requests made simultaneously by different clients, for the same resource.
|
|
||||||
- 410 Gone This error code indicates that the requested data is no longer hosted on the server and therefore further requests made for it, would be futile.
|
|
||||||
- 411 Length Required If the request made by the client does not include information about the length of the requested data or resource, this error code is displayed.
|
|
||||||
- 412 Precondition Failed Some requests made by clients come attached with conditions that need to be satisfied by the server, before data transaction may happen. If these conditions are not met, error 412 results.
|
|
||||||
- 413 Request Entity Too Large When a client makes a request which is too overwhelming for the server’s resources to handle, it presents this error code.
|
|
||||||
- 414 Requested URI Too Long A Uniform Resource Identifier (URI) is a character string used to describe a data stream or resource on a server. Error 414 occurs when the server is unable to process the URI, because of limited resources and long string length.
|
|
||||||
- 415 Unsupported Media Type A server may be designed to allow only certain formats for media files. When error 415 is displayed, it indicates that the format of file being uploaded through a client request, does not match the requisite format.
|
|
||||||
- 416 Request Range Not Satisfiable Sometimes, a client may request for only a small part of a file, instead of asking for the entire file. If this request is not specified properly and the part of the file requested does not exist, this error is displayed.
|
|
||||||
- 417 Expectation Failed This error code is displayed when the server cannot meet the specifications provided in the request.
|
|
||||||
- 422 Unprocessable Entity This error is displayed when the request made, cannot be processed due to an error in semantic structure.
|
|
||||||
- 423 Locked This error is displayed when a requested piece of data or resource has been locked, making it inaccessible for a server.
|
|
||||||
- 424 Failed Dependency A server may process a succession of requests from a client with the fulfillment of each, dependent on the one made before. This error is displayed when a request made before is not fulfilled, due to which the current request cannot be processed.
|
|
||||||
- 426 Upgrade Required This error signifies that the client may need to switch over to a secure protocol like TLS to get the request processed.
|
|
||||||
- 444 No Response This error signifies that the server has simply rejected the client request and terminated connection.
|
|
||||||
- 449 Retry With This is a request made by the server to the client, to make the request again after executing certain actions or making specific changes in request. This is an error code introduced by Microsoft.
|
|
||||||
- 499 Client Closed Request When client terminates a connection made with the server, while its processing the associated request, this error code is displayed.
|
|
||||||
- 450 Blocked By Windows Parental Controls Another error code introduced by Microsoft, this one is displayed when a URL is blocked by parental control settings on the web browsers.
|
|
||||||
|
|
||||||
#### HTTP Server Errors
|
|
||||||
- 500 Internal Server Error A generic message displayed by the server, when the problem with the request cannot be specified by any other appropriate code.
|
|
||||||
- 501 Not Implemented This error indicates the inability of the server to process a request, as it hasn’t been configured to respond to the request method used.
|
|
||||||
- 502 Bad Gateway Sometimes, hosted pages on web servers are transmitted to client via proxy servers. If the proxy server (to which a client has sent a request), fails connecting with the web server (known as the upstream server), error 502 results.
|
|
||||||
- 503 Service Unavailable When the server is already overloaded with multiple requests, it will temporarily stop entertaining new requests, by displaying a 503 error code.
|
|
||||||
- 504 Gateway Timeout When the request made by a proxy server to the web server hosting a resource times out, error 504 is reported.
|
|
||||||
- 505 HTTP Version Not Supported An error code seen rarely, it is displayed when the web server does not support the protocol version of the client request.
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
## cgi env variables
|
## cgi env variables
|
||||||
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||||
@@ -301,6 +232,62 @@ SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
|
|||||||
REDIRECT_STATUS : for exemple, 200
|
REDIRECT_STATUS : for exemple, 200
|
||||||
```
|
```
|
||||||
|
|
||||||
|
g 50 34 48
|
||||||
|
p 30 23 32
|
||||||
|
l 20 14 20
|
||||||
|
71
|
||||||
|
|
||||||
|
---
|
||||||
|
## http status
|
||||||
|
[rfc 2616](https://datatracker.ietf.org/doc/html/rfc2616#section-10)
|
||||||
|
|
||||||
|
#### Informational
|
||||||
|
- 100 Continue
|
||||||
|
- 101 Switching Protocols
|
||||||
|
#### Successful
|
||||||
|
- 200 OK
|
||||||
|
- 201 Created
|
||||||
|
- 202 Accepted
|
||||||
|
- 203 Non-Authoritative Information
|
||||||
|
- 204 No Content
|
||||||
|
- 205 Reset Content
|
||||||
|
- 206 Partial Content
|
||||||
|
#### Redirection
|
||||||
|
- 300 Multiple Choices
|
||||||
|
- 301 Moved Permanently
|
||||||
|
- 302 Found
|
||||||
|
- 303 See Other
|
||||||
|
- 304 Not Modified
|
||||||
|
- 305 Use Proxy
|
||||||
|
- 306 (Unused)
|
||||||
|
- 307 Temporary Redirect
|
||||||
|
#### Client Error
|
||||||
|
- 400 Bad Request
|
||||||
|
- 401 Unauthorized
|
||||||
|
- 402 Payment Required
|
||||||
|
- 403 Forbidden
|
||||||
|
- 404 Not Found
|
||||||
|
- 405 Method Not Allowed
|
||||||
|
- 406 Not Acceptable
|
||||||
|
- 407 Proxy Authentication Required
|
||||||
|
- 408 Request Timeout
|
||||||
|
- 409 Conflict
|
||||||
|
- 410 Gone
|
||||||
|
- 411 Length Required
|
||||||
|
- 412 Precondition Failed
|
||||||
|
- 413 Request Entity Too Large
|
||||||
|
- 414 Request-URI Too Long
|
||||||
|
- 415 Unsupported Media Type
|
||||||
|
- 416 Requested Range Not Satisfiable
|
||||||
|
- 417 Expectation Failed
|
||||||
|
#### Server Error
|
||||||
|
- 500 Internal Server Error
|
||||||
|
- 501 Not Implemented
|
||||||
|
- 502 Bad Gateway
|
||||||
|
- 503 Service Unavailable
|
||||||
|
- 504 Gateway Timeout
|
||||||
|
- 505 HTTP Version Not Supported
|
||||||
|
|
||||||
---
|
---
|
||||||
## ressources
|
## ressources
|
||||||
|
|
||||||
|
|||||||
116
Tester/expected_results/expected_path_test.txt
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
telnet> Trying 127.0.0.1...
|
||||||
|
Connected to localhost.
|
||||||
|
Escape character is '^]'.
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 193
|
||||||
|
|
||||||
|
<!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>
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 290
|
||||||
|
|
||||||
|
<!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>
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 290
|
||||||
|
|
||||||
|
<!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>
|
||||||
|
HTTP/1.1
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 1277
|
||||||
|
|
||||||
|
<!DOCTYPE html><html><head><title>Index of ./Tester/www/</title></head><body><h1>Index of ./Tester/www/</h1><hr><pre><a style="font-size:1.5em" href="/list/..">..</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/upload_form.html">upload_form.html</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/form_get.html">form_get.html</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/kermit.ico">kermit.ico</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/upload_form_single.html">upload_form_single.html</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/Cagneyc_intro.gif">Cagneyc_intro.gif</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/Van_Eyck_Portrait_Arnolfini.jpg">Van_Eyck_Portrait_Arnolfini.jpg</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/root.png">root.png</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/test">test</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/drill.jpg">drill.jpg</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/punpun.png">punpun.png</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/favicon.ico">favicon.ico</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/index.html">index.html</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/rfc2119.html">rfc2119.html</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/error_pages">error_pages</a>
|
||||||
|
<a style="font-size:1.5em" href="/list/rfc2119_files">rfc2119_files</a>
|
||||||
|
</pre><hr></body></html>HTTP/1.1 404 Not Found
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 210
|
||||||
|
|
||||||
|
<!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>
|
||||||
|
HTTP/1.1 404 Not Found
|
||||||
|
Server: Webserv/0.1
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Length: 210
|
||||||
|
|
||||||
|
<!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>
|
||||||
95
Tester/telnet_test.sh
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
# DO I EVEN NEED THIS FILE ? could i just put this stuff in maint_test.sh? or would it not modify the vars...
|
||||||
|
|
||||||
|
|
||||||
|
# you need to put absolutely everything in ""
|
||||||
|
|
||||||
|
|
||||||
|
#test_file=$1
|
||||||
|
#source $test_file
|
||||||
|
|
||||||
|
connect_to_telnet="open $host $port"
|
||||||
|
|
||||||
|
|
||||||
|
start_telnet()
|
||||||
|
{
|
||||||
|
echo "open $host $port"
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
echo "$connect_to_telnet"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
run_this_test
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#### DEPRICATED ######
|
||||||
|
|
||||||
|
run_telnet()
|
||||||
|
{
|
||||||
|
echo "$connect_to_telnet"
|
||||||
|
sleep 1
|
||||||
|
echo -e "$request"
|
||||||
|
echo
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# ret = $(arg | telnet)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
run_a_test()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} &> test.log
|
||||||
|
|
||||||
|
echo -e "$request" | telnet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#######
|
||||||
|
# This is where stuff is launched
|
||||||
|
#######
|
||||||
|
|
||||||
|
#rm -rf telnet.out
|
||||||
|
|
||||||
|
#./webserv $config_file 2>&1 > webserv.log & run_all
|
||||||
|
#./webserv $config_file 2>&1 > webserv.log &
|
||||||
|
#./webserv $config_file &> /dev/null &
|
||||||
|
#./webserv $config_file 1>&1 > webserv.log &
|
||||||
|
|
||||||
|
#./webserv $config_file &> webserv.log &
|
||||||
|
|
||||||
|
#run_all
|
||||||
|
#run_this_test
|
||||||
|
|
||||||
|
#echo -e "${_GREEN}Running Telnet Test on '$test_name'${_END}"
|
||||||
|
|
||||||
|
#sleep 1
|
||||||
|
|
||||||
|
#run | telnet >> telnet.out
|
||||||
|
#run | telnet
|
||||||
|
|
||||||
|
#pkill webserv
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
16
Tester/telnet_test2.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
echo "open duckduckgo.com 80"
|
||||||
|
sleep 1
|
||||||
|
echo "GET / HTTP/1.1"
|
||||||
|
echo "Host: duckduckgo.com"
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
#(
|
||||||
|
#echo open duckduckgo.com 80
|
||||||
|
#echo "GET / HTTP/1.1"
|
||||||
|
#echo "Host: duckduckgo.com"
|
||||||
|
#echo "exit"
|
||||||
|
#) | telnet
|
||||||
97
Tester/test1.config
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
server {
|
||||||
|
|
||||||
|
# this is a comment
|
||||||
|
|
||||||
|
server_name server1;
|
||||||
|
|
||||||
|
listen 0.0.0.0:4040;
|
||||||
|
|
||||||
|
# client_body_limit asdfa;
|
||||||
|
client_body_limit 5000;
|
||||||
|
# Max == 18446744073709551615 / 1024 == 18014398509481984
|
||||||
|
|
||||||
|
index index.html; # this is another comment
|
||||||
|
#index mdr.html; # this is another comment
|
||||||
|
|
||||||
|
root ./Tester/www/;
|
||||||
|
|
||||||
|
error_page 404 ./Tester/www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
root ./Tester/www/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /srcs/cgi-bin/ {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
allow_methods POST;
|
||||||
|
cgi_ext php;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /list {
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /cgi-bin {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
cgi_ext out php sh;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /upload {
|
||||||
|
allow_methods POST;
|
||||||
|
autoindex on;
|
||||||
|
upload_dir ./Tester/www/user_files/;
|
||||||
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
|
}
|
||||||
|
|
||||||
|
location /the_dump {
|
||||||
|
allow_methods GET DELETE;
|
||||||
|
root ./Tester/www/user_files;
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ./Tester/www/test/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /stylesheet {
|
||||||
|
root ./stylesheet/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/index1.html {
|
||||||
|
root ./Tester/www/test/index1.html;
|
||||||
|
index index1.html subdex.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /hilarious_404/ {
|
||||||
|
redirect 301 https://berniesanders.com/404/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
root ./Tester/www/test/something.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/ {
|
||||||
|
# allow_methods
|
||||||
|
root ./Tester/www/test/test_deeper/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/super_deep {
|
||||||
|
root ./Tester/www/test/test_deeper/super_deep/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# location /test/test_deeper/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
# }
|
||||||
|
|
||||||
|
}
|
||||||
95
Tester/test2.config
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
server {
|
||||||
|
|
||||||
|
# this is a comment
|
||||||
|
|
||||||
|
server_name server1;
|
||||||
|
|
||||||
|
listen 0.0.0.0:8080;
|
||||||
|
|
||||||
|
client_body_limit 1;
|
||||||
|
# Max == 18446744073709551615 / 1024 == 18014398509481984
|
||||||
|
|
||||||
|
index index.html; # this is another comment
|
||||||
|
|
||||||
|
root ./Tester/www/;
|
||||||
|
|
||||||
|
error_page 404 ./Tester/www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
root ./Tester/www/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /srcs/cgi-bin/ {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
allow_methods POST;
|
||||||
|
cgi_ext php;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /list {
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /cgi-bin {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
cgi_ext out php sh;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /upload {
|
||||||
|
allow_methods POST;
|
||||||
|
autoindex on;
|
||||||
|
upload_dir ./Tester/www/user_files/;
|
||||||
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
|
}
|
||||||
|
|
||||||
|
location /the_dump {
|
||||||
|
allow_methods GET DELETE;
|
||||||
|
root ./Tester/www/user_files;
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ./Tester/www/test/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /stylesheet {
|
||||||
|
root ./stylesheet/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/index1.html {
|
||||||
|
root ./Tester/www/test/index1.html;
|
||||||
|
index index1.html subdex.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /hilarious_404/ {
|
||||||
|
redirect 301 https://berniesanders.com/404/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
root ./Tester/www/test/something.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/ {
|
||||||
|
# allow_methods
|
||||||
|
root ./Tester/www/test/test_deeper/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/super_deep {
|
||||||
|
root ./Tester/www/test/test_deeper/super_deep/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# location /test/test_deeper/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
# }
|
||||||
|
|
||||||
|
}
|
||||||
98
Tester/test3.config
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
server {
|
||||||
|
|
||||||
|
# this is a comment
|
||||||
|
|
||||||
|
server_name server1;
|
||||||
|
|
||||||
|
listen 8080;
|
||||||
|
# listen 70000;
|
||||||
|
|
||||||
|
# client_body_limit asdfa;
|
||||||
|
client_body_limit 1000;
|
||||||
|
# Max == 18446744073709551615 / 1024 == 18014398509481984
|
||||||
|
|
||||||
|
index index.html; # this is another comment
|
||||||
|
#index mdr.html; # this is another comment
|
||||||
|
|
||||||
|
root ./Tester/www/;
|
||||||
|
|
||||||
|
error_page 404 ./Tester/www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
root ./Tester/www/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /srcs/cgi-bin/ {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
allow_methods POST;
|
||||||
|
cgi_ext php;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /list {
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /cgi-bin {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
cgi_ext out php sh;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /upload {
|
||||||
|
allow_methods POST;
|
||||||
|
autoindex on;
|
||||||
|
upload_dir ./Tester/www/user_files/;
|
||||||
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
|
}
|
||||||
|
|
||||||
|
location /the_dump {
|
||||||
|
allow_methods GET DELETE;
|
||||||
|
root ./Tester/www/user_files;
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ./Tester/www/test/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /stylesheet {
|
||||||
|
root ./stylesheet/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/index1.html {
|
||||||
|
root ./Tester/www/test/index1.html;
|
||||||
|
index index1.html subdex.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /hilarious_404/ {
|
||||||
|
redirect 301 https://berniesanders.com/404/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
root ./Tester/www/test/something.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/ {
|
||||||
|
# allow_methods
|
||||||
|
root ./Tester/www/test/test_deeper/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /test/test_deeper/super_deep {
|
||||||
|
root ./Tester/www/test/test_deeper/super_deep/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# location /test/test_deeper/something.html {
|
||||||
|
# allow_methods DELETE;
|
||||||
|
# }
|
||||||
|
|
||||||
|
}
|
||||||
88
Tester/test_body.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Body Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test2.config"
|
||||||
|
port=8080
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
#paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
|
||||||
|
paths=("/") # you can add many
|
||||||
|
#methods=("GET" "POST" "DELETE")
|
||||||
|
methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host\n"
|
||||||
|
body="this is a message \n"
|
||||||
|
body+="this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message \n"
|
||||||
|
|
||||||
|
# turns out this does nothing...
|
||||||
|
#header_cycle=("Content-Length: 17\n" "Content-Length: 14" "Content-Length: 25")
|
||||||
|
#header_cycle=("Content-Length: 17\n" "\n")
|
||||||
|
#header_cycle=("Content-Length: 17\n")
|
||||||
|
#header_cycle=()
|
||||||
|
#header+="Content-Length: 8\n"
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
echo size is ${#body} >> telnet.log
|
||||||
|
|
||||||
|
# for i in "${header_cycle[@]}"
|
||||||
|
for i in "${paths[@]}"
|
||||||
|
do
|
||||||
|
# l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
l1="${methods[0]} $i ${httpz[0]}"
|
||||||
|
# header_send="$header$i"
|
||||||
|
header_send="$header"
|
||||||
|
## if you add an extra \n after header send you get a status code 200 OK and 400 bad request, but like both
|
||||||
|
# request="$l1\n$header_send\n$body\n"
|
||||||
|
request="$l1\n$header_send\n$body"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_body_test.txt"
|
||||||
|
files+=("expected_body_test.txt")
|
||||||
|
#files+=("expected_path_root_test.txt")
|
||||||
|
#files+=("expected_path_testnoslash_test.txt")
|
||||||
|
#files+=("expected_path_testslash_test.txt")
|
||||||
|
#files+=("expected_path_list_test.txt")
|
||||||
|
#files+=("expected_path_badlist_test.txt")
|
||||||
|
#files+=("expected_path_wrong_test.txt")
|
||||||
|
|
||||||
|
|
||||||
|
#local_expected_test_file=$file
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
83
Tester/test_header.sh
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Header Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test1.config"
|
||||||
|
port=4040
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
paths=("/") # you can add many
|
||||||
|
#methods=("GET" "POST" "DELETE")
|
||||||
|
methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# the parts we will send to webserv
|
||||||
|
|
||||||
|
|
||||||
|
# let main.sh handle the l1
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host\n"
|
||||||
|
header+="Nonsense: fu\n"
|
||||||
|
|
||||||
|
header_cycle=("Transfer-encoding: fu" "Content-encoding: fu")
|
||||||
|
|
||||||
|
#header+="Transfer-encoding: fu\n"
|
||||||
|
#header+="Content-endcoding: fu\n"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${header_cycle[@]}"
|
||||||
|
do
|
||||||
|
header_send="$header$i"
|
||||||
|
request="$l1\n$header_send\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_header_test.txt"
|
||||||
|
files+=("expected_header_test.txt")
|
||||||
|
#files+=("expected_path_root_test.txt")
|
||||||
|
|
||||||
|
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
83
Tester/test_method.sh
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Method Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test1.config"
|
||||||
|
port=4040
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
paths=("/") # you can add many
|
||||||
|
methods=("GET" "POST" "DELETE" "GUMBALL")
|
||||||
|
#methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# the parts we will send to webserv
|
||||||
|
|
||||||
|
|
||||||
|
# let main.sh handle the l1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
header="Host: $host\n"
|
||||||
|
header+="Nonsense: fu\n"
|
||||||
|
|
||||||
|
#header_cycle=("Transfer-encoding: fu" "Content-encoding: fu")
|
||||||
|
|
||||||
|
#header+="Transfer-encoding: fu\n"
|
||||||
|
#header+="Content-endcoding: fu\n"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${methods[@]}"
|
||||||
|
do
|
||||||
|
l1="$i ${paths[0]} ${httpz[0]}"
|
||||||
|
request="$l1\n$header\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_method_test.txt"
|
||||||
|
files+=("expected_method_test.txt")
|
||||||
|
#files+=("expected_path_root_test.txt")
|
||||||
|
|
||||||
|
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
74
Tester/test_path.sh
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Path Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test1.config"
|
||||||
|
port=4040
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
|
||||||
|
#paths=("/") # you can add many
|
||||||
|
#methods=("GET" "POST" "DELETE")
|
||||||
|
methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${paths[@]}"
|
||||||
|
do
|
||||||
|
l1="${methods[0]} $i ${httpz[0]}"
|
||||||
|
request="$l1\n$header\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_path_test.txt"
|
||||||
|
files+=("expected_path_test.txt")
|
||||||
|
#files+=("expected_path_root_test.txt")
|
||||||
|
#files+=("expected_path_testnoslash_test.txt")
|
||||||
|
#files+=("expected_path_testslash_test.txt")
|
||||||
|
#files+=("expected_path_list_test.txt")
|
||||||
|
#files+=("expected_path_badlist_test.txt")
|
||||||
|
#files+=("expected_path_wrong_test.txt")
|
||||||
|
|
||||||
|
|
||||||
|
#local_expected_test_file=$file
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
74
Tester/test_port.sh
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Port Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test3.config"
|
||||||
|
port=8080
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
#paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
|
||||||
|
paths=("/") # you can add many
|
||||||
|
#methods=("GET" "POST" "DELETE")
|
||||||
|
methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${paths[@]}"
|
||||||
|
do
|
||||||
|
l1="${methods[0]} $i ${httpz[0]}"
|
||||||
|
request="$l1\n$header\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_path_test.txt"
|
||||||
|
files+=("expected_path_test.txt")
|
||||||
|
#files+=("expected_path_root_test.txt")
|
||||||
|
#files+=("expected_path_testnoslash_test.txt")
|
||||||
|
#files+=("expected_path_testslash_test.txt")
|
||||||
|
#files+=("expected_path_list_test.txt")
|
||||||
|
#files+=("expected_path_badlist_test.txt")
|
||||||
|
#files+=("expected_path_wrong_test.txt")
|
||||||
|
|
||||||
|
|
||||||
|
#local_expected_test_file=$file
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
62
Tester/test_template.sh
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Template test all good"
|
||||||
|
|
||||||
|
config_file="./Tester/test1.config"
|
||||||
|
port=4040
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
paths=("/") # you can add many
|
||||||
|
methods=("GET" "POST" "DELETE")
|
||||||
|
#methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# the parts we will send to webserv
|
||||||
|
|
||||||
|
|
||||||
|
# let main.sh handle the l1
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${methods[@]}"
|
||||||
|
do
|
||||||
|
l1="$i ${paths[0]} ${httpz[0]}"
|
||||||
|
request="$l1\n$header\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
# } &>> test.log
|
||||||
|
# } 2>>&1
|
||||||
|
# } &>> /dev/stdout
|
||||||
|
echo -e "$request"
|
||||||
|
# echo
|
||||||
|
sleep 1
|
||||||
|
# {
|
||||||
|
# echo -e "\n------\n"
|
||||||
|
# } > /dev/stdout
|
||||||
|
# echo -e "\n\n\n------\n\n\n" >> telnet.out
|
||||||
|
# echo -e "\n\n\n------\n\n\n" &> /dev/stdout
|
||||||
|
echo -e "\n\n" >> telnet.log
|
||||||
|
# echo -e "\n\n------\n\n" >> telnet.out
|
||||||
|
# echo -e "\n------\n" > /dev/stdout
|
||||||
|
# run_a_test
|
||||||
|
# echo
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
66
Tester/test_valid_uri.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
test_name="Valid URIs Test"
|
||||||
|
|
||||||
|
config_file="./Tester/test1.config"
|
||||||
|
port=4040
|
||||||
|
host="localhost"
|
||||||
|
|
||||||
|
paths=("/" "/stylesheet/style.css" "/test/something.html" "/test/something.html/" "/test/" "/list" "/hilarious_404" "/redirect") # you can add many
|
||||||
|
#paths=("/") # you can add many
|
||||||
|
#methods=("GET" "POST" "DELETE")
|
||||||
|
methods=("GET")
|
||||||
|
httpz=("HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
|
||||||
|
|
||||||
|
header="Host: $host"
|
||||||
|
body=
|
||||||
|
|
||||||
|
|
||||||
|
run_this_test()
|
||||||
|
{
|
||||||
|
|
||||||
|
for i in "${paths[@]}"
|
||||||
|
do
|
||||||
|
l1="${methods[0]} $i ${httpz[0]}"
|
||||||
|
request="$l1\n$header\n$body\n"
|
||||||
|
{
|
||||||
|
echo "----- $test_name -----"
|
||||||
|
echo -e "$_RED$request$_END"
|
||||||
|
} >> telnet.log
|
||||||
|
echo -e "$request"
|
||||||
|
sleep 1
|
||||||
|
echo -e "\n" >> telnet.log
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# expected result...
|
||||||
|
|
||||||
|
|
||||||
|
files=()
|
||||||
|
|
||||||
|
file="expected_valid_uri_test.txt"
|
||||||
|
files+=("expected_valid_files_test.txt")
|
||||||
|
|
||||||
|
local_expected_test_files=()
|
||||||
|
test_path=""
|
||||||
|
|
||||||
|
add_path()
|
||||||
|
{
|
||||||
|
for i in "${files[@]}"
|
||||||
|
do
|
||||||
|
local_expected_test_files+=("$test_path$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
Tester/www/Cagneyc_intro.gif
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
Tester/www/Van_Eyck_Portrait_Arnolfini.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
Tester/www/drill.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
11
Tester/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
Tester/www/favicon.ico
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
84
Tester/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>
|
||||||
|
|
||||||
11
Tester/www/index.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!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>
|
||||||
BIN
Tester/www/kermit.ico
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Tester/www/punpun.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
312
Tester/www/rfc2119.html
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
<!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">
|
||||||
|
|
||||||
|
<meta name="robots" content="index,follow">
|
||||||
|
|
||||||
|
<link rel="icon" href="https://www.rfc-editor.org/rfc/inline-errata/css/images/rfc.png" type="image/png">
|
||||||
|
<link rel="shortcut icon" href="https://www.rfc-editor.org/rfc/inline-errata/css/images/rfc.png" type="image/png">
|
||||||
|
<title>rfc2119</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="./rfc2119_files/errata-base.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="./rfc2119_files/errata-color.css" title="Default: Basic Colors">
|
||||||
|
<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"></script>
|
||||||
|
</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>
|
||||||
139
Tester/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
Tester/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
Tester/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
Tester/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
Tester/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
Tester/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
Tester/www/root.png
Normal file
|
After Width: | Height: | Size: 446 KiB |
12
Tester/www/test/index1.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Webserv test index</title>
|
||||||
|
<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
Tester/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>
|
||||||
17
Tester/www/test/submit_form.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Webserv test Something</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 style="text-align:center">Webserv in Test</h1>
|
||||||
|
<hr>
|
||||||
|
<p style="text-align:center">Time to submit something:</p>
|
||||||
|
<!-- <form action="./srcs/cgi-bin/upload_file.php" method="post" enctype="multipart/form-data"> -->
|
||||||
|
<form action="/uploaded" method="post" enctype="multipart/form-data">
|
||||||
|
<!-- <input type="hidden" name="upload_dir" value="./www/uploaded/"> -->
|
||||||
|
<input type="file" id="fileToUpload" name="myFile">
|
||||||
|
<input type="submit" value="Upload File">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11
Tester/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
Tester/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
Tester/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>
|
||||||
12
Tester/www/upload_form.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!--
|
||||||
|
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" name="upload_file1">
|
||||||
|
<input type="file" name="upload_file2">
|
||||||
|
<input type="file" name="upload_file3">
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
10
Tester/www/upload_form_single.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" name="upload_file1">
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
4
compare.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
telnet> Trying 127.0.0.1...
|
||||||
|
telnet> ?Invalid command
|
||||||
|
telnet> ?Invalid command
|
||||||
|
telnet> telnet> telnet>
|
||||||
@@ -1,24 +1,69 @@
|
|||||||
|
|
||||||
server {
|
server {
|
||||||
|
|
||||||
# this is a comment
|
# this is a comment
|
||||||
|
|
||||||
server_name our_server;
|
server_name server1;
|
||||||
|
|
||||||
listen 0.0.0.0:4040;
|
listen 0.0.0.0:4040;
|
||||||
|
|
||||||
# client_body_limit asdfa;
|
# client_body_limit asdfa;
|
||||||
# client_body_limit 400;
|
client_body_limit 5000;
|
||||||
|
# Max == 18446744073709551615 / 1024 == 18014398509481984
|
||||||
|
|
||||||
index index.html; # this is another comment
|
index index.html; # this is another comment
|
||||||
|
#index mdr.html; # this is another comment
|
||||||
|
|
||||||
root ./www/;
|
root ./www/;
|
||||||
|
|
||||||
error_page 404 ./www/error_pages/error_404.html;
|
error_page 404 ./www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
root ./www/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /srcs/cgi-bin/ {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
allow_methods POST;
|
||||||
|
cgi_ext php;
|
||||||
|
}
|
||||||
|
|
||||||
location /list {
|
location /list {
|
||||||
autoindex on;
|
autoindex on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /cgi-bin {
|
||||||
|
root ./srcs/cgi-bin/;
|
||||||
|
cgi_ext out php sh;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /upload {
|
||||||
|
allow_methods POST;
|
||||||
|
# autoindex on;
|
||||||
|
# root ./www/;
|
||||||
|
# index upload_form_single.html;
|
||||||
|
|
||||||
|
# upload_dir ./www/user_files/;
|
||||||
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
|
}
|
||||||
|
|
||||||
|
location /uploaded {
|
||||||
|
allow_methods GET;
|
||||||
|
autoindex on;
|
||||||
|
# upload_dir ./www/user_files/;
|
||||||
|
root ./www/user_files;
|
||||||
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
|
}
|
||||||
|
|
||||||
|
location /the_dump {
|
||||||
|
allow_methods GET DELETE;
|
||||||
|
root ./www/user_files;
|
||||||
|
autoindex on;
|
||||||
|
}
|
||||||
|
|
||||||
location /redirect {
|
location /redirect {
|
||||||
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
|
||||||
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
|
||||||
@@ -26,9 +71,15 @@ server {
|
|||||||
|
|
||||||
location /test {
|
location /test {
|
||||||
index index1.html subdex.html;
|
index index1.html subdex.html;
|
||||||
|
root ./www/test/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /stylesheet {
|
||||||
|
root ./stylesheet/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /test/index1.html {
|
location /test/index1.html {
|
||||||
|
root ./www/test/index1.html;
|
||||||
index index1.html subdex.html;
|
index index1.html subdex.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,37 +87,33 @@ server {
|
|||||||
redirect 301 https://berniesanders.com/404/;
|
redirect 301 https://berniesanders.com/404/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
|
|
||||||
location /stylesheet/style.css {
|
|
||||||
# root ./www/../;
|
|
||||||
root ./;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /test/something.html {
|
location /test/something.html {
|
||||||
# allow_methods DELETE;
|
# allow_methods DELETE;
|
||||||
|
root ./www/test/something.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
# location /something/long/here {
|
|
||||||
# }
|
|
||||||
|
|
||||||
location /test/test_deeper/ {
|
location /test/test_deeper/ {
|
||||||
# allow_methods
|
# allow_methods
|
||||||
autoindex on;
|
root ./www/test/test_deeper/;
|
||||||
|
index index1.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /test/test_deeper/super_deep {
|
location /test/test_deeper/super_deep {
|
||||||
autoindex on;
|
root ./www/test/test_deeper/super_deep/;
|
||||||
|
index something.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
# location /test/test_deeper/something.html {
|
# location /test/test_deeper/something.html {
|
||||||
# allow_methods DELETE;
|
# allow_methods DELETE;
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
}
|
||||||
# ok in theory if one were to go to /test they would get the index in www
|
|
||||||
# as opposed to the one in /website...
|
server {
|
||||||
# location /test {
|
server_name server2;
|
||||||
# root /www;
|
|
||||||
# }
|
listen 0.0.0.0:4040;
|
||||||
|
|
||||||
|
index index.html;
|
||||||
|
root ./www2/;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
<!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>
|
|
||||||
118
main_test.sh
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
# import and run all tests
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Colors
|
||||||
|
##########
|
||||||
|
|
||||||
|
|
||||||
|
_GREY='\033[30m'
|
||||||
|
_RED='\033[0;31m'
|
||||||
|
_GREEN='\033[32m'
|
||||||
|
_YELLOW='\033[33m'
|
||||||
|
_BLUE='\033[34m'
|
||||||
|
_PURPLE='\033[35m'
|
||||||
|
_CYAN='\033[36m'
|
||||||
|
_WHITE='\033[37m'
|
||||||
|
_END='\033[0m'
|
||||||
|
|
||||||
|
|
||||||
|
#test_file_names=("test_template.sh" "test_header.sh" "test_path.sh")
|
||||||
|
#test_file_names=("test_method.sh" "test_header.sh" "test_path.sh")
|
||||||
|
test_file_names=("test_port.sh")
|
||||||
|
#test_file_names=("test_body.sh")
|
||||||
|
#test_file_names=("test_valid_uri.sh")
|
||||||
|
#test_file_names=("test_path.sh")
|
||||||
|
|
||||||
|
test_files=()
|
||||||
|
expected_result_files=()
|
||||||
|
|
||||||
|
fill_test_files()
|
||||||
|
{
|
||||||
|
for i in "${test_file_names[@]}"
|
||||||
|
do
|
||||||
|
test_files+=("./Tester/$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# needs to be invoked for each test_file!
|
||||||
|
# acutally kinda useless...
|
||||||
|
fill_expected_files()
|
||||||
|
{
|
||||||
|
for i in "${local_expected_test_files[@]}"
|
||||||
|
do
|
||||||
|
expected_result_files+=("./Tester/expected_results/$i")
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test_all()
|
||||||
|
{
|
||||||
|
|
||||||
|
arg=${1}
|
||||||
|
c=0
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
rm -rf telnet.log
|
||||||
|
rm -rf webserv.log
|
||||||
|
rm -rf compare.txt
|
||||||
|
rm -rf expected.txt
|
||||||
|
|
||||||
|
fill_test_files
|
||||||
|
|
||||||
|
for i in "${test_files[@]}"
|
||||||
|
do
|
||||||
|
source $i
|
||||||
|
source ./Tester/telnet_test.sh
|
||||||
|
|
||||||
|
./webserv $config_file &>> webserv.log &
|
||||||
|
echo -e "${_GREEN}Running Telnet Test on '$test_name'${_END}"
|
||||||
|
sleep 1
|
||||||
|
run | telnet | tee compare.txt >> telnet.log
|
||||||
|
# run | telnet > compare.txt >> telnet.log
|
||||||
|
# run | telnet >> telnet.log
|
||||||
|
pkill webserv
|
||||||
|
echo -e "\n\n------\n" &>> webserv.log
|
||||||
|
|
||||||
|
fill_expected_files
|
||||||
|
expected_result_file="./Tester/expected_results/$file"
|
||||||
|
|
||||||
|
if [ "$arg" = "diff" ];
|
||||||
|
then
|
||||||
|
DIFF=$(diff -q compare.txt $expected_result_file)
|
||||||
|
if [ "$DIFF" == "" ];
|
||||||
|
then
|
||||||
|
echo "Good diff"
|
||||||
|
else
|
||||||
|
diff compare.txt $expected_result_file
|
||||||
|
fi
|
||||||
|
elif [ "$arg" = "create" ];
|
||||||
|
then
|
||||||
|
if ! test -f $expected_result_file;
|
||||||
|
then
|
||||||
|
cat compare.txt > $expected_result_file
|
||||||
|
fi
|
||||||
|
elif [ "$arg" = "replace" ];
|
||||||
|
then
|
||||||
|
cat compare.txt > $expected_result_file
|
||||||
|
fi
|
||||||
|
|
||||||
|
c+=1
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$arg" = "" ];
|
||||||
|
then
|
||||||
|
cat telnet.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
test_all $1
|
||||||
|
|
||||||
85
memo.txt
@@ -1,18 +1,45 @@
|
|||||||
IN 42 SUBJECT AND/OR PRIORITY :
|
----Priorité élevée------------------------
|
||||||
- CGI
|
- CGI (TODO HUGO)
|
||||||
- chunked request (response not mandatory it seems)
|
|
||||||
- fix need for index and autoindex config
|
- Need to test normal body parsing (Verif avec HUGO)
|
||||||
- Ecrire des tests !
|
|
||||||
- "root" need to replace "location->path" part of "client.path"
|
- curl --resolve, for testing hostname
|
||||||
replace up to the last "/" of the "location->path" part
|
curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040
|
||||||
(if its a folder this will be in fact the entire path)
|
- test limit de connexions sur listen()
|
||||||
|
|
||||||
- handle redirection (Work, but weird behavior need deeper test)
|
- handle redirection (Work, but weird behavior need deeper test)
|
||||||
- upload files with config "upload_dir"
|
- Ecrire des tests !
|
||||||
- _determine_location() review (New version to complete and test)
|
|
||||||
- replace atoi() with a better function to avoid overflow
|
- cgi_cpp_status.cpp with POST dont show error page. Normal or not ?
|
||||||
like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/
|
|
||||||
-----------------------------
|
For non blocking CGI :
|
||||||
- 408 Request Timeout
|
// We could maybe,
|
||||||
|
// add FD_RD_FR_CHLD to epoll,
|
||||||
|
// return to the main loop,
|
||||||
|
// read FD_RD_FR_CHLD each time epoll say its ready,
|
||||||
|
// then try waitpid() with WNOHANG after each read.
|
||||||
|
// when waitpid() tell us its finish (or maybe when epoll return EPOLLHUP)
|
||||||
|
// then actually parse the script_output and send it to the client.
|
||||||
|
|
||||||
|
- check status in autoindex
|
||||||
|
|
||||||
|
- merge changes from hugo5 to master (attention a pas casse svp :clown:)
|
||||||
|
|
||||||
|
|
||||||
|
----Priorité modérée------------------------
|
||||||
|
- namespace utils ?
|
||||||
|
- change "std::string" to reference "std::string &" in most functions
|
||||||
|
and add "const" if apropriate.
|
||||||
|
- peut-être check si ip > 32bits
|
||||||
|
|
||||||
|
|
||||||
|
----Priorité faible------------------------
|
||||||
|
- idealy, we should not clear() raw_request after a response,
|
||||||
|
but just the part we actually parsed for this response.
|
||||||
|
I think the client could send multiples request on the same connection one after the other without waiting for response.
|
||||||
|
So raw_request could contain more than the first request we handle.
|
||||||
|
- chunked request (need testing)
|
||||||
|
- client_body_limit 0 valeur special pour desactiver dans config
|
||||||
- gerer le champ "Accept" du client
|
- gerer le champ "Accept" du client
|
||||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||||
@@ -24,10 +51,28 @@ IN 42 SUBJECT AND/OR PRIORITY :
|
|||||||
and add "const" if apropriate.
|
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.
|
- 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.
|
||||||
|
|
||||||
|
Valgrind error RESOLVED ! :
|
||||||
|
==847174== 1,314 bytes in 1 blocks are definitely lost in loss record 1 of 1
|
||||||
|
==847174== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||||
|
==847174== by 0x49AA35D: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
|
||||||
|
==847174== by 0x49ABB52: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
|
||||||
|
==847174== by 0x426BC5: Webserv::_read_cgi_output(cgi_pipe_rfd&) (run_loop.cpp:39) // (LUKE: its an string.append() call)
|
||||||
|
==847174== by 0x427962: Webserv::run() (run_loop.cpp:153)
|
||||||
|
==847174== by 0x4052E9: main (main.cpp:38)
|
||||||
|
|
||||||
__________________________
|
RESOLVED, AGAIN :D !
|
||||||
--------------------------
|
--loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
----Discord 42------------
|
47067----loop epoll()
|
||||||
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.
|
EPOLLHUP on client fd 8
|
||||||
Vous avez des modules sur vos navigateur ou des logiciels externe. C'est assez rapide et gratuit.
|
47068----loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
|
47069----loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
|
47070----loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
|
47071----loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
|
47072----loop epoll()
|
||||||
|
EPOLLHUP on client fd 8
|
||||||
|
47073----loop epoll()
|
||||||
|
|||||||
53
multipart_request.txt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
POST /upload HTTP/1.1
|
||||||
|
Host: localhost:4040
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Length: 364
|
||||||
|
Cache-Control: max-age=0
|
||||||
|
sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"
|
||||||
|
sec-ch-ua-mobile: ?0
|
||||||
|
sec-ch-ua-platform: "Windows"
|
||||||
|
Upgrade-Insecure-Requests: 1
|
||||||
|
Origin: http://localhost:4040
|
||||||
|
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeIV4xrEzThmNUcJf
|
||||||
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
||||||
|
Sec-Fetch-Site: same-origin
|
||||||
|
Sec-Fetch-Mode: navigate
|
||||||
|
Sec-Fetch-User: ?1
|
||||||
|
Sec-Fetch-Dest: document
|
||||||
|
Referer: http://localhost:4040/upload_form.html
|
||||||
|
Accept-Encoding: gzip, deflate, br
|
||||||
|
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
|
||||||
|
dnt: 1
|
||||||
|
sec-gpc: 1
|
||||||
|
|
||||||
|
------WebKitFormBoundaryeIV4xrEzThmNUcJf
|
||||||
|
Content-Disposition: form-data; name="upload_file"; filename=".gitignore"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.swp
|
||||||
|
*.out
|
||||||
|
*.exe
|
||||||
|
*.stackdump
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.dSYM
|
||||||
|
.vscode
|
||||||
|
*.lnk
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
builds
|
||||||
|
|
||||||
|
ubuntu_tester
|
||||||
|
ubuntu_cgi_tester
|
||||||
|
webserv
|
||||||
|
!**/webserv/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
large.jpg
|
||||||
|
|
||||||
|
------WebKitFormBoundaryeIV4xrEzThmNUcJf--
|
||||||
318
srcs/Client.cpp
@@ -8,9 +8,12 @@
|
|||||||
Client::Client()
|
Client::Client()
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
read_body_size(0),
|
body_complete(false),
|
||||||
|
request_complete(false),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
|
cgi_pipe_rfd(0),
|
||||||
|
cgi_pid(0),
|
||||||
_fd(0),
|
_fd(0),
|
||||||
_port(""),
|
_port(""),
|
||||||
_ip(""),
|
_ip(""),
|
||||||
@@ -22,9 +25,12 @@ Client::Client()
|
|||||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||||
: status(0),
|
: status(0),
|
||||||
header_complete(false),
|
header_complete(false),
|
||||||
read_body_size(0),
|
body_complete(false),
|
||||||
|
request_complete(false),
|
||||||
assigned_server(NULL),
|
assigned_server(NULL),
|
||||||
assigned_location(NULL),
|
assigned_location(NULL),
|
||||||
|
cgi_pipe_rfd(0),
|
||||||
|
cgi_pid(0),
|
||||||
_fd(afd),
|
_fd(afd),
|
||||||
_port(aport),
|
_port(aport),
|
||||||
_ip(aip),
|
_ip(aip),
|
||||||
@@ -41,7 +47,6 @@ Client::~Client() {
|
|||||||
Client::Client( Client const & src )
|
Client::Client( Client const & src )
|
||||||
: status ( src.status ),
|
: status ( src.status ),
|
||||||
header_complete ( src.header_complete ),
|
header_complete ( src.header_complete ),
|
||||||
read_body_size ( src.read_body_size ),
|
|
||||||
assigned_server ( src.assigned_server ),
|
assigned_server ( src.assigned_server ),
|
||||||
assigned_location ( src.assigned_location ),
|
assigned_location ( src.assigned_location ),
|
||||||
_fd ( src._fd ),
|
_fd ( src._fd ),
|
||||||
@@ -71,27 +76,38 @@ Client & Client::operator=( Client const & rhs )
|
|||||||
* PUBLIC MEMBER FUNCTIONS
|
* PUBLIC MEMBER FUNCTIONS
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
// http headers :
|
/* HTTP Headers :
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
https://www.iana.org/assignments/http-fields/http-fields.xhtml
|
||||||
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
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_headers(std::vector<ServerConfig> &servers)
|
void Client::parse_request_headers(std::vector<ServerConfig> &servers)
|
||||||
{
|
{
|
||||||
|
if (raw_request.find(CRLF CRLF) == NPOS)
|
||||||
|
return ;
|
||||||
|
header_complete = true;
|
||||||
clear_request(); // not mandatory
|
clear_request(); // not mandatory
|
||||||
|
|
||||||
_parse_request_line();
|
_parse_request_line();
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
_parse_request_fields();
|
_parse_request_fields();
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
print_client("headers");
|
// print_client("headers");
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
assigned_server = ::_determine_process_server(this, servers);
|
assigned_server = _determine_process_server(this, servers);
|
||||||
assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
|
assigned_location = _determine_location(*assigned_server, _request.abs_path);
|
||||||
_check_request_errors();
|
_check_request_errors();
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
_parse_port_hostname(get_rq_headers("Host")); // use getter for headers because it works case insensitive
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
// std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n";
|
||||||
|
|
||||||
// dont clear raw_request, we need it for future reparsing of body
|
// dont clear raw_request, we need it for future reparsing of body
|
||||||
// see call of parse_request() in _read_request()
|
// see call of parse_request() in _read_request()
|
||||||
@@ -100,54 +116,142 @@ print_client("headers");
|
|||||||
|
|
||||||
void Client::parse_request_body()
|
void Client::parse_request_body()
|
||||||
{
|
{
|
||||||
|
std::cerr << "parse_request_body()\n";
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
pos = raw_request.find(CRLF CRLF);
|
pos = raw_request.find(CRLF CRLF);
|
||||||
pos += std::string(CRLF CRLF).size();
|
if (pos == NPOS)
|
||||||
_request.body = raw_request.substr(pos);
|
{
|
||||||
|
std::cerr << "parse_request_body() bad call, header incomplete\n";
|
||||||
|
// QUESTION from hugo : don't we change the status here ?
|
||||||
|
// RESPONSE from luke : C'est vrai. Peut-être mettre un 500, c'etait plus du debug à la base.
|
||||||
|
// C'est seulement si on appelle la fonction au mauvais endroit, avant d'avoir un header complet, que ça arrive.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get_rq_headers("Transfer-Encoding").empty()
|
||||||
|
&& get_rq_headers("Transfer-Encoding") == "chunked")
|
||||||
|
{
|
||||||
|
// Chunked decoding WIP. How to test this ? dont know how to send chunks with telnet.
|
||||||
|
_parse_chunked_body(pos + CRLF_SIZE*2);
|
||||||
|
}
|
||||||
|
else if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
|
||||||
|
{
|
||||||
|
std::cerr << "Content-Length = " << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
||||||
|
if (get_rq_headers("Content-Type").find("multipart/form-data") != NPOS)
|
||||||
|
_parse_multipart_body(pos);
|
||||||
|
else
|
||||||
|
_request.body = raw_request.substr(pos + CRLF_SIZE*2);
|
||||||
|
body_complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// Body checks
|
||||||
if (_request.body.size() > assigned_server->client_body_limit)
|
if (_request.body.size() > assigned_server->client_body_limit)
|
||||||
status = 413; // HTTP Client Errors
|
status = 413; // HTTP Client Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::fill_script_path(std::string script)
|
void Client::_parse_chunked_body(size_t pos)
|
||||||
|
{
|
||||||
|
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 (errno == ERANGE)
|
||||||
|
{
|
||||||
|
status = 413;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
/* if (endptr == endptr_copy)
|
||||||
|
{
|
||||||
|
std::cerr << "parse_request_body(), no conversion possible\n";
|
||||||
|
return;
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
chunk_field_end = _request.body.find(CRLF, pos);
|
||||||
|
if (chunk_field_end == NPOS)
|
||||||
|
{
|
||||||
|
std::cerr << "parse_request_body(), chunk_field no CRLF\n";
|
||||||
|
// status = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_field_end += CRLF_SIZE;
|
||||||
|
_request.body.erase(pos, chunk_field_end);
|
||||||
|
pos += chunk_size + CRLF_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_request.headers.erase("Transfer-Encoding");
|
||||||
|
body_complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Client::fill_script_path(std::string &path, size_t pos)
|
||||||
{
|
{
|
||||||
size_t pos;
|
|
||||||
int len = script.size();
|
|
||||||
std::string path = this->get_rq_abs_path();
|
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
|
|
||||||
pos = path.find(script);
|
_request.script.path = path.substr(0, pos);
|
||||||
if (pos == 0)
|
_request.script.info = path.substr(pos);
|
||||||
{
|
|
||||||
tmp = path.substr(0, pos + len);
|
|
||||||
_request.script.path = "./srcs" + tmp; // TODO: root path ?
|
|
||||||
_request.script.info = path.substr(pos + len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clear()
|
void Client::clear()
|
||||||
{
|
{
|
||||||
clear_request();
|
clear_request();
|
||||||
header_complete = false;
|
|
||||||
read_body_size = 0;
|
|
||||||
assigned_server = NULL;
|
|
||||||
assigned_location = NULL;
|
|
||||||
raw_request.clear();
|
raw_request.clear();
|
||||||
response.clear();
|
response.clear();
|
||||||
status = 0;
|
status = 0;
|
||||||
|
header_complete = false;
|
||||||
|
body_complete = false;
|
||||||
|
request_complete = false;
|
||||||
|
assigned_server = NULL;
|
||||||
|
assigned_location = NULL;
|
||||||
|
cgi_pipe_rfd = 0;
|
||||||
|
cgi_pid = 0;
|
||||||
|
cgi_output.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clear_request()
|
void Client::clear_request()
|
||||||
{
|
{
|
||||||
clear_script();
|
clear_script();
|
||||||
_request.method = UNKNOWN;
|
_request.method = UNKNOWN;
|
||||||
_request.uri.clear();
|
_request.target.clear();
|
||||||
_request.version.clear();
|
_request.version.clear();
|
||||||
_request.headers.clear();
|
_request.headers.clear();
|
||||||
_request.body.clear();
|
_request.body.clear();
|
||||||
|
_request.multi_bodys.clear();
|
||||||
_request.abs_path.clear();
|
_request.abs_path.clear();
|
||||||
_request.query.clear();
|
_request.query.clear();
|
||||||
_request.port.clear();
|
_request.port.clear();
|
||||||
@@ -164,8 +268,7 @@ void Client::clear_script()
|
|||||||
void Client::print_client(std::string message)
|
void Client::print_client(std::string message)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string>::iterator it;
|
std::map<std::string, std::string>::iterator it;
|
||||||
|
std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
|
||||||
std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
|
|
||||||
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n";
|
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n";
|
||||||
::print_special(raw_request);
|
::print_special(raw_request);
|
||||||
std::cout << "\n__\n"
|
std::cout << "\n__\n"
|
||||||
@@ -173,7 +276,7 @@ void Client::print_client(std::string message)
|
|||||||
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
<< "get_cl_port() : [" << get_cl_port() << "]\n"
|
||||||
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
|
||||||
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\n"
|
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\n"
|
||||||
<< "get_rq_uri() : [" << get_rq_uri() << "]\n"
|
<< "get_rq_target() : [" << get_rq_target() << "]\n"
|
||||||
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
|
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
|
||||||
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
<< "get_rq_query() : [" << get_rq_query() << "]\n"
|
||||||
<< "get_rq_version() : [" << get_rq_version() << "]\n"
|
<< "get_rq_version() : [" << get_rq_version() << "]\n"
|
||||||
@@ -202,7 +305,7 @@ const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
|
|||||||
http_method Client::get_rq_method() const { return _request.method; }
|
http_method Client::get_rq_method() const { return _request.method; }
|
||||||
std::string Client::get_rq_method_str() const
|
std::string Client::get_rq_method_str() const
|
||||||
{ return ::http_methods_to_str(_request.method); }
|
{ return ::http_methods_to_str(_request.method); }
|
||||||
std::string Client::get_rq_uri() const { return _request.uri; }
|
std::string Client::get_rq_target() const { return _request.target; }
|
||||||
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
||||||
std::string Client::get_rq_query() const { return _request.query; }
|
std::string Client::get_rq_query() const { return _request.query; }
|
||||||
std::string Client::get_rq_version() const { return _request.version; }
|
std::string Client::get_rq_version() const { return _request.version; }
|
||||||
@@ -211,6 +314,7 @@ std::string Client::get_rq_port() const { return _request.port; }
|
|||||||
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
||||||
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
||||||
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
||||||
|
|
||||||
std::string Client::get_rq_headers(const std::string & key) const
|
std::string Client::get_rq_headers(const std::string & key) const
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string>::const_iterator it;
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
@@ -237,26 +341,29 @@ void Client::_parse_request_line()
|
|||||||
{
|
{
|
||||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n";
|
std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n";
|
||||||
status = 400; // "bad request"
|
status = 400; // "bad request"
|
||||||
|
// if the header is fucked up, then this will be triggered, but for some reason in test_body.sh case another response 200 OK is sent first?
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_request.method = str_to_http_method(line[0]);
|
_request.method = str_to_http_method(line[0]);
|
||||||
_request.uri = line[1];
|
_request.target = line[1];
|
||||||
_parse_request_uri(line[1]);
|
_parse_request_target(line[1]);
|
||||||
_request.version = line[2];
|
_request.version = line[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::_parse_request_uri( std::string uri )
|
void Client::_parse_request_target( std::string target )
|
||||||
{
|
{
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
pos = uri.find("?");
|
pos = target.find("?");
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
_request.query = uri.substr(pos + 1);
|
_request.query = target.substr(pos + 1);
|
||||||
else
|
else
|
||||||
_request.query = "";
|
_request.query = "";
|
||||||
_request.abs_path = uri.substr(0, pos);
|
_request.abs_path = target.substr(0, pos);
|
||||||
|
if (_request.abs_path[_request.abs_path.size() - 1] == '/')
|
||||||
|
_request.abs_path.erase(_request.abs_path.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::_parse_request_fields()
|
void Client::_parse_request_fields()
|
||||||
@@ -268,11 +375,11 @@ void Client::_parse_request_fields()
|
|||||||
headers = raw_request;
|
headers = raw_request;
|
||||||
// delete first line
|
// delete first line
|
||||||
pos = headers.find(CRLF);
|
pos = headers.find(CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
headers.erase(0, pos + std::string(CRLF).size());
|
headers.erase(0, pos + CRLF_SIZE);
|
||||||
// delete body part
|
// delete body part
|
||||||
pos = headers.find(CRLF CRLF);
|
pos = headers.find(CRLF CRLF);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
headers.erase(pos);
|
headers.erase(pos);
|
||||||
else {
|
else {
|
||||||
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
|
||||||
@@ -287,6 +394,7 @@ void Client::_parse_request_fields()
|
|||||||
::str_map_key_tolower(_request.headers);
|
::str_map_key_tolower(_request.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO : I think its now useless. Probably to delete.
|
||||||
void Client::_parse_port_hostname(std::string host)
|
void Client::_parse_port_hostname(std::string host)
|
||||||
{
|
{
|
||||||
size_t pos;
|
size_t pos;
|
||||||
@@ -296,7 +404,7 @@ void Client::_parse_port_hostname(std::string host)
|
|||||||
|
|
||||||
pos = host.find(':');
|
pos = host.find(':');
|
||||||
// port :
|
// port :
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
_request.port = "4040"; // TODO: make equal to default port in config
|
_request.port = "4040"; // TODO: make equal to default port in config
|
||||||
else
|
else
|
||||||
_request.port = host.substr(pos);
|
_request.port = host.substr(pos);
|
||||||
@@ -308,7 +416,10 @@ void Client::_parse_port_hostname(std::string host)
|
|||||||
|
|
||||||
void Client::_check_request_errors()
|
void Client::_check_request_errors()
|
||||||
{
|
{
|
||||||
//////////////////////
|
/* Debug */ std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n";
|
||||||
|
/* Debug */ std::cerr << "strtoul=" << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
||||||
|
/* Debug */ std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n";
|
||||||
|
///////////////////////
|
||||||
// Request line checks
|
// Request line checks
|
||||||
if (_request.method == UNKNOWN)
|
if (_request.method == UNKNOWN)
|
||||||
status = 501; // HTTP Client Errors
|
status = 501; // HTTP Client Errors
|
||||||
@@ -330,18 +441,117 @@ void Client::_check_request_errors()
|
|||||||
response.append(CRLF CRLF);
|
response.append(CRLF CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status)
|
//////////////////
|
||||||
return;
|
|
||||||
|
|
||||||
/////////////////
|
|
||||||
// Headers checks
|
// Headers checks
|
||||||
if (!this->get_rq_headers("Content-Length").empty()
|
else if (!get_rq_headers("Content-Length").empty()
|
||||||
&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
|
&& std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
|
||||||
status = 413; // HTTP Client Errors
|
status = 413;
|
||||||
|
else if (!get_rq_headers("Transfer-Encoding").empty()
|
||||||
|
&& get_rq_headers("Transfer-Encoding") != "chunked" )
|
||||||
|
status = 501;
|
||||||
|
else if (!get_rq_headers("Content-Encoding").empty())
|
||||||
|
{
|
||||||
|
status = 415;
|
||||||
|
response.append("Accept-Encoding:"); // empty, no encoding accepted
|
||||||
|
response.append(CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerConfig *Client::_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Behavior like this :
|
||||||
|
http://nginx.org/en/docs/http/request_processing.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::string server_name = client->get_rq_headers("Host");
|
||||||
|
// /* Debug */ std::cerr << "server_name = " << server_name << "\n";
|
||||||
|
size_t pos = server_name.rfind(':');
|
||||||
|
if (pos != NPOS)
|
||||||
|
server_name.erase(pos);
|
||||||
|
// /* Debug */ std::cerr << "server_name = " << server_name << "\n";
|
||||||
|
|
||||||
|
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?
|
||||||
|
const LocationConfig *Client::_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 /
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: technically i think this is useless...
|
||||||
|
std::string uri = path;
|
||||||
|
if (uri[uri.size() - 1] == '/' && uri.size() != 1)
|
||||||
|
uri.erase(uri.size() - 1);
|
||||||
|
|
||||||
|
|
||||||
|
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||||
|
{
|
||||||
|
// std::cout << it->path << " -- ";
|
||||||
|
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/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************
|
/*********************************************
|
||||||
* OVERLOAD
|
* OVERLOAD
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
# include "ServerConfig.hpp"
|
# include "ServerConfig.hpp"
|
||||||
|
# include "colors.h"
|
||||||
|
|
||||||
struct Script
|
struct Script
|
||||||
{
|
{
|
||||||
@@ -18,15 +19,22 @@ struct Script
|
|||||||
std::string info;
|
std::string info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MultipartBody
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::string body;
|
||||||
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
http_method method;
|
http_method method;
|
||||||
std::string uri;
|
std::string target;
|
||||||
std::string abs_path;
|
std::string abs_path;
|
||||||
std::string query;
|
std::string query;
|
||||||
std::string version;
|
std::string version;
|
||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
|
std::vector<MultipartBody> multi_bodys;
|
||||||
std::string port;
|
std::string port;
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
struct Script script;
|
struct Script script;
|
||||||
@@ -45,9 +53,14 @@ class Client
|
|||||||
std::string response;
|
std::string response;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
bool header_complete;
|
bool header_complete;
|
||||||
size_t read_body_size;
|
bool body_complete;
|
||||||
|
bool request_complete;
|
||||||
|
// size_t read_body_size; // unused for now
|
||||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||||
const LocationConfig *assigned_location;
|
const LocationConfig *assigned_location;
|
||||||
|
int cgi_pipe_rfd;
|
||||||
|
pid_t cgi_pid;
|
||||||
|
std::string cgi_output;
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
int get_cl_fd() const;
|
int get_cl_fd() const;
|
||||||
@@ -58,7 +71,7 @@ class Client
|
|||||||
// requests getters
|
// requests getters
|
||||||
http_method get_rq_method() const;
|
http_method get_rq_method() const;
|
||||||
std::string get_rq_method_str() const;
|
std::string get_rq_method_str() const;
|
||||||
std::string get_rq_uri() const;
|
std::string get_rq_target() const;
|
||||||
std::string get_rq_abs_path() const;
|
std::string get_rq_abs_path() const;
|
||||||
std::string get_rq_query() const;
|
std::string get_rq_query() const;
|
||||||
std::string get_rq_version() const;
|
std::string get_rq_version() const;
|
||||||
@@ -69,12 +82,15 @@ class Client
|
|||||||
std::string get_rq_script_info() const;
|
std::string get_rq_script_info() const;
|
||||||
std::string get_rq_headers(const std::string & key) const;
|
std::string get_rq_headers(const std::string & key) const;
|
||||||
|
|
||||||
|
const std::vector<MultipartBody> &get_rq_multi_bodys() const;
|
||||||
|
const std::string get_rq_multi_bodys_headers(const std::string & key, std::vector<MultipartBody>::const_iterator body_it) const;
|
||||||
|
|
||||||
void parse_request_headers(std::vector<ServerConfig> &servers);
|
void parse_request_headers(std::vector<ServerConfig> &servers);
|
||||||
void parse_request_body();
|
void parse_request_body();
|
||||||
void clear();
|
void clear();
|
||||||
void clear_request();
|
void clear_request();
|
||||||
void clear_script();
|
void clear_script();
|
||||||
bool fill_script_path(std::string script);
|
void fill_script_path(std::string &path, size_t pos);
|
||||||
// DEBUG
|
// DEBUG
|
||||||
void print_client(std::string message = "");
|
void print_client(std::string message = "");
|
||||||
|
|
||||||
@@ -87,19 +103,20 @@ class Client
|
|||||||
|
|
||||||
void _parse_request_line();
|
void _parse_request_line();
|
||||||
void _parse_request_fields();
|
void _parse_request_fields();
|
||||||
void _parse_request_uri( std::string uri );
|
void _parse_request_target( std::string target );
|
||||||
void _parse_port_hostname(std::string host);
|
void _parse_port_hostname(std::string host);
|
||||||
|
void _parse_chunked_body(size_t pos);
|
||||||
|
void _parse_multipart_body(size_t pos);
|
||||||
void _check_request_errors();
|
void _check_request_errors();
|
||||||
|
|
||||||
|
ServerConfig*
|
||||||
|
_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
|
||||||
|
const LocationConfig*
|
||||||
|
_determine_location(const ServerConfig &server, const std::string &path);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const Client& lhs, const Client& rhs);
|
bool operator==(const Client& lhs, const Client& rhs);
|
||||||
bool operator==(const Client& lhs, int fd);
|
bool operator==(const Client& lhs, int fd);
|
||||||
bool operator==(int fd, const Client& rhs);
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
103
srcs/Client_multipart_body.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
#include "Client.hpp"
|
||||||
|
|
||||||
|
const std::vector<MultipartBody> &Client::get_rq_multi_bodys() const { return _request.multi_bodys; }
|
||||||
|
const std::string Client::get_rq_multi_bodys_headers(const std::string & key, std::vector<MultipartBody>::const_iterator body_it) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
|
||||||
|
it = body_it->headers.find(::str_tolower(key));
|
||||||
|
if (it == body_it->headers.end())
|
||||||
|
return ""; // IF return reference compiler "warning: returning reference to local temporary"
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::_parse_multipart_body(size_t pos)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** Parsing roughly like described in :
|
||||||
|
** https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
|
||||||
|
*/
|
||||||
|
MultipartBody new_body;
|
||||||
|
std::string boundary;
|
||||||
|
size_t start_pos;
|
||||||
|
size_t end_pos;
|
||||||
|
std::string tmp;
|
||||||
|
size_t tmp_pos;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
// Get boundary
|
||||||
|
boundary = get_rq_headers("Content-Type");
|
||||||
|
start_pos = boundary.find("boundary=");
|
||||||
|
if (start_pos == NPOS)
|
||||||
|
{
|
||||||
|
status = 400; std::cerr << "_parse_multipart_body() error 1\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
start_pos += sizeof("boundary=")-1;
|
||||||
|
boundary = boundary.substr(start_pos);
|
||||||
|
std::cerr << "boundary =|" << boundary << "|\n";
|
||||||
|
|
||||||
|
|
||||||
|
// Search boundary
|
||||||
|
start_pos = raw_request.find("--" + boundary, pos);
|
||||||
|
if (start_pos == NPOS || start_pos + sizeof("--")-1 + boundary.size() > raw_request.size())
|
||||||
|
{
|
||||||
|
status = 400; std::cerr << "_parse_multipart_body() error 2\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
start_pos += sizeof("--")-1 + boundary.size() + CRLF_SIZE;
|
||||||
|
|
||||||
|
while (1) // TODO : test loop for multi body
|
||||||
|
{
|
||||||
|
end_pos = raw_request.find("--" + boundary, start_pos);
|
||||||
|
if (end_pos == NPOS)
|
||||||
|
{
|
||||||
|
status = 400; std::cerr << "_parse_multipart_body() error 3\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* // Maye useful for multi body (remove "start_pos - CRLF_SIZE" if used)
|
||||||
|
end_pos = raw_request.rfind(CRLF, end_pos); if (end_pos == NPOS) {status = 400; return; } */
|
||||||
|
|
||||||
|
new_body.body = raw_request.substr(start_pos, end_pos - start_pos - CRLF_SIZE);
|
||||||
|
|
||||||
|
// Split headers from body
|
||||||
|
tmp_pos = new_body.body.find(CRLF CRLF);
|
||||||
|
if (tmp_pos != NPOS)
|
||||||
|
{
|
||||||
|
ret = ::parse_http_headers(new_body.body.substr(0, tmp_pos), new_body.headers);
|
||||||
|
::str_map_key_tolower(new_body.headers);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
status = 400; std::cerr << "_parse_multipart_body() error 4\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tmp_pos += CRLF_SIZE*2;
|
||||||
|
new_body.body.erase(0, tmp_pos);
|
||||||
|
// ::print_map(new_body.headers);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // No headers case
|
||||||
|
tmp_pos = new_body.body.find(CRLF);
|
||||||
|
if (tmp_pos != 0)
|
||||||
|
{
|
||||||
|
status = 400; std::cerr << "_parse_multipart_body() error 5\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_request.multi_bodys.push_back(new_body);
|
||||||
|
|
||||||
|
// Move start for next loop
|
||||||
|
start_pos = end_pos + sizeof("--")-1 + boundary.size();
|
||||||
|
if ( start_pos + 2 + CRLF_SIZE == raw_request.size()
|
||||||
|
&& raw_request[start_pos] == '-'
|
||||||
|
&& raw_request[start_pos+1] == '-')
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ::print_special(raw_request);
|
||||||
|
std::cerr << "start_pos = " << start_pos << "\n";
|
||||||
|
std::cerr << "raw_request.size() = " << raw_request.size() << "\n";
|
||||||
|
std::cerr << raw_request.substr(start_pos); */
|
||||||
|
}
|
||||||
|
}
|
||||||
95
srcs/cgi-bin/Makefile
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
# - - - - - - #
|
||||||
|
# #
|
||||||
|
# COLORS #
|
||||||
|
# #
|
||||||
|
# - - - - - - #
|
||||||
|
|
||||||
|
GRAY = "\e[0;30m"
|
||||||
|
RED = "\e[0;31m"
|
||||||
|
GREEN = "\e[0;32m"
|
||||||
|
YELLOW = "\e[0;33m"
|
||||||
|
BLUE = "\e[0;34m"
|
||||||
|
PURPLE = "\e[0;35m"
|
||||||
|
CYAN = "\e[0;36m"
|
||||||
|
WHITE = "\e[0;37m"
|
||||||
|
|
||||||
|
B_GRAY = "\e[1;30m"
|
||||||
|
B_RED = "\e[1;31m"
|
||||||
|
B_GREEN = "\e[1;32m"
|
||||||
|
B_YELLOW = "\e[1;33m"
|
||||||
|
B_BLUE = "\e[1;34m"
|
||||||
|
B_PURPLE = "\e[1;35m"
|
||||||
|
B_CYAN = "\e[1;36m"
|
||||||
|
B_WHITE = "\e[1;37m"
|
||||||
|
|
||||||
|
RESET = "\e[0m"
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
|
# . name = value \ . += append to a variable #
|
||||||
|
# VARIABLES . value . != set result of command #
|
||||||
|
# . name is case sensitive . ?= set if not already set #
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
|
|
||||||
|
NAME = $(SRCS_X:.cpp=.out)
|
||||||
|
|
||||||
|
CXX = c++
|
||||||
|
CXXFLAGS = -Wall -Wextra #-Werror
|
||||||
|
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||||
|
CXXFLAGS += -std=c++98
|
||||||
|
CXXFLAGS += -g
|
||||||
|
|
||||||
|
VPATH = $(SRCS_D)
|
||||||
|
HEADERS_D = .
|
||||||
|
SRCS_D = .
|
||||||
|
|
||||||
|
SRCS = cgi_utils.cpp
|
||||||
|
SRCS_X = \
|
||||||
|
cgi_cpp.cpp \
|
||||||
|
cgi_cpp_bad_headers.cpp \
|
||||||
|
cgi_cpp_empty.cpp \
|
||||||
|
cgi_cpp_empty_lines.cpp \
|
||||||
|
cgi_cpp_len.cpp \
|
||||||
|
cgi_cpp_len_big.cpp \
|
||||||
|
cgi_cpp_len_small.cpp \
|
||||||
|
cgi_cpp_no_body.cpp \
|
||||||
|
cgi_cpp_no_headers.cpp \
|
||||||
|
cgi_cpp_only_crlf.cpp \
|
||||||
|
cgi_cpp_sleep.cpp \
|
||||||
|
cgi_cpp_status.cpp \
|
||||||
|
cgi_cpp_download.cpp \
|
||||||
|
|
||||||
|
OBJS_D = builds
|
||||||
|
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
||||||
|
OBJS_X = $(SRCS_X:%.cpp=$(OBJS_D)/%.o)
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
|
# . target: prerequisites . $@ : target #
|
||||||
|
# RULES . recipe . $< : 1st prerequisite #
|
||||||
|
# . recipe . $^ : all prerequisites #
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
|
||||||
|
|
||||||
|
all: $(NAME)
|
||||||
|
|
||||||
|
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
|
||||||
|
@echo $(B_GREEN)"compilation :" $@ $(RESET)
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(OBJS_D):
|
||||||
|
mkdir $@
|
||||||
|
|
||||||
|
$(NAME): $(OBJS) $(OBJS_X)
|
||||||
|
$(NAME):
|
||||||
|
@echo $(B_YELLOW)"linkage :" $@ $(RESET)
|
||||||
|
$(CXX) $(OBJS) $(@:%.out=$(OBJS_D)/%.o) -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJS_D)
|
||||||
|
|
||||||
|
fclean: clean
|
||||||
|
rm -f $(NAME)
|
||||||
|
|
||||||
|
re: fclean all
|
||||||
|
|
||||||
|
.PHONY : all clean fclean re
|
||||||
|
|
||||||
4
srcs/cgi-bin/cgi
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
echo "status: 100\r\n"
|
||||||
|
echo "\r\n\r\n"
|
||||||
|
echo "hiii"
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# include <iostream>
|
|
||||||
# include <string>
|
|
||||||
# include <sstream>
|
|
||||||
|
|
||||||
int main (int ac, char **av) {
|
|
||||||
std::string to_send;
|
|
||||||
std::string header;
|
|
||||||
std::string end_header = "\r\n\r\n";
|
|
||||||
std::string response;
|
|
||||||
std::stringstream strs;
|
|
||||||
|
|
||||||
header = "HTTP/1.1 200 OK\n";
|
|
||||||
header += "Content-Type: text/html; charset=UTF-8\n";
|
|
||||||
header += "Content-Length: ";
|
|
||||||
|
|
||||||
response = "<!DOCTYPE html>\n";
|
|
||||||
response += "<html>\n";
|
|
||||||
response += "<head>\n";
|
|
||||||
response += "<title>CGI</title>\n";
|
|
||||||
response += "</head>\n";
|
|
||||||
response += "<body>\n";
|
|
||||||
response += "<h2>CGI request :</h2>\n";
|
|
||||||
for (int i = 1; i < ac; i++)
|
|
||||||
{
|
|
||||||
response += "<p>";
|
|
||||||
response += av[i];
|
|
||||||
response += "</p>\n";
|
|
||||||
}
|
|
||||||
response += "</body>\n";
|
|
||||||
response += "</html>\n";
|
|
||||||
|
|
||||||
strs << response.size();
|
|
||||||
header += strs.str();
|
|
||||||
header += end_header;
|
|
||||||
to_send = header;
|
|
||||||
to_send += response;
|
|
||||||
|
|
||||||
std::cout << to_send;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
4
srcs/cgi-bin/cgi.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
echo "status: 100\r\n"
|
||||||
|
echo "\r\n\r\n"
|
||||||
|
echo "hiii"
|
||||||
25
srcs/cgi-bin/cgi_cpp.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <cstdio>
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
24
srcs/cgi-bin/cgi_cpp_bad_headers.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string http_status;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Bad-Headers: wrong";
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
55
srcs/cgi-bin/cgi_cpp_download.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
std::string form_infos;
|
||||||
|
std::string path;
|
||||||
|
std::ifstream ifd;
|
||||||
|
std::stringstream buf;
|
||||||
|
size_t status;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
(void)env;
|
||||||
|
|
||||||
|
http_header = "Content-Type: image/jpeg" CRLF;
|
||||||
|
|
||||||
|
form_infos = get_form_infos(rq_body);
|
||||||
|
path = get_value("file", rq_body);
|
||||||
|
path = "./www/" + path;
|
||||||
|
|
||||||
|
status = ::eval_file_read(path);
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
std::cout << "Status: " << status << CRLF CRLF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifd.open(path.c_str());
|
||||||
|
if (!ifd)
|
||||||
|
{
|
||||||
|
std::cout << "Status: " << 500 << CRLF CRLF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf << ifd.rdbuf();
|
||||||
|
if (!ifd || !buf)
|
||||||
|
{
|
||||||
|
std::cout << "Status: " << 500 << CRLF CRLF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << buf.str();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
12
srcs/cgi-bin/cgi_cpp_empty.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
(void)env;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
23
srcs/cgi-bin/cgi_cpp_empty_lines.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF CRLF CRLF CRLF CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
25
srcs/cgi-bin/cgi_cpp_len.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
http_header += "Content-Length: " + itos(http_body.size()) + CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
25
srcs/cgi-bin/cgi_cpp_len_big.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
http_header += "Content-Length: " + itos(http_body.size() + 100) + CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
24
srcs/cgi-bin/cgi_cpp_len_small.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
http_header += "Content-Length: " + itos(http_body.size() - 100) + CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
22
srcs/cgi-bin/cgi_cpp_no_body.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_status;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
(void)env;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
24
srcs/cgi-bin/cgi_cpp_no_headers.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string http_status;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
std::cout << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
15
srcs/cgi-bin/cgi_cpp_only_crlf.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
(void)env;
|
||||||
|
|
||||||
|
std::cout << CRLF CRLF;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
29
srcs/cgi-bin/cgi_cpp_sleep.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string rq_body;
|
||||||
|
size_t time;
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
ss << get_value("sleep", rq_body);
|
||||||
|
ss >> time;
|
||||||
|
sleep(time);
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
27
srcs/cgi-bin/cgi_cpp_status.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
# include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
int main (int ac, char **av, char ** env)
|
||||||
|
{
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::string http_status;
|
||||||
|
std::string rq_body;
|
||||||
|
|
||||||
|
std::cin >> rq_body;
|
||||||
|
|
||||||
|
(void)ac;
|
||||||
|
(void)av;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
|
||||||
|
fill_body_basic(env, http_body, rq_body);
|
||||||
|
|
||||||
|
http_status = get_value("Status", rq_body);
|
||||||
|
http_header += "Status: " + http_status + CRLF;
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF CRLF << http_body;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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";
|
||||||
|
?>
|
||||||
3
srcs/cgi-bin/cgi_style.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
h1, h2, h3, p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
199
srcs/cgi-bin/cgi_utils.cpp
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
#include "cgi_utils.hpp"
|
||||||
|
|
||||||
|
std::string str_tolower(std::string str)
|
||||||
|
{
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
split(const std::string & input, std::string delim, char ctrim)
|
||||||
|
{
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::string tmp;
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
while (end != 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 itos(int n)
|
||||||
|
{
|
||||||
|
std::stringstream strs;
|
||||||
|
|
||||||
|
strs << n;
|
||||||
|
return ( strs.str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string parse_env(const std::string & env)
|
||||||
|
{
|
||||||
|
std::string ret = "";
|
||||||
|
char * ret_env;
|
||||||
|
|
||||||
|
ret_env = getenv(env.c_str());
|
||||||
|
if (ret_env != NULL)
|
||||||
|
ret = ret_env;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string print_env(char **env, std::string tag)
|
||||||
|
{
|
||||||
|
std::string ret = "";
|
||||||
|
|
||||||
|
for (int i = 0; env[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
ret += "<" + tag + ">";
|
||||||
|
ret += env[i];
|
||||||
|
ret += "</" + tag + "><br>";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
print_form(std::string form, std::string tag_key, std::string tag_val)
|
||||||
|
{
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::vector<std::string> sub_split_str;
|
||||||
|
std::vector<std::string>::const_iterator it;
|
||||||
|
std::string ret = "";
|
||||||
|
std::string key;
|
||||||
|
|
||||||
|
split_str = split(form, "&");
|
||||||
|
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||||
|
{
|
||||||
|
sub_split_str = split(*it, "=");
|
||||||
|
key = sub_split_str[0];
|
||||||
|
if (key == "fname")
|
||||||
|
key = "first name";
|
||||||
|
else if (key == "lname")
|
||||||
|
key = "last name";
|
||||||
|
ret += "<br><" + tag_key + ">" + key + ": </" + tag_key + ">";
|
||||||
|
ret += "<" + tag_val + ">" + sub_split_str[1] + "</" + tag_val + ">";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_form_infos(const std::string & rq_body)
|
||||||
|
{
|
||||||
|
std::string form_infos;
|
||||||
|
std::string method;
|
||||||
|
|
||||||
|
method = parse_env("REQUEST_METHOD");
|
||||||
|
|
||||||
|
if (method == "POST")
|
||||||
|
form_infos = rq_body;
|
||||||
|
else if (method == "GET")
|
||||||
|
form_infos = parse_env("QUERY_STRING");
|
||||||
|
|
||||||
|
return form_infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_value(const std::string & key, const std::string & rq_body)
|
||||||
|
{
|
||||||
|
std::string infos;
|
||||||
|
std::string ret;
|
||||||
|
size_t pos;
|
||||||
|
size_t end;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
infos = get_form_infos(rq_body);
|
||||||
|
pos = str_tolower(infos).find(str_tolower(key));
|
||||||
|
if (pos == NPOS)
|
||||||
|
return "";
|
||||||
|
pos = infos.find_first_of("=", pos);
|
||||||
|
if (pos == NPOS)
|
||||||
|
return "";
|
||||||
|
pos++;
|
||||||
|
end = infos.find_first_of("&", pos);
|
||||||
|
if (end == NPOS)
|
||||||
|
end = infos.size();
|
||||||
|
len = end - pos;
|
||||||
|
ret = infos.substr(pos, len);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fill_body_basic(char **env, std::string & http_body, const std::string & rq_body)
|
||||||
|
{
|
||||||
|
std::string rq_method = "not found";
|
||||||
|
std::string rq_query;
|
||||||
|
std::string form_infos;
|
||||||
|
|
||||||
|
rq_method = parse_env("REQUEST_METHOD");
|
||||||
|
rq_query = parse_env("QUERY_STRING");
|
||||||
|
|
||||||
|
if (rq_method == "POST")
|
||||||
|
form_infos = rq_body;
|
||||||
|
else if (rq_method == "GET")
|
||||||
|
form_infos = rq_query;
|
||||||
|
|
||||||
|
http_body = HTML_BODY_TOP;
|
||||||
|
|
||||||
|
http_body += "<br><h3>method used: </h3>";
|
||||||
|
http_body += "<p>" + rq_method + "</p>";
|
||||||
|
|
||||||
|
http_body += "<br><h3>form body: </h3>";
|
||||||
|
http_body += "<p>" + rq_body + "</p>";
|
||||||
|
|
||||||
|
http_body += "<br><h3>form query: </h3>";
|
||||||
|
http_body += "<p>" + rq_query + "</p>";
|
||||||
|
|
||||||
|
http_body += "<br><br><h3>output:</h3><br>";
|
||||||
|
http_body += print_form(form_infos, "p", "p");
|
||||||
|
|
||||||
|
http_body += "<br><br><h3>cgi_env_variables:</h3><br>";
|
||||||
|
http_body += print_env(env, "p");
|
||||||
|
|
||||||
|
http_body += HTML_BODY_BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t eval_file_read(const std::string &path)
|
||||||
|
{
|
||||||
|
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(), R_OK) == -1)
|
||||||
|
{
|
||||||
|
std::perror("err access()");
|
||||||
|
return 403; // FORBIDDEN, file doesn't have access permission
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
68
srcs/cgi-bin/cgi_utils.hpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
#ifndef CGI_UTILS_HPP
|
||||||
|
# define CGI_UTILS_HPP
|
||||||
|
|
||||||
|
# include <iostream>
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
# include <fstream>
|
||||||
|
# include <vector>
|
||||||
|
# include <stdlib.h> // getenv
|
||||||
|
# include <algorithm> // transform
|
||||||
|
# include <unistd.h> // sleep, close, access
|
||||||
|
|
||||||
|
# define CR "\r"
|
||||||
|
# define LF "\n"
|
||||||
|
# define CRLF CR LF
|
||||||
|
# define CRLF_SIZE 2
|
||||||
|
# define NPOS std::string::npos
|
||||||
|
|
||||||
|
# define HTML_BODY_TOP "<!DOCTYPE html>"\
|
||||||
|
"<html>"\
|
||||||
|
" <head>"\
|
||||||
|
" <meta charset=\"UTF-8\">"\
|
||||||
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"\
|
||||||
|
" <title>CGI</title>"\
|
||||||
|
" <link href=\"./cgi_style.css\" type=\"text/css\" rel=\"stylesheet\">"\
|
||||||
|
" </head>"\
|
||||||
|
" <body>"\
|
||||||
|
" <h1>CGI</h1><br>"
|
||||||
|
# define HTML_BODY_BOTTOM " <br><h1>END CGI</h1>"\
|
||||||
|
" </body>"\
|
||||||
|
"</html>"
|
||||||
|
|
||||||
|
std::string
|
||||||
|
str_tolower(std::string str);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
trim(std::string str, char del);
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
split(const std::string & input, std::string delim, char ctrim = '\0');
|
||||||
|
|
||||||
|
std::string
|
||||||
|
itos(int n);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
parse_env(const std::string & env);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
print_env(char **env, std::string tag = "p");
|
||||||
|
|
||||||
|
std::string
|
||||||
|
get_form_infos(const std::string & rq_body);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
get_value(const std::string & key, const std::string & rq_body);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
print_form(std::string form, std::string key = "p", std::string val = "p");
|
||||||
|
|
||||||
|
void
|
||||||
|
fill_body_basic(char **env, std::string & http_body, const std::string & rq_body);
|
||||||
|
|
||||||
|
size_t
|
||||||
|
eval_file_read(const std::string &path);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
10
srcs/cgi-bin/tmp.php
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#! /usr/bin/php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(isset($_FILE["fileToUpload"]))
|
||||||
|
echo "there is a file";
|
||||||
|
else
|
||||||
|
echo "\n\nHeader: something\r\n\r\nno file\r\n";
|
||||||
|
|
||||||
|
?>
|
||||||
56
srcs/cgi-bin/upload_file.php
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#! /usr/bin/php
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$target_dir = "user_files/";
|
||||||
|
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
|
||||||
|
$uploadOk = 1;
|
||||||
|
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
// Check if image file is a actual image or fake image
|
||||||
|
if(isset($_POST["submit"])) {
|
||||||
|
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
|
||||||
|
if($check !== false) {
|
||||||
|
echo "File is an image - " . $check["mime"] . ".";
|
||||||
|
$uploadOk = 1;
|
||||||
|
} else {
|
||||||
|
echo "File is not an image.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file already exists
|
||||||
|
if (file_exists($target_file)) {
|
||||||
|
echo "Sorry, file already exists.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file size
|
||||||
|
if ($_FILES["fileToUpload"]["size"] > 500000) {
|
||||||
|
echo "Sorry, your file is too large.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow certain file formats
|
||||||
|
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
|
||||||
|
&& $imageFileType != "gif" ) {
|
||||||
|
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if $uploadOk is set to 0 by an error
|
||||||
|
if ($uploadOk == 0) {
|
||||||
|
echo "Sorry, your file was not uploaded.";
|
||||||
|
// if everything is ok, try to upload file
|
||||||
|
} else {
|
||||||
|
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
|
||||||
|
echo "The file ". htmlspecialchars( basename( $_FILES["fileToUpload"]["name"])). " has been uploaded.";
|
||||||
|
} else {
|
||||||
|
echo "Sorry, there was an error uploading your file.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
67
srcs/cgi-bin/upload_file1.php
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#! /usr/bin/php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// $FileName=$_FILES['myFile']['filename'];
|
||||||
|
# $TmpName=$_FILES['myFile']['tmp_name'];
|
||||||
|
# move_uploaded_file($TmpName, $FileName);
|
||||||
|
# echo("File was uploaded successfully!");
|
||||||
|
|
||||||
|
// this part needs to be grabed from POST uri
|
||||||
|
$target_dir = "./www/user_files/";
|
||||||
|
//$target_dir = $_POST["upload_dir"];
|
||||||
|
//$target_dir = $_GET["upload_dir"];
|
||||||
|
//$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
|
||||||
|
$target_file = $target_dir . basename($_FILES["myFile"]["name"]);
|
||||||
|
|
||||||
|
$uploadOk = 1;
|
||||||
|
$fileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
|
||||||
|
//$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
// Check if image file is a actual image or fake image
|
||||||
|
//if(isset($_POST["submit"])) {
|
||||||
|
// $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
|
||||||
|
// if($check !== false) {
|
||||||
|
// echo "File is an image - " . $check["mime"] . ".";
|
||||||
|
// $uploadOk = 1;
|
||||||
|
// } else {
|
||||||
|
// echo "File is not an image.";
|
||||||
|
// $uploadOk = 0;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Check if file already exists
|
||||||
|
if (file_exists($target_file)) {
|
||||||
|
echo "Sorry, file already exists.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file size
|
||||||
|
if ($_FILES["fileToUpload"]["size"] > 500000) {
|
||||||
|
echo "Sorry, your file is too large.";
|
||||||
|
$uploadOk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow certain file formats
|
||||||
|
//if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
|
||||||
|
//&& $imageFileType != "gif" ) {
|
||||||
|
// echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
|
||||||
|
// $uploadOk = 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Check if $uploadOk is set to 0 by an error
|
||||||
|
if ($uploadOk == 0) {
|
||||||
|
echo "Sorry, your file was not uploaded.";
|
||||||
|
// if everything is ok, try to upload file
|
||||||
|
} else {
|
||||||
|
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
|
||||||
|
echo "The file ". htmlspecialchars( basename( $_FILES["fileToUpload"]["name"])). " has been uploaded.";
|
||||||
|
} else {
|
||||||
|
echo "Sorry, there was an error uploading your file.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
@@ -1,14 +1,3 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* ConfigParser.hpp :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
|
|
||||||
/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef CONFIGPARSER_HPP
|
#ifndef CONFIGPARSER_HPP
|
||||||
# define CONFIGPARSER_HPP
|
# define CONFIGPARSER_HPP
|
||||||
@@ -22,11 +11,9 @@
|
|||||||
# include <exception> // exception, what
|
# include <exception> // exception, what
|
||||||
# include <stdexcept> // runtime_error, invalid_argument
|
# include <stdexcept> // runtime_error, invalid_argument
|
||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
# include <cstdlib> // strtol, stroul
|
||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
# include <fstream> // ifstream
|
# include <fstream> // ifstream
|
||||||
//# include <unistd.h> // access()
|
|
||||||
# include <dirent.h> // opendir(), doesn't work...
|
|
||||||
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
||||||
# include <algorithm> // sort() in Post
|
# include <algorithm> // sort() in Post
|
||||||
|
|
||||||
@@ -34,87 +21,41 @@ class ConfigParser {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// canonical
|
ConfigParser();
|
||||||
|
|
||||||
ConfigParser(const char* path); // a string?
|
|
||||||
~ConfigParser();
|
~ConfigParser();
|
||||||
|
ConfigParser(const std::string &config_file);
|
||||||
|
|
||||||
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
|
void read_config(const std::string &config_file);
|
||||||
// ConfigParser & operator=(const ConfigParser& rhs);
|
|
||||||
|
|
||||||
// void parse(); // return void cuz throw exceptions.
|
|
||||||
std::vector<ServerConfig> * parse(); // const?
|
std::vector<ServerConfig> * parse(); // const?
|
||||||
// std::vector<ServerConfig> parse(); // const?
|
|
||||||
|
|
||||||
// other parses?
|
|
||||||
|
|
||||||
// i thought if it were an instance of this class you could call
|
// i thought if it were an instance of this class you could call
|
||||||
// private member functions from anywhere...
|
// private member functions from anywhere... // QUESTION : Wut ?
|
||||||
void _print_content() const;
|
void print_content() const;
|
||||||
|
|
||||||
|
|
||||||
// I don't love that this is here but...
|
|
||||||
// doesn't work use the operator overload
|
|
||||||
// bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _content;
|
std::string _content;
|
||||||
|
|
||||||
// explicit?
|
|
||||||
// what exaclty does explicit do again?
|
|
||||||
ConfigParser(); // might need a path as arg?
|
|
||||||
// should this be in private since it always needs a path?
|
|
||||||
|
|
||||||
|
|
||||||
ServerConfig _parse_server(size_t *start);
|
ServerConfig _parse_server(size_t *start);
|
||||||
LocationConfig _parse_location(size_t *start);
|
LocationConfig _parse_location(size_t *start);
|
||||||
|
|
||||||
|
|
||||||
void _set_server_values(ServerConfig *server, const std::string key, std::string value);
|
void _set_server_values(ServerConfig *server, const std::string key, std::string value);
|
||||||
void _set_location_values(LocationConfig *location, const std::string key, std::string value);
|
void _set_location_values(LocationConfig *location, const std::string key, std::string value);
|
||||||
|
|
||||||
|
/* Extra */
|
||||||
std::string _pre_set_val_check(const std::string key, \
|
std::string _pre_set_val_check(const std::string key,
|
||||||
const std::string value);
|
const std::string value);
|
||||||
|
|
||||||
std::string _get_first_word(size_t *curr); // const?
|
std::string _get_first_word(size_t *curr); // const?
|
||||||
std::string _get_rest_of_line(size_t *curr); // const?
|
std::string _get_rest_of_line(size_t *curr); // const?
|
||||||
|
|
||||||
|
/* Post Processing */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// some sort of post processing...
|
|
||||||
|
|
||||||
void _post_processing(std::vector<ServerConfig> *servers);
|
void _post_processing(std::vector<ServerConfig> *servers);
|
||||||
|
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
|
||||||
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// no idea if it should go here...
|
|
||||||
//bool compareLocationConfigs(const LocationConfig &a,
|
|
||||||
// const LocationConfig &b);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// def needs work line a better name an do i even need this?
|
|
||||||
// should it be in Utils instead?
|
|
||||||
class MyException : public std::invalid_argument
|
|
||||||
{
|
|
||||||
MyException(const std::string str)
|
|
||||||
: std::invalid_argument(str) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,16 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* LocationConfig.hpp :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
|
||||||
/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef LOCATIONCONFIG_HPP
|
#ifndef LOCATIONCONFIG_HPP
|
||||||
# define LOCATIONCONFIG_HPP
|
# define LOCATIONCONFIG_HPP
|
||||||
|
|
||||||
# include <map>
|
|
||||||
# include <vector>
|
# include <vector>
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <iostream>
|
|
||||||
# include <sys/stat.h> // stat()
|
|
||||||
|
|
||||||
# include <stdio.h> // printf(), gotta go
|
|
||||||
|
|
||||||
|
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
|
|
||||||
// again, struct instead?
|
struct LocationConfig
|
||||||
class LocationConfig
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
// canonic stuff?
|
|
||||||
|
|
||||||
std::string path; // /path and /path/ are fine
|
std::string path; // /path and /path/ are fine
|
||||||
// i add trailing / if a dir
|
// i remove trailing / systematically
|
||||||
std::string root;
|
std::string root;
|
||||||
std::vector<std::string> index;
|
std::vector<std::string> index;
|
||||||
unsigned int allow_methods;
|
unsigned int allow_methods;
|
||||||
@@ -42,10 +21,8 @@ public:
|
|||||||
|
|
||||||
int redirect_status; // only in location
|
int redirect_status; // only in location
|
||||||
std::string redirect_uri; // only 1 per location
|
std::string redirect_uri; // only 1 per location
|
||||||
// au pire you do location / { return 301 http://location; }
|
|
||||||
// and that's how you get the redirect from the root.
|
|
||||||
|
|
||||||
void print_all()
|
void print_all() const
|
||||||
{
|
{
|
||||||
std::cout << "\nPRINTING A LOCATION\n";
|
std::cout << "\nPRINTING A LOCATION\n";
|
||||||
|
|
||||||
@@ -63,14 +40,13 @@ public:
|
|||||||
std::cout << "------\n";
|
std::cout << "------\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// works a lot better than using a compare function...
|
bool operator<(const LocationConfig& rhs) const
|
||||||
bool operator<(const LocationConfig& rhs) const
|
|
||||||
{
|
{
|
||||||
int comp_lhs = 0;
|
int comp_lhs = 0;
|
||||||
int comp_rhs = 0;
|
int comp_rhs = 0;
|
||||||
size_t tmp = 0;
|
size_t tmp = 0;
|
||||||
|
|
||||||
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
|
while ((tmp = this->path.find_first_of("/", tmp)) != NPOS)
|
||||||
{
|
{
|
||||||
++tmp;
|
++tmp;
|
||||||
++comp_lhs;
|
++comp_lhs;
|
||||||
@@ -78,7 +54,7 @@ public:
|
|||||||
if (path[path.find_last_of("/") + 1] != '\0')
|
if (path[path.find_last_of("/") + 1] != '\0')
|
||||||
++comp_lhs;
|
++comp_lhs;
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
|
while ((tmp = rhs.path.find_first_of("/", tmp)) != NPOS)
|
||||||
{
|
{
|
||||||
++tmp;
|
++tmp;
|
||||||
++comp_rhs;
|
++comp_rhs;
|
||||||
@@ -86,19 +62,16 @@ public:
|
|||||||
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
|
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
|
||||||
++comp_rhs;
|
++comp_rhs;
|
||||||
|
|
||||||
return (comp_lhs < comp_rhs); // right comparison ? not <= ?
|
return (comp_lhs < comp_rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator==(const LocationConfig& rhs) const
|
||||||
|
{
|
||||||
|
if (path.compare(rhs.path) == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,22 +10,17 @@
|
|||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
|
|
||||||
// a class that's all public? just so we have options?
|
struct ServerConfig
|
||||||
class ServerConfig
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
// do i need some canonic stuff?
|
|
||||||
|
|
||||||
std::vector<std::string> server_name;
|
std::vector<std::string> server_name;
|
||||||
// we could shove default in here if we wanted to...
|
|
||||||
|
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string port; // port needs to be something else... not quite an int
|
std::string port;
|
||||||
|
|
||||||
std::string root; // ./www/ or www work www/ and www work
|
std::string root; // ./www/ or www work www/ and www work
|
||||||
// i do remove trailing / tho
|
// i do remove trailing / tho
|
||||||
|
|
||||||
unsigned int client_body_limit; // set to default max if none set
|
size_t client_body_limit;
|
||||||
|
|
||||||
std::vector<std::string> index;
|
std::vector<std::string> index;
|
||||||
std::map<int, std::string> error_pages;
|
std::map<int, std::string> error_pages;
|
||||||
@@ -33,7 +28,7 @@ public:
|
|||||||
std::vector<LocationConfig> locations;
|
std::vector<LocationConfig> locations;
|
||||||
|
|
||||||
|
|
||||||
void print_all()
|
void print_all() const
|
||||||
{
|
{
|
||||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
||||||
|
|
||||||
@@ -44,14 +39,14 @@ public:
|
|||||||
for (size_t i = 0; i < index.size(); i++)
|
for (size_t i = 0; i < index.size(); i++)
|
||||||
std::cout << index[i] << " ";
|
std::cout << index[i] << " ";
|
||||||
std::cout << "\nerror_pages: ";
|
std::cout << "\nerror_pages: ";
|
||||||
for(std::map<int, std::string>::iterator it = error_pages.begin(); \
|
for(std::map<int, std::string>::const_iterator it = error_pages.begin(); \
|
||||||
it != error_pages.end(); it++)
|
it != error_pages.end(); it++)
|
||||||
std::cout << it->first << "--" << it->second << " ";
|
std::cout << it->first << "--" << it->second << " ";
|
||||||
// for (size_t i = 0; i < error_pages.size(); i++)
|
// for (size_t i = 0; i < error_pages.size(); i++)
|
||||||
// std::cout << error_pages->first << "--" << error_pages->second << " ";
|
// std::cout << error_pages->first << "--" << error_pages->second << " ";
|
||||||
|
|
||||||
// std::cout << "skiping Locations for now...\n";
|
// std::cout << "skiping Locations for now...\n";
|
||||||
for (std::vector<LocationConfig>::iterator it = locations.begin(); it < locations.end(); it++)
|
for (std::vector<LocationConfig>::const_iterator it = locations.begin(); it < locations.end(); it++)
|
||||||
it->print_all();
|
it->print_all();
|
||||||
|
|
||||||
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
// prolly get rid of this file...
|
|
||||||
|
|
||||||
|
|
||||||
#include "LocationConfig.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// Ok so maybe it can't be a member functions?
|
|
||||||
bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b)
|
|
||||||
{
|
|
||||||
int len_a;
|
|
||||||
int len_b;
|
|
||||||
size_t tmp = 0;
|
|
||||||
|
|
||||||
// consider adding 1 to path that ends in a file not folder.
|
|
||||||
|
|
||||||
|
|
||||||
while ((tmp = a.path.find_first_of("/", tmp)) != std::string::npos)
|
|
||||||
++len_a;
|
|
||||||
tmp = 0;
|
|
||||||
while ((tmp = b.path.find_first_of("/", tmp)) != std::string::npos)
|
|
||||||
++len_b;
|
|
||||||
return (len_a < len_b); // right comparison ? not <= ?
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "ConfigParser.hpp"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
|
// should i be sending & references?
|
||||||
|
// const?
|
||||||
std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
std::string ConfigParser::_pre_set_val_check(const std::string key,
|
||||||
const std::string value)
|
const std::string value)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -13,57 +11,49 @@ std::string ConfigParser::_pre_set_val_check(const std::string key, \
|
|||||||
// check values for ; at end and right number of words depending on key
|
// check values for ; at end and right number of words depending on key
|
||||||
|
|
||||||
// std::cout << "pre check\n";
|
// std::cout << "pre check\n";
|
||||||
if (key.find_first_of(";") != std::string::npos)
|
if (key.find_first_of(";") != NPOS)
|
||||||
throw std::invalid_argument("bad config file arguments 2");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
// there shouldn't be any tabs, right? not between values...
|
if (value.find_first_of("\t") != NPOS)
|
||||||
if (value.find_first_of("\t") != std::string::npos)
|
|
||||||
{
|
|
||||||
// std::cout << value << "\n";
|
|
||||||
throw std::invalid_argument("why would you put tabs between values");
|
throw std::invalid_argument("why would you put tabs between values");
|
||||||
}
|
|
||||||
|
|
||||||
size_t i = value.find_first_of(";");
|
size_t i = value.find_first_of(";");
|
||||||
// so you can't have no ;
|
// so you can't have no ;
|
||||||
// you can't have just ;
|
// you can't have just ;
|
||||||
// and you can't have a ; not at the end or several ;
|
// and you can't have a ; not at the end or several ;
|
||||||
// in theory value_find_last_of should find the only ;
|
// in theory value_find_last_of should find the only ;
|
||||||
if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \
|
if (i == NPOS || (value.find_last_not_of(" \n")) != i \
|
||||||
|| value.compare(";") == 0)
|
|| value.compare(";") == 0)
|
||||||
throw std::invalid_argument("bad config file arguments 4");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
|
|
||||||
return (value.substr(0, i));
|
return (value.substr(0, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const?
|
||||||
|
|
||||||
// assumes curr is on a space or \t or \n
|
// assumes curr is on a space or \t or \n
|
||||||
// get first word? next word? word?
|
|
||||||
std::string ConfigParser::_get_first_word(size_t *curr)
|
std::string ConfigParser::_get_first_word(size_t *curr)
|
||||||
{
|
{
|
||||||
size_t start;
|
size_t start;
|
||||||
|
|
||||||
// are these checks excessive?
|
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
|
||||||
throw std::invalid_argument("bad config file arguments");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
if ((*curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||||
throw std::invalid_argument("bad config file arguments");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
std::string key = _content.substr(start, *curr - start);
|
std::string key = _content.substr(start, *curr - start);
|
||||||
|
|
||||||
return (key);
|
return (key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const?
|
||||||
// also assumes curr is on a space \t or \n
|
// also assumes curr is on a space \t or \n
|
||||||
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||||
{
|
{
|
||||||
size_t start;
|
size_t start;
|
||||||
|
|
||||||
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
|
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
|
||||||
throw std::invalid_argument("bad config file arguments");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
if ((*curr = _content.find_first_of("\n", start)) == NPOS)
|
||||||
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
|
|
||||||
throw std::invalid_argument("bad config file arguments");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
|
|
||||||
std::string values = _content.substr(start, *curr - start);
|
std::string values = _content.substr(start, *curr - start);
|
||||||
@@ -71,9 +61,7 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConfigParser::print_content() const
|
||||||
|
|
||||||
void ConfigParser::_print_content() const
|
|
||||||
{
|
{
|
||||||
std::cout << _content;
|
std::cout << _content;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,34 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* ConfigParser.cpp :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2022/07/13 22:11:17 by me #+# #+# */
|
|
||||||
/* Updated: 2022/08/03 17:51:35 by lperrey ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "ConfigParser.hpp"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
// Default
|
ConfigParser::ConfigParser() {};
|
||||||
ConfigParser::ConfigParser()
|
ConfigParser::~ConfigParser() {};
|
||||||
|
|
||||||
|
ConfigParser::ConfigParser(const std::string &config_file)
|
||||||
{
|
{
|
||||||
std::cout << "Default Constructor\n";
|
read_config(config_file);
|
||||||
// don't use yet, you have no idea what the defaults are
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigParser::ConfigParser(const char* path)
|
void ConfigParser::read_config(const std::string &config_file)
|
||||||
{
|
{
|
||||||
std::cout << "Param Constructor\n";
|
|
||||||
|
|
||||||
std::ifstream file;
|
std::ifstream file;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
size_t comment;
|
size_t comment;
|
||||||
|
|
||||||
_content.clear();
|
file.open(config_file.c_str());
|
||||||
file.open(path);
|
if (!file)
|
||||||
if (file.is_open())
|
throw std::invalid_argument("failed to open config");
|
||||||
|
else
|
||||||
{
|
{
|
||||||
|
_content.clear();
|
||||||
while (!file.eof())
|
while (!file.eof())
|
||||||
{
|
{
|
||||||
getline(file, buf);
|
getline(file, buf);
|
||||||
// remove # comments here.
|
// remove # comments here.
|
||||||
if ((comment = buf.find_first_of("#")) == std::string::npos)
|
if ((comment = buf.find_first_of("#")) == NPOS)
|
||||||
{
|
{
|
||||||
// remove empty lines, i think...
|
// remove empty lines, i think...
|
||||||
if ((buf.find_first_not_of(" \t")) != std::string::npos)
|
if ((buf.find_first_not_of(" \t")) != NPOS)
|
||||||
_content.append(buf + '\n');
|
_content.append(buf + '\n');
|
||||||
}
|
}
|
||||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||||
@@ -48,49 +38,29 @@ ConfigParser::ConfigParser(const char* path)
|
|||||||
_content.append(tmp + '\n');
|
_content.append(tmp + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw std::invalid_argument("failed to open config");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigParser::~ConfigParser()
|
// const?
|
||||||
{
|
|
||||||
// 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> * ConfigParser::parse()
|
||||||
{
|
{
|
||||||
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
|
||||||
// std::vector<ServerConfig> ret;
|
|
||||||
|
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
size_t curr = _content.find_first_not_of(" \t\n", 0);
|
size_t curr = _content.find_first_not_of(" \t\n", 0);
|
||||||
|
|
||||||
if (curr == std::string::npos)
|
if (curr == NPOS)
|
||||||
throw std::invalid_argument("empty config file");
|
throw std::invalid_argument("empty config file");
|
||||||
while (curr != std::string::npos)
|
while (curr != NPOS)
|
||||||
{
|
{
|
||||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos)
|
if ((start = _content.find_first_not_of(" \t\n", curr)) == NPOS)
|
||||||
throw std::invalid_argument("empty config file");
|
throw std::invalid_argument("empty config file");
|
||||||
|
|
||||||
if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
if ((curr = _content.find_first_of(" \t\n", start)) == NPOS)
|
||||||
throw std::invalid_argument("empty config file");
|
throw std::invalid_argument("empty config file");
|
||||||
std::string key = _content.substr(start, curr - start);
|
std::string key = _content.substr(start, curr - start);
|
||||||
if (key != "server")
|
if (key != "server")
|
||||||
throw std::invalid_argument("bad config file arguments 1");
|
throw std::invalid_argument("bad config file arguments");
|
||||||
ret->push_back(_parse_server(&curr));
|
ret->push_back(_parse_server(&curr));
|
||||||
}
|
}
|
||||||
_post_processing(ret);
|
_post_processing(ret);
|
||||||
@@ -103,13 +73,13 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
|
|||||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||||
|
|
||||||
ret.client_body_limit = 0;
|
ret.client_body_limit = 0;
|
||||||
if (curr == std::string::npos || _content[curr] != '{')
|
if (curr == NPOS || _content[curr] != '{')
|
||||||
throw std::invalid_argument("bad config file syntax 1");
|
throw std::invalid_argument("bad config file syntax");
|
||||||
|
|
||||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
|
||||||
throw std::invalid_argument("bad config file syntax");
|
throw std::invalid_argument("bad config file syntax");
|
||||||
// are there other things to check for?
|
// are there other things to check for?
|
||||||
while (curr != std::string::npos) // here curr == { + 1
|
while (curr != NPOS) // here curr == { + 1
|
||||||
{
|
{
|
||||||
// so this moves curr to past the word...
|
// so this moves curr to past the word...
|
||||||
std::string key = _get_first_word(&curr);
|
std::string key = _get_first_word(&curr);
|
||||||
@@ -147,17 +117,16 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
|||||||
ret.path = _get_first_word(&curr);
|
ret.path = _get_first_word(&curr);
|
||||||
if (ret.path[0] != '/')
|
if (ret.path[0] != '/')
|
||||||
throw std::invalid_argument("Location path require a leading /");
|
throw std::invalid_argument("Location path require a leading /");
|
||||||
// ret.path.insert(0, "/");
|
|
||||||
// in theory now curr should be right after the "path"
|
// in theory now curr should be right after the "path"
|
||||||
|
|
||||||
curr = _content.find_first_not_of(" \t\n", curr);
|
curr = _content.find_first_not_of(" \t\n", curr);
|
||||||
|
|
||||||
if (curr == std::string::npos || _content[curr] != '{')
|
if (curr == NPOS || _content[curr] != '{')
|
||||||
throw std::invalid_argument("bad config file syntax 2");
|
|
||||||
|
|
||||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
|
||||||
throw std::invalid_argument("bad config file syntax");
|
throw std::invalid_argument("bad config file syntax");
|
||||||
while (curr != std::string::npos)
|
|
||||||
|
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...
|
// so this moves curr to past the word...
|
||||||
std::string key = _get_first_word(&curr);
|
std::string key = _get_first_word(&curr);
|
||||||
@@ -180,7 +149,7 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
|||||||
|
|
||||||
|
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
void ConfigParser::_set_server_values(ServerConfig *server,
|
||||||
const std::string key, std::string value)
|
const std::string key, std::string value)
|
||||||
{
|
{
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
@@ -193,20 +162,23 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
throw std::invalid_argument("missing value");
|
throw std::invalid_argument("missing value");
|
||||||
else if (key == "server_name" && server->server_name.empty())
|
else if (key == "server_name" && server->server_name.empty())
|
||||||
{
|
{
|
||||||
for (std::vector<std::string>::iterator it = server->server_name.begin(); \
|
for (size_t i = 0; i < size; i++)
|
||||||
it < server->server_name.end(); it++)
|
|
||||||
{
|
{
|
||||||
if (it->compare(tmp_val[0]) == 0)
|
for (std::vector<std::string>::const_iterator it = server->server_name.begin();
|
||||||
throw std::invalid_argument("server_name already exists");
|
it < server->server_name.end(); it++)
|
||||||
|
{
|
||||||
|
if (it->compare(tmp_val[i]) == 0)
|
||||||
|
throw std::invalid_argument("server_name already exists");
|
||||||
|
}
|
||||||
|
server->server_name.push_back(tmp_val[i]);
|
||||||
}
|
}
|
||||||
server->server_name.push_back(tmp_val[0]);
|
|
||||||
}
|
}
|
||||||
else if (key == "listen" && size == 1 && server->host == "" \
|
else if (key == "listen" && size == 1 && server->host == ""
|
||||||
&& server->port == "")
|
&& server->port == "") // QUESTION LUKE : C'est quoi cette condition ? Si listen est vide ? Je comprends pas trop.
|
||||||
{
|
{
|
||||||
if (tmp_val[0].find_first_of(":") == std::string::npos)
|
if (tmp_val[0].find_first_of(":") == NPOS)
|
||||||
{
|
{
|
||||||
if (!::isNumeric(tmp_val[0]))
|
if (!::isNumeric_btw(0, 65535, tmp_val[0]))
|
||||||
throw std::invalid_argument("bad port number");
|
throw std::invalid_argument("bad port number");
|
||||||
server->host = "0.0.0.0";
|
server->host = "0.0.0.0";
|
||||||
server->port = tmp_val[0];
|
server->port = tmp_val[0];
|
||||||
@@ -223,7 +195,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
if (!::isNumeric_btw(0, 255, ip[i]))
|
if (!::isNumeric_btw(0, 255, ip[i]))
|
||||||
throw std::invalid_argument("bad host ip");
|
throw std::invalid_argument("bad host ip");
|
||||||
}
|
}
|
||||||
if (!::isNumeric(tmp2[1]))
|
if (!::isNumeric_btw(0, 65535, tmp2[1]))
|
||||||
throw std::invalid_argument("bad port number");
|
throw std::invalid_argument("bad port number");
|
||||||
server->host = tmp2[0];
|
server->host = tmp2[0];
|
||||||
server->port = tmp2[1];
|
server->port = tmp2[1];
|
||||||
@@ -234,28 +206,32 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
// remove trailing /
|
// remove trailing /
|
||||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||||
|
// tmp_val[0].push_back('/');
|
||||||
server->root = tmp_val[0];
|
server->root = tmp_val[0];
|
||||||
}
|
}
|
||||||
else if (key == "client_body_limit" && size == 1 \
|
else if (key == "client_body_limit" && size == 1
|
||||||
&& server->client_body_limit == 0)
|
&& server->client_body_limit == 0)
|
||||||
{
|
{
|
||||||
if (!::isNumeric(tmp_val[0]))
|
if (!::isNumeric(tmp_val[0]))
|
||||||
throw std::invalid_argument("client_body_limit not a number");
|
throw std::invalid_argument("client_body_limit not a number");
|
||||||
server->client_body_limit = atoi(tmp_val[0].c_str());
|
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||||
|
if (errno == ERANGE || server->client_body_limit > (ULONG_MAX / KB) )
|
||||||
|
throw std::invalid_argument("client_body_limit too big");
|
||||||
|
server->client_body_limit = server->client_body_limit * KB;
|
||||||
}
|
}
|
||||||
else if (key == "index")
|
else if (key == "index")
|
||||||
{
|
{
|
||||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
for (size_t i = 0; i != tmp_val.size(); i++)
|
||||||
server->index.push_back(tmp_val[i]);
|
server->index.push_back(tmp_val[i]);
|
||||||
}
|
}
|
||||||
else if (key == "error_page")
|
else if (key == "error_page")
|
||||||
{
|
{
|
||||||
std::string path = tmp_val[tmp_val.size() - 1];
|
std::string path = tmp_val[size - 1];
|
||||||
for (unsigned long i = 0; i != tmp_val.size() - 1; i++)
|
for (size_t i = 0; i < size - 1; i++)
|
||||||
{
|
{
|
||||||
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
if (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||||
throw std::invalid_argument("invalid error code");
|
throw std::invalid_argument("invalid error code");
|
||||||
int status_code = atoi(tmp_val[i].c_str());
|
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
|
||||||
if (server->error_pages.find(status_code) != server->error_pages.end())
|
if (server->error_pages.find(status_code) != server->error_pages.end())
|
||||||
throw std::invalid_argument("redeclaring error page");
|
throw std::invalid_argument("redeclaring error page");
|
||||||
server->error_pages[status_code] = path;
|
server->error_pages[status_code] = path;
|
||||||
@@ -267,7 +243,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
|
|
||||||
|
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
void ConfigParser::_set_location_values(LocationConfig *location, \
|
void ConfigParser::_set_location_values(LocationConfig *location,
|
||||||
const std::string key, std::string value)
|
const std::string key, std::string value)
|
||||||
{
|
{
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
@@ -283,18 +259,19 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
|||||||
// remove trailing /
|
// remove trailing /
|
||||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||||
|
// tmp_val[0].push_back('/');
|
||||||
location->root = tmp_val[0];
|
location->root = tmp_val[0];
|
||||||
}
|
}
|
||||||
else if (key == "autoindex" && size == 1)
|
else if (key == "autoindex" && size == 1)
|
||||||
location->autoindex = (tmp_val[0] == "on" ? true : false);
|
location->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||||
else if (key == "index")
|
else if (key == "index")
|
||||||
{
|
{
|
||||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
for (size_t i = 0; i < size; i++)
|
||||||
location->index.push_back(tmp_val[i]);
|
location->index.push_back(tmp_val[i]);
|
||||||
}
|
}
|
||||||
else if (key == "allow_methods" && location->allow_methods == 0)
|
else if (key == "allow_methods" && location->allow_methods == 0)
|
||||||
{
|
{
|
||||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
for (size_t i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||||
if (m == UNKNOWN)
|
if (m == UNKNOWN)
|
||||||
@@ -304,28 +281,27 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
|||||||
}
|
}
|
||||||
else if (key == "cgi_ext")
|
else if (key == "cgi_ext")
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < tmp_val.size(); i++)
|
for (size_t i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
if (tmp_val[i][0] == '.')
|
if (tmp_val[i][0] == '.')
|
||||||
throw std::invalid_argument("cgi_ext should not have a leading '.'");
|
throw std::invalid_argument("cgi_ext should not have a leading '.'");
|
||||||
location->cgi_ext.push_back(tmp_val[i]);
|
location->cgi_ext.push_back(tmp_val[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == "redirect" && location->redirect_status == 0 \
|
else if (key == "redirect" && location->redirect_status == 0
|
||||||
&& location->redirect_uri == "")
|
&& location->redirect_uri == "")
|
||||||
{
|
{
|
||||||
if (tmp_val.size() != 2)
|
if (size != 2)
|
||||||
throw std::invalid_argument("wrong number of values");
|
throw std::invalid_argument("wrong number of values");
|
||||||
if (tmp_val[0] != "301" && tmp_val[0] != "302"
|
if (tmp_val[0] != "301" && tmp_val[0] != "302"
|
||||||
&& tmp_val[0] != "303" && tmp_val[0] != "307"
|
&& tmp_val[0] != "303" && tmp_val[0] != "307"
|
||||||
&& tmp_val[0] != "308")
|
&& tmp_val[0] != "308")
|
||||||
throw std::invalid_argument("bad redirect status");
|
throw std::invalid_argument("bad redirect status");
|
||||||
std::cout << tmp_val[1] << '\n';
|
|
||||||
if (tmp_val[1].compare(0, 7, "http://")
|
if (tmp_val[1].compare(0, 7, "http://")
|
||||||
&& tmp_val[1].compare(0, 8, "https://"))
|
&& tmp_val[1].compare(0, 8, "https://"))
|
||||||
throw std::invalid_argument("bad redirect uri");
|
throw std::invalid_argument("bad redirect uri");
|
||||||
|
|
||||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10);
|
||||||
location->redirect_uri = tmp_val[1];
|
location->redirect_uri = tmp_val[1];
|
||||||
}
|
}
|
||||||
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
|
||||||
|
|||||||
@@ -22,12 +22,7 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
|||||||
throw std::invalid_argument("Config file needs an Index");
|
throw std::invalid_argument("Config file needs an Index");
|
||||||
|
|
||||||
if (it->client_body_limit == 0)
|
if (it->client_body_limit == 0)
|
||||||
it->client_body_limit = 5000; // what is the recomended size?
|
it->client_body_limit = 1 * MB;
|
||||||
|
|
||||||
|
|
||||||
// if error_pages is left empty, we'll use the defaults which
|
|
||||||
// i believe are set elsewhere...
|
|
||||||
|
|
||||||
|
|
||||||
if (!_find_root_path_location(it->locations))
|
if (!_find_root_path_location(it->locations))
|
||||||
{
|
{
|
||||||
@@ -46,23 +41,37 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
|||||||
|
|
||||||
while (it_l != it->locations.end())
|
while (it_l != it->locations.end())
|
||||||
{
|
{
|
||||||
|
if (it_l->path[it_l->path.size() - 1] == '/'
|
||||||
|
&& it_l->path.size() > 1)
|
||||||
|
it_l->path.erase(it_l->path.size() - 1);
|
||||||
|
|
||||||
if (it_l->root == "")
|
if (it_l->root == "")
|
||||||
it_l->root = it->root;
|
it_l->root = it->root;
|
||||||
|
|
||||||
if (it_l->allow_methods == UNKNOWN)
|
if (it_l->allow_methods == UNKNOWN)
|
||||||
it_l->allow_methods = ANY_METHODS;
|
it_l->allow_methods = ANY_METHODS;
|
||||||
|
|
||||||
if (it_l->index.empty())
|
if (it_l->index.empty() && it_l->autoindex == false)
|
||||||
it_l->index = it->index;
|
it_l->index = it->index;
|
||||||
|
|
||||||
// nothing to be done for cgi_ext, error_pages, redirect
|
// nothing to be done for cgi_ext, error_pages, redirect
|
||||||
|
|
||||||
if (path_is_valid(it_l->root + it_l->path) == 1 \
|
|
||||||
&& it_l->path[it_l->path.size() - 1] != '/')
|
|
||||||
it_l->path.push_back('/');
|
|
||||||
|
|
||||||
++it_l;
|
++it_l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it_l = it->locations.begin();
|
||||||
|
while (it_l != it->locations.end())
|
||||||
|
{
|
||||||
|
std::vector<LocationConfig>::const_iterator tmp = it_l + 1;
|
||||||
|
while (tmp != it->locations.end())
|
||||||
|
{
|
||||||
|
if (it_l->path == tmp->path)
|
||||||
|
throw std::invalid_argument("Duplicate locations in config file");
|
||||||
|
++tmp;
|
||||||
|
}
|
||||||
|
++it_l;
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(it->locations.begin(), it->locations.end());
|
std::sort(it->locations.begin(), it->locations.end());
|
||||||
std::reverse(it->locations.begin(), it->locations.end());
|
std::reverse(it->locations.begin(), it->locations.end());
|
||||||
|
|
||||||
@@ -70,19 +79,15 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
|
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations) const
|
||||||
{
|
{
|
||||||
std::vector<LocationConfig>::iterator it = locations.begin();
|
std::vector<LocationConfig>::const_iterator it = locations.begin();
|
||||||
|
|
||||||
while (it != locations.end())
|
while (it != locations.end())
|
||||||
{
|
{
|
||||||
if (it->path.compare("/") == 0)
|
if (it->path.compare("/") == 0)
|
||||||
{
|
|
||||||
// std::cout << "in compare: " << it->path << " -- ";
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,9 @@ int main(int ac, char **av)
|
|||||||
{
|
{
|
||||||
std::string config = (ac == 2 ? av[1] : "./default.config");
|
std::string config = (ac == 2 ? av[1] : "./default.config");
|
||||||
|
|
||||||
// like this just looks kinda gross, why bother creating an instance
|
|
||||||
// and not immediately parsing? like it servers no other purpose...
|
|
||||||
// what if i call parse directly in the constructor?
|
|
||||||
// oh because the constructor has no return, but still
|
|
||||||
// is there a better way?
|
|
||||||
|
|
||||||
ConfigParser configParser(config.c_str());
|
ConfigParser configParser(config);
|
||||||
|
// configParser.print_content();
|
||||||
configParser._print_content();
|
|
||||||
|
|
||||||
// i don't love that servers has to be a pointer...
|
// i don't love that servers has to be a pointer...
|
||||||
std::vector<ServerConfig>* servers = configParser.parse();
|
std::vector<ServerConfig>* servers = configParser.parse();
|
||||||
@@ -27,8 +21,9 @@ int main(int ac, char **av)
|
|||||||
// use an iterator you moron
|
// use an iterator you moron
|
||||||
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
|
||||||
{
|
{
|
||||||
// std::cout << it->server_name << " ";
|
(void)0;
|
||||||
it->print_all();
|
// std::cout << it->server_name << " ";
|
||||||
|
// it->print_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ std::vector<std::string>
|
|||||||
size_t end = 0;
|
size_t end = 0;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
while (end != std::string::npos)
|
while (end != NPOS)
|
||||||
{
|
{
|
||||||
end = input.find(delim, start);
|
end = input.find(delim, start);
|
||||||
len = end - start;
|
len = end - start;
|
||||||
if (end == std::string::npos)
|
if (end == NPOS)
|
||||||
len = end;
|
len = end;
|
||||||
tmp = input.substr(start, len);
|
tmp = input.substr(start, len);
|
||||||
if (ctrim != '\0')
|
if (ctrim != '\0')
|
||||||
@@ -60,13 +60,13 @@ std::string trim(std::string str, char del)
|
|||||||
|
|
||||||
// delete leadings del
|
// delete leadings del
|
||||||
pos = str.find_first_not_of(del);
|
pos = str.find_first_not_of(del);
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
pos = str.size();
|
pos = str.size();
|
||||||
str = str.substr(pos);
|
str = str.substr(pos);
|
||||||
|
|
||||||
// delete trailing del
|
// delete trailing del
|
||||||
pos = str.find_last_not_of(del);
|
pos = str.find_last_not_of(del);
|
||||||
if (pos != std::string::npos)
|
if (pos != NPOS)
|
||||||
str = str.substr(0, pos + 1);
|
str = str.substr(0, pos + 1);
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
@@ -110,7 +110,7 @@ bool isNumeric_btw(int low, int high, std::string str)
|
|||||||
if (std::isdigit(str[i]) == false)
|
if (std::isdigit(str[i]) == false)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int n = std::atoi(str.c_str());
|
int n = std::strtol(str.c_str(), NULL, 10);
|
||||||
if (n < low || n > high)
|
if (n < low || n > high)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -151,29 +151,40 @@ std::string http_methods_to_str(unsigned int methods)
|
|||||||
|
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
|
|
||||||
// you could make this &path...
|
file_type eval_file_type(const std::string &path)
|
||||||
int path_is_valid(std::string path)
|
|
||||||
{
|
{
|
||||||
const char *tmp_path = path.c_str();
|
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
if (stat(tmp_path, &s) == 0)
|
if (stat(path.c_str(), &s) != -1)
|
||||||
{
|
{
|
||||||
if (S_ISREG(s.st_mode))
|
if (S_ISREG(s.st_mode))
|
||||||
{
|
return (IS_FILE);
|
||||||
// std::cout << "is a file\n";
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
else if (S_ISDIR(s.st_mode))
|
else if (S_ISDIR(s.st_mode))
|
||||||
{
|
return (IS_DIR);
|
||||||
// std::cout << "is a Dir\n";
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// std::cout << "path is neither dir nor file\n";
|
else
|
||||||
return (0);
|
{
|
||||||
|
std::perror("err stat()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (IS_OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t eval_file_access(const 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 access permission
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
replace_all_substr(
|
replace_all_substr(
|
||||||
@@ -187,7 +198,7 @@ void
|
|||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
pos = str.find(ori_substr, pos);
|
pos = str.find(ori_substr, pos);
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
break;
|
break;
|
||||||
str.replace(pos, ori_substr.size(), new_substr);
|
str.replace(pos, ori_substr.size(), new_substr);
|
||||||
pos += new_substr.size();
|
pos += new_substr.size();
|
||||||
@@ -201,8 +212,8 @@ std::string str_tolower(std::string str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// identify a line in a string, by delim (ex. '\n')
|
// identify a line in a string, by delim (ex. '\n')
|
||||||
// delete this line from the string
|
// delete this line from the string (and the following nl sequence characters)
|
||||||
// and return the deleted line
|
// and return the deleted line (without the followinf nl sequence characters)
|
||||||
std::string
|
std::string
|
||||||
extract_line(std::string & str, size_t pos, std::string delim)
|
extract_line(std::string & str, size_t pos, std::string delim)
|
||||||
{
|
{
|
||||||
@@ -212,18 +223,18 @@ std::string
|
|||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
begin = str.rfind(delim, pos);
|
begin = str.rfind(delim, pos);
|
||||||
if (begin == std::string::npos)
|
if (begin == NPOS)
|
||||||
begin = 0;
|
begin = 0;
|
||||||
else
|
else if (begin < pos)
|
||||||
begin += delim.size();
|
begin += delim.size();
|
||||||
|
|
||||||
end = str.find(delim, pos);
|
end = str.find(delim, pos);
|
||||||
len = end;
|
len = end;
|
||||||
if (end != std::string::npos)
|
if (end != NPOS)
|
||||||
len = end - begin;
|
len = end - begin;
|
||||||
|
|
||||||
del_str = str.substr(begin, len);
|
del_str = str.substr(begin, len);
|
||||||
str.erase(begin, len);
|
str.erase(begin, len + delim.size());
|
||||||
return del_str;
|
return del_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +256,7 @@ size_t
|
|||||||
std::vector<std::string> list;
|
std::vector<std::string> list;
|
||||||
std::vector<std::string>::iterator it;
|
std::vector<std::string>::iterator it;
|
||||||
std::vector<std::string>::iterator it_end;
|
std::vector<std::string>::iterator it_end;
|
||||||
size_t err = 0;
|
size_t err_count = 0;
|
||||||
size_t pos;
|
size_t pos;
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string val;
|
std::string val;
|
||||||
@@ -256,15 +267,15 @@ size_t
|
|||||||
for (it = list.begin(); it != it_end; it++)
|
for (it = list.begin(); it != it_end; it++)
|
||||||
{
|
{
|
||||||
pos = (*it).find(':');
|
pos = (*it).find(':');
|
||||||
if (pos == std::string::npos)
|
if (pos == NPOS)
|
||||||
{
|
{
|
||||||
err++;
|
err_count++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
key = (*it).substr(0, pos);
|
key = (*it).substr(0, pos);
|
||||||
if ( key.find(' ') != std::string::npos )
|
if ( key.find(' ') != NPOS )
|
||||||
{
|
{
|
||||||
err++;
|
err_count++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// bad idea, in cgi we need to have the original value
|
// bad idea, in cgi we need to have the original value
|
||||||
@@ -273,7 +284,7 @@ size_t
|
|||||||
val = ::trim(val, ' ');
|
val = ::trim(val, ' ');
|
||||||
fields.insert( std::pair<std::string, std::string>(key, val) );
|
fields.insert( std::pair<std::string, std::string>(key, val) );
|
||||||
}
|
}
|
||||||
return err;
|
return err_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void str_map_key_tolower(std::map<std::string, std::string> & mp)
|
void str_map_key_tolower(std::map<std::string, std::string> & mp)
|
||||||
|
|||||||
@@ -6,16 +6,37 @@
|
|||||||
# include <map>
|
# include <map>
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <sstream>
|
# include <sstream>
|
||||||
# include <cstdlib> // atoi
|
# include <iostream>
|
||||||
|
# include <cstdlib> // strtol, strtoul
|
||||||
|
# include <climits> // LONG_MAX
|
||||||
|
# include <cerrno> // errno
|
||||||
# include <sys/stat.h> // stat()
|
# include <sys/stat.h> // stat()
|
||||||
# include <cctype> // tolower
|
# include <cctype> // tolower
|
||||||
# include <algorithm> // transform
|
# include <algorithm> // transform
|
||||||
# include <cstdio> // fflush
|
# include <cstdio> // perror, fflush
|
||||||
# include "colors.h"
|
# include <unistd.h> // close, access
|
||||||
|
# include "colors.h" // for debug print_special
|
||||||
|
|
||||||
# define CR "\r"
|
# define CR "\r"
|
||||||
# define LF "\n"
|
# define LF "\n"
|
||||||
# define CRLF CR LF
|
# define CRLF CR LF
|
||||||
|
# define CRLF_SIZE 2
|
||||||
|
# define NPOS std::string::npos
|
||||||
|
# define KB 1024
|
||||||
|
# define MB 1048576
|
||||||
|
|
||||||
|
/* Equivalent for end of http header size :
|
||||||
|
** std::string(CRLF CRLF).size();
|
||||||
|
** sizeof(CRLF CRLF) - 1;
|
||||||
|
** CRLF_SIZE*2
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum file_type
|
||||||
|
{
|
||||||
|
IS_OTHER,
|
||||||
|
IS_FILE,
|
||||||
|
IS_DIR
|
||||||
|
};
|
||||||
|
|
||||||
enum http_method
|
enum http_method
|
||||||
{
|
{
|
||||||
@@ -45,15 +66,42 @@ std::string itos(int n);
|
|||||||
std::string trim(std::string str, char del);
|
std::string trim(std::string str, char del);
|
||||||
http_method str_to_http_method(std::string &str);
|
http_method str_to_http_method(std::string &str);
|
||||||
std::string http_methods_to_str(unsigned int methods);
|
std::string http_methods_to_str(unsigned int methods);
|
||||||
int path_is_valid(std::string path);
|
file_type eval_file_type(const std::string &path);
|
||||||
|
size_t eval_file_access(const std::string &path, int mode);
|
||||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||||
std::string str_tolower(std::string str);
|
std::string str_tolower(std::string str);
|
||||||
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
||||||
std::string get_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 );
|
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 str_map_key_tolower(std::map<std::string, std::string> & mp);
|
||||||
void throw_test();
|
|
||||||
// debug
|
// debug
|
||||||
|
void throw_test();
|
||||||
void print_special(std::string str);
|
void print_special(std::string str);
|
||||||
|
|
||||||
|
|
||||||
|
/* Template */
|
||||||
|
|
||||||
|
template <typename T1, typename T2 >
|
||||||
|
void print_pair(const std::pair<T1,T2> p)
|
||||||
|
{
|
||||||
|
std::cout << p.first << ": ";
|
||||||
|
std::cout << p.second << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename T >
|
||||||
|
void print_map(const std::map<Key,T>& c)
|
||||||
|
{
|
||||||
|
typename std::map<Key,T>::const_iterator it = c.begin();
|
||||||
|
typename std::map<Key,T>::const_iterator it_end = c.end();
|
||||||
|
|
||||||
|
std::cout << " --print_map():\n";
|
||||||
|
std::cout << "map.size() = " << c.size() << "\n";
|
||||||
|
while (it != it_end)
|
||||||
|
{
|
||||||
|
print_pair(*it);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
std::cout << " --\n";
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,14 +23,16 @@
|
|||||||
# include <algorithm> // find
|
# include <algorithm> // find
|
||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <cstdio> // perror, remove
|
# include <cstdio> // perror, remove
|
||||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
# include <cstdlib> // strtol, strtoul
|
||||||
# include <dirent.h> // opendir()
|
# include <dirent.h> // opendir()
|
||||||
|
# include <locale> // isalpha, local
|
||||||
|
|
||||||
# include "Client.hpp"
|
# include "Client.hpp"
|
||||||
# include "ServerConfig.hpp"
|
# include "ServerConfig.hpp"
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
# include "http_status.hpp"
|
# include "http_status.hpp"
|
||||||
# include "autoindex.hpp"
|
# include "autoindex.hpp"
|
||||||
|
# include "colors.h"
|
||||||
|
|
||||||
extern bool g_run;
|
extern bool g_run;
|
||||||
extern int g_last_signal;
|
extern int g_last_signal;
|
||||||
@@ -69,60 +71,99 @@ class Webserv
|
|||||||
std::map<std::string, std::string> _mime_types;
|
std::map<std::string, std::string> _mime_types;
|
||||||
|
|
||||||
// accept.cpp
|
// accept.cpp
|
||||||
void _accept_connection(listen_socket &lsocket);
|
void _accept_connection(listen_socket &lsocket);
|
||||||
std::map<std::string, std::string>
|
std::map<std::string, std::string>
|
||||||
_extract_infos(struct sockaddr_in addr);
|
_extract_infos(struct sockaddr_in addr);
|
||||||
// request.cpp
|
// request.cpp
|
||||||
void _request(Client *client);
|
void _request(Client *client);
|
||||||
int _read_request(Client *client);
|
int _read_request(Client *client);
|
||||||
// response.cpp
|
// response.cpp
|
||||||
void _response(Client *client);
|
void _response(Client *client);
|
||||||
int _send_response(Client *client);
|
int _send_response(Client *client);
|
||||||
void _append_base_headers(Client *client);
|
void _append_base_headers(Client *client);
|
||||||
void _construct_response(Client *client);
|
void _construct_response(Client *client);
|
||||||
void _process_method(Client *client);
|
void _process_method(Client *client, std::string &path);
|
||||||
void _insert_status_line(Client *client);
|
std::string _replace_url_root(Client *client, std::string path);
|
||||||
void _error_html_response(Client *client);
|
void _insert_status_line(Client *client);
|
||||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
void _error_html_response(Client *client);
|
||||||
// ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||||
// 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
|
// method_get.cpp
|
||||||
void _get(Client *client);
|
void _get(Client *client, std::string &path);
|
||||||
void _get_file(Client *client, const std::string &path);
|
void _get_file(Client *client, const std::string &path);
|
||||||
void _autoindex(Client *client, std::string &path);
|
void _autoindex(Client *client, const std::string &path);
|
||||||
|
std::string _determine_file_extension(const std::string &path) const;
|
||||||
// method_post.cpp
|
// method_post.cpp
|
||||||
void _post(Client *client);
|
void _post(Client *client, const std::string &path);
|
||||||
void _post_file(Client *client, const std::string &path);
|
void _upload_files(Client *client);
|
||||||
// method_delete.cpp
|
// method_delete.cpp
|
||||||
void _delete(Client *client);
|
void _delete(Client *client, const std::string &path);
|
||||||
void _delete_file(Client *client, const std::string &path);
|
void _delete_file(Client *client, const std::string &path);
|
||||||
// cgi_script.cpp
|
// epoll_update.cpp
|
||||||
bool _is_cgi(Client *client);
|
int _epoll_update(int fd, uint32_t events, int op);
|
||||||
std::string _exec_cgi(Client *client);
|
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||||
char** _set_env(Client *client);
|
// signal.cpp
|
||||||
char* _dup_env(std::string var, std::string val);
|
void _handle_last_signal();
|
||||||
char* _dup_env(std::string var, int i);
|
// close.cpp
|
||||||
std::string _exec_script(Client *client, char **env);
|
void _close_client(int fd);
|
||||||
|
void _close_all_clients();
|
||||||
|
void _close_all_clients_fd();
|
||||||
|
void _close_all_clients_cgi_fd();
|
||||||
|
void _close_cgi_pipe_rfd(int fd);
|
||||||
|
void _close_all_cgi_pipe_rfd();
|
||||||
|
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();
|
||||||
|
// cgi.cpp
|
||||||
|
bool _is_cgi(Client *client, std::string path);
|
||||||
|
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
|
||||||
|
void _exec_cgi(Client *client);
|
||||||
|
void _set_env_vector(Client *client, std::vector<std::string> &env_vector);
|
||||||
|
void _set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector);
|
||||||
|
std::string _dup_env(std::string var, std::string val);
|
||||||
|
std::string _dup_env(std::string var, int i);
|
||||||
|
void _exec_script(Client *client, char *env[]);
|
||||||
void _check_script_output(Client *client, std::string & output);
|
void _check_script_output(Client *client, std::string & output);
|
||||||
void _check_script_status(Client *client, std::string & output);
|
void _check_script_status(Client *client, std::string & output);
|
||||||
void _check_script_fields(Client *client, std::string & output);
|
size_t _check_script_fields(const std::string & output, size_t status);
|
||||||
// epoll_update.cpp
|
void _check_fields_duplicates(Client *client, std::string & output);
|
||||||
int _epoll_update(int fd, uint32_t events, int op);
|
void _add_script_body_length_header(std::string & output);
|
||||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
void _remove_body_leading_empty_lines(std::string & output);
|
||||||
// signal.cpp
|
|
||||||
void _handle_last_signal();
|
Client *_find_cgi_fd(int cgi_fd);
|
||||||
// close.cpp
|
void _read_cgi_output(Client *client);
|
||||||
void _close_client(int fd);
|
void _handle_epoll_error_cgi_fd(uint32_t events, Client *client);
|
||||||
void _close_all_clients();
|
void _cgi_epollhup(uint32_t events, Client *client);
|
||||||
void _close_all_listen_sockets();
|
|
||||||
// init.cpp
|
|
||||||
void _bind(int socket_fd, in_port_t port, std::string host);
|
///////////////////////
|
||||||
void _listen(int socket_fd, unsigned int max_connections);
|
class ExecFail : public std::exception
|
||||||
void _init_http_status_map();
|
{
|
||||||
void _init_mime_types_map();
|
public :
|
||||||
|
virtual const char* what() const throw() {
|
||||||
|
return ("Exec CGI fail");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
HTTP Semantics:
|
||||||
|
https://www.rfc-editor.org/rfc/rfc9110.html
|
||||||
|
https://www.bortzmeyer.org/9110.html
|
||||||
|
https://www.bortzmeyer.org/cours-http-cnam.html
|
||||||
|
HTTP/1.1:
|
||||||
|
https://www.rfc-editor.org/rfc/rfc9112.html
|
||||||
|
https://www.bortzmeyer.org/9112.html
|
||||||
|
CGI:
|
||||||
|
https://www.rfc-editor.org/rfc/rfc3875.html
|
||||||
|
*/
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ void Webserv::_accept_connection(listen_socket &lsocket)
|
|||||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
infos = _extract_infos(addr);
|
infos = _extract_infos(addr);
|
||||||
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
Client new_client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||||
_clients.push_back(client);
|
_clients.push_back(new_client);
|
||||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
#ifndef AUTOINDEX_HPP
|
#ifndef AUTOINDEX_HPP
|
||||||
# define AUTOINDEX_HPP
|
# define AUTOINDEX_HPP
|
||||||
|
|
||||||
// # define HTML_ERROR(STATUS) "\r\n<!DOCTYPE html><html><head><title>"STATUS"</title></head><body><h1 style=\"text-align:center\">"STATUS"</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
|
||||||
|
|
||||||
# define AUTOINDEX_START \
|
# define AUTOINDEX_START \
|
||||||
"<!DOCTYPE html>"\
|
"<!DOCTYPE html>"\
|
||||||
"<html>"\
|
"<html>"\
|
||||||
@@ -27,6 +25,4 @@
|
|||||||
"</body>"\
|
"</body>"\
|
||||||
"</html>"
|
"</html>"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
387
srcs/webserv/cgi.cpp
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
|
||||||
|
#include "Webserv.hpp"
|
||||||
|
/*
|
||||||
|
CGI RFC:
|
||||||
|
https://www.rfc-editor.org/rfc/rfc3875.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Webserv::_is_cgi(Client *client, std::string path)
|
||||||
|
{
|
||||||
|
std::string script_path;
|
||||||
|
size_t file_type;
|
||||||
|
size_t file_mode = client->status;
|
||||||
|
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_access( script_path, X_OK );
|
||||||
|
if (!file_mode)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client->clear_script();
|
||||||
|
client->status = file_mode; // 404 not_found OR 403 forbidden
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v_ext;
|
||||||
|
std::vector<std::string>::const_iterator it;
|
||||||
|
std::vector<std::string>::const_iterator it_end;
|
||||||
|
size_t len;
|
||||||
|
std::locale loc; // for isalpha()
|
||||||
|
|
||||||
|
v_ext = client->assigned_location->cgi_ext;
|
||||||
|
if (v_ext.empty())
|
||||||
|
return NPOS;
|
||||||
|
it_end = client->assigned_location->cgi_ext.end();
|
||||||
|
while (pos < path.size())
|
||||||
|
{
|
||||||
|
if (path.compare(pos, 2, "./") == 0)
|
||||||
|
pos += 2;
|
||||||
|
pos = path.find('.', pos);
|
||||||
|
if (pos == NPOS)
|
||||||
|
return pos;
|
||||||
|
it = client->assigned_location->cgi_ext.begin();
|
||||||
|
for ( ; it != it_end; ++it)
|
||||||
|
{
|
||||||
|
len = (*it).size();
|
||||||
|
if (path.compare(pos + 1, len, *it) == 0)
|
||||||
|
if ( !std::isalpha(path[pos + 1 + len], loc) )
|
||||||
|
return pos + 1 + len;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return NPOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_exec_cgi(Client *client)
|
||||||
|
{
|
||||||
|
char* env_cstr[19] = {NULL};
|
||||||
|
std::vector<std::string> env_vector;
|
||||||
|
env_vector.reserve(18);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
_set_env_vector(client, env_vector);
|
||||||
|
try {
|
||||||
|
_set_env_cstr(env_cstr, env_vector);
|
||||||
|
_exec_script(client, env_cstr);
|
||||||
|
|
||||||
|
while (env_cstr[i] != NULL)
|
||||||
|
delete[] env_cstr[i++];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (const Webserv::ExecFail& e)
|
||||||
|
{
|
||||||
|
while (env_cstr[i] != NULL)
|
||||||
|
delete[] env_cstr[i++];
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Webserv::_dup_env(std::string var, std::string val = "")
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
|
||||||
|
str = var + "=" + val;
|
||||||
|
return (str);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Webserv::_dup_env(std::string var, int i)
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::string val;
|
||||||
|
|
||||||
|
val = ::itos(i);
|
||||||
|
str = var + "=" + val;
|
||||||
|
return (str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : verifier que les variables sont corrects
|
||||||
|
/*
|
||||||
|
https://www.rfc-editor.org/rfc/rfc3875#section-4.1
|
||||||
|
*/
|
||||||
|
void Webserv::_set_env_vector(Client *client, std::vector<std::string> &env_vector)
|
||||||
|
{
|
||||||
|
env_vector.push_back(_dup_env("AUTH_TYPE")); // authentification not supporte
|
||||||
|
env_vector.push_back(_dup_env("CONTENT_LENGTH" , client->get_rq_body().size()));
|
||||||
|
env_vector.push_back(_dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type")));
|
||||||
|
env_vector.push_back(_dup_env("GATEWAY_INTERFACE" , "CGI/1.1")); // https://www.rfc-editor.org/rfc/rfc3875#section-4.1.4
|
||||||
|
env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info())); // LUKE: To Check
|
||||||
|
env_vector.push_back(_dup_env("PATH_TRANSLATED")); // not supported // LUKE: Why not supported ?
|
||||||
|
env_vector.push_back(_dup_env("QUERY_STRING" , client->get_rq_query()));
|
||||||
|
env_vector.push_back(_dup_env("REMOTE_ADDR" , client->get_cl_ip()));
|
||||||
|
env_vector.push_back(_dup_env("REMOTE_HOST" , client->get_cl_ip())); // equal to REMOTE_ADDR or empty
|
||||||
|
env_vector.push_back(_dup_env("REMOTE_IDENT")); // authentification not supported
|
||||||
|
env_vector.push_back(_dup_env("REMOTE_USER")); // authentification not supported
|
||||||
|
env_vector.push_back(_dup_env("REQUEST_METHOD" , client->get_rq_method_str()));
|
||||||
|
env_vector.push_back(_dup_env("SCRIPT_NAME" , "/" + client->get_rq_script_path())); // LUKE: To Check
|
||||||
|
env_vector.push_back(_dup_env("SERVER_NAME" , client->get_cl_lsocket()->host));
|
||||||
|
env_vector.push_back(_dup_env("SERVER_PORT" , client->get_cl_lsocket()->port));
|
||||||
|
env_vector.push_back(_dup_env("SERVER_PROTOCOL" , "HTTP/1.1"));
|
||||||
|
env_vector.push_back(_dup_env("SERVER_SOFTWARE" , "Webserv/0.1"));
|
||||||
|
env_vector.push_back(_dup_env("REDIRECT_STATUS" , "200"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector)
|
||||||
|
{
|
||||||
|
std::vector<std::string>::const_iterator it = env_vector.begin();
|
||||||
|
std::vector<std::string>::const_iterator it_end = env_vector.end();
|
||||||
|
int i = 0;
|
||||||
|
while (it != it_end)
|
||||||
|
{
|
||||||
|
env_cstr[i] = new char[it->size()+1];
|
||||||
|
std::strcpy(env_cstr[i], it->c_str());
|
||||||
|
++it;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
env_cstr[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Webserv::_exec_script(Client *client, char *env[])
|
||||||
|
{
|
||||||
|
#define RD 0
|
||||||
|
#define WR 1
|
||||||
|
#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]
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
char * const nll[1] = {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::perror("err fork()");
|
||||||
|
else if (pid == 0) // child
|
||||||
|
{
|
||||||
|
std::signal(SIGPIPE, SIG_DFL);
|
||||||
|
std::signal(SIGINT, SIG_DFL);
|
||||||
|
|
||||||
|
_close_all_clients_fd();
|
||||||
|
::close(_epfd);
|
||||||
|
::close(FD_WR_TO_CHLD);
|
||||||
|
::close(FD_RD_FR_CHLD);
|
||||||
|
if (dup2(FD_RD_FR_PRNT, STDIN_FILENO) == -1)
|
||||||
|
{
|
||||||
|
std::perror("err dup2()");
|
||||||
|
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
|
||||||
|
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
|
||||||
|
throw ExecFail();
|
||||||
|
}
|
||||||
|
if (dup2(FD_WR_TO_PRNT, STDOUT_FILENO) == -1)
|
||||||
|
{
|
||||||
|
std::perror("err dup2()");
|
||||||
|
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
|
||||||
|
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
|
||||||
|
throw ExecFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
::close(FD_RD_FR_PRNT);
|
||||||
|
::close(FD_WR_TO_PRNT);
|
||||||
|
|
||||||
|
path = client->get_rq_script_path(); // Wut ? Only relative path ?
|
||||||
|
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
|
||||||
|
if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing
|
||||||
|
{
|
||||||
|
std::perror("err execve()");
|
||||||
|
::close(STDIN_FILENO); // Valgind debug, not essential
|
||||||
|
::close(STDOUT_FILENO); // Valgind debug, not essential
|
||||||
|
throw ExecFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //parent
|
||||||
|
{
|
||||||
|
::close(FD_RD_FR_PRNT);
|
||||||
|
::close(FD_WR_TO_PRNT);
|
||||||
|
::write(FD_WR_TO_CHLD, body.c_str(), body.size()); // move this before the fork ?
|
||||||
|
::close(FD_WR_TO_CHLD);
|
||||||
|
|
||||||
|
// add FD_RD_FR_CHLD to epoll,
|
||||||
|
_epoll_update(FD_RD_FR_CHLD, EPOLLIN, EPOLL_CTL_ADD);
|
||||||
|
// stop monitoring client->fd until the cgi-script as done is job
|
||||||
|
_epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL);
|
||||||
|
|
||||||
|
client->cgi_pipe_rfd = FD_RD_FR_CHLD;
|
||||||
|
client->cgi_pid = pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STATUS_500 std::string("Status: 500" CRLF CRLF);
|
||||||
|
|
||||||
|
void Webserv::_check_script_output(Client *client, std::string & output)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
pos = client->cgi_output.find(CRLF CRLF);
|
||||||
|
if (pos == 0 || pos == NPOS)
|
||||||
|
{
|
||||||
|
client->status = 500;;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_check_script_status(client, output);
|
||||||
|
if (client->status >= 400 && client->status < 600)
|
||||||
|
return;
|
||||||
|
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script status]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
client->status = _check_script_fields(output, client->status);
|
||||||
|
_check_fields_duplicates(client, output);
|
||||||
|
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script fields]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
_remove_body_leading_empty_lines(output);
|
||||||
|
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script empty lines]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
_add_script_body_length_header(output);
|
||||||
|
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script content length]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Webserv::_check_script_fields(const std::string & output, size_t status)
|
||||||
|
{
|
||||||
|
std::string headers;
|
||||||
|
std::string body;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
pos = output.find(CRLF CRLF);
|
||||||
|
if (pos == NPOS) // there is not empty line
|
||||||
|
return 500;
|
||||||
|
headers = output.substr(0, pos);
|
||||||
|
body = output.substr(pos + CRLF_SIZE * 2);
|
||||||
|
headers = str_tolower(headers);
|
||||||
|
pos = headers.find("content-type");
|
||||||
|
if (pos == NPOS) // there is no content-type field
|
||||||
|
{
|
||||||
|
if (!body.empty()) // there is body
|
||||||
|
return 500;
|
||||||
|
if (headers.find("location") == NPOS) // there is no location field
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
else if (headers.find("location") != NPOS) // there is a location field
|
||||||
|
{
|
||||||
|
if (body.empty()) // there is no body
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_check_fields_duplicates(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 * 2));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
|
||||||
|
|
||||||
bool Webserv::_is_cgi(Client *client)
|
|
||||||
{
|
|
||||||
// TODO see how it works with config
|
|
||||||
if (client->fill_script_path("/cgi-bin/php-cgi"))
|
|
||||||
return true;
|
|
||||||
if (client->fill_script_path("/cgi-bin/cgi_cpp.cgi"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Webserv::_exec_cgi(Client *client)
|
|
||||||
{
|
|
||||||
char** env;
|
|
||||||
std::string script_output;
|
|
||||||
|
|
||||||
env = _set_env(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 = "")
|
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
|
|
||||||
str = var + "=" + 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"); // 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Webserv::_exec_script(Client *client, char **env)
|
|
||||||
{
|
|
||||||
#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]
|
|
||||||
|
|
||||||
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];
|
|
||||||
int save_in = dup(STDIN_FILENO);
|
|
||||||
int save_out = dup(STDOUT_FILENO);
|
|
||||||
|
|
||||||
pipe(fd_in);
|
|
||||||
pipe(fd_out);
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
if (pid == -1)
|
|
||||||
std::cerr << "fork crashed" << std::endl;
|
|
||||||
else if (pid == 0)
|
|
||||||
{
|
|
||||||
close(FD_WR_TO_CHLD);
|
|
||||||
close(FD_RD_FR_CHLD);
|
|
||||||
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
|
||||||
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
|
||||||
// DEBUG
|
|
||||||
std::cerr << "execve:\n";
|
|
||||||
execve(client->get_rq_script_path().c_str(), nll, env);
|
|
||||||
// for tests execve crash :
|
|
||||||
//execve("wrong", nll, env);
|
|
||||||
std::cerr << "execve crashed.\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
dup2(save_in, STDIN_FILENO);
|
|
||||||
dup2(save_out, STDOUT_FILENO);
|
|
||||||
return script_output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Webserv::_check_script_output(Client *client, std::string & output)
|
|
||||||
{
|
|
||||||
_check_script_status(client, output);
|
|
||||||
_check_script_fields(client, output);
|
|
||||||
// _check_script_empty_lines(client, output);
|
|
||||||
// _check_script_space_colons(client, output);
|
|
||||||
// _check_script_new_lines(client, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Webserv::_check_script_status(Client *client, std::string & output)
|
|
||||||
{
|
|
||||||
size_t pos;
|
|
||||||
int status_pos;
|
|
||||||
|
|
||||||
pos = output.find("Status:");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
status_pos = pos + std::string("Status:").size();
|
|
||||||
client->status = atoi(output.c_str() + status_pos);
|
|
||||||
::extract_line(output, pos, CRLF);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
client->status = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Webserv::_check_script_fields(Client *client, std::string & output)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> srv_fld; // server_field
|
|
||||||
std::map<std::string, std::string> scr_fld; // script_field
|
|
||||||
std::map<std::string, std::string>::iterator it_srv;
|
|
||||||
std::map<std::string, std::string>::iterator it_scr;
|
|
||||||
std::string tmp;
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
// put server headers in map
|
|
||||||
tmp = client->response;
|
|
||||||
pos = tmp.find(CRLF CRLF);
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
tmp.erase(pos);
|
|
||||||
::parse_http_headers(tmp, srv_fld);
|
|
||||||
// put script headers in map
|
|
||||||
tmp = output;
|
|
||||||
pos = tmp.find(CRLF CRLF);
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
tmp.erase(pos);
|
|
||||||
::parse_http_headers(tmp, scr_fld);
|
|
||||||
// compare both map to supress duplicates
|
|
||||||
for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++)
|
|
||||||
{
|
|
||||||
for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++)
|
|
||||||
{
|
|
||||||
if (str_tolower(it_srv->first) == str_tolower(it_scr->first))
|
|
||||||
{
|
|
||||||
pos = client->response.find(it_srv->first);
|
|
||||||
::extract_line(client->response, pos, CRLF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
void Webserv::_close_client(int fd)
|
void Webserv::_close_client(int fd)
|
||||||
{
|
{
|
||||||
std::vector<Client>::iterator it = _clients.begin();
|
std::vector<Client>::iterator it = _clients.begin();
|
||||||
while (it != _clients.end())
|
std::vector<Client>::iterator it_end = _clients.end();
|
||||||
|
while (it != it_end)
|
||||||
{
|
{
|
||||||
if (*it == fd)
|
if (*it == fd)
|
||||||
{
|
{
|
||||||
@@ -21,18 +22,45 @@ void Webserv::_close_client(int fd)
|
|||||||
|
|
||||||
void Webserv::_close_all_clients()
|
void Webserv::_close_all_clients()
|
||||||
{
|
{
|
||||||
while (!_clients.empty())
|
_close_all_clients_fd();
|
||||||
|
_clients.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_close_all_clients_fd()
|
||||||
|
{
|
||||||
|
_close_all_clients_cgi_fd();
|
||||||
|
std::vector<Client>::iterator it = _clients.begin();
|
||||||
|
std::vector<Client>::iterator it_end = _clients.end();
|
||||||
|
while (it != it_end)
|
||||||
{
|
{
|
||||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
std::cerr << "close fd " << it->get_cl_fd() << "\n";
|
||||||
if (::close(_clients.back().get_cl_fd()) == -1)
|
if (::close(it->get_cl_fd()) == -1)
|
||||||
std::perror("err close()");
|
std::perror("err close()");
|
||||||
_clients.pop_back();
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_close_all_listen_sockets()
|
void Webserv::_close_all_clients_cgi_fd()
|
||||||
{
|
{
|
||||||
|
std::vector<Client>::iterator it = _clients.begin();
|
||||||
|
std::vector<Client>::iterator it_end = _clients.end();
|
||||||
|
while (it != it_end)
|
||||||
|
{
|
||||||
|
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||||
|
if (it->cgi_pipe_rfd)
|
||||||
|
{
|
||||||
|
std::cerr << "close cgi-fd " << it->cgi_pipe_rfd << "\n";
|
||||||
|
if (::close(it->cgi_pipe_rfd) == -1)
|
||||||
|
std::perror("err close()");
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Webserv::_close_all_listen_sockets()
|
||||||
|
{ // TODO : change like clients (clear in place of pop_back)
|
||||||
while (!_listen_sockets.empty())
|
while (!_listen_sockets.empty())
|
||||||
{
|
{
|
||||||
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||||
@@ -42,3 +70,74 @@ void Webserv::_close_all_listen_sockets()
|
|||||||
_listen_sockets.pop_back();
|
_listen_sockets.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** Many common code with init_virtual_servers(). Could refactor it.
|
||||||
|
*/
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
std::cerr << "close lsocket " << it->fd << "\n";
|
||||||
|
if (::close(it->fd) == -1)
|
||||||
|
std::perror("err close()");
|
||||||
|
std::cerr << "try to reopen lsocket\n";
|
||||||
|
ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||||
|
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
|
||||||
|
if (std::find(_clients.begin(), _clients.end(), fd) != _clients.end())
|
||||||
|
std::cerr << "Found the client in _clients" << "\n"; // DEBUG
|
||||||
|
|
||||||
|
_close_client(fd); // " EPOLLHUP on client fd 8 " en boucle quand on mattraque un peu les CGI, donc il ne le trouve pas dans _clients. Etrange.
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#ifndef HTTP_STATUS_HPP
|
#ifndef HTTP_STATUS_HPP
|
||||||
# define HTTP_STATUS_HPP
|
# define HTTP_STATUS_HPP
|
||||||
|
|
||||||
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
/*
|
||||||
|
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 :
|
First version of macro HTML_ERROR(STATUS) dont work with call like this :
|
||||||
@@ -41,7 +43,9 @@
|
|||||||
# define S403 "403 Forbidden"
|
# define S403 "403 Forbidden"
|
||||||
# define S404 "404 Not Found"
|
# define S404 "404 Not Found"
|
||||||
# define S405 "405 Method Not Allowed"
|
# define S405 "405 Method Not Allowed"
|
||||||
|
# define S408 "408 Request Timeout"
|
||||||
# define S413 "413 Content Too Large"
|
# define S413 "413 Content Too Large"
|
||||||
|
# define S415 "415 Unsupported Media Type"
|
||||||
|
|
||||||
# define S500 "500 Internal Server Error"
|
# define S500 "500 Internal Server Error"
|
||||||
# define S501 "501 Not Implemented"
|
# define S501 "501 Not Implemented"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ?
|
ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
std::perror("err socket()");
|
std::perror("err socket()");
|
||||||
@@ -48,8 +48,8 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
|||||||
//
|
//
|
||||||
// HUGO ADD END
|
// HUGO ADD END
|
||||||
|
|
||||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
|
||||||
_listen(new_socket.fd, 512); // 512 arbitrary
|
_listen(new_socket.fd, 42); // 42 arbitrary
|
||||||
|
|
||||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||||
throw std::runtime_error("Socket init");
|
throw std::runtime_error("Socket init");
|
||||||
@@ -89,7 +89,7 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
|||||||
|
|
||||||
void Webserv::_init_http_status_map()
|
void Webserv::_init_http_status_map()
|
||||||
{
|
{
|
||||||
/* "map.insert()" over "map.operator[]" :
|
/* "map.insert()" more appropriate than "map.operator[]" :
|
||||||
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||||
*/
|
*/
|
||||||
typedef std::map<int, std::string>::value_type status_pair;
|
typedef std::map<int, std::string>::value_type status_pair;
|
||||||
@@ -110,10 +110,13 @@ void Webserv::_init_http_status_map()
|
|||||||
_http_status.insert(status_pair(403, S403));
|
_http_status.insert(status_pair(403, S403));
|
||||||
_http_status.insert(status_pair(404, S404));
|
_http_status.insert(status_pair(404, S404));
|
||||||
_http_status.insert(status_pair(405, S405));
|
_http_status.insert(status_pair(405, S405));
|
||||||
|
_http_status.insert(status_pair(408, S408));
|
||||||
_http_status.insert(status_pair(413, S413));
|
_http_status.insert(status_pair(413, S413));
|
||||||
|
_http_status.insert(status_pair(415, S415));
|
||||||
|
|
||||||
_http_status.insert(status_pair(500, S500));
|
_http_status.insert(status_pair(500, S500));
|
||||||
_http_status.insert(status_pair(501, S501));
|
_http_status.insert(status_pair(501, S501));
|
||||||
|
_http_status.insert(status_pair(505, S505));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_init_mime_types_map()
|
void Webserv::_init_mime_types_map()
|
||||||
|
|||||||
@@ -1,40 +1,31 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
#include "Webserv.hpp"
|
||||||
|
|
||||||
void Webserv::_delete(Client *client)
|
void Webserv::_delete(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_rq_abs_path();
|
|
||||||
path.insert(0, client->assigned_location->root);
|
|
||||||
|
|
||||||
/* CGI Here ? */
|
|
||||||
|
|
||||||
_delete_file(client, path);
|
_delete_file(client, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
std::cout << "_delete_file()\n";
|
||||||
{
|
client->status = ::eval_file_access(path, W_OK);
|
||||||
std::perror("err access()");
|
if (client->status)
|
||||||
client->status = 404;
|
return;
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(path.c_str(), W_OK) == -1)
|
if (std::remove(path.c_str()) == -1)
|
||||||
{
|
|
||||||
std::perror("err access()");
|
|
||||||
client->status = 403;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remove(path.c_str()) == -1)
|
|
||||||
{
|
{
|
||||||
std::perror("err remove()");
|
std::perror("err remove()");
|
||||||
client->status = 500;
|
if (errno == ENOTEMPTY || errno == EEXIST)
|
||||||
|
client->status = 403;
|
||||||
|
else
|
||||||
|
client->status = 500;
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->status = 204;
|
||||||
|
client->response.append(CRLF);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +1,54 @@
|
|||||||
|
|
||||||
#include "Webserv.hpp"
|
#include "Webserv.hpp"
|
||||||
|
#define MAX_FILESIZE 1 * MB // unused
|
||||||
|
|
||||||
void Webserv::_get(Client *client)
|
// const?
|
||||||
{
|
/*
|
||||||
/* RULES **
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-get
|
||||||
|
|
||||||
if path is a valid dir check if index is specified and serve that
|
|
||||||
if no index and autoindex, server that
|
|
||||||
if file, server that!
|
|
||||||
|
|
||||||
Where does cgi fit in in all this ???
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_rq_abs_path();
|
void Webserv::_get(Client *client, std::string &path)
|
||||||
|
{
|
||||||
// this might not be the best thing, a voir
|
std::cout << "_get()\n";
|
||||||
path.insert(0, client->assigned_location->root);
|
if (eval_file_type(path) == IS_DIR)
|
||||||
|
|
||||||
std::cerr << "path = " << path << "\n";
|
|
||||||
|
|
||||||
// path = root + location.path
|
|
||||||
// we will tack on an index if there is a valid one
|
|
||||||
// or autoindex if allowed
|
|
||||||
// or let _get_file sort out the error otherwise.
|
|
||||||
|
|
||||||
if (path_is_valid(path) == 1)
|
|
||||||
{
|
{
|
||||||
// std::cout << "path is valid\n";
|
|
||||||
if (path[path.size() - 1] != '/')
|
if (path[path.size() - 1] != '/')
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||||
{
|
{
|
||||||
// std::cout << "location path: " << client->assigned_location->path << '\n';
|
if (eval_file_type(path + client->assigned_location->index[i]) == IS_FILE)
|
||||||
// std::cout << "location index: " << client->assigned_location->index[i] << '\n';
|
|
||||||
// std::cout << "path with index: " << path + assigned_location->index[i] << '\n';
|
|
||||||
if (path_is_valid(path + client->assigned_location->index[i]) == 2)
|
|
||||||
{
|
{
|
||||||
// std::cout << "found a valid index\n";
|
|
||||||
path.append(client->assigned_location->index[i]);
|
path.append(client->assigned_location->index[i]);
|
||||||
_get_file(client, path);
|
_get_file(client, path);
|
||||||
|
std::cerr << "Added an index\n"; //debug
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (client->assigned_location->autoindex == true)
|
if (client->assigned_location->autoindex == true)
|
||||||
{
|
|
||||||
_autoindex(client, path);
|
_autoindex(client, path);
|
||||||
return ;
|
else
|
||||||
}
|
client->status = 404;
|
||||||
}
|
}
|
||||||
// else
|
else
|
||||||
// _get_file(client, path);
|
_get_file(client, path);
|
||||||
// what about cgi ???
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TMP HUGO
|
|
||||||
//
|
|
||||||
std::string script_output;
|
|
||||||
if (_is_cgi(client))
|
|
||||||
{
|
|
||||||
script_output = _exec_cgi(client);
|
|
||||||
_check_script_output(client, script_output);
|
|
||||||
client->response += script_output;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// END TMP HUGO
|
|
||||||
|
|
||||||
_get_file(client, path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
|
||||||
void Webserv::_get_file(Client *client, const std::string &path)
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
std::ios::binary
|
std::ios::binary
|
||||||
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.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.
|
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
|
||||||
*/
|
*/
|
||||||
|
void Webserv::_get_file(Client *client, const std::string &path)
|
||||||
|
{
|
||||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
|
|
||||||
std::cout << "made it to get_file\n";
|
std::cout << "_get_file()\n";
|
||||||
|
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
client->status = ::eval_file_access(path, R_OK);
|
||||||
{
|
if (client->status)
|
||||||
std::perror("err access()");
|
return;
|
||||||
client->status = 404;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(path.c_str(), R_OK) == -1)
|
ifd.open(path.c_str());
|
||||||
{
|
|
||||||
std::perror("err access()");
|
|
||||||
client->status = 403;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
ifd.open(path.c_str(), std::ios::ate);
|
|
||||||
if (!ifd)
|
if (!ifd)
|
||||||
{
|
{
|
||||||
std::cerr << path << ": ifd.open fail" << '\n';
|
std::cerr << path << ": ifd.open fail" << '\n';
|
||||||
@@ -106,17 +56,6 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
|||||||
}
|
}
|
||||||
else
|
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();
|
buf << ifd.rdbuf();
|
||||||
if (!ifd || !buf)
|
if (!ifd || !buf)
|
||||||
{
|
{
|
||||||
@@ -132,41 +71,51 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// i only sort of need &path...
|
/*
|
||||||
// def can improve but works for now...
|
// WIP Low priority : Chunk or not chunk (if filesize > MAX_FILESIZE)
|
||||||
//void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &path)
|
// WITH flag "std::ios::ate" for open()
|
||||||
void Webserv::_autoindex(Client *client, std::string &path)
|
std::streampos size = ifd.tellg();
|
||||||
|
ifd.seekg(0, std::ios::beg);
|
||||||
|
if (size > MAX_FILESIZE)
|
||||||
{
|
{
|
||||||
// std::cout << "made it to _autoindex\n";
|
// Chunked GET here
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// const?
|
||||||
|
void Webserv::_autoindex(Client *client, const std::string &path)
|
||||||
|
{
|
||||||
|
std::cout << "_autoindex()\n";
|
||||||
|
|
||||||
(void)path;
|
|
||||||
std::string dir_list;
|
std::string dir_list;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
|
|
||||||
// std::cout << "location root: " << client->assigned_location->root << " location path: "
|
// std::cout << "Path in auto is: " << path << '\n';
|
||||||
// << client->assigned_location->path << '\n';
|
if ( (dir = opendir(path.c_str()) ) != NULL)
|
||||||
|
|
||||||
// if ((dir = opendir (path.c_str())) != NULL)
|
|
||||||
if ((dir = opendir ((client->assigned_location->root + client->assigned_location->path).c_str())) != NULL)
|
|
||||||
{
|
{
|
||||||
/* print all the files and directories within directory */
|
|
||||||
dir_list.append(AUTOINDEX_START);
|
dir_list.append(AUTOINDEX_START);
|
||||||
dir_list.append(client->assigned_location->path);
|
dir_list.append(path);
|
||||||
dir_list.append(AUTOINDEX_MID1);
|
dir_list.append(AUTOINDEX_MID1);
|
||||||
dir_list.append(client->assigned_location->path);
|
dir_list.append(path);
|
||||||
dir_list.append(AUTOINDEX_MID2);
|
dir_list.append(AUTOINDEX_MID2);
|
||||||
while ((ent = readdir (dir)) != NULL)
|
while ((ent = readdir (dir)) != NULL)
|
||||||
{
|
{
|
||||||
if (strcmp(".", ent->d_name) == 0)
|
if (strcmp(".", ent->d_name) == 0)
|
||||||
continue ;
|
continue ;
|
||||||
dir_list.append("<a href=\"");
|
dir_list.append("<a style=\"font-size:1.5em\" href=\"");
|
||||||
dir_list.append(client->assigned_location->path.c_str());
|
dir_list.append(client->get_rq_abs_path());
|
||||||
|
if (dir_list[dir_list.size() - 1] != '/')
|
||||||
|
dir_list.push_back('/');
|
||||||
dir_list.append(ent->d_name);
|
dir_list.append(ent->d_name);
|
||||||
dir_list.append("\">");
|
dir_list.append("\">");
|
||||||
dir_list.append(ent->d_name);
|
dir_list.append(ent->d_name);
|
||||||
|
if (ent->d_type == DT_DIR)
|
||||||
|
dir_list.append("/");
|
||||||
dir_list.append("</a>");
|
dir_list.append("</a>");
|
||||||
dir_list.append("\r\n"); // is this right?
|
dir_list.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// <a href="http://nginx.org/">nginx.org</a>.<br/>
|
// <a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||||
@@ -176,16 +125,22 @@ void Webserv::_autoindex(Client *client, std::string &path)
|
|||||||
// <a href="/test/test_deeper/..">..</a>
|
// <a href="/test/test_deeper/..">..</a>
|
||||||
|
|
||||||
dir_list.append(AUTOINDEX_END);
|
dir_list.append(AUTOINDEX_END);
|
||||||
// std::cout << "\n\n" << dir_list << '\n';
|
|
||||||
closedir (dir);
|
closedir (dir);
|
||||||
|
client->status = 200;
|
||||||
_append_body(client, dir_list, "html");
|
_append_body(client, dir_list, "html");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// in theory not possible cuz we already checked...
|
client->status = 500;
|
||||||
/* could not open directory */
|
perror("could not open dir");
|
||||||
// perror ("");
|
|
||||||
std::cout << "could not open dir\n";
|
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(""));
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,62 +2,133 @@
|
|||||||
#include "Webserv.hpp"
|
#include "Webserv.hpp"
|
||||||
|
|
||||||
|
|
||||||
void Webserv::_post(Client *client)
|
void Webserv::_post(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||||
*/
|
*/
|
||||||
std::string path = client->get_rq_abs_path();
|
(void)path; // unused, we use "assigned_location->upload_dir" instead
|
||||||
path.insert(0, client->assigned_location->root);
|
std::cout << "_post()\n";
|
||||||
|
std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n";
|
||||||
|
|
||||||
/* CGI Here ? */
|
|
||||||
|
|
||||||
_post_file(client, path);
|
if (client->get_rq_abs_path() != client->assigned_location->path)
|
||||||
|
client->status = 404; // 404 ? J'ai un doute.
|
||||||
|
else if (client->assigned_location->upload_dir.empty())
|
||||||
|
client->status = 404; // 404 ? J'ai un doute.
|
||||||
|
else if (client->get_rq_multi_bodys().empty())
|
||||||
|
{
|
||||||
|
client->status = 415;
|
||||||
|
client->response.append("Accept: multipart/form-data"); // empty, no encoding accepted
|
||||||
|
client->response.append(CRLF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_upload_files(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webserv::_post_file(Client *client, const std::string &path)
|
#define DEFAULT_NAME "unnamed_file"
|
||||||
|
// TODO : Loop for multi body
|
||||||
|
void Webserv::_upload_files(Client *client)
|
||||||
{
|
{
|
||||||
|
std::cout << "_upload_files()\n";
|
||||||
std::ofstream ofd;
|
std::ofstream ofd;
|
||||||
|
std::vector<MultipartBody>::const_iterator body_it = client->get_rq_multi_bodys().begin();
|
||||||
|
std::string path;
|
||||||
|
std::string filename;
|
||||||
|
size_t pos;
|
||||||
|
bool file_existed = false;
|
||||||
|
|
||||||
bool file_existed;
|
client->status = ::eval_file_access(client->assigned_location->upload_dir, W_OK);
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
if (client->status)
|
||||||
file_existed = false;
|
return;
|
||||||
else
|
|
||||||
file_existed = true;
|
|
||||||
|
|
||||||
// How to determine status 403 for file that dont already exist ?
|
while (body_it != client->get_rq_multi_bodys().end())
|
||||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
|
||||||
{
|
{
|
||||||
std::perror("err access()");
|
if (body_it->body.empty())
|
||||||
client->status = 403;
|
{
|
||||||
return ;
|
++body_it;
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
// Content-Disposition: form-data; name="upload_file"; filename="camion.jpg"
|
||||||
|
::print_map(body_it->headers);
|
||||||
|
filename = client->get_rq_multi_bodys_headers("Content-Disposition", body_it);
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
pos = filename.find("filename=");
|
||||||
|
if (pos != NPOS)
|
||||||
|
{
|
||||||
|
filename = filename.substr(pos + sizeof("filename=")-1);
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
// A l'arrache pour enlever les "
|
||||||
|
filename.erase(0, 1);
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
filename.erase(filename.size()-1, 1);
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
if (filename.empty())
|
||||||
|
filename = DEFAULT_NAME;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filename = DEFAULT_NAME;
|
||||||
|
}
|
||||||
|
std::cerr << "filename ="<< filename << "\n";
|
||||||
|
path = client->assigned_location->upload_dir; // Assume there a final '/'
|
||||||
|
path.append(filename);
|
||||||
|
|
||||||
ofd.open(path.c_str(), std::ios::trunc);
|
|
||||||
if (!ofd)
|
if (::access(path.c_str(), F_OK) == -1)
|
||||||
{
|
file_existed = false;
|
||||||
std::cerr << path << ": ofd.open fail" << '\n';
|
else
|
||||||
client->status = 500;
|
file_existed = true;
|
||||||
}
|
|
||||||
else
|
// How to determine status 403 for file that dont already exist ? access() on the upload_dir ?
|
||||||
{
|
if (file_existed && ::access(path.c_str(), W_OK) == -1)
|
||||||
// Content-Length useless at this point ?
|
{
|
||||||
ofd << client->get_rq_body();
|
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;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ofd << body_it->body;
|
||||||
if (!ofd)
|
if (!ofd)
|
||||||
{
|
{
|
||||||
std::cerr << path << ": ofd.write fail" << '\n';
|
std::cerr << path << ": ofd.write fail" << '\n';
|
||||||
client->status = 500;
|
client->status = 500;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (file_existed)
|
++body_it;
|
||||||
{
|
ofd.close();
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->status = 204;
|
||||||
|
client->response.append(CRLF);
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (file_existed) // with multi body it doesn't make much sense
|
||||||
|
{
|
||||||
|
client->status = 200;
|
||||||
|
client->response.append("Location: ");
|
||||||
|
client->response.append("/index.html"); // WIP
|
||||||
|
client->response.append(CRLF);
|
||||||
|
client->response.append(CRLF);
|
||||||
|
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client->status = 201;
|
||||||
|
client->response.append("Location: ");
|
||||||
|
client->response.append("/index.html"); // WIP
|
||||||
|
client->response.append(CRLF);
|
||||||
|
client->response.append(CRLF);
|
||||||
|
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
#include "parsing_message_http.hpp"
|
|
||||||
|
|
||||||
std::string
|
|
||||||
parse_http_body(std::string message)
|
|
||||||
{
|
|
||||||
std::string body;
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
pos = message.find(CRLF CRLF);
|
|
||||||
pos += std::string(CRLF CRLF).size();
|
|
||||||
// TODO: copying just like that might fail in case of binary or images
|
|
||||||
body = message.substr(pos);
|
|
||||||
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||