Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a548e314 | ||
|
|
3dad938e3c | ||
|
|
ae9a9b37f1 | ||
|
|
e1a68bc3e4 | ||
|
|
f10931042f | ||
|
|
972f52ebc8 | ||
|
|
da1f4b6e37 | ||
|
|
78c3ffa456 | ||
|
|
988e15c738 | ||
|
|
63365a9067 | ||
|
|
7ecfc22c7b | ||
|
|
e0fd743b5b | ||
|
|
63ff42b57d | ||
|
|
08928c46fc | ||
|
|
6f171ec07b | ||
|
|
b0df37df60 | ||
|
|
05c7faf1f3 | ||
|
|
79bdc1ecbf | ||
|
|
914d4cb0c0 | ||
|
|
e596d8f093 | ||
|
|
3102253092 | ||
|
|
a9ada4cb28 | ||
|
|
0026106bf6 | ||
|
|
6f5b28dd93 | ||
|
|
164f717e00 | ||
|
|
83536ee8ad | ||
|
|
6791b13a52 | ||
|
|
21283e2493 | ||
|
|
6c0dd771c3 | ||
|
|
c331b69600 | ||
|
|
41b5e57800 | ||
|
|
d79281751a | ||
|
|
8f167d85f3 | ||
|
|
b30a3a1407 | ||
|
|
5aabeb6b46 | ||
|
|
f252887d53 |
BIN
42_testers/cgi_tester
Executable file
BIN
42_testers/cgi_tester
Executable file
Binary file not shown.
BIN
42_testers/tester
Executable file
BIN
42_testers/tester
Executable file
Binary file not shown.
23
Makefile
23
Makefile
@@ -3,28 +3,22 @@ NAME = webserv
|
||||
CXX = c++
|
||||
|
||||
CXXFLAGS = -Wall -Wextra #-Werror
|
||||
CXXFLAGS += $(HEADERS_I)
|
||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||
CXXFLAGS += -std=c++98
|
||||
CXXFLAGS += -g
|
||||
CXXFLAGS += -g3
|
||||
CXXFLAGS += -MMD -MP #header dependencie
|
||||
#CXXFLAGS += -O3
|
||||
|
||||
#SHELL = /bin/zsh
|
||||
VPATH = $(SRCS_D)
|
||||
|
||||
HEADERS_I = $(HEADERS_D:%=-I%)
|
||||
HEADERS_D = srcs \
|
||||
headers
|
||||
HEADERS = Webserv.hpp \
|
||||
ConfigParser.hpp \
|
||||
ServerConfig.hpp \
|
||||
LocationConfig.hpp \
|
||||
Client.hpp \
|
||||
MethodType.hpp \
|
||||
utils.hpp \
|
||||
srcs/webserv \
|
||||
srcs/config
|
||||
|
||||
SRCS_D = srcs \
|
||||
srcs/webserv
|
||||
srcs/webserv \
|
||||
srcs/config
|
||||
|
||||
SRCS = main.cpp \
|
||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||
accept.cpp request.cpp response.cpp \
|
||||
@@ -33,6 +27,9 @@ SRCS = main.cpp \
|
||||
ConfigParserUtils.cpp \
|
||||
ConfigParserPost.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
parsing_message_http.cpp \
|
||||
|
||||
OBJS_D = builds
|
||||
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
|
||||
|
||||
322
README.md
Normal file
322
README.md
Normal file
@@ -0,0 +1,322 @@
|
||||
|
||||
## work together
|
||||
|
||||
#### message
|
||||
- err access line response.cpp:169
|
||||
- g pas fini la gestion de sortie du cgi (comparaison des headers fields pour supprimer les doublons)
|
||||
- g rendu independantes les fonctions qui parses un message http (requete ou response) dans `parsing_http_message.cpp`
|
||||
- il y a une erreur importante dans la fonction qui parse les headers d'un message http : ca ne verifie pas si la premier ligne est un header ou la "first line"
|
||||
|
||||
#### TODO
|
||||
- `_is_cgi()` and `_fill_cgi_path()`
|
||||
- `_cgi_output()` change status in `client->status`
|
||||
- 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
|
||||
|
||||
#### 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
|
||||
|
||||
#### questions
|
||||
- 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 ?
|
||||
|
||||
#### notifications
|
||||
- i changed the Client getters in two categories :
|
||||
- getters for requests infos : `get_rq_<info>`
|
||||
- getters for client sides infos : `get_cl_<info>` (such as ip of client)
|
||||
- i changed the variables in request struct in Client :
|
||||
- `path` become `uri` (ex. `/path/to/file?var=val`)
|
||||
- add `abs_path` (ex. `/path/to/file` )
|
||||
- add `query` (ex. `var=val`)
|
||||
- the header fields names, as key in map, are stored in lowercase, and getters are case-insensitives
|
||||
|
||||
|
||||
respsonse.cpp
|
||||
```
|
||||
_response()
|
||||
{
|
||||
_determine_process_server()
|
||||
_send_response()
|
||||
{
|
||||
_append_base_headers()
|
||||
_construct_response()
|
||||
{
|
||||
_process_method()
|
||||
{
|
||||
_get()
|
||||
{
|
||||
_exec_cgi()
|
||||
}
|
||||
}
|
||||
_insert_status_line()
|
||||
::send(headers)
|
||||
::send(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## man
|
||||
|
||||
- **htons, htonl, ntohs, ntohl :** converts the unsigned short or integer argument between host byte order and network byte order
|
||||
- **poll :** waits for one of a set of file descriptors to become ready to perform I/O
|
||||
- alternatives : select, epoll (epoll_create, epoll_ctl, epoll_wait), kqueue (kqueue, kevent)
|
||||
- **socket :** creates an endpoint for communication and returns a file descriptor that refers to that endpoint
|
||||
- **listen :** marks a socket as a passive socket, that is, as a socket that will be used to accept incoming connection requests using accept()
|
||||
- **accept :** used with connection-based socket types. It extracts the first connection request on the queue of pending connections for the listening socket, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket is unaffected by this call
|
||||
- **send :** (~write) used to transmit a message to another socket. May be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write() is the presence of flags. With a zero flags argument, send() is equivalent to write()
|
||||
- **recv :** (~read) used to receive messages from a socket. May be used to receive data on both connectionless and connection-oriented sockets. The only difference between recv() and read() is the presence of flags. With a zero flags argument, recv() is generally equivalent to read()
|
||||
- **bind :** associate a socket fd to a local address. When a socket is created with socket(), it exists in a name space (address family) but has no address assigned to it. It is normally necessary to assign a local address using bind() before a socket may receive connections (see accept())
|
||||
- **connect :** connects a socket fd to a remote address
|
||||
- **inet_addr :** converts the Internet host address cp from IPv4 numbers-and-dots notation into binary data in network byte order. Use of this function is problematic because in case of error it returns -1, wich is a valid address (255.255.255.255). Avoid its use in favor of inet_aton(), inet_pton(), or getaddrinfo()
|
||||
- **setsockopt :** manipulate options for a socket fd. Options may exist at multiple protocol levels; they are always present at the uppermost socket level
|
||||
- **getsockname :** returns the current address to which a socket fd is bound
|
||||
- **fcntl :** manipulate an open fd, by performing some actions, like duplicate it or changing its flags
|
||||
|
||||
---
|
||||
## todo
|
||||
|
||||
- [ ] read the RFC and do some tests with telnet and NGINX
|
||||
#### parsing config
|
||||
- [ ] Your program has to take a configuration file as argument, or use a default path.
|
||||
- [ ] Choose the port and host of each ’server’.
|
||||
- [ ] Setup the server_names or not.
|
||||
- [ ] The first server for a host:port will be the default for this host:port (that means it will answer to all the requests that don’t belong to an other server).
|
||||
- [ ] Setup default error pages.
|
||||
- [ ] Limit client body size.
|
||||
- [ ] Setup routes with one or multiple of the following rules/configuration (routes wont be using regexp):
|
||||
- [ ] Define a list of accepted HTTP methods for the route.
|
||||
- [ ] Define a HTTP redirection.
|
||||
- [ ] Define a directory or a file from where the file should be searched (for example, if url /kapouet is rooted to /tmp/www, url /kapouet/pouic/toto/pouet is /tmp/www/pouic/toto/pouet).
|
||||
- [ ] Turn on or off directory listing.
|
||||
- [ ] Set a default file to answer if the request is a directory.
|
||||
- [ ] Execute CGI based on certain file extension (for example .php).
|
||||
- [ ] Make the route able to accept uploaded files and configure where they should be saved.
|
||||
#### connection basic
|
||||
- [ ] You can’t execve another web server.
|
||||
- [ ] Your server must never block and the client can be bounced properly if necessary.
|
||||
- [ ] It must be non-blocking and use only 1 poll() (or equivalent) for all the I/O operations between the client and the server (listen included).
|
||||
- [ ] poll() (or equivalent) must check read and write at the same time.
|
||||
- [ ] You must never do a read or a write operation without going through poll() (or equivalent).
|
||||
- [ ] Checking the value of errno is strictly forbidden after a read or a write operation.
|
||||
- [ ] You don’t need to use poll() (or equivalent) before reading your configuration file. Because you have to use non-blocking file descriptors, it is possible to use read/recv or write/send functions with no poll() (or equivalent), and your server wouldn’t be blocking. But it would consume more system resources. Thus, if you try to read/recv or write/send in any file descriptor without using poll() (or equivalent), your grade will be 0.
|
||||
- [ ] You can use every macro and define like FD_SET, FD_CLR, FD_ISSET, FD_ZERO (understanding what and how they do it is very useful).
|
||||
- [ ] A request to your server should never hang forever.
|
||||
- [ ] Your server must be compatible with the web browser of your choice.
|
||||
#### parsing request HTTP (fields, ...)
|
||||
- [ ] We will consider that NGINX is HTTP 1.1 compliant and may be used to compare headers and answer behaviors.
|
||||
#### response HTTP (fields, ...)
|
||||
- [ ] Your HTTP response status codes must be accurate.
|
||||
- [ ] You server must have default error pages if none are provided.
|
||||
- [ ] You can’t use fork for something else than CGI (like PHP, or Python, and so forth).
|
||||
- [ ] You must be able to serve a fully static website.
|
||||
#### upload files
|
||||
- [ ] Clients must be able to upload files.
|
||||
#### CGI
|
||||
- [ ] You need at least GET, POST, and DELETE methods.
|
||||
- [ ] Do you wonder what a CGI is?
|
||||
- [ ] Because you won’t call the CGI directly, use the full path as PATH_INFO.
|
||||
- [ ] Just remember that, for chunked request, your server needs to unchunked it and the CGI will expect EOF as end of the body.
|
||||
- [ ] Same things for the output of the CGI. If no content_length is returned from the CGI, EOF will mark the end of the returned data.
|
||||
- [ ] Your program should call the CGI with the file requested as first argument.
|
||||
- [ ] The CGI should be run in the correct directory for relative path file access.
|
||||
- [ ] Your server should work with one CGI (php-CGI, Python, and so forth).
|
||||
#### write tests
|
||||
- [ ] Stress tests your server. It must stay available at all cost.
|
||||
- [ ] Do not test with only one program.
|
||||
- [ ] Write your tests with a more convenient language such as Python or Golang, and so forth. Even in C or C++ if you want to
|
||||
#### persistent connexion
|
||||
- [ ] Your server must be able to listen to multiple ports (see Configuration file)
|
||||
- [ ] Your server should never die.
|
||||
|
||||
---
|
||||
## cgi rfc
|
||||
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
|
||||
|
||||
#### summary :
|
||||
- the cgi-script will send back at least one header field followed by an empty line
|
||||
- this header field will be one of three :
|
||||
- "Content-Type"
|
||||
- "Location"
|
||||
- "Status"
|
||||
- the cgi-script may send back more header fields
|
||||
- the server must check and modify few things :
|
||||
- there is no duplicate in headers fields (if there is, resolve conflict)
|
||||
- there is no space between the field name and the ":"
|
||||
- the newlines are of form "\r\n", and not "\n" only
|
||||
- if the location field is not present, then if the status field is not present either, then the status code is 200
|
||||
- the cgi-script can return a location field, of two types :
|
||||
- local redirection : start with a "/", the server must answer as if this was the request uri
|
||||
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
|
||||
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
|
||||
|
||||
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
|
||||
|
||||
- The server [...] receives the request from the client
|
||||
- selects a CGI script to handle the request
|
||||
- converts the client request to a CGI request
|
||||
- executes the script and converts the CGI response into a response for the client
|
||||
|
||||
[3.3: script uri](https://www.rfc-editor.org/rfc/rfc3875#section-3.3)
|
||||
|
||||
- the 'Script-URI' [...] MUST have the property that if the client had accessed this URI instead, then the script would have been executed
|
||||
|
||||
[4: how the server prepare the cgi requests](https://www.rfc-editor.org/rfc/rfc3875#section-4)
|
||||
|
||||
- the cgi receives 2 differents set of informations :
|
||||
- the request meta-variables (in UNIX, by env variables)
|
||||
- and the message-body
|
||||
|
||||
[4.1: request meta-variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||
|
||||
- a header field that spans multiple lines MUST be merged onto a single line
|
||||
|
||||
[4.2: request message-body](https://www.rfc-editor.org/rfc/rfc3875#section-4.2)
|
||||
|
||||
- unless defined otherwise, the script access request data by reading stdin
|
||||
|
||||
[6: how the response from the script is returned to the server](https://www.rfc-editor.org/rfc/rfc3875#section-6)
|
||||
|
||||
- The response comprises 2 parts, separated by a blank line :
|
||||
- a message-header
|
||||
- and a message-body
|
||||
- The message-header contains one or more header fields
|
||||
- The body may be NULL
|
||||
|
||||
[6.2: responses types](https://www.rfc-editor.org/rfc/rfc3875#section-6.2)
|
||||
|
||||
- four types of responses :
|
||||
- document response
|
||||
- local redirect response
|
||||
- client redirect response
|
||||
- client redirect response with document
|
||||
- document response :
|
||||
- it must return a Content-Type header field
|
||||
- a Status-Header field is optional (200 is assumed if omited)
|
||||
- the server must check the cgi-script output, and modifie it to comply with the protocol version
|
||||
- local redirect response :
|
||||
- it must return only a Location field
|
||||
- it contains a local path URI and query string ('local-pathquery')
|
||||
- the local path URI must start with a "/"
|
||||
- the server must generate the response for this local-pathquery
|
||||
- client redirect response :
|
||||
- it must return only a Location field
|
||||
- it contains an absolute URI path, to indicate the client that it should reprocess the request with this URI
|
||||
- the absolute URI always start with the name of scheme followed by ":"
|
||||
- the http server must generate a 302 'Found' message
|
||||
- client redirect response with document
|
||||
- it must return a Location field with an absolute URI path
|
||||
- it must return the Status header field, with a value of 302 'Found'
|
||||
- the server must check the cgi-script output, and modifie it to comply with the protocol version
|
||||
|
||||
[6.3: cgi header fields](https://www.rfc-editor.org/rfc/rfc3875#section-6.3)
|
||||
|
||||
- whitespace is permitted between the ":" and the field-value
|
||||
- but not between the field-name and the ":"
|
||||
- the CGI script can set three differents fields :
|
||||
- Content-Type
|
||||
- Location
|
||||
- Status
|
||||
- Content-Type :
|
||||
- if there is a body in the response, a Content-Type field must be present
|
||||
- if there is no Content-Type, the server must not attempt to determine one
|
||||
- Location :
|
||||
- the local URI path must be an absolut path, not a relative path, nor NULL
|
||||
- the local URI path must, then, start with "/"
|
||||
- the absolut URI start with "<name-of-scheme>:"
|
||||
- Status :
|
||||
- a 3-digit integer code
|
||||
- 4 standards :
|
||||
- 200 'OK' indicates success, it's the default value
|
||||
- 302 'Found' with Location header and response message-body
|
||||
- 400 'Bad Request' an unknown request format, like missing CONTENT-TYPE
|
||||
- 501 'Not Implemented' the script received unsupported REQUEST-METHOD
|
||||
- construction: `Status:400 "explication of the error"\n`
|
||||
- the cgi-script can return other header fields, concerning the response message
|
||||
- the server must translate cgi-headers syntax into http-header syntax
|
||||
- for exemple, newline can be encoded in different ways
|
||||
- the cgi-script must not return header fields concerning client-side communication
|
||||
- the server can remove such fields
|
||||
- (not sure : https://www.rfc-editor.org/rfc/rfc3875#section-6.3.4)
|
||||
- the server must resolve conflicts between script-header fields and themselves
|
||||
|
||||
[6.3: cgi message body](https://www.rfc-editor.org/rfc/rfc3875#section-6.4)
|
||||
|
||||
- the server must read it untill EOF
|
||||
- the server must not modify it, except to convert charset if needed
|
||||
|
||||
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
|
||||
|
||||
|
||||
---
|
||||
## cgi env variables
|
||||
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
|
||||
[wikipedia variables environnements cgi](https://fr.wikipedia.org/wiki/Variables_d%27environnement_CGI)
|
||||
[cgi server variables on adobe](https://helpx.adobe.com/coldfusion/cfml-reference/reserved-words-and-variables/cgi-environment-cgi-scope-variables/cgi-server-variables.html)
|
||||
|
||||
```None
|
||||
AUTH_TYPE : if the srcipt is protected, the authentification method used to validate the user
|
||||
CONTENT_LENGTH : length of the request body-message
|
||||
CONTENT_TYPE : (Content-Type field) if there is attached information, as with method POST or PUT, this is the content type of the data (e.g. "text/plain", it is set by the attribute "enctype" in html <form> as three values : "application/x-www-form-urlencoded", "multipart/form-data", "text/plain")
|
||||
GATEWAY_INTERFACE : CGI version (e.g. CGI/1.1)
|
||||
PATH_INFO : if any, path of the resquest in addition to the cgi script path (e.g. for cgi script path = "/usr/web/cgi-bin/script.cgi", and the url = "http://server.org/cgi-bin/script.cgi/house", the PATH-INFO would be "house")
|
||||
PATH_TRANSLATED : full path of the request, like path-to-cgi/PATH_INFO, null if PATH_INFO is null (e.g. for "http://server.org/cgi-bin/prog/the/path", PATH_INFO would be : "/the/path" and PATH_TRANSLATED would be : "/usr/web/cgi-bin/prog/the/path")
|
||||
QUERY_STRING : everything following the ? in the url sent by client (e.g. for url "http://server.org/query?var1=val2&var2=val2", it would be : "var1=val2&var2=val2")
|
||||
REMOTE_ADDR : ip address of the client
|
||||
REMOTE_HOST : host name of the client, empty if not known, or equal to REMOTE_ADDR
|
||||
REMOTE_IDENT : if known, username of the client, otherwise empty, use for logging only
|
||||
REMOTE_USER : username of client, if script is protected and the server support user authentification
|
||||
REQUEST_METHOD : method used for the request (for http, usually POST or GET)
|
||||
SCRIPT_NAME : path to the cgi, relative to the root, used for self-referencing URLs (e.g. "/cgi-bin/script.cgi")
|
||||
SERVER_NAME : name of the server, as hostname, IP address, or DNS (e.g. dns : "www.server.org")
|
||||
SERVER_PORT : the port number your server is listening on (e.g. 80)
|
||||
SERVER_PROTOCOL : protocol used for the request (e.g. HTTP/1.1)
|
||||
SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
|
||||
```
|
||||
[redirect status for php-cgi](https://woozle.org/papers/php-cgi.html)
|
||||
```None
|
||||
REDIRECT_STATUS : for exemple, 200
|
||||
```
|
||||
|
||||
---
|
||||
## ressources
|
||||
|
||||
- [correction](https://github.com/AliMaskar96/42-Correction-Sheets/blob/master/ng_5_webserv.pdf)
|
||||
- [create an http server](https://medium.com/from-the-scratch/http-server-what-do-you-need-to-know-to-build-a-simple-http-server-from-scratch-d1ef8945e4fa)
|
||||
- [guide to network programming](https://beej.us/guide/bgnet/)
|
||||
- [same, translated in french](http://vidalc.chez.com/lf/socket.html)
|
||||
- [bind() vs connect()](https://stackoverflow.com/questions/27014955/socket-connect-vs-bind)
|
||||
- [INADDR_ANY for bind](https://stackoverflow.com/questions/16508685/understanding-inaddr-any-for-socket-programming)
|
||||
- [hack with CGI](https://www.youtube.com/watch?v=ph6-AKByBU4)
|
||||
- [http headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
|
||||
- [list of http headers fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
|
||||
- [http request ibm](https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests)
|
||||
- [http request other](https://www.tutorialspoint.com/http/http_requests.htm)
|
||||
- [request line uri](https://stackoverflow.com/questions/40311306/when-is-absoluteuri-used-from-the-http-request-specs)
|
||||
|
||||
0
YoupiBanane/Yeah/not_happy.bad_extension
Normal file
0
YoupiBanane/Yeah/not_happy.bad_extension
Normal file
0
YoupiBanane/nop/other.pouic
Normal file
0
YoupiBanane/nop/other.pouic
Normal file
0
YoupiBanane/nop/youpi.bad_extension
Normal file
0
YoupiBanane/nop/youpi.bad_extension
Normal file
0
YoupiBanane/youpi.bad_extension
Normal file
0
YoupiBanane/youpi.bad_extension
Normal file
0
YoupiBanane/youpi.bla
Normal file
0
YoupiBanane/youpi.bla
Normal file
@@ -16,6 +16,7 @@ server {
|
||||
location /board {
|
||||
allow_methods GET;
|
||||
root ./www/html;
|
||||
cgi_ext php cgi
|
||||
}
|
||||
|
||||
location /board/content {
|
||||
|
||||
@@ -12,6 +12,12 @@ server {
|
||||
index index.html; # this is another comment
|
||||
root ./www/;
|
||||
|
||||
# If not explicitly set, ConfigParser need to genererate a location block
|
||||
# like this for path "/" (based on field "root" and "index" of the server)
|
||||
location / {
|
||||
root ./www/;
|
||||
index index.html;
|
||||
}
|
||||
|
||||
allow_methods GET;
|
||||
}
|
||||
|
||||
|
||||
BIN
docs/webserv_correction.pdf
Normal file
BIN
docs/webserv_correction.pdf
Normal file
Binary file not shown.
20
memo.txt
20
memo.txt
@@ -1,7 +1,21 @@
|
||||
- un thread par serveur présent dans le fichier de config ?
|
||||
|
||||
----------------
|
||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||
- handle redirection
|
||||
- maybe add a "last_action_time" in Client for timeout handling
|
||||
little global timeout on epoll, like 100ms, then find client that actualy need to timeout
|
||||
if (actual_time - client.last_action_time > 10000ms){timeout(client)}
|
||||
- add headers "Date" and "Last-Modified" to response
|
||||
- change "std::string" to reference "std::string &" in most functions
|
||||
and add "const" if apropriate.
|
||||
- http_method en mode binary flags. "std::vector<http_method> allow_methods" -> "unsigned int allow_methods;"
|
||||
- Dans le parsing, trier les "locations" par ordre de precision.
|
||||
Compter les "/" dans le chemin, les locations avec le plus de "/" seront en premier dans le vector.
|
||||
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
|
||||
|
||||
----------------
|
||||
|
||||
__________________________
|
||||
--------------------------
|
||||
|
||||
----Discord 42------------
|
||||
Un truc cool et surtout bien utile ici c'est d'utiliser un proxy entre ton navigateur et ton serveur pour vérifier ce qui est envoyé en raw. Les navigateurs peuvent avoir des comportements différents.
|
||||
Vous avez des modules sur vos navigateur ou des logiciels externe. C'est assez rapide et gratuit.
|
||||
246
srcs/Client.cpp
Normal file
246
srcs/Client.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
|
||||
#include "Client.hpp"
|
||||
|
||||
char Client::buf[MAX_FILESIZE+1];
|
||||
|
||||
|
||||
/*********************************************
|
||||
* COPLIENS
|
||||
*********************************************/
|
||||
|
||||
Client::Client()
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd(0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Client::Client(int afd, listen_socket *lsocket, struct sockaddr_in addr)
|
||||
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd(afd)
|
||||
, _port(aport)
|
||||
, _ip(aip)
|
||||
, _lsocket(lsocket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
return;
|
||||
}
|
||||
|
||||
// WIP placeholder because of const values
|
||||
Client::Client( Client const & src )
|
||||
: body_size(0)
|
||||
, status(0)
|
||||
, _fd ( src._fd )
|
||||
, _port ( src._port )
|
||||
, _ip ( src._ip )
|
||||
, _lsocket ( src._lsocket )
|
||||
{
|
||||
raw_request = src.raw_request;
|
||||
response = src.response;
|
||||
// buf = strdup(src.buf); // TODO: this doesn't work
|
||||
body_size = src.body_size;
|
||||
status = src.status;
|
||||
return;
|
||||
}
|
||||
|
||||
// WIP placeholder because of const values
|
||||
Client & Client::operator=( Client const & rhs )
|
||||
{
|
||||
if ( this != &rhs )
|
||||
{
|
||||
// stuff
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************
|
||||
* PUBLIC MEMBER FUNCTIONS
|
||||
*********************************************/
|
||||
|
||||
// http headers :
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
|
||||
// https://www.tutorialspoint.com/http/http_requests.htm
|
||||
void Client::parse_request()
|
||||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
|
||||
// DEBUG
|
||||
std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n";
|
||||
|
||||
_parse_request_line();
|
||||
_parse_request_headers();
|
||||
_parse_request_body();
|
||||
_parse_port_hostname(this->get_rq_headers("Host"));
|
||||
raw_request.clear();
|
||||
}
|
||||
|
||||
bool Client::fill_script_path(std::string script)
|
||||
{
|
||||
size_t pos;
|
||||
int len = script.size();
|
||||
std::string path = this->get_rq_abs_path();
|
||||
std::string tmp;
|
||||
|
||||
pos = path.find(script);
|
||||
if (pos == 0)
|
||||
{
|
||||
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()
|
||||
{
|
||||
clear_request();
|
||||
raw_request.clear();
|
||||
response.clear();
|
||||
body_size = 0;
|
||||
status = 0;
|
||||
}
|
||||
|
||||
void Client::clear_request()
|
||||
{
|
||||
clear_script();
|
||||
_request.method = UNKNOWN;
|
||||
_request.uri.clear();
|
||||
_request.version.clear();
|
||||
_request.headers.clear();
|
||||
_request.body.clear();
|
||||
_request.abs_path.clear();
|
||||
_request.query.clear();
|
||||
_request.port.clear();
|
||||
_request.hostname.clear();
|
||||
}
|
||||
|
||||
void Client::clear_script()
|
||||
{
|
||||
_request.script.path.clear();
|
||||
_request.script.info.clear();
|
||||
}
|
||||
|
||||
|
||||
/*********************************************
|
||||
* GETTERS
|
||||
*********************************************/
|
||||
|
||||
// client side
|
||||
int Client::get_cl_fd() const { return _fd; }
|
||||
const std::string & Client::get_cl_ip() const { return _ip; }
|
||||
const std::string & Client::get_cl_port() const { return _port; }
|
||||
const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
|
||||
|
||||
// requette
|
||||
http_method Client::get_rq_method() const { return _request.method; }
|
||||
std::string Client::get_rq_method_str() const
|
||||
{ return ::http_methods_to_str(_request.method); }
|
||||
std::string Client::get_rq_uri() const { return _request.uri; }
|
||||
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
|
||||
std::string Client::get_rq_query() const { return _request.query; }
|
||||
std::string Client::get_rq_version() const { return _request.version; }
|
||||
std::string Client::get_rq_body() const { return _request.body; }
|
||||
std::string Client::get_rq_port() const { return _request.port; }
|
||||
std::string Client::get_rq_hostname() const { return _request.hostname; }
|
||||
std::string Client::get_rq_script_path()const { return _request.script.path; }
|
||||
std::string Client::get_rq_script_info()const { return _request.script.info; }
|
||||
std::string Client::get_rq_headers(const std::string & key) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it;
|
||||
|
||||
it = _request.headers.find(::str_tolower(key));
|
||||
if (it == _request.headers.end())
|
||||
return "";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************
|
||||
* PRIVATE MEMBER FUNCTIONS
|
||||
*********************************************/
|
||||
|
||||
void Client::_parse_request_line()
|
||||
{
|
||||
std::vector<std::string> line;
|
||||
int ret;
|
||||
|
||||
ret = ::parse_http_first_line(raw_request, line);
|
||||
if (ret != 3)
|
||||
{
|
||||
std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n";
|
||||
status = 400; // "bad request"
|
||||
}
|
||||
else
|
||||
{
|
||||
_request.method = str_to_http_method(line[0]);
|
||||
_request.uri = line[1];
|
||||
_parse_request_uri(line[1]);
|
||||
_request.version = line[2];
|
||||
}
|
||||
}
|
||||
|
||||
void Client::_parse_request_uri( std::string uri )
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
pos = uri.find("?");
|
||||
if (pos != std::string::npos)
|
||||
_request.query = uri.substr(pos + 1);
|
||||
else
|
||||
_request.query = "";
|
||||
_request.abs_path = uri.substr(0, pos);
|
||||
}
|
||||
|
||||
void Client::_parse_request_headers()
|
||||
{
|
||||
// TODO: check error and adjust status
|
||||
_request.headers = ::parse_http_headers(raw_request);
|
||||
}
|
||||
|
||||
void Client::_parse_request_body()
|
||||
{
|
||||
// TODO: check error and adjust status
|
||||
_request.body = ::parse_http_body(raw_request);
|
||||
}
|
||||
|
||||
void Client::_parse_port_hostname(std::string host)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
if (host == "")
|
||||
std::cerr << "no host\n";
|
||||
|
||||
pos = host.find(':');
|
||||
// port :
|
||||
if (pos == std::string::npos)
|
||||
_request.port = "4040"; // TODO: make equal to default port in config
|
||||
else
|
||||
_request.port = host.substr(pos);
|
||||
if (_request.port == ":")
|
||||
_request.port = "";
|
||||
// hostname :
|
||||
_request.hostname = host.substr(0, pos);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************
|
||||
* OVERLOAD
|
||||
*********************************************/
|
||||
|
||||
bool operator==(const Client& lhs, const Client& rhs)
|
||||
{ return lhs.get_cl_fd() == rhs.get_cl_fd(); }
|
||||
bool operator==(const Client& lhs, int fd)
|
||||
{ return lhs.get_cl_fd() == fd; }
|
||||
bool operator==(int fd, const Client& rhs)
|
||||
{ return fd == rhs.get_cl_fd(); }
|
||||
|
||||
@@ -5,24 +5,87 @@
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string.h> // strdup
|
||||
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# include "utils.hpp"
|
||||
# include "parsing_message_http.hpp"
|
||||
|
||||
struct Script
|
||||
{
|
||||
std::string path;
|
||||
std::string info;
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
http_method method;
|
||||
std::string uri;
|
||||
std::string abs_path;
|
||||
std::string query;
|
||||
std::string version;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
std::string port;
|
||||
std::string hostname;
|
||||
struct Script script;
|
||||
};
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
// Client(Placeholder);
|
||||
// Client();
|
||||
// Client(Client const &src);
|
||||
// ~Client();
|
||||
// Client &operator=(Client const &rhs);
|
||||
// Client &operator=(int);
|
||||
Client();
|
||||
Client(int afd, listen_socket *lsocket, std::string aport, std::string aip);
|
||||
~Client();
|
||||
Client(Client const &src);
|
||||
Client &operator=(Client const &rhs);
|
||||
|
||||
int fd;
|
||||
std::string raw_request;
|
||||
std::map<std::string, std::string> request;
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
static char buf[MAX_FILESIZE+1];
|
||||
size_t body_size;
|
||||
unsigned int status;
|
||||
|
||||
// private:
|
||||
// getters
|
||||
int get_cl_fd() const;
|
||||
const std::string & get_cl_port() const;
|
||||
const std::string & get_cl_ip() const;
|
||||
const listen_socket * get_cl_lsocket() const;
|
||||
|
||||
// requests getters
|
||||
http_method get_rq_method() const;
|
||||
std::string get_rq_method_str() const;
|
||||
std::string get_rq_uri() const;
|
||||
std::string get_rq_abs_path() const;
|
||||
std::string get_rq_query() const;
|
||||
std::string get_rq_version() const;
|
||||
std::string get_rq_body() const;
|
||||
std::string get_rq_port() const;
|
||||
std::string get_rq_hostname() const;
|
||||
std::string get_rq_script_path() const;
|
||||
std::string get_rq_script_info() const;
|
||||
std::string get_rq_headers(const std::string & key) const;
|
||||
|
||||
void parse_request();
|
||||
void clear();
|
||||
void clear_request();
|
||||
void clear_script();
|
||||
bool fill_script_path(std::string script);
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
std::string _port;
|
||||
std::string _ip;
|
||||
listen_socket * _lsocket;
|
||||
struct Request _request;
|
||||
|
||||
void _parse_request_line();
|
||||
void _parse_request_headers();
|
||||
void _parse_request_body();
|
||||
void _parse_request_uri( std::string uri );
|
||||
void _parse_port_hostname(std::string host);
|
||||
|
||||
};
|
||||
|
||||
@@ -31,3 +94,4 @@ bool operator==(const Client& lhs, int fd);
|
||||
bool operator==(int fd, const Client& rhs);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
|
||||
#ifndef METHODTYPE_HPP
|
||||
# define METHODTYPE_HPP
|
||||
|
||||
enum MethodType
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
DELETE,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
41
srcs/cgi-bin/cgi.cpp
Normal file
41
srcs/cgi-bin/cgi.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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;
|
||||
}
|
||||
BIN
srcs/cgi-bin/cgi_cpp.cgi
Executable file
BIN
srcs/cgi-bin/cgi_cpp.cgi
Executable file
Binary file not shown.
29
srcs/cgi-bin/php-cgi
Executable file
29
srcs/cgi-bin/php-cgi
Executable file
@@ -0,0 +1,29 @@
|
||||
#! /usr/bin/php
|
||||
|
||||
<?php
|
||||
echo "Status: 200\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";
|
||||
?>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/13 22:11:17 by me #+# #+# */
|
||||
/* Updated: 2022/07/31 13:18:14 by simplonco ### ########.fr */
|
||||
/* Updated: 2022/08/03 17:51:35 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -262,8 +262,8 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
MethodType m = _str_to_method_type(tmp_val[i]);
|
||||
if (m == 3)
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
server->allow_methods.push_back(m);
|
||||
}
|
||||
@@ -347,8 +347,8 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
MethodType m = _str_to_method_type(tmp_val[i]);
|
||||
if (m == 3)
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
location->allow_methods.push_back(m);
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
/* ::: :::::::: */
|
||||
/* ConfigParser.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
|
||||
/* Updated: 2022/07/27 19:27:57 by me ### ########.fr */
|
||||
/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
# include "ServerConfig.hpp"
|
||||
# include "LocationConfig.hpp"
|
||||
# include "MethodType.hpp"
|
||||
# include "utils.hpp"
|
||||
|
||||
# include <map>
|
||||
@@ -76,8 +75,6 @@ private:
|
||||
std::string _get_rest_of_line(size_t *curr); // const?
|
||||
|
||||
|
||||
// why static? it's an enum...
|
||||
static MethodType _str_to_method_type(std::string str);
|
||||
|
||||
|
||||
|
||||
@@ -82,20 +82,6 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
return (values);
|
||||
}
|
||||
|
||||
|
||||
MethodType ConfigParser::_str_to_method_type(std::string str)
|
||||
{
|
||||
if (str == "GET")
|
||||
return GET;
|
||||
else if (str == "POST")
|
||||
return POST;
|
||||
else if (str == "DELETE")
|
||||
return DELETE;
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_print_content() const
|
||||
{
|
||||
std::cout << _content;
|
||||
@@ -3,18 +3,16 @@
|
||||
/* ::: :::::::: */
|
||||
/* LocationConfig.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/07/25 20:09:48 by me ### ########.fr */
|
||||
/* Updated: 2022/08/02 14:06:07 by lperrey ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef LOCATIONCONFIG_HPP
|
||||
# define LOCATIONCONFIG_HPP
|
||||
|
||||
# include "MethodType.hpp"
|
||||
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string>
|
||||
@@ -31,7 +29,7 @@ public:
|
||||
int client_body_limit;
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
std::vector<MethodType> allow_methods;
|
||||
std::vector<http_method> allow_methods;
|
||||
std::map<std::string, std::string> cgi_info;
|
||||
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
@@ -1,19 +1,8 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* ServerConfig.hpp :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: me <erlazo@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 15:55:16 by me #+# #+# */
|
||||
/* Updated: 2022/07/23 16:19:43 by me ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef SERVERCONFIG_HPP
|
||||
# define SERVERCONFIG_HPP
|
||||
|
||||
# include "MethodType.hpp"
|
||||
# include "utils.hpp"
|
||||
# include "LocationConfig.hpp"
|
||||
|
||||
# include <map>
|
||||
@@ -41,7 +30,7 @@ public:
|
||||
// there can only be one.
|
||||
std::string root;
|
||||
|
||||
int client_body_limit; // set to default max if none set
|
||||
unsigned int client_body_limit; // set to default max if none set
|
||||
|
||||
// might be the only one we let slide if bad input...
|
||||
bool autoindex;
|
||||
@@ -53,7 +42,7 @@ public:
|
||||
// i'm tempted to do something diff for storing method types...
|
||||
// fuck it, you can only call allow_methods once in Server
|
||||
// once more in each location.
|
||||
std::vector<MethodType> allow_methods;
|
||||
std::vector<http_method> allow_methods;
|
||||
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
@@ -68,8 +57,6 @@ public:
|
||||
// int redirect_status;
|
||||
// std::string redirect_uri;
|
||||
|
||||
|
||||
|
||||
void print_all()
|
||||
{
|
||||
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
|
||||
@@ -100,18 +87,7 @@ public:
|
||||
|
||||
std::cout << "\n----------\n";
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
120
srcs/utils.cpp
120
srcs/utils.cpp
@@ -3,9 +3,9 @@
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter)
|
||||
{
|
||||
std::vector<std::string> answer;
|
||||
std::stringstream ss(input);
|
||||
std::string temp;
|
||||
std::vector<std::string> answer;
|
||||
std::stringstream ss(input);
|
||||
std::string temp;
|
||||
|
||||
while (getline(ss, temp, delimiter))
|
||||
answer.push_back(temp);
|
||||
@@ -13,6 +13,33 @@ std::vector<std::string> split(std::string input, char delimiter)
|
||||
return answer;
|
||||
}
|
||||
|
||||
std::string trim(std::string str, char c)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
// delete leadings c
|
||||
pos = str.find_first_not_of(c);
|
||||
if (pos == std::string::npos)
|
||||
return str;
|
||||
str = str.substr(pos);
|
||||
|
||||
// delete endings c
|
||||
pos = str.find_last_not_of(c);
|
||||
if (pos == std::string::npos)
|
||||
return str;
|
||||
str = str.substr(0, pos + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string itos(int n)
|
||||
{
|
||||
std::stringstream strs;
|
||||
|
||||
strs << n;
|
||||
return ( strs.str() );
|
||||
}
|
||||
|
||||
bool isNumeric(std::string str)
|
||||
{
|
||||
for (size_t i = 0; i < str.length(); i++)
|
||||
@@ -23,7 +50,6 @@ bool isNumeric(std::string str)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isNumeric_btw(int low, int high, std::string str)
|
||||
{
|
||||
for (size_t i = 0; i < str.length(); i++)
|
||||
@@ -37,11 +63,85 @@ bool isNumeric_btw(int low, int high, std::string str)
|
||||
return true;
|
||||
}
|
||||
|
||||
char* itoa(int n)
|
||||
http_method str_to_http_method(std::string &str)
|
||||
{
|
||||
std::stringstream strs;
|
||||
|
||||
strs << n;
|
||||
// casts : https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used
|
||||
return ( const_cast<char*>( strs.str().c_str() ) );
|
||||
if (str == "GET")
|
||||
return GET;
|
||||
else if (str == "POST")
|
||||
return POST;
|
||||
else if (str == "DELETE")
|
||||
return DELETE;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
std::string http_methods_to_str(unsigned int methods)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
if (methods & GET)
|
||||
str.append("GET");
|
||||
if (methods & POST)
|
||||
{
|
||||
if (!str.empty())
|
||||
str.append(", ");
|
||||
str.append("POST");
|
||||
}
|
||||
if (methods & DELETE)
|
||||
{
|
||||
if (!str.empty())
|
||||
str.append(", ");
|
||||
str.append("DELETE");
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr)
|
||||
{
|
||||
if (ori_substr.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while (1)
|
||||
{
|
||||
pos = str.find(ori_substr, pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
str.replace(pos, ori_substr.size(), new_substr);
|
||||
pos += new_substr.size();
|
||||
}
|
||||
}
|
||||
|
||||
std::string str_tolower(std::string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
void del_line_in_str(std::string * str, size_t pos, std::string delim)
|
||||
{
|
||||
size_t begin;
|
||||
size_t end;
|
||||
|
||||
begin = (*str).rfind(delim, pos);
|
||||
if (begin == std::string::npos)
|
||||
begin = 0;
|
||||
else
|
||||
begin += delim.size();
|
||||
|
||||
end = (*str).find(delim, pos);
|
||||
if (end == std::string::npos)
|
||||
end = 0;
|
||||
else
|
||||
end += delim.size();
|
||||
|
||||
(*str).erase(begin, end - begin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool operator==(const listen_socket& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
|
||||
bool operator==(int fd, const listen_socket& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
|
||||
@@ -5,11 +5,50 @@
|
||||
# include <vector>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <cstdlib> // atoi
|
||||
# include <cctype> // tolower
|
||||
# include <algorithm> // transform
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter);
|
||||
bool isNumeric(std::string str);
|
||||
bool isNumeric_btw(int low, int high, std::string str);
|
||||
char* itoa(int n);
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
|
||||
// enum http_method
|
||||
// {
|
||||
// UNKNOWN = 0b00000000,
|
||||
// GET = 0b00000001,
|
||||
// POST = 0b00000010,
|
||||
// DELETE = 0b00000100,
|
||||
// ANY_METHODS = 0b11111111,
|
||||
// };
|
||||
|
||||
enum http_method
|
||||
{
|
||||
UNKNOWN = 0b0,
|
||||
GET = 1 << 0,
|
||||
POST = 1 << 1,
|
||||
DELETE = 1 << 2,
|
||||
ANY_METHODS = 0b11111111,
|
||||
};
|
||||
|
||||
struct listen_socket
|
||||
{
|
||||
int fd;
|
||||
std::string host;
|
||||
std::string port;
|
||||
};
|
||||
bool operator==(const listen_socket& lhs, int fd);
|
||||
bool operator==(int fd, const listen_socket& rhs);
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter);
|
||||
bool isNumeric(std::string str);
|
||||
bool isNumeric_btw(int low, int high, std::string str);
|
||||
std::string itos(int n);
|
||||
std::string trim(std::string str, char c);
|
||||
http_method str_to_http_method(std::string &str);
|
||||
std::string http_methods_to_str(unsigned int methods);
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||
std::string str_tolower(std::string str);
|
||||
void del_line_in_str(std::string * str, size_t pos, std::string delim);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,41 +17,22 @@
|
||||
# include <iostream> // cout, cin
|
||||
# include <cstring> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
|
||||
# include <netinet/in.h> // sockaddr_in, struct in_addr
|
||||
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
|
||||
# include <algorithm> // find
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
|
||||
// TODO: A virer
|
||||
//# include "ConfigParser.hpp"
|
||||
//# include "LocationConfig.hpp"
|
||||
//# include "MethodType.hpp"
|
||||
//# include "utils.hpp"
|
||||
// TODO: A virer
|
||||
# include "http_status.hpp"
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
void signal_handler(int signum);
|
||||
|
||||
/* enum // WIP test
|
||||
{
|
||||
SERVER_FD = 1,
|
||||
CLIENT_FD
|
||||
};
|
||||
|
||||
struct s // WIP test
|
||||
{
|
||||
int fd;
|
||||
Client *ptr;
|
||||
};
|
||||
*/
|
||||
void signal_handler(int signum);
|
||||
|
||||
// these might only be TMP
|
||||
# define FAILURE -1
|
||||
@@ -77,21 +58,51 @@ class Webserv
|
||||
|
||||
private:
|
||||
int _epfd;
|
||||
std::vector<int> _listen_sockets;
|
||||
std::vector<listen_socket> _listen_sockets;
|
||||
std::vector<ServerConfig> _servers;
|
||||
std::vector<Client> _clients;
|
||||
std::vector<Client> _clients;
|
||||
std::map<int, std::string> _http_status;
|
||||
std::map<std::string, std::string> _mime_types;
|
||||
|
||||
// accept.cpp
|
||||
void _accept_connection(int fd);
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
std::map<std::string, std::string>
|
||||
_extract_infos(struct sockaddr_in addr);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
void _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
void _send_response(Client *client);
|
||||
void _construct_response(Client *client);
|
||||
void _send_response(Client *client, ServerConfig &server);
|
||||
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client, ServerConfig &server);
|
||||
void _process_method(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _insert_status_line(Client *client);
|
||||
void _get_ressource(Client *client);
|
||||
void _error_html_response(Client *client, ServerConfig &server);
|
||||
void _append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension = "");
|
||||
|
||||
void _get(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
|
||||
void _post(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _post_file(Client *client, const std::string &path);
|
||||
|
||||
void _delete(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
|
||||
ServerConfig &_determine_process_server(Client *client);
|
||||
LocationConfig &_determine_location(ServerConfig &server, std::string const &path);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client);
|
||||
std::string _exec_cgi(Client *client);
|
||||
char** _set_env(Client *client);
|
||||
char* _dup_env(std::string var, std::string val);
|
||||
char* _dup_env(std::string var, int i);
|
||||
std::string _exec_script(Client *client, char **env);
|
||||
void _check_script_output(Client *client, std::string output);
|
||||
void _check_script_status(Client *client, std::string output);
|
||||
void _check_script_fields(Client *client, std::string output);
|
||||
// epoll_update.cpp
|
||||
int _epoll_update(int fd, uint32_t events, int op);
|
||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||
@@ -100,9 +111,13 @@ class Webserv
|
||||
// close.cpp
|
||||
void _close_client(int fd);
|
||||
void _close_all_clients();
|
||||
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);
|
||||
void _init_http_status_map();
|
||||
void _init_mime_types_map();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,16 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_accept_connection(int fd)
|
||||
void Webserv::_accept_connection(listen_socket &lsocket)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len;
|
||||
int accepted_fd;
|
||||
std::map<std::string, std::string> infos;
|
||||
|
||||
std::cerr << "accept()\n";
|
||||
addr_len = sizeof addr;
|
||||
accepted_fd = ::accept(fd, (sockaddr*)&addr, &addr_len);
|
||||
accepted_fd = ::accept(lsocket.fd, (sockaddr*)&addr, &addr_len);
|
||||
if (accepted_fd == -1)
|
||||
{
|
||||
std::perror("err accept()");
|
||||
@@ -19,8 +20,22 @@ void Webserv::_accept_connection(int fd)
|
||||
}
|
||||
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
_clients.push_back(Client());
|
||||
_clients.back().fd = accepted_fd;
|
||||
|
||||
infos = _extract_infos(addr);
|
||||
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
|
||||
_clients.push_back(client);
|
||||
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>
|
||||
Webserv::_extract_infos(struct sockaddr_in addr)
|
||||
{
|
||||
struct in_addr ip_conversion;
|
||||
std::map<std::string, std::string> infos;
|
||||
|
||||
infos["port"] = ::itos( addr.sin_port );
|
||||
ip_conversion.s_addr = addr.sin_addr.s_addr;
|
||||
infos["ip"] = inet_ntoa( ip_conversion );
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ Webserv::Webserv()
|
||||
throw std::runtime_error("Epoll init");
|
||||
}
|
||||
|
||||
_init_http_status_map();
|
||||
_init_mime_types_map();
|
||||
|
||||
std::signal(SIGPIPE, signal_handler);
|
||||
std::signal(SIGINT, signal_handler);
|
||||
}
|
||||
@@ -42,10 +45,9 @@ Webserv::Webserv(std::vector<ServerConfig>* servers)
|
||||
|
||||
Webserv::~Webserv()
|
||||
{
|
||||
//close(_socket_fd);
|
||||
// TODO : CLOSE ALL _listen_sockets
|
||||
close(_epfd);
|
||||
_close_all_clients();
|
||||
_close_all_listen_sockets();
|
||||
std::cerr << "Server destroyed\n";
|
||||
}
|
||||
|
||||
|
||||
178
srcs/webserv/cgi_script.cpp
Normal file
178
srcs/webserv/cgi_script.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
|
||||
#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];
|
||||
|
||||
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";
|
||||
return script_output;
|
||||
}
|
||||
|
||||
void Webserv::_check_script_output(Client *client, std::string output)
|
||||
{
|
||||
// TODO: it doesn't work with execve error, i don't know why yet ?
|
||||
_check_script_status(client, output);
|
||||
_check_script_fields(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);
|
||||
::del_line_in_str(&output, pos, CRLF);
|
||||
}
|
||||
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;
|
||||
size_t pos;
|
||||
|
||||
srv_fld = parse_http_headers(client->response);
|
||||
scr_fld = parse_http_headers(output);
|
||||
// wip: 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 (it_srv->first == it_scr->first)
|
||||
{
|
||||
pos = client->response.find(it_srv->first);
|
||||
::del_line_in_str(&client->response, pos, CRLF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ void Webserv::_close_client(int fd)
|
||||
std::vector<Client>::iterator it = _clients.begin();
|
||||
while (it != _clients.end())
|
||||
{
|
||||
if (it->fd == fd)
|
||||
if (*it == fd)
|
||||
{
|
||||
// _epoll_update(fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(fd) == -1)
|
||||
@@ -25,10 +25,23 @@ void Webserv::_close_all_clients()
|
||||
while (!_clients.empty())
|
||||
{
|
||||
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(_clients.back().fd) == -1)
|
||||
if (::close(_clients.back().get_cl_fd()) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << _clients.back().fd << "\n";
|
||||
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
|
||||
_clients.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_close_all_listen_sockets()
|
||||
{
|
||||
while (!_listen_sockets.empty())
|
||||
{
|
||||
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
|
||||
if (::close(_listen_sockets.back().fd) == -1)
|
||||
std::perror("err close()");
|
||||
else
|
||||
std::cerr << "close fd " << _listen_sockets.back().fd << "\n";
|
||||
_listen_sockets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
42
srcs/webserv/http_status.hpp
Normal file
42
srcs/webserv/http_status.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
#ifndef HTTP_STATUS_HPP
|
||||
# define HTTP_STATUS_HPP
|
||||
|
||||
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
|
||||
/*
|
||||
First version of macro HTML_ERROR(STATUS) dont work with call like this :
|
||||
client->response.append( HTML_ERROR( _http_status[client->status].c_str() ) );
|
||||
so I made the other version with dynamic replacement of STATUS .
|
||||
*/
|
||||
// # define HTML_ERROR(STATUS) "\r\n<!DOCTYPE html><html><head><title>"STATUS"</title></head><body><h1 style=\"text-align:center\">"STATUS"</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
|
||||
# define STATUS_PLACEHOLDER "$STATUS"
|
||||
# define HTML_ERROR \
|
||||
"<!DOCTYPE html>"\
|
||||
"<html>"\
|
||||
"<head>"\
|
||||
"<title>" STATUS_PLACEHOLDER "</title>"\
|
||||
"</head>"\
|
||||
"<body>"\
|
||||
"<h1 style=\"text-align:center\">" STATUS_PLACEHOLDER "</h1>"\
|
||||
"<hr>"\
|
||||
"<p style=\"text-align:center\">Le Webserv/0.1</p>"\
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
// When new status added, need to update _init_http_status_map()
|
||||
# define S200 "200 OK"
|
||||
# define S201 "201 Created"
|
||||
# define S204 "204 No Content"
|
||||
|
||||
# define S400 "400 Bad Request"
|
||||
# define S403 "403 Forbidden"
|
||||
# define S404 "404 Not Found"
|
||||
# define S405 "405 Method Not Allowed"
|
||||
# define S413 "413 Content Too Large"
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,9 @@
|
||||
void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
{
|
||||
int ret;
|
||||
std::vector<int> _open_ports;
|
||||
std::vector<std::string> open_sockets;
|
||||
struct listen_socket new_socket;
|
||||
std::string host_port;
|
||||
|
||||
_servers = *servers;
|
||||
_listen_sockets.clear();
|
||||
@@ -12,7 +14,11 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
|
||||
while (it != _servers.end())
|
||||
{
|
||||
if ( std::find(_open_ports.begin(), _open_ports.end(), std::atoi(it->port.data()) ) != _open_ports.end() )
|
||||
host_port.clear();
|
||||
host_port.append(it->host);
|
||||
host_port.append(":");
|
||||
host_port.append(it->port);
|
||||
if ( std::find(open_sockets.begin(), open_sockets.end(), host_port) != open_sockets.end() )
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
@@ -24,27 +30,31 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
|
||||
std::perror("err socket()");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
new_socket.fd = ret;
|
||||
new_socket.host = it->host;
|
||||
new_socket.port = it->port;
|
||||
_listen_sockets.push_back(new_socket);
|
||||
|
||||
// HUGO ADD
|
||||
//
|
||||
// allow socket descriptor to be reuseable
|
||||
// I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
|
||||
int on = 1;
|
||||
if (setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
if (setsockopt(new_socket.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
||||
{
|
||||
::perror("err setsockopt()");
|
||||
throw std::runtime_error("Socket init");
|
||||
}
|
||||
//
|
||||
// HUGO ADD END
|
||||
_listen_sockets.push_back(ret);
|
||||
|
||||
_bind(_listen_sockets.back(), std::atoi(it->port.data()), it->host);
|
||||
_listen(_listen_sockets.back(), 512); // 512 arbitrary
|
||||
_bind(new_socket.fd, std::atoi(it->port.c_str()), it->host);
|
||||
_listen(new_socket.fd, 512); // 512 arbitrary
|
||||
|
||||
if (_epoll_update(_listen_sockets.back(), EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
if (_epoll_update(new_socket.fd, EPOLLIN, EPOLL_CTL_ADD) == -1)
|
||||
throw std::runtime_error("Socket init");
|
||||
|
||||
_open_ports.push_back(std::atoi(it->port.data()));
|
||||
open_sockets.push_back(host_port);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -76,3 +86,136 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
throw std::runtime_error("Socket listen");
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_init_http_status_map()
|
||||
{
|
||||
_http_status[200] = S200;
|
||||
_http_status[201] = S201;
|
||||
_http_status[204] = S204;
|
||||
|
||||
_http_status[400] = S400;
|
||||
_http_status[403] = S403;
|
||||
_http_status[404] = S404;
|
||||
_http_status[405] = S405;
|
||||
_http_status[413] = S413;
|
||||
|
||||
_http_status[500] = S500;
|
||||
_http_status[501] = S501;
|
||||
}
|
||||
|
||||
void Webserv::_init_mime_types_map()
|
||||
{
|
||||
_mime_types[""] = "application/octet-stream";
|
||||
|
||||
_mime_types["html"] = "text/html";
|
||||
_mime_types["htm"] = "text/html";
|
||||
_mime_types["shtml"] = "text/html";
|
||||
_mime_types["css"] = "text/css";
|
||||
_mime_types["xml"] = "text/xml";
|
||||
_mime_types["gif"] = "image/gif";
|
||||
_mime_types["jpeg"] = "image/jpeg";
|
||||
_mime_types["jpg"] = "image/jpeg";
|
||||
_mime_types["js"] = "application/javascript";
|
||||
_mime_types["atom"] = "application/atom+xml";
|
||||
_mime_types["rss"] = "application/rss+xml";
|
||||
|
||||
_mime_types["mml"] = "text/mathml";
|
||||
_mime_types["txt"] = "text/plain";
|
||||
_mime_types["jad"] = "text/vnd.sun.j2me.app-descriptor";
|
||||
_mime_types["wml"] = "text/vnd.wap.wml";
|
||||
_mime_types["htc"] = "text/x-component";
|
||||
|
||||
_mime_types["png"] = "image/png";
|
||||
_mime_types["tif"] = "image/tiff";
|
||||
_mime_types["tiff"] = "image/tiff";
|
||||
_mime_types["wbmp"] = "image/vnd.wap.wbmp";
|
||||
_mime_types["ico"] = "image/x-icon";
|
||||
_mime_types["jng"] = "image/x-jng";
|
||||
_mime_types["bmp"] = "image/x-ms-bmp";
|
||||
_mime_types["svg"] = "image/svg+xml";
|
||||
_mime_types["svgz"] = "image/svg+xml";
|
||||
_mime_types["webp"] = "image/webp";
|
||||
|
||||
_mime_types["woff"] = "application/font-woff";
|
||||
_mime_types["jar"] = "application/java-archive";
|
||||
_mime_types["war"] = "application/java-archive";
|
||||
_mime_types["ear"] = "application/java-archive";
|
||||
_mime_types["json"] = "application/json";
|
||||
_mime_types["hqx"] = "application/mac-binhex40";
|
||||
_mime_types["doc"] = "application/msword";
|
||||
_mime_types["pdf"] = "application/pdf";
|
||||
_mime_types["ps"] = "application/postscript";
|
||||
_mime_types["eps"] = "application/postscript";
|
||||
_mime_types["ai"] = "application/postscript";
|
||||
_mime_types["rtf"] = "application/rtf";
|
||||
_mime_types["m3u8"] = "application/vnd.apple.mpegurl";
|
||||
_mime_types["xls"] = "application/vnd.ms-excel";
|
||||
_mime_types["eot"] = "application/vnd.ms-fontobject";
|
||||
_mime_types["ppt"] = "application/vnd.ms-powerpoint";
|
||||
_mime_types["wmlc"] = "application/vnd.wap.wmlc";
|
||||
_mime_types["kml"] = "application/vnd.google-earth.kml+xml";
|
||||
_mime_types["kmz"] = "application/vnd.google-earth.kmz";
|
||||
_mime_types["7z"] = "application/x-7z-compressed";
|
||||
_mime_types["cco"] = "application/x-cocoa";
|
||||
_mime_types["jardiff"] = "application/x-java-archive-diff";
|
||||
_mime_types["jnlp"] = "application/x-java-jnlp-file";
|
||||
_mime_types["run"] = "application/x-makeself";
|
||||
_mime_types["pl"] = "application/x-perl";
|
||||
_mime_types["pm"] = "application/x-perl";
|
||||
_mime_types["prc"] = "application/x-pilot";
|
||||
_mime_types["pdb"] = "application/x-pilot";
|
||||
_mime_types["rar"] = "application/x-rar-compressed";
|
||||
_mime_types["rpm"] = "application/x-redhat-package-manager";
|
||||
_mime_types["sea"] = "application/x-sea";
|
||||
_mime_types["swf"] = "application/x-shockwave-flash";
|
||||
_mime_types["sit"] = "application/x-stuffit";
|
||||
_mime_types["tcl"] = "application/x-tcl";
|
||||
_mime_types["tk"] = "application/x-tcl";
|
||||
_mime_types["der"] = "application/x-x509-ca-cert";
|
||||
_mime_types["pem"] = "application/x-x509-ca-cert";
|
||||
_mime_types["crt"] = "application/x-x509-ca-cert";
|
||||
_mime_types["xpi"] = "application/x-xpinstall";
|
||||
_mime_types["xhtml"] = "application/xhtml+xml";
|
||||
_mime_types["xspf"] = "application/xspf+xml";
|
||||
_mime_types["zip"] = "application/zip";
|
||||
|
||||
_mime_types["bin"] = "application/octet-stream";
|
||||
_mime_types["exe"] = "application/octet-stream";
|
||||
_mime_types["dll"] = "application/octet-stream";
|
||||
_mime_types["deb"] = "application/octet-stream";
|
||||
_mime_types["dmg"] = "application/octet-stream";
|
||||
_mime_types["iso"] = "application/octet-stream";
|
||||
_mime_types["img"] = "application/octet-stream";
|
||||
_mime_types["msi"] = "application/octet-stream";
|
||||
_mime_types["msp"] = "application/octet-stream";
|
||||
_mime_types["msm"] = "application/octet-stream";
|
||||
|
||||
_mime_types["docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
_mime_types["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
_mime_types["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
|
||||
_mime_types["mid"] = "audio/midi";
|
||||
_mime_types["midi"] = "audio/midi";
|
||||
_mime_types["kar"] = "audio/midi";
|
||||
_mime_types["mp3"] = "audio/mpeg";
|
||||
_mime_types["ogg"] = "audio/ogg";
|
||||
_mime_types["m4a"] = "audio/x-m4a";
|
||||
_mime_types["ra"] = "audio/x-realaudio";
|
||||
|
||||
_mime_types["3gpp"] = "video/3gpp";
|
||||
_mime_types["3gp"] = "video/3gpp";
|
||||
_mime_types["ts"] = "video/mp2t";
|
||||
_mime_types["mp4"] = "video/mp4";
|
||||
_mime_types["mpeg"] = "video/mpeg";
|
||||
_mime_types["mpg"] = "video/mpeg";
|
||||
_mime_types["mov"] = "video/quicktime";
|
||||
_mime_types["webm"] = "video/webm";
|
||||
_mime_types["flv"] = "video/x-flv";
|
||||
_mime_types["m4v"] = "video/x-m4v";
|
||||
_mime_types["mng"] = "video/x-mng";
|
||||
_mime_types["asx"] = "video/x-ms-asf";
|
||||
_mime_types["asf"] = "video/x-ms-asf";
|
||||
_mime_types["wmv"] = "video/x-ms-wmv";
|
||||
_mime_types["avi"] = "video/x-msvideo";
|
||||
}
|
||||
|
||||
|
||||
90
srcs/webserv/parsing_message_http.cpp
Normal file
90
srcs/webserv/parsing_message_http.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
#include "parsing_message_http.hpp"
|
||||
|
||||
size_t
|
||||
parse_http_first_line(std::string message, std::vector<std::string> &line)
|
||||
{
|
||||
std::vector<std::string> sline;
|
||||
std::string sub;
|
||||
std::string tmp;
|
||||
size_t pos;
|
||||
size_t ret;
|
||||
|
||||
// TODO: check for err in substr
|
||||
pos = message.find(CRLF);
|
||||
sub = message.substr(0, pos);
|
||||
sline = ::split(sub, ' ');
|
||||
ret = sline.size();
|
||||
if (ret != 3)
|
||||
return ret;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
tmp = ::trim(sline[i], ' ');
|
||||
tmp = ::trim(tmp, '\r');
|
||||
line.push_back(tmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>
|
||||
parse_http_headers(std::string message)
|
||||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
std::vector<std::string> list;
|
||||
std::vector<std::string>::iterator it;
|
||||
std::string sub;
|
||||
std::string key;
|
||||
std::string val;
|
||||
size_t pos;
|
||||
|
||||
pos = (message).find(CRLF CRLF);
|
||||
sub = (message).substr(0, pos);
|
||||
list = ::split(sub, '\n');
|
||||
if ( maybe_http_first_line( *list.begin() ) )
|
||||
list.erase(list.begin());
|
||||
|
||||
for (it = list.begin(); it != list.end(); it++)
|
||||
{
|
||||
// TODO: if pattern is not "NAME: value" return error
|
||||
pos = (*it).find(':');
|
||||
key = (*it).substr( 0, pos );
|
||||
key = ::trim(key, ' ');
|
||||
key = ::trim(key, '\r');
|
||||
key = ::str_tolower(key);
|
||||
val = (*it).substr( pos + 1 );
|
||||
val = ::trim(val, ' ');
|
||||
val = ::trim(val, '\r');
|
||||
headers.insert( std::pair<std::string, std::string>(key, val) );
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool maybe_http_first_line(std::string str)
|
||||
{
|
||||
// method SP target SP version https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.1
|
||||
// version SP status SP reason https://www.rfc-editor.org/rfc/rfc7230.html#section-3.1.2
|
||||
|
||||
std::vector<std::string> sline;
|
||||
|
||||
sline = ::split(str, ' ');
|
||||
if (sline.size() != 3)
|
||||
return false;
|
||||
if (sline[0].find(':') != std::string::npos)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
36
srcs/webserv/parsing_message_http.hpp
Normal file
36
srcs/webserv/parsing_message_http.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
#ifndef PARSING_MESSAGE_HTTP_HPP
|
||||
# define PARSING_MESSAGE_HTTP_HPP
|
||||
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
# include <vector>
|
||||
# include <map>
|
||||
# include "utils.hpp"
|
||||
|
||||
size_t
|
||||
parse_http_first_line(std::string message, std::vector<std::string> &line);
|
||||
|
||||
std::map<std::string, std::string>
|
||||
parse_http_headers(std::string message);
|
||||
|
||||
std::string
|
||||
parse_http_body(std::string message);
|
||||
|
||||
bool
|
||||
maybe_http_first_line(std::string);
|
||||
|
||||
// http message structure :
|
||||
//
|
||||
// start-line
|
||||
// request-line
|
||||
// method SP target SP version
|
||||
// response-line
|
||||
// version SP status SP reason
|
||||
// header-fields
|
||||
// name ":" SP value
|
||||
// CRLF
|
||||
// body
|
||||
|
||||
#endif
|
||||
|
||||
2
srcs/webserv/parsing_request.cpp
Normal file
2
srcs/webserv/parsing_request.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "parsing_request.hpp"
|
||||
|
||||
@@ -15,19 +15,19 @@ void Webserv::_read_request(Client *client)
|
||||
char buf[BUFSIZE+1];
|
||||
ssize_t ret;
|
||||
|
||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
|
||||
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ;
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err recv()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
std::cerr << "client.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
}
|
||||
if (ret == 0) // Not sure what to do in case of 0. Just close ?
|
||||
{
|
||||
_close_client(client->fd);
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
}
|
||||
/*
|
||||
@@ -37,6 +37,8 @@ void Webserv::_read_request(Client *client)
|
||||
|
||||
buf[ret] = '\0';
|
||||
client->raw_request.append(buf);
|
||||
client->parse_request();
|
||||
|
||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
|
||||
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,131 +3,195 @@
|
||||
|
||||
void Webserv::_response(Client *client)
|
||||
{
|
||||
_send_response(client);
|
||||
client->status = 200; // default value
|
||||
|
||||
ServerConfig &server = _determine_process_server(client);
|
||||
_send_response(client, server);
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
}
|
||||
|
||||
void Webserv::_send_response(Client *client)
|
||||
void Webserv::_send_response(Client *client, ServerConfig &server)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "send()\n";
|
||||
std::cerr << "RAW_REQUEST\n|\n" << client->raw_request << "|\n"; // DEBUG
|
||||
|
||||
_construct_response(client);
|
||||
_append_base_headers(client);
|
||||
_construct_response(client, server);
|
||||
_insert_status_line(client);
|
||||
if (client->status >= 400)
|
||||
_error_html_response(client, server);
|
||||
|
||||
ret = ::send(client->fd, client->response.data(), client->response.size(), 0);
|
||||
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
std::cerr << "client.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
}
|
||||
|
||||
if (client->raw_request.find("Connection: close") != std::string::npos)
|
||||
_close_client(client->fd);
|
||||
// Body send (WIP for working binary files)
|
||||
if (client->body_size)
|
||||
{
|
||||
ret = ::send(client->get_cl_fd(), client->buf, client->body_size, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client.get_cl_fd() =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||
_close_client(client->get_cl_fd());
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
_close_client(client->get_cl_fd());
|
||||
else
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->raw_request.clear();
|
||||
client->response.clear();
|
||||
_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_construct_response(Client *client)
|
||||
void Webserv::_append_base_headers(Client *client)
|
||||
{
|
||||
client->status = 200;
|
||||
client->response.append("Server: Webserv/0.1\r\n");
|
||||
client->response.append("Server: Webserv/0.1" CRLF);
|
||||
|
||||
if (client->raw_request.find("Connection: close") != std::string::npos)
|
||||
client->response.append("Connection: close\r\n");
|
||||
if (client->get_rq_headers("Connection") == "close")
|
||||
client->response.append("Connection: close" CRLF);
|
||||
else
|
||||
client->response.append("Connection: keep-alive\r\n");
|
||||
|
||||
_get_ressource(client);
|
||||
|
||||
_insert_status_line(client);
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
}
|
||||
|
||||
void Webserv::_construct_response(Client *client, ServerConfig &server)
|
||||
{
|
||||
// TODO : Move this in read(), stop read if content too large
|
||||
if (client->get_rq_body().size() > server.client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return;
|
||||
}
|
||||
LocationConfig &location = _determine_location(server, client->get_rq_uri());
|
||||
_process_method(client, server, location);
|
||||
}
|
||||
|
||||
void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE
|
||||
// after update in ConfigParser, use the "allow_methods" of location.
|
||||
// TODO in ConfigParser : by default if no field in config file, "allow_methods" must be set to ANY_METHODS
|
||||
|
||||
if (client->get_rq_method() == UNKNOWN)
|
||||
{
|
||||
client->status = 501;
|
||||
}
|
||||
else if (allow_methods & client->get_rq_method())
|
||||
{
|
||||
switch (client->get_rq_method())
|
||||
{
|
||||
case (GET):
|
||||
_get(client, server, location); break;
|
||||
case (POST):
|
||||
_post(client, server, location); break;
|
||||
case (DELETE):
|
||||
_delete(client, server, location); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 405;
|
||||
client->response.append("Allow: ");
|
||||
client->response.append(::http_methods_to_str(allow_methods));
|
||||
client->response.append(CRLF);
|
||||
}
|
||||
}
|
||||
|
||||
#define E400 "\r\n<!DOCTYPE html><html><head><title>400 Bad Request</title></head><body><h1 style=\"text-align:center\">400 Bad Request</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
#define E404 "\r\n<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1 style=\"text-align:center\">404 Not Found</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
#define E500 "\r\n<!DOCTYPE html><html><head><title>500 Internal Server Error</title></head><body><h1 style=\"text-align:center\">500 Internal Server Error</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
void Webserv::_insert_status_line(Client *client)
|
||||
{
|
||||
std::string status_line;
|
||||
|
||||
status_line.append("HTTP/1.1 ");
|
||||
// WIP, maybe make a map for status response
|
||||
switch (client->status)
|
||||
{
|
||||
case (200):
|
||||
status_line.append("200 OK");
|
||||
break;
|
||||
case (400):
|
||||
status_line.append("400 Not Found");
|
||||
client->response.append(E400);
|
||||
break;
|
||||
case (404):
|
||||
status_line.append("404 Not Found");
|
||||
client->response.append(E404);
|
||||
break;
|
||||
case (500):
|
||||
status_line.append("500 Internal Server Error");
|
||||
client->response.append(E500);
|
||||
break;
|
||||
}
|
||||
status_line.append("\r\n");
|
||||
|
||||
status_line.append(_http_status[client->status]);
|
||||
status_line.append(CRLF);
|
||||
client->response.insert(0, status_line);
|
||||
}
|
||||
|
||||
#define ROOT "website"
|
||||
#define INDEX "index.html"
|
||||
#define MAX_FILESIZE 1000000 // (1Mo)
|
||||
void Webserv::_get_ressource(Client *client)
|
||||
void Webserv::_error_html_response(Client *client, ServerConfig &server)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
char buf[MAX_FILESIZE+1];
|
||||
char *tmp;
|
||||
|
||||
// Mini parsing à l'arrache du PATH
|
||||
std::string path;
|
||||
try
|
||||
if (server.error_pages[client->status].empty())
|
||||
{
|
||||
path = client->raw_request.substr(0, client->raw_request.find("\r\n"));
|
||||
path = path.substr(0, path.rfind(" "));
|
||||
path = path.substr(path.find("/"));
|
||||
if (path == "/")
|
||||
path.append(INDEX);
|
||||
path.insert(0, ROOT);
|
||||
}
|
||||
catch (std::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
client->status = 400;
|
||||
return ;
|
||||
std::string html_page = HTML_ERROR;
|
||||
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
|
||||
_append_body(client, html_page.c_str(), html_page.size(), "html");
|
||||
}
|
||||
else
|
||||
_get_file(client, server.error_pages[client->status]);
|
||||
}
|
||||
|
||||
if (access(path.data(), R_OK) == -1)
|
||||
#define INDEX "index.html" // temp wip
|
||||
void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
std::string path = client->get_rq_uri();
|
||||
|
||||
if (path == "/") // TODO : index and autoindex
|
||||
path.append(INDEX);
|
||||
path.insert(0, location.root);
|
||||
|
||||
std::cerr << "path = " << path << "\n";
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
std::string script_output;
|
||||
if (_is_cgi(client))
|
||||
{
|
||||
script_output = _exec_cgi(client);
|
||||
// DEBUG
|
||||
std::cout << "\n____script_output____\n" << script_output << "\n_______________\n";
|
||||
// wip check output of script
|
||||
_check_script_output(client, script_output);
|
||||
client->response += script_output;
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
// char buf[MAX_FILESIZE+1];
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.data(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?)
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?)
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": open fail" << '\n';
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// WIP : Chunk or not chunk (if filesize too big)
|
||||
std::streampos size = ifd.tellg();
|
||||
|
||||
// WIP : Chunk or not chunk (if filesize too big)
|
||||
if (size > MAX_FILESIZE)
|
||||
{
|
||||
// Then chunk
|
||||
@@ -138,21 +202,211 @@ void Webserv::_get_ressource(Client *client)
|
||||
}
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
ifd.read(buf, size);
|
||||
buf[ifd.gcount()] = '\0';
|
||||
ifd.read(client->buf, size);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
client->buf[ifd.gcount()] = '\0';
|
||||
|
||||
client->response.append("Content-Type: text/html; charset=UTF-8\r\n");
|
||||
std::string file_ext = "";
|
||||
size_t dot_pos = path.rfind(".");
|
||||
std::cerr << "dot_pos = " << dot_pos << "\n";
|
||||
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
|
||||
file_ext = path.substr(dot_pos + 1);
|
||||
std::cerr << "file_ext = " << file_ext << "\n";
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
tmp = ::itoa(ifd.gcount());
|
||||
client->response.append(tmp);
|
||||
client->response.append("\r\n");
|
||||
|
||||
// Body
|
||||
client->response.append("\r\n");
|
||||
client->response.append(buf);
|
||||
|
||||
client->body_size = ifd.gcount();
|
||||
// WIP, pass empty body argument because append to string mess up binary file
|
||||
_append_body(client, "", client->body_size, file_ext);
|
||||
}
|
||||
|
||||
ifd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension)
|
||||
{
|
||||
/*
|
||||
TODO : determine Content-Type
|
||||
how ? read the body ?
|
||||
or before in other way (like based and file extension) and pass here as argument ?
|
||||
http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that.
|
||||
*/
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
client->response.append("Content-Type: ");
|
||||
client->response.append(mime_type);
|
||||
if (mime_type.find("text/") != std::string::npos)
|
||||
client->response.append("; charset=UTF-8");
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body_size);
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
}
|
||||
|
||||
void Webserv::_post(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
std::string path = client->get_rq_uri();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::binary | std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used body.size() so Content-Length useless at this point ?
|
||||
// Maybe usefull in _read_request() for rejecting too big content.
|
||||
// Need to _determine_process_server() as soon as possible,
|
||||
// like in _read_request() for stopping read if body is too big ?
|
||||
ofd.write(client->get_rq_body().c_str(), client->get_rq_body().size());
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
|
||||
ofd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_delete(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
std::string path = client->get_rq_uri();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
ServerConfig &Webserv::_determine_process_server(Client *client)
|
||||
{
|
||||
/*
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
_determine_process_server() should be complete.
|
||||
TODO : test it
|
||||
*/
|
||||
|
||||
std::string const &server_name = client->get_rq_headers("Host");
|
||||
std::vector<ServerConfig>::iterator it = _servers.begin();
|
||||
std::vector<ServerConfig>::iterator default_server = _servers.end();
|
||||
|
||||
while (it != _servers.end())
|
||||
{
|
||||
if (it->host == client->get_cl_lsocket()->host
|
||||
&& it->port == client->get_cl_lsocket()->port)
|
||||
{
|
||||
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
|
||||
break;
|
||||
else if (default_server == _servers.end())
|
||||
default_server = it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it != _servers.end())
|
||||
return (*it);
|
||||
else
|
||||
return (*default_server);
|
||||
}
|
||||
|
||||
LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string const &path)
|
||||
{
|
||||
/*
|
||||
Assume there is at least one location in vector<LocationConfig> for path "/"
|
||||
TODO in ConfigParser :
|
||||
If no location block in config file, one need to be generated
|
||||
for path "/", and filled with fields "root" and "index" based on parent server block
|
||||
*/
|
||||
|
||||
std::vector<LocationConfig>::iterator it = server.locations.begin();
|
||||
while (it != server.locations.end())
|
||||
{
|
||||
if (it->path.compare(0, path.size(), path))
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
if (it != server.locations.end())
|
||||
return (*it);
|
||||
else
|
||||
return (server.locations.front());
|
||||
}
|
||||
|
||||
@@ -4,14 +4,6 @@
|
||||
#define MAX_EVENTS 42 // arbitrary
|
||||
#define TIMEOUT 3000
|
||||
|
||||
// Temp. To move in other file
|
||||
bool operator==(const Client& lhs, const Client& rhs)
|
||||
{ return lhs.fd == rhs.fd; }
|
||||
bool operator==(const Client& lhs, int fd)
|
||||
{ return lhs.fd == fd; }
|
||||
bool operator==(int fd, const Client& rhs)
|
||||
{ return fd == rhs.fd; }
|
||||
|
||||
void Webserv::run()
|
||||
{
|
||||
std::cerr << "Server started\n";
|
||||
@@ -19,6 +11,7 @@ void Webserv::run()
|
||||
int nfds;
|
||||
int i;
|
||||
int count_loop = 0;
|
||||
std::vector<listen_socket>::iterator it_socket;
|
||||
|
||||
g_run = true;
|
||||
while (g_run)
|
||||
@@ -42,9 +35,9 @@ void Webserv::run()
|
||||
while (i < nfds)
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
if ((std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd) != _listen_sockets.end())
|
||||
&& (events[i].events & EPOLLIN))
|
||||
_accept_connection(events[i].data.fd);
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Le Webserv</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,300 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- saved from url=(0057)https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://dublincore.org/documents/2008/08/04/dc-html/"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>rfc2119</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="Verified-headnote-styling">
|
||||
<span style="font-weight: bold;">This is a purely informative rendering of an RFC that includes verified errata. This rendering may not be used as a reference.</span>
|
||||
<br>
|
||||
<br>
|
||||
The following 'Verified' errata have been incorporated in this document:
|
||||
<a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid493">EID 493</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 494</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_495">EID 495</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 496</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid498">EID 498</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_499">EID 499</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid500">EID 500</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_5101">EID 5101</a>
|
||||
</div>
|
||||
|
||||
<pre>Network Working Group S. Bradner
|
||||
Request for Comments: 2119 Harvard University
|
||||
BCP: 14 March 1997
|
||||
Category: Best Current Practice
|
||||
|
||||
|
||||
Key words for use in RFCs to Indicate Requirement Levels
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This document specifies an Internet Best Current Practices for the
|
||||
Internet Community, and requests discussion and suggestions for
|
||||
improvements. Distribution of this memo is unlimited.
|
||||
|
||||
Abstract
|
||||
|
||||
In many standards track documents several words are used to signify
|
||||
the requirements in the specification. These words are often
|
||||
capitalized. This document defines these words as they should be
|
||||
interpreted in IETF documents. Authors who follow these guidelines
|
||||
should incorporate this phrase near the beginning of their document:
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-499"> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL <button id="btn_499" target="expand_499" onclick="hideFunction("expand_499")">Expand</button>
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
|
||||
RECOMMENDED", "MAY", and "OPTIONAL" in this document are to
|
||||
be interpreted as described in RFC 2119.</span>
|
||||
<div class="nodeCloseClass" id="expand_499"><div class="Verified-endnote-styling" id="eid499">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid499">EID 499</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> Abstract
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
|
||||
"OPTIONAL" in this document are to be interpreted as described in
|
||||
RFC 2119.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
|
||||
RECOMMENDED", "MAY", and "OPTIONAL" in this document are to
|
||||
be interpreted as described in RFC 2119.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
The phrase "NOT RECOMMENDED" is missing from this sentence.
|
||||
</div>
|
||||
</div>
|
||||
Note that the force of these words is modified by the requirement
|
||||
level of the document in which they are used.
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-495">1. MUST This word, or the terms "REQUIRED" or "SHALL", means that the <button id="btn_495" target="expand_495" onclick="hideFunction("expand_495")">Expand</button>
|
||||
definition is an absolute requirement of the specification.
|
||||
<div class="Verified-endnote-styling" id="eid493">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid493">EID 493</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", mean that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", means that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid498">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid498">EID 498</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED", means that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid500">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid500">EID 500</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", means that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
<div class="nodeCloseClass" id="expand_495"><div class="Verified-endnote-styling" id="eid495">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid495">EID 495</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 1
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
1. MUST This word, or the terms "REQUIRED" or "SHALL", mean that the
|
||||
definition is an absolute requirement of the specification.
|
||||
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
1. MUST This word, or the terms "REQUIRED" or "SHALL", means that the
|
||||
definition is an absolute requirement of the specification.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
2. MUST NOT This phrase, or the phrase "SHALL NOT", mean that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
|
||||
3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
|
||||
4. SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
|
||||
<span class="Verified-inline-styling" id="inline-5101">5. MAY This word, or the adjective "OPTIONAL", mean that an item is <button id="btn_5101" target="expand_5101" onclick="hideFunction("expand_5101")">Expand</button>
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides).</span>
|
||||
<div class="nodeCloseClass" id="expand_5101"><div class="Verified-endnote-styling" id="eid5101">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid5101">EID 5101</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 5
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
5. MAY This word, or the adjective "OPTIONAL", mean that an item is
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides.)
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
5. MAY This word, or the adjective "OPTIONAL", mean that an item is
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides).
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
Full stop should appear outside the parentheses in the last sentence.
|
||||
</div>
|
||||
</div>
|
||||
6. Guidance in the use of these Imperatives
|
||||
|
||||
Imperatives of the type defined in this memo must be used with care
|
||||
and sparingly. In particular, they MUST only be used where it is
|
||||
actually required for interoperation or to limit behavior which has
|
||||
potential for causing harm <span class="Verified-inline-styling" id="inline-494">(e.g., limiting retransmissions)</span> For <button id="btn_494" target="expand_494" onclick="hideFunction("expand_494")">Expand Multiple</button>
|
||||
<div class="nodeCloseClass" id="expand_494"><div class="Verified-endnote-styling" id="eid494">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid494">EID 494</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 6
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
(e.g., limiting retransmisssions)
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
(e.g., limiting retransmissions)
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
<div class="Verified-endnote-styling" id="eid496">
|
||||
<pre><b><i><a href="https://www.rfc-editor.org/errata/eid496">EID 496</a> (Verified) is as follows:</i></b>
|
||||
|
||||
<b>Section:</b> 6
|
||||
|
||||
<b>Original Text:</b>
|
||||
|
||||
In particular, they MUST only be used where it is actually required
|
||||
for interoperation or to limit behavior which has potential for
|
||||
causing harm (e.g., limiting retransmisssions) For example, they
|
||||
must not be used to try to impose a particular method on
|
||||
implementors where the method is not required for interoperability.
|
||||
|
||||
<b>Corrected Text:</b>
|
||||
|
||||
In particular, they MUST only be used where it is actually required
|
||||
for interoperation or to limit behavior which has potential for
|
||||
causing harm (e.g., limiting retransmissions). For example, they
|
||||
must not be used to try to impose a particular method on
|
||||
implementors where the method is not required for interoperability.
|
||||
</pre>
|
||||
<b>Notes:</b><br>
|
||||
|
||||
</div>
|
||||
</div> example, they must not be used to try to impose a particular method
|
||||
on implementors where the method is not required for
|
||||
interoperability.
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
These terms are frequently used to specify behavior with security
|
||||
implications. The effects on security of not implementing a MUST or
|
||||
SHOULD, or doing something the specification says MUST NOT or SHOULD
|
||||
NOT be done may be very subtle. Document authors should take the time
|
||||
to elaborate the security implications of not following
|
||||
recommendations or requirements as most implementors will not have
|
||||
had the benefit of the experience and discussion that produced the
|
||||
specification.
|
||||
|
||||
8. Acknowledgments
|
||||
|
||||
The definitions of these terms are an amalgam of definitions taken
|
||||
from a number of RFCs. In addition, suggestions have been
|
||||
incorporated from a number of people including Robert Ullmann, Thomas
|
||||
Narten, Neal McBurnett, and Robert Elz.
|
||||
|
||||
9. Author's Address
|
||||
|
||||
Scott Bradner
|
||||
Harvard University
|
||||
1350 Mass. Ave.
|
||||
Cambridge, MA 02138
|
||||
|
||||
phone - +1 617 495 3864
|
||||
|
||||
email - sob@harvard.edu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</pre></body></html>
|
||||
BIN
www/drill.jpg
Normal file
BIN
www/drill.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
www/favicon.ico
Normal file
BIN
www/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
13
www/form_get.html
Normal file
13
www/form_get.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<form method="get">
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
13
www/form_post.html
Normal file
13
www/form_post.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post">
|
||||
<input type="submit" value="submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- <link href="styles/style.css" rel="stylesheet"> -->
|
||||
<title>Le Webserv</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
</html>
|
||||
BIN
www/kermit.ico
Normal file
BIN
www/kermit.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -13,7 +13,7 @@
|
||||
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-monochrome.css" title="Monochrome">
|
||||
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-printer.css" title="Printer">
|
||||
|
||||
<script src="./rfc2119_files/errata.js.téléchargement"></script>
|
||||
<script src="./rfc2119_files/errata.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="Verified-headnote-styling">
|
||||
139
www/rfc2119_files/errata-base.css
Normal file
139
www/rfc2119_files/errata-base.css
Normal file
@@ -0,0 +1,139 @@
|
||||
<style type="text/css">
|
||||
@media only screen
|
||||
and (min-width: 992px)
|
||||
and (max-width: 1199px) {
|
||||
body { font-size: 14pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-width: 768px)
|
||||
and (max-width: 991px) {
|
||||
body { font-size: 14pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-width: 480px)
|
||||
and (max-width: 767px) {
|
||||
body { font-size: 11pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (max-width: 479px) {
|
||||
body { font-size: 8pt; }
|
||||
div.content { width: 96ex; margin: 0 auto; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-device-width : 375px)
|
||||
and (max-device-width : 667px) {
|
||||
body { font-size: 9.5pt; }
|
||||
div.content { width: 96ex; margin: 0; }
|
||||
}
|
||||
@media only screen
|
||||
and (min-device-width: 1200px) {
|
||||
body { font-size: 10pt; margin: 0 4em; }
|
||||
div.content { width: 96ex; margin: 0; }
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
font-weight: bold;
|
||||
line-height: 0pt;
|
||||
display: inline;
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre {
|
||||
font-size: 1em;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.pre {
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
}
|
||||
.header{
|
||||
font-weight: bold;
|
||||
}
|
||||
.newpage {
|
||||
page-break-before: always;
|
||||
}
|
||||
.invisible {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
a.selflink {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
font-family: monospace;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.noprint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen {
|
||||
.grey, .grey a:link, .grey a:visited {
|
||||
color: #777;
|
||||
}
|
||||
.docinfo {
|
||||
background-color: #EEE;
|
||||
}
|
||||
.top {
|
||||
border-top: 7px solid #EEE;
|
||||
}
|
||||
.bgwhite { background-color: white; }
|
||||
.bgred { background-color: #F44; }
|
||||
.bggrey { background-color: #666; }
|
||||
.bgbrown { background-color: #840; }
|
||||
.bgorange { background-color: #FA0; }
|
||||
.bgyellow { background-color: #EE0; }
|
||||
.bgmagenta{ background-color: #F4F; }
|
||||
.bgblue { background-color: #66F; }
|
||||
.bgcyan { background-color: #4DD; }
|
||||
.bggreen { background-color: #4F4; }
|
||||
|
||||
.legend { font-size: 90%; }
|
||||
.cplate { font-size: 70%; border: solid grey 1px; }
|
||||
}
|
||||
|
||||
|
||||
.Verified-headnote-styling, .Held-headnote-styling, .Reported-headnote-styling, .Rejected-headnote-styling {
|
||||
border:dashed;
|
||||
margin:8px;
|
||||
padding;24px;
|
||||
overflow-wrap: normal;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.Verified-endnote-styling, .Held-endnote-styling, .Reported-endnote-styling, .Rejected-endnote-styling {
|
||||
border:dashed;
|
||||
margin:8px;
|
||||
padding:24px;
|
||||
overflow:auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.Verified-ineline-styling, .Held-ineline-styling, .Reported-ineline-styling, .Rejected-ineline-styling {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.nodeCloseClass {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.nodeOpenClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
</style>
|
||||
43
www/rfc2119_files/errata-color.css
Normal file
43
www/rfc2119_files/errata-color.css
Normal file
@@ -0,0 +1,43 @@
|
||||
<style type="text/css">
|
||||
.verified {color: green}
|
||||
.supplementary-styling {
|
||||
background-color: yellow
|
||||
}
|
||||
.old-text {color: red}
|
||||
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
background-color: LightGreen;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Verified-inline-styling {
|
||||
color: Green;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
background-color: LightBlue;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
color: Blue;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color:GoldenRod;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
|
||||
</style>
|
||||
34
www/rfc2119_files/errata-monochrome.css
Normal file
34
www/rfc2119_files/errata-monochrome.css
Normal file
@@ -0,0 +1,34 @@
|
||||
<style type="text/css">
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
font-wieght: bold;
|
||||
}
|
||||
.Verified-inline-styling {
|
||||
font-wieght: bold;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
font-style: italic;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color: Yellow;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
43
www/rfc2119_files/errata-printer.css
Normal file
43
www/rfc2119_files/errata-printer.css
Normal file
@@ -0,0 +1,43 @@
|
||||
<style type="text/css">
|
||||
.Verified-headnote-styling, .Verified-endnote-styling {
|
||||
font-wieght: bold;
|
||||
}
|
||||
.Verified-inline-styling {
|
||||
font-wieght: bold;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Held-headnote-styling, .Held-endnote-styling {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.Held-inline-styling {
|
||||
font-style: italic;
|
||||
background-color: lightGrey;
|
||||
}
|
||||
|
||||
.Reported-headnote-styling, .Reported-endnote-styling {
|
||||
background-color: Beige;
|
||||
}
|
||||
|
||||
.Reported-inline-styling {
|
||||
color: Yellow;
|
||||
}
|
||||
|
||||
.Rejected-headnote-styling, .Rejected-endnote-styling {
|
||||
background-color: LightPink;
|
||||
}
|
||||
|
||||
.Rejected-inline-styling {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.nodeCloseClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
.nodeOpenClass {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
</style>
|
||||
4
www/rfc2119_files/errata.js
Normal file
4
www/rfc2119_files/errata.js
Normal file
@@ -0,0 +1,4 @@
|
||||
function hideFunction(nodeId) {
|
||||
var ul = document.getElementById(nodeId)
|
||||
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
|
||||
}
|
||||
4
www/rfc2119_files/errata.js.téléchargement
Normal file
4
www/rfc2119_files/errata.js.téléchargement
Normal file
@@ -0,0 +1,4 @@
|
||||
function hideFunction(nodeId) {
|
||||
var ul = document.getElementById(nodeId)
|
||||
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
|
||||
}
|
||||
BIN
www/root.png
Normal file
BIN
www/root.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 446 KiB |
Reference in New Issue
Block a user