96 Commits

Author SHA1 Message Date
hugogogo
f7b31c7db7 added semaphore and shared memory to print without superposition between parent and child process 2022-08-22 14:52:31 +02:00
hugogogo
5225e3258b lots of log message and fixed bad_file_descriptor issue 2022-08-22 01:50:28 +02:00
lperrey
469eca8aa9 clean comment and stuff 2022-08-18 12:44:09 +02:00
lperrey
9b0fcc1520 littles adjustements but still no solution to the main problem 2022-08-18 10:48:57 +02:00
Hugo LAMY
dda0103fb8 clean wip in form_cgi 2022-08-18 08:35:43 +02:00
lperrey
3d17db996a Debug in progress, somes fix but not the main problem 2022-08-18 06:03:09 +02:00
lperrey
a008c12058 more kermit added 2022-08-18 00:21:01 +02:00
Eric LAZO
6694ffeb66 Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv 2022-08-18 00:19:31 +02:00
Eric LAZO
3530595b01 clean up config 2022-08-18 00:19:23 +02:00
lperrey
b34e49311b body write to CGI now monitored by epoll
+ kermit.jpg :)
2022-08-17 23:42:12 +02:00
lperrey
2613ca2e1a little delete fix in main.cpp 2022-08-17 20:55:58 +02:00
Eric LAZO
e537e9bb78 Merge remote-tracking branch 'origin/master' into eric_config_parser 2022-08-17 20:46:05 +02:00
Eric LAZO
3763249a3e baseline tests of Telnet Test are ready 2022-08-17 20:44:17 +02:00
Hugo LAMY
366178303f Merge branch 'hugo5' 2022-08-17 20:42:09 +02:00
Hugo LAMY
df24de46c7 works on download script 2022-08-17 20:41:22 +02:00
Eric LAZO
72b12132d4 Merge remote-tracking branch 'origin/master' into eric_config_parser 2022-08-17 19:46:02 +02:00
Eric LAZO
da5d1f38b0 merging 2022-08-17 19:45:37 +02:00
Hugo LAMY
875810c992 merge hugo5 with script cgi tests 2022-08-17 18:26:06 +02:00
Hugo LAMY
c6ebb95dc5 added script download test 2022-08-17 18:21:55 +02:00
Eric LAZO
4fdbb1e0eb conclusion, no real php cgi 2022-08-17 17:59:53 +02:00
Eric LAZO
b45ed52a2a pulling more 2022-08-17 16:18:37 +02:00
Me
1a99c6cc84 merging small fixes andmore telnet tests 2022-08-17 02:51:32 +02:00
lperrey
9f3903d497 Fixed, close() forgottens fds after fork
+ minors changes
+ Super new epoll loop output :)
2022-08-17 01:49:31 +02:00
lperrey
ccc542f52b Fixed big problem with CGI handling
+ remains somes minors problems
2022-08-17 01:11:20 +02:00
Eric LAZO
19a9d3b464 more telnet tests 2022-08-16 23:54:30 +02:00
lperrey
4dc70373f8 debug message 2022-08-16 20:58:48 +02:00
Eric LAZO
1b7d388231 a few small fixes here and there, more telnet tests, starting Siege load testing 2022-08-16 20:41:57 +02:00
Hugo LAMY
517f5dfc8a script test sleep input by user 2022-08-16 20:28:40 +02:00
lperrey
2c6bc096cc WIP CGI monitered by epoll
+ OK, but some errors case need to be lookup
2022-08-16 18:38:58 +02:00
Hugo LAMY
5c5f298493 tmp clean of debug messages 2022-08-16 18:37:02 +02:00
Hugo LAMY
1ed4128afc more script tests
+ add cgi checks for error in script output
+ fix script_path relative to absolute
+ cgi makefile more efficient
2022-08-16 18:24:49 +02:00
LuckyLaszlo
ff443c80b1 _exec_script() close fd and reset signal
+ somes more adjustements in _exec_script()
+ rough notes for non blocking CGI
2022-08-16 06:50:20 +02:00
LuckyLaszlo
4602844f5a Multiples minors changes (cgi env, comment, ...) 2022-08-16 04:00:33 +02:00
Hugo LAMY
2a1aec8f1d cgi scrip with 10 tests
+ if http error status, script output not appended to respons
2022-08-16 02:06:26 +02:00
Hugo LAMY
4a0c0ee238 Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv 2022-08-16 02:04:57 +02:00
lperrey
5627b6d1a2 fixed last commit mistake
+ renamed cgi_script.cpp
2022-08-16 02:04:35 +02:00
Hugo LAMY
5e403fc71c Merge branch 'hugo5' 2022-08-16 01:46:52 +02:00
lperrey
f8c6923c6d fixed CGI alloc
+ handle unknow status from script
2022-08-16 01:46:24 +02:00
Hugo LAMY
8c2aff6c6b modif and added several scipts for testing cgi
+ modif html page for script tests
+ script output not added to response in case of http error
2022-08-16 01:39:46 +02:00
Eric LAZO
e691c517e8 Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv 2022-08-16 00:26:55 +02:00
Eric LAZO
973388bf97 Tester is much better, ready for tests to be added 2022-08-16 00:26:28 +02:00
lperrey
48af92b3bc fixed _error_html_response()
+ renamed eval_file_mode() in eval_file_access()
+ memo update
2022-08-15 22:12:55 +02:00
Hugo LAMY
c05536ca01 hugo merge and clean some debug messages 2022-08-15 21:21:40 +02:00
Hugo LAMY
008b2a635f Merge branch 'hugo5' 2022-08-15 21:18:06 +02:00
Eric LAZO
54b6d1f6d6 not much 2022-08-15 20:47:13 +02:00
Eric LAZO
c9c8946e70 merge, the whole thing is cleaner 2022-08-15 19:54:26 +02:00
Eric LAZO
19da59ecf8 cleaned things up 2022-08-15 19:40:21 +02:00
LuckyLaszlo
fe89be65f6 Tested and fixed server_name resolution 2022-08-15 19:31:43 +02:00
hugogogo
1c13e254d5 basic cgi script test working 2022-08-15 18:26:41 +02:00
LuckyLaszlo
3fe37ea451 DELETE method tested and fixed
+ added 404 for directories without index and autoindex
+ minors adjustements
2022-08-15 18:16:11 +02:00
hugogogo
6ad6ec7d63 wip cgi ouptput 2022-08-15 16:18:47 +02:00
hugogogo
036256522a makefile for cgi_scripts 2022-08-15 15:28:19 +02:00
lperrey
a284e400c1 changed client_body_limit (now in KB)
+ multiples little adjusts
2022-08-15 02:14:27 +02:00
Hugo LAMY
9ac84f706d fix error in check scrip output length 2022-08-15 02:07:06 +02:00
Hugo LAMY
b9d4317f51 in _is_cgi, fix error in return http error status
+ fix error in checking script output : reverse order between calcul of content_length and erasing empty line
2022-08-15 01:52:36 +02:00
Eric LAZO
aa03880601 still working on telnet tester 2022-08-15 01:47:37 +02:00
Hugo LAMY
a6bfea3b56 Merge branch 'hugo4' 2022-08-15 00:32:47 +02:00
Hugo LAMY
f6f63931ad litle fix in utils trim
+ added 2 functions to check script output
+ created a html page with 4 forms for tests
+ created 2 cpp forms, with and without creating header content-length
2022-08-15 00:24:08 +02:00
Eric LAZO
8000668b36 Merge branch 'eric_config_parser' 2022-08-14 21:42:08 +02:00
Eric LAZO
21efbef86a better telnet tester, merging 2022-08-14 21:41:46 +02:00
Eric LAZO
076f46fb85 .gitignore 2022-08-14 21:29:20 +02:00
Eric LAZO
683dbadb91 better telnet tester 2022-08-14 21:26:57 +02:00
LuckyLaszlo
8d4961c9b5 memo.txt update 2022-08-14 17:53:29 +02:00
Hugo LAMY
d663a4c7e6 added debug print 2022-08-14 16:17:24 +02:00
LuckyLaszlo
37c5be9ffc Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv 2022-08-14 06:32:07 +02:00
LuckyLaszlo
ce0a004d28 Le merge / 20 2022-08-14 06:30:27 +02:00
LuckyLaszlo
84babec82b added multipart upload file,
it works, but need some adjustements,
refactoring and testing
2022-08-14 06:25:06 +02:00
Me
ddafa229c6 something done to the telnet tester 2022-08-14 03:49:29 +02:00
Hugo LAMY
be499328f6 fixed error in is_cgi, path was missing dot 2022-08-14 01:17:55 +02:00
Hugo LAMY
41db4fc12b Merge branch 'hugo4' 2022-08-14 01:10:01 +02:00
Hugo LAMY
a20a5eff27 Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv 2022-08-14 01:09:25 +02:00
Hugo LAMY
db4c7468cc is_cgi function is back and is working
+ in utils added function eval_file_mode(), it returns http errors codes about file
2022-08-14 01:05:20 +02:00
Eric LAZO
dda32c759a decent start to bash tester 2022-08-13 22:38:31 +02:00
LuckyLaszlo
b0949615c8 fixed mistake in _read_request()
+ changes to some log output
2022-08-13 19:17:32 +02:00
hugogogo
285f2d049f + in fill_script_path() : scipt_path and script_info are filled, removing leadin dot
+ in _cgi_pos() : check validity of extension if it's only alpha characters
+ severall pbm appears
2022-08-13 14:38:50 +02:00
hugogogo
defb2ada61 fixed pbm in extract_line : delete the line and the newline sequence character
+ fixed pbm n _cgi_pos : last return is npos, not pos
2022-08-13 12:39:05 +02:00
Me
ebd0fda52c more mucking about with telnet 2022-08-13 05:57:50 +02:00
Me
c3240b8618 i found a mistake but still doesn't work... 2022-08-13 03:52:33 +02:00
Me
3495ff19a8 mucked about with Telnet 2022-08-13 03:01:41 +02:00
LuckyLaszlo
058701f657 upload_form.html
+ need to add multipart/form-data parsing
2022-08-13 01:15:59 +02:00
Me
a69273b88e fixed an autoindex issue, working on uploading files 2022-08-12 23:45:13 +02:00
hugogogo
f17bc9fa58 wip test path cgi :
- _cgi_pos seems to be ok
  - construct url from return is not implemented yet
+ renamed script with extension .php and .cpp
2022-08-12 23:31:59 +02:00
Eric LAZO
4d395088d0 trying to get submit form to work... 2022-08-12 20:08:49 +02:00
LuckyLaszlo
c7bbf29a1b NPOS macro 2022-08-12 18:16:49 +02:00
LuckyLaszlo
b44acafefe CGI discussion and a little bit of work done 2022-08-12 18:08:39 +02:00
Me
cade79c37f started non hardcoded cgi call 2022-08-12 16:20:50 +02:00
Eric LAZO
52824f30bd merged and added colors to makefile 2022-08-12 15:24:10 +02:00
hugogogo
2a70c6b26d Merge branch 'hugo3'
+ script output verification works (field duplicate and status)
+ parsing_message_http :
  - the file is deleted
  - of the three functions it contained, only one still exist
  - 'parse_http_headers' is now in utils
+ changes in utils :
  - http headers parsing doesn't change key case itself
  - a new function change key case tolower case in map<str, str>
  - added split_trim() that perform a split and a trim at once
  - resolved pbm in trim
  - added print_special to print special char '\r' and '\n'
  - del_line became extract_line, because it returns the deleted line
  - added get line, that does the same without deleting
  - moved http_header in utils, and made it more consistent
+ in Client :
  - parse_request is now named parse_request_headers to work with parse body
  - private function _parse_request_headers is then now _parse_request_fields
    (because it doesn't take care of request first line, but only fields)
  - added a debug function 'print_client'
2022-08-12 14:05:11 +02:00
LuckyLaszlo
400efbe720 Wip chunked decoding
+ Need to test normal body parsing
+ path_is_valid() renamed eval_file_type()
+ replaced atoi with strtol/strtoul
2022-08-12 05:50:00 +02:00
Me
f7c0ff1a8a clean up 2022-08-12 04:38:07 +02:00
LuckyLaszlo
ab0bc2c4c0 added timeout response (status 408)
+ added EPOLLERR and EPOLLHUP handling
+ fix root substitution for default "/" location (temp or permanent ?)
+ tested siege a little, seems good
2022-08-11 07:12:13 +02:00
Me
08f6929db9 fixed autoindex 2022-08-11 02:15:40 +02:00
Me
c32fc2c8a2 I really hope this is the last time we fix Location Picker, i think it's good it's pretty elegant, basically set root and location path to never have / at end and that made things easier 2022-08-11 02:01:24 +02:00
Me
0b51d13f13 a few more things for location picker 2022-08-11 00:36:24 +02:00
LuckyLaszlo
360c27df5f done root substitution
+ autoindex/index adjustement
2022-08-10 19:59:05 +02:00
Me
69c1a6f6bf A Much better way of picking locations 2022-08-10 16:14:21 +02:00
142 changed files with 6066 additions and 206322 deletions

2
.gitignore vendored
View File

@@ -22,3 +22,5 @@ webserv
*.log
large.jpg
webserv_tester
webserv_tester2

3
1 Normal file
View File

@@ -0,0 +1,3 @@
------

23
42.config Normal file
View File

@@ -0,0 +1,23 @@
server {
server_name server1;
listen 0.0.0.0:4040;
client_body_limit 1000;
index youpi.bla; # this is another comment
root ./YoupiBanane/;
error_page 404 ./www/error_pages/error_404.html;
location / {
allow_methods GET;
}
location /directory {
index youpi.bad_extention;
}
}

View File

@@ -2,12 +2,12 @@
NAME = webserv
CXX = clang++
CXXFLAGS = -Wall -Wextra #-Werror
CXXFLAGS = -Wall -Wextra -Werror
CXXFLAGS += $(HEADERS_D:%=-I%)
CXXFLAGS += -std=c++98
CXXFLAGS += -g3
CXXFLAGS += -MMD -MP #header dependencie
#CXXFLAGS += -O3
CXXFLAGS += -g
CXXFLAGS += -MMD -MP # header dependencie
# for debug
VPATH = $(SRCS_D)
@@ -17,44 +17,61 @@ HEADERS_D = srcs \
SRCS_D = srcs \
srcs/webserv \
srcs/config
srcs/config \
SRCS = main.cpp \
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
accept.cpp request.cpp response.cpp \
method_get.cpp method_post.cpp method_delete.cpp \
run_loop.cpp \
parser.cpp \
extraConfig.cpp \
postProcessing.cpp \
run_loop.cpp timeout.cpp \
parser.cpp extraConfig.cpp postProcessing.cpp \
utils.cpp \
cgi_script.cpp \
Client.cpp \
cgi.cpp cgi_epoll.cpp \
Client.cpp Client_multipart_body.cpp \
OBJS_D = builds
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
DEPS = $(OBJS:.o=.d) #header dependencie
# --------------------
# ------ RULES -------
# --------------------
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# . target: prerequisites . $@ : target #
# RULES . recipe . $< : 1st prerequisite #
# . @recipe (silent) . $^ : all prerequisites #
# . target: VAR = assignment . | : order-only prereq. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
all: $(NAME)
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(CXX) -c $< -o $@ $(CXXFLAGS)
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
$(OBJS_D):
mkdir $@
$(NAME): $(OBJS)
$(CXX) $^ -o $(NAME)
# $(CXX) $^ -o $(NAME)
# add -lpthread for semaphore library
$(CXX) $^ -o $(NAME) -lpthread
echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
# CGI
cgi:
make -C srcs/cgi-bin
cgiclean:
make clean -C srcs/cgi-bin
cgifclean:
make fclean -C srcs/cgi-bin
cgire:
make re -C srcs/cgi-bin
clean:
rm -rf $(OBJS_D)
echo "$(_CYAN).o Files Deleted 🤫$(_END)"
fclean: clean
rm -f $(NAME)
echo "$(_CYAN)$(NAME) Deleted 🤫$(_END)"
re: fclean all
@@ -62,3 +79,24 @@ re: fclean all
-include $(DEPS) # header dependencie
.SILENT:
# ------------------
# ----- COLORS -----
# ------------------
_GREY=$ \033[30m
_RED=$ \033[31m
_GREEN=$ \033[32m
_YELLOW=$ \033[33m
_BLUE=$ \033[34m
_PURPLE=$ \033[35m
_CYAN=$ \033[36m
_WHITE=$ \033[37m
_END=$ \033[0m

149
README.md
View File

@@ -1,29 +1,6 @@
## work together
#### TODO hugo
- `_is_cgi()` and `_fill_cgi_path()`
- two cgi tests :
? - a basic form with "name" and "something", that return a html page with that
? - for GET and POST
? - a script called by a file extension in URI
#### questions
- in client, fd is in privat, so accesible by getter, is it ok ?
- in client.cpp i fill the port, is there a default one in case it's not in the request ?
- timeout server but still works ?
- path contains double "//" from `Webserv::_get()` in response.cpp
- cgi path ? defined in config ? and root path ? :
- `Client.cpp : fill_script_path()`
- `cgi.cpp : is_cgi()`
- `cgi.cpp : set_env()`
- what if the uri contains a php file, and the config said php must be handled by cgi, but the path to this php in the uri is wrong ?
- is it ok ? `http://my_site.com/cgi-bin/php-cgi` (real path)
- is it ok ? `http://my_site.com/php-cgi` (reconstruct path ?)
- is it ok ? `http://my_site.com/something/php-cgi` (what about 'something' ?)
- is it ok ? `http://my_site.com/something/cgi-bin/php-cgi` (real path with 'something' before ? )
- I don't save the STDIN and STDOUT before dup2 in child process, is it wrong ?
- the response page is received long after the cgi-script is done, why ?
---
## man
@@ -104,18 +81,6 @@
[rfc 3875](https://www.rfc-editor.org/rfc/rfc3875)
#### output cgi script :
! TODO : change all the '\n' by '\r\n'
! TODO : there is at least one header field followed by '\r\n\r\n' :
- "Content-Type"
- "Location"
- "Status"
! TODO : there is no space between filed name and ":"
! TODO?: handle Location field, either :
- local : start with '/' --> rerun the request with new uri
- client : start with '<scheme>:' --> send back status code 302
-> TODO : there is no field duplicate (resolve conflicts)
-> TODO : if status field, change server status for this one
-> TODO : if no Location field && no Status field -> status code = 200
#### summary :
- the cgi-script will send back at least one header field followed by an empty line
@@ -125,13 +90,18 @@
- "Status"
- the cgi-script may send back more header fields
- the server must check and modify few things :
- there is no duplicate in headers fields (if there is, resolve conflict)
- there is no space between the field name and the ":"
- the newlines are of form "\r\n", and not "\n" only
- if the location field is not present, then if the status field is not present either, then the status code is 200
- the cgi-script can return a location field, of two types :
- local redirection : start with a "/", the server must answer as if this was the request uri
- client redirection : start with <name-of-cheme>":", the server must send back a status 302 with this uri to the client
- there is no field duplicate (resolve conflicts)
- there is no space between filed name and ":"
- change all the '\n' by '\r\n'
- if no Location field && no Status field -> status code = 200
- handle Location field, either :
- local : start with '/' --> rerun the request with new uri
- client : start with '<scheme>:' --> send back status code 302
- there is at least one header field followed by '\r\n\r\n' :
- "Content-Type"
- "Location"
- "Status"
- if status field, change server status for this one
- to pass the body-message to the cgi-script, we write it into the temporary fd on which the script read it's standard input
[3.1: server responsabilities](https://www.rfc-editor.org/rfc/rfc3875#section-3.1)
@@ -232,45 +202,6 @@
[7 and 8: usefull informations about implementation and security](https://www.rfc-editor.org/rfc/rfc3875#section-7)
---
## http errors
#### HTTP Client Errors
- 400 Bad Request This error code indicates that the request cannot be processed because of wrong syntax usage by the client.
- 401 Unauthorized This error code indicates that the client is not authorized to receive the requested data, without authentication. A login name and password based authentication might be required to access the requested data.
- 403 Forbidden There is no way you can access the requested data. A 403 error announces that the data is off limits.
- 404 Not Found This error indicates that the resources requested by the client are currently unavailable.
- 405 Method Not Allowed This error indicates wrong usage of request method. Depending on the kind of data requested, the appropriate request method must be chosen.
- 406 Not Acceptable When the data provided by a web server does not match the specifications made in Accept header of the client HTTP request, this error is the result.
- 407 Proxy Authentication Required This error clearly indicates that an authentication from the proxy server is required to gain access to requested resources.
- 408 Request Timeout This type of error indicates that the client was delayed in making a request, within the specified time allocated to it, by the server.
- 409 Conflict This error code is displayed when the server perceives a conflict between two requests made simultaneously by different clients, for the same resource.
- 410 Gone This error code indicates that the requested data is no longer hosted on the server and therefore further requests made for it, would be futile.
- 411 Length Required If the request made by the client does not include information about the length of the requested data or resource, this error code is displayed.
- 412 Precondition Failed Some requests made by clients come attached with conditions that need to be satisfied by the server, before data transaction may happen. If these conditions are not met, error 412 results.
- 413 Request Entity Too Large When a client makes a request which is too overwhelming for the servers resources to handle, it presents this error code.
- 414 Requested URI Too Long A Uniform Resource Identifier (URI) is a character string used to describe a data stream or resource on a server. Error 414 occurs when the server is unable to process the URI, because of limited resources and long string length.
- 415 Unsupported Media Type A server may be designed to allow only certain formats for media files. When error 415 is displayed, it indicates that the format of file being uploaded through a client request, does not match the requisite format.
- 416 Request Range Not Satisfiable Sometimes, a client may request for only a small part of a file, instead of asking for the entire file. If this request is not specified properly and the part of the file requested does not exist, this error is displayed.
- 417 Expectation Failed This error code is displayed when the server cannot meet the specifications provided in the request.
- 422 Unprocessable Entity This error is displayed when the request made, cannot be processed due to an error in semantic structure.
- 423 Locked This error is displayed when a requested piece of data or resource has been locked, making it inaccessible for a server.
- 424 Failed Dependency A server may process a succession of requests from a client with the fulfillment of each, dependent on the one made before. This error is displayed when a request made before is not fulfilled, due to which the current request cannot be processed.
- 426 Upgrade Required This error signifies that the client may need to switch over to a secure protocol like TLS to get the request processed.
- 444 No Response This error signifies that the server has simply rejected the client request and terminated connection.
- 449 Retry With This is a request made by the server to the client, to make the request again after executing certain actions or making specific changes in request. This is an error code introduced by Microsoft.
- 499 Client Closed Request When client terminates a connection made with the server, while its processing the associated request, this error code is displayed.
- 450 Blocked By Windows Parental Controls Another error code introduced by Microsoft, this one is displayed when a URL is blocked by parental control settings on the web browsers.
#### HTTP Server Errors
- 500 Internal Server Error A generic message displayed by the server, when the problem with the request cannot be specified by any other appropriate code.
- 501 Not Implemented This error indicates the inability of the server to process a request, as it hasnt been configured to respond to the request method used.
- 502 Bad Gateway Sometimes, hosted pages on web servers are transmitted to client via proxy servers. If the proxy server (to which a client has sent a request), fails connecting with the web server (known as the upstream server), error 502 results.
- 503 Service Unavailable When the server is already overloaded with multiple requests, it will temporarily stop entertaining new requests, by displaying a 503 error code.
- 504 Gateway Timeout When the request made by a proxy server to the web server hosting a resource times out, error 504 is reported.
- 505 HTTP Version Not Supported An error code seen rarely, it is displayed when the web server does not support the protocol version of the client request.
---
## cgi env variables
[cgi env variables](https://www.rfc-editor.org/rfc/rfc3875#section-4.1)
@@ -301,6 +232,62 @@ SERVER_SOFTWARE : the server software you're using (e.g. Apache 1.3)
REDIRECT_STATUS : for exemple, 200
```
g 50 34 48
p 30 23 32
l 20 14 20
71
---
## http status
[rfc 2616](https://datatracker.ietf.org/doc/html/rfc2616#section-10)
#### Informational
- 100 Continue
- 101 Switching Protocols
#### Successful
- 200 OK
- 201 Created
- 202 Accepted
- 203 Non-Authoritative Information
- 204 No Content
- 205 Reset Content
- 206 Partial Content
#### Redirection
- 300 Multiple Choices
- 301 Moved Permanently
- 302 Found
- 303 See Other
- 304 Not Modified
- 305 Use Proxy
- 306 (Unused)
- 307 Temporary Redirect
#### Client Error
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 406 Not Acceptable
- 407 Proxy Authentication Required
- 408 Request Timeout
- 409 Conflict
- 410 Gone
- 411 Length Required
- 412 Precondition Failed
- 413 Request Entity Too Large
- 414 Request-URI Too Long
- 415 Unsupported Media Type
- 416 Requested Range Not Satisfiable
- 417 Expectation Failed
#### Server Error
- 500 Internal Server Error
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 505 HTTP Version Not Supported
---
## ressources

View File

@@ -0,0 +1,29 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Type: text/html; charset=UTF-8
Content-Length: 199
<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 style="text-align:center">405 Method Not Allowed</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>HTTP/1.1 204 No Content
Server: Webserv/0.1
Connection: keep-alive
HTTP/1.1 404 Not Found
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 210
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
<hr>
<p style=\"text-align:center\">Le Webserv/0.1</p>
</body>
</html>

View File

@@ -0,0 +1,15 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 501 Not Implemented
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 193
<!DOCTYPE html><html><head><title>501 Not Implemented</title></head><body><h1 style="text-align:center">501 Not Implemented</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>HTTP/1.1 415 Unsupported Media Type
Accept-Encoding:
Content-Type: text/html; charset=UTF-8
Content-Length: 207
<!DOCTYPE html><html><head><title>415 Unsupported Media Type</title></head><body><h1 style="text-align:center">415 Unsupported Media Type</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>

View File

@@ -0,0 +1,37 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 193
<!DOCTYPE html>
<html>
<head>
<title>Le Webserv</title>
</head>
<body>
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Type: text/html; charset=UTF-8
Content-Length: 199
<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 style="text-align:center">405 Method Not Allowed</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Type: text/html; charset=UTF-8
Content-Length: 199
<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 style="text-align:center">405 Method Not Allowed</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>HTTP/1.1 501 Not Implemented
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 193
<!DOCTYPE html><html><head><title>501 Not Implemented</title></head><body><h1 style="text-align:center">501 Not Implemented</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>

View File

@@ -0,0 +1,112 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 193
<!DOCTYPE html>
<html>
<head>
<title>Le Webserv</title>
</head>
<body>
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 227
<!DOCTYPE html>
<html>
<head>
<title>Webserv test index</title>
<link rel="stylesheet" href="/stylesheet/style.css">
</head>
<body>
<h1>Webserv Test Index</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 227
<!DOCTYPE html>
<html>
<head>
<title>Webserv test index</title>
<link rel="stylesheet" href="/stylesheet/style.css">
</head>
<body>
<h1>Webserv Test Index</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 1281
<!DOCTYPE html><html><head><title>Index of ./Tester/www/</title></head><body><h1>Index of ./Tester/www/</h1><hr><pre><a style="font-size:1.5em" href="/list/..">../</a>
<a style="font-size:1.5em" href="/list/upload_form.html">upload_form.html</a>
<a style="font-size:1.5em" href="/list/form_get.html">form_get.html</a>
<a style="font-size:1.5em" href="/list/kermit.ico">kermit.ico</a>
<a style="font-size:1.5em" href="/list/upload_form_single.html">upload_form_single.html</a>
<a style="font-size:1.5em" href="/list/Cagneyc_intro.gif">Cagneyc_intro.gif</a>
<a style="font-size:1.5em" href="/list/Van_Eyck_Portrait_Arnolfini.jpg">Van_Eyck_Portrait_Arnolfini.jpg</a>
<a style="font-size:1.5em" href="/list/root.png">root.png</a>
<a style="font-size:1.5em" href="/list/test">test/</a>
<a style="font-size:1.5em" href="/list/drill.jpg">drill.jpg</a>
<a style="font-size:1.5em" href="/list/punpun.png">punpun.png</a>
<a style="font-size:1.5em" href="/list/favicon.ico">favicon.ico</a>
<a style="font-size:1.5em" href="/list/index.html">index.html</a>
<a style="font-size:1.5em" href="/list/rfc2119.html">rfc2119.html</a>
<a style="font-size:1.5em" href="/list/error_pages">error_pages/</a>
<a style="font-size:1.5em" href="/list/rfc2119_files">rfc2119_files/</a>
</pre><hr></body></html>HTTP/1.1 404 Not Found
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 210
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
<hr>
<p style=\"text-align:center\">Le Webserv/0.1</p>
</body>
</html>
HTTP/1.1 404 Not Found
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 210
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
<hr>
<p style=\"text-align:center\">Le Webserv/0.1</p>
</body>
</html>

View File

@@ -0,0 +1,110 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 193
<!DOCTYPE html>
<html>
<head>
<title>Le Webserv</title>
</head>
<body>
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/css; charset=UTF-8
Content-Length: 43
h1 {
color: red;
text-align: center;
}
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 207
<!DOCTYPE html>
<html>
<head>
<title>Webserv test Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Something</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 207
<!DOCTYPE html>
<html>
<head>
<title>Webserv test Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Something</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 227
<!DOCTYPE html>
<html>
<head>
<title>Webserv test index</title>
<link rel="stylesheet" href="/stylesheet/style.css">
</head>
<body>
<h1>Webserv Test Index</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>
HTTP/1.1 200 OK
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 1281
<!DOCTYPE html><html><head><title>Index of ./Tester/www/</title></head><body><h1>Index of ./Tester/www/</h1><hr><pre><a style="font-size:1.5em" href="/list/..">../</a>
<a style="font-size:1.5em" href="/list/upload_form.html">upload_form.html</a>
<a style="font-size:1.5em" href="/list/form_get.html">form_get.html</a>
<a style="font-size:1.5em" href="/list/kermit.ico">kermit.ico</a>
<a style="font-size:1.5em" href="/list/upload_form_single.html">upload_form_single.html</a>
<a style="font-size:1.5em" href="/list/Cagneyc_intro.gif">Cagneyc_intro.gif</a>
<a style="font-size:1.5em" href="/list/Van_Eyck_Portrait_Arnolfini.jpg">Van_Eyck_Portrait_Arnolfini.jpg</a>
<a style="font-size:1.5em" href="/list/root.png">root.png</a>
<a style="font-size:1.5em" href="/list/test">test/</a>
<a style="font-size:1.5em" href="/list/drill.jpg">drill.jpg</a>
<a style="font-size:1.5em" href="/list/punpun.png">punpun.png</a>
<a style="font-size:1.5em" href="/list/favicon.ico">favicon.ico</a>
<a style="font-size:1.5em" href="/list/index.html">index.html</a>
<a style="font-size:1.5em" href="/list/rfc2119.html">rfc2119.html</a>
<a style="font-size:1.5em" href="/list/error_pages">error_pages/</a>
<a style="font-size:1.5em" href="/list/rfc2119_files">rfc2119_files/</a>
</pre><hr></body></html>HTTP/1.1 301 Moved Permanently
Location: https://berniesanders.com/404/
HTTP/1.1 307 Temporary Redirect
Location: https://fr.wikipedia.org/wiki/Ketchup

95
Tester/telnet_test.sh Executable file
View File

@@ -0,0 +1,95 @@
#! /bin/bash
# DO I EVEN NEED THIS FILE ? could i just put this stuff in maint_test.sh? or would it not modify the vars...
# you need to put absolutely everything in ""
#test_file=$1
#source $test_file
connect_to_telnet="open $host $port"
start_telnet()
{
echo "open $host $port"
sleep 1
}
run()
{
echo "$connect_to_telnet"
sleep 1
run_this_test
}
#### DEPRICATED ######
run_telnet()
{
echo "$connect_to_telnet"
sleep 1
echo -e "$request"
echo
sleep 1
# ret = $(arg | telnet)
}
run_a_test()
{
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} &> test.log
echo -e "$request" | telnet
}
#######
# This is where stuff is launched
#######
#rm -rf telnet.out
#./webserv $config_file 2>&1 > webserv.log & run_all
#./webserv $config_file 2>&1 > webserv.log &
#./webserv $config_file &> /dev/null &
#./webserv $config_file 1>&1 > webserv.log &
#./webserv $config_file &> webserv.log &
#run_all
#run_this_test
#echo -e "${_GREEN}Running Telnet Test on '$test_name'${_END}"
#sleep 1
#run | telnet >> telnet.out
#run | telnet
#pkill webserv

16
Tester/telnet_test2.sh Executable file
View File

@@ -0,0 +1,16 @@
#! /bin/bash
echo "open duckduckgo.com 80"
sleep 1
echo "GET / HTTP/1.1"
echo "Host: duckduckgo.com"
echo
echo
sleep 2
#(
#echo open duckduckgo.com 80
#echo "GET / HTTP/1.1"
#echo "Host: duckduckgo.com"
#echo "exit"
#) | telnet

107
Tester/test1.config Normal file
View File

@@ -0,0 +1,107 @@
server {
# this is a comment
server_name server1;
listen 0.0.0.0:4040;
# client_body_limit asdfa;
client_body_limit 5000;
# Max == 18446744073709551615 / 1024 == 18014398509481984
index index.html; # this is another comment
#index mdr.html; # this is another comment
root ./Tester/www/;
error_page 404 ./Tester/www/error_pages/error_404.html;
location / {
allow_methods GET;
root ./Tester/www/;
}
location /to_delete.txt {
allow_methods GET DELETE;
root ./Tester/www/to_delete.txt;
}
location /i_do_not_exist.txt {
allow_methods GET DELETE;
root ./Tester/www/to_delete.txt;
}
location /srcs/cgi-bin/ {
root ./srcs/cgi-bin/;
allow_methods POST;
cgi_ext php;
}
location /list {
autoindex on;
}
location /cgi-bin {
root ./srcs/cgi-bin/;
cgi_ext out php sh;
}
location /upload {
allow_methods POST;
autoindex on;
upload_dir ./Tester/www/user_files/;
# root doesnt matter if used only with POST and no CGI
}
location /the_dump {
allow_methods GET DELETE;
root ./Tester/www/user_files;
autoindex on;
}
location /redirect {
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
}
location /test {
index index1.html subdex.html;
root ./Tester/www/test/;
}
location /stylesheet {
root ./stylesheet/;
}
location /test/index1.html {
root ./Tester/www/test/index1.html;
index index1.html subdex.html;
}
location /hilarious_404/ {
redirect 301 https://berniesanders.com/404/;
}
location /test/something.html {
# allow_methods DELETE;
root ./Tester/www/test/something.html;
}
location /test/test_deeper/ {
# allow_methods
root ./Tester/www/test/test_deeper/;
}
location /test/test_deeper/super_deep {
root ./Tester/www/test/test_deeper/super_deep/;
}
# location /test/test_deeper/something.html {
# allow_methods DELETE;
# }
}

95
Tester/test2.config Normal file
View File

@@ -0,0 +1,95 @@
server {
# this is a comment
server_name server1;
listen 0.0.0.0:8080;
client_body_limit 1;
# Max == 18446744073709551615 / 1024 == 18014398509481984
index index.html; # this is another comment
root ./Tester/www/;
error_page 404 ./Tester/www/error_pages/error_404.html;
location / {
allow_methods GET;
root ./Tester/www/;
}
location /srcs/cgi-bin/ {
root ./srcs/cgi-bin/;
allow_methods POST;
cgi_ext php;
}
location /list {
autoindex on;
}
location /cgi-bin {
root ./srcs/cgi-bin/;
cgi_ext out php sh;
}
location /upload {
allow_methods POST;
autoindex on;
upload_dir ./Tester/www/user_files/;
# root doesnt matter if used only with POST and no CGI
}
location /the_dump {
allow_methods GET DELETE;
root ./Tester/www/user_files;
autoindex on;
}
location /redirect {
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
}
location /test {
index index1.html subdex.html;
root ./Tester/www/test/;
}
location /stylesheet {
root ./stylesheet/;
}
location /test/index1.html {
root ./Tester/www/test/index1.html;
index index1.html subdex.html;
}
location /hilarious_404/ {
redirect 301 https://berniesanders.com/404/;
}
location /test/something.html {
# allow_methods DELETE;
root ./Tester/www/test/something.html;
}
location /test/test_deeper/ {
# allow_methods
root ./Tester/www/test/test_deeper/;
}
location /test/test_deeper/super_deep {
root ./Tester/www/test/test_deeper/super_deep/;
}
# location /test/test_deeper/something.html {
# allow_methods DELETE;
# }
}

98
Tester/test3.config Normal file
View File

@@ -0,0 +1,98 @@
server {
# this is a comment
server_name server1;
listen 8080;
# listen 70000;
# client_body_limit asdfa;
client_body_limit 1000;
# Max == 18446744073709551615 / 1024 == 18014398509481984
index index.html; # this is another comment
#index mdr.html; # this is another comment
root ./Tester/www/;
error_page 404 ./Tester/www/error_pages/error_404.html;
location / {
allow_methods GET;
root ./Tester/www/;
}
location /srcs/cgi-bin/ {
root ./srcs/cgi-bin/;
allow_methods POST;
cgi_ext php;
}
location /list {
autoindex on;
}
location /cgi-bin {
root ./srcs/cgi-bin/;
cgi_ext out php sh;
}
location /upload {
allow_methods POST;
autoindex on;
upload_dir ./Tester/www/user_files/;
# root doesnt matter if used only with POST and no CGI
}
location /the_dump {
allow_methods GET DELETE;
root ./Tester/www/user_files;
autoindex on;
}
location /redirect {
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
}
location /test {
index index1.html subdex.html;
root ./Tester/www/test/;
}
location /stylesheet {
root ./stylesheet/;
}
location /test/index1.html {
root ./Tester/www/test/index1.html;
index index1.html subdex.html;
}
location /hilarious_404/ {
redirect 301 https://berniesanders.com/404/;
}
location /test/something.html {
# allow_methods DELETE;
root ./Tester/www/test/something.html;
}
location /test/test_deeper/ {
# allow_methods
root ./Tester/www/test/test_deeper/;
}
location /test/test_deeper/super_deep {
root ./Tester/www/test/test_deeper/super_deep/;
}
# location /test/test_deeper/something.html {
# allow_methods DELETE;
# }
}

88
Tester/test_body.sh Normal file
View File

@@ -0,0 +1,88 @@
#! /bin/bash
test_name="Body Test"
config_file="./Tester/test2.config"
port=8080
host="localhost"
#paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
paths=("/") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("GET")
httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host\n"
body="this is a message \n"
body+="this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message this is a message \n"
# turns out this does nothing...
#header_cycle=("Content-Length: 17\n" "Content-Length: 14" "Content-Length: 25")
#header_cycle=("Content-Length: 17\n" "\n")
#header_cycle=("Content-Length: 17\n")
#header_cycle=()
header+="Content-Length: 8\n"
run_this_test()
{
echo size is ${#body} >> telnet.log
# for i in "${header_cycle[@]}"
for i in "${paths[@]}"
do
# l1="${methods[0]} ${paths[0]} ${httpz[0]}"
l1="${methods[0]} $i ${httpz[0]}"
# header_send="$header$i"
header_send="$header"
## if you add an extra \n after header send you get a status code 200 OK and 400 bad request, but like both
# request="$l1\n$header_send\n$body\n"
request="$l1\n$header_send\n$body"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_body_test.txt"
files+=("expected_body_test.txt")
#files+=("expected_path_root_test.txt")
#files+=("expected_path_testnoslash_test.txt")
#files+=("expected_path_testslash_test.txt")
#files+=("expected_path_list_test.txt")
#files+=("expected_path_badlist_test.txt")
#files+=("expected_path_wrong_test.txt")
#local_expected_test_file=$file
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

75
Tester/test_delete.sh Normal file
View File

@@ -0,0 +1,75 @@
#! /bin/bash
test_name="Delete Test"
config_file="./Tester/test1.config"
port=4040
host="localhost"
#paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
paths=("/" "/to_delete.txt" "/i_do_not_exist.txt") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("DELETE")
httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host"
body=
run_this_test()
{
echo "we will delete" > ./Tester/www/to_delete.txt
for i in "${paths[@]}"
do
l1="${methods[0]} $i ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_delete_test.txt"
#files+=("expected_path_root_test.txt")
#files+=("expected_path_testnoslash_test.txt")
#files+=("expected_path_testslash_test.txt")
#files+=("expected_path_list_test.txt")
#files+=("expected_path_badlist_test.txt")
#files+=("expected_path_wrong_test.txt")
#local_expected_test_file=$file
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

83
Tester/test_header.sh Normal file
View File

@@ -0,0 +1,83 @@
#! /bin/bash
test_name="Header Test"
config_file="./Tester/test1.config"
port=4040
host="localhost"
paths=("/") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("GET")
httpz=("HTTP/1.1")
# the parts we will send to webserv
# let main.sh handle the l1
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host\n"
header+="Nonsense: fu\n"
header_cycle=("Transfer-encoding: fu" "Content-encoding: fu")
#header+="Transfer-encoding: fu\n"
#header+="Content-endcoding: fu\n"
body=
run_this_test()
{
for i in "${header_cycle[@]}"
do
header_send="$header$i"
request="$l1\n$header_send\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_header_test.txt"
files+=("expected_header_test.txt")
#files+=("expected_path_root_test.txt")
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

80
Tester/test_method.sh Normal file
View File

@@ -0,0 +1,80 @@
#! /bin/bash
test_name="Method Test"
config_file="./Tester/test1.config"
port=4040
host="localhost"
paths=("/") # you can add many
methods=("GET" "POST" "DELETE" "GUMBALL")
#methods=("GET")
httpz=("HTTP/1.1")
# the parts we will send to webserv
header="Host: $host\n"
header+="Nonsense: fu\n"
#header_cycle=("Transfer-encoding: fu" "Content-encoding: fu")
body=
run_this_test()
{
for i in "${methods[@]}"
do
l1="$i ${paths[0]} ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_method_test.txt"
files+=("expected_method_test.txt")
#files+=("expected_path_root_test.txt")
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

74
Tester/test_path.sh Normal file
View File

@@ -0,0 +1,74 @@
#! /bin/bash
test_name="Path Test"
config_file="./Tester/test1.config"
port=4040
host="localhost"
paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
#paths=("/") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("GET")
httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host\n"
body=
run_this_test()
{
for i in "${paths[@]}"
do
l1="${methods[0]} $i ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_path_test.txt"
files+=("expected_path_test.txt")
#files+=("expected_path_root_test.txt")
#files+=("expected_path_testnoslash_test.txt")
#files+=("expected_path_testslash_test.txt")
#files+=("expected_path_list_test.txt")
#files+=("expected_path_badlist_test.txt")
#files+=("expected_path_wrong_test.txt")
#local_expected_test_file=$file
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

73
Tester/test_port.sh Normal file
View File

@@ -0,0 +1,73 @@
#! /bin/bash
test_name="Port Test"
config_file="./Tester/test3.config"
port=8080
host="localhost"
#paths=("/" "/test" "/test/" "/list" "list" "/wrong") # you can add many
paths=("/") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("GET")
httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host"
body=
run_this_test()
{
for i in "${paths[@]}"
do
l1="${methods[0]} $i ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_port_test.txt"
#files+=("expected_path_root_test.txt")
#files+=("expected_path_testnoslash_test.txt")
#files+=("expected_path_testslash_test.txt")
#files+=("expected_path_list_test.txt")
#files+=("expected_path_badlist_test.txt")
#files+=("expected_path_wrong_test.txt")
#local_expected_test_file=$file
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

62
Tester/test_template.sh Normal file
View File

@@ -0,0 +1,62 @@
#! /bin/bash
test_name="Template test all good"
config_file="./Tester/test1.config"
port=4040
host="localhost"
paths=("/") # you can add many
methods=("GET" "POST" "DELETE")
#methods=("GET")
httpz=("HTTP/1.1")
# the parts we will send to webserv
# let main.sh handle the l1
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host"
body=
run_this_test()
{
for i in "${methods[@]}"
do
l1="$i ${paths[0]} ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
# } &>> test.log
# } 2>>&1
# } &>> /dev/stdout
echo -e "$request"
# echo
sleep 1
# {
# echo -e "\n------\n"
# } > /dev/stdout
# echo -e "\n\n\n------\n\n\n" >> telnet.out
# echo -e "\n\n\n------\n\n\n" &> /dev/stdout
echo -e "\n\n" >> telnet.log
# echo -e "\n\n------\n\n" >> telnet.out
# echo -e "\n------\n" > /dev/stdout
# run_a_test
# echo
done
}
# expected result...

66
Tester/test_valid_uri.sh Normal file
View File

@@ -0,0 +1,66 @@
#! /bin/bash
test_name="Valid URIs Test"
config_file="./Tester/test1.config"
port=4040
host="localhost"
paths=("/" "/stylesheet/style.css" "/test/something.html" "/test/something.html/" "/test/" "/list" "/hilarious_404" "/redirect") # you can add many
#paths=("/") # you can add many
#methods=("GET" "POST" "DELETE")
methods=("GET")
httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host"
body=
run_this_test()
{
for i in "${paths[@]}"
do
l1="${methods[0]} $i ${httpz[0]}"
request="$l1\n$header\n$body\n"
{
echo "----- $test_name -----"
echo -e "$_RED$request$_END"
} >> telnet.log
echo -e "$request"
sleep 1
echo -e "\n" >> telnet.log
done
}
# expected result...
files=()
file="expected_valid_uri_test.txt"
files+=("expected_valid_files_test.txt")
local_expected_test_files=()
test_path=""
add_path()
{
for i in "${files[@]}"
do
local_expected_test_files+=("$test_path$i")
done
}
#add_path

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
Tester/www/drill.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
<hr>
<p style=\"text-align:center\">Le Webserv/0.1</p>
</body>
</html>

BIN
Tester/www/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

84
Tester/www/form_get.html Normal file
View File

@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
display: flex;
flex-direction: row;
margin: auto;
}
form {
display: flex;
flex-direction: column;
border: 1px solid red;
margin: 20px auto;
padding: 5px;
}
form > * {
display: flex;
margin: 5px auto 5px 5px;
}
mark {
margin: 0px 3px;
}
</style>
</head>
<body>
<form method="get" action="/cgi-bin/cgi_cpp.out">
<p><mark>get</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<input type="submit" value="submit">
</form>
<br>
<form method="post" action="/cgi-bin/cgi_cpp.out">
<p><mark>post</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<input type="submit" value="submit">
</form>
<br>
<form method="get" action="/cgi-bin/cgi_cpp_content_length.out">
<p><mark>get</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<input type="submit" value="submit">
</form>
<br>
<form method="post" action="/cgi-bin/cgi_cpp_content_length.out">
<p><mark>post</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<input type="submit" value="submit">
</form>
<br>
</body>
</html>

11
Tester/www/index.html Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Le Webserv</title>
</head>
<body>
<h1 style="text-align:center">Le index (˘ ͜ʖ˘)</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

BIN
Tester/www/kermit.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
Tester/www/punpun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

312
Tester/www/rfc2119.html Normal file
View File

@@ -0,0 +1,312 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- saved from url=(0057)https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://dublincore.org/documents/2008/08/04/dc-html/"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="robots" content="index,follow">
<link rel="icon" href="https://www.rfc-editor.org/rfc/inline-errata/css/images/rfc.png" type="image/png">
<link rel="shortcut icon" href="https://www.rfc-editor.org/rfc/inline-errata/css/images/rfc.png" type="image/png">
<title>rfc2119</title>
<link rel="stylesheet" type="text/css" href="./rfc2119_files/errata-base.css">
<link rel="stylesheet" type="text/css" href="./rfc2119_files/errata-color.css" title="Default: Basic Colors">
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-monochrome.css" title="Monochrome">
<link rel="alternative stylesheet" type="text/css" href="./rfc2119_files/errata-printer.css" title="Printer">
<script src="./rfc2119_files/errata.js"></script>
</head>
<body>
<div class="Verified-headnote-styling">
<span style="font-weight: bold;">This is a purely informative rendering of an RFC that includes verified errata. This rendering may not be used as a reference.</span>
<br>
<br>
The following 'Verified' errata have been incorporated in this document:
<a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid493">EID 493</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 494</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_495">EID 495</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_494">EID 496</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid498">EID 498</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_499">EID 499</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#eid500">EID 500</a>, <a href="https://www.rfc-editor.org/rfc/inline-errata/rfc2119.html#btn_5101">EID 5101</a>
</div>
<pre>Network Working Group S. Bradner
Request for Comments: 2119 Harvard University
BCP: 14 March 1997
Category: Best Current Practice
Key words for use in RFCs to Indicate Requirement Levels
Status of this Memo
This document specifies an Internet Best Current Practices for the
Internet Community, and requests discussion and suggestions for
improvements. Distribution of this memo is unlimited.
Abstract
In many standards track documents several words are used to signify
the requirements in the specification. These words are often
capitalized. This document defines these words as they should be
interpreted in IETF documents. Authors who follow these guidelines
should incorporate this phrase near the beginning of their document:
<span class="Verified-inline-styling" id="inline-499"> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL <button id="btn_499" target="expand_499" onclick="hideFunction(&quot;expand_499&quot;)">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(&quot;expand_495&quot;)">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(&quot;expand_5101&quot;)">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(&quot;expand_494&quot;)">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>

View 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>

View 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>

View 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>

View 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>

View File

@@ -0,0 +1,4 @@
function hideFunction(nodeId) {
var ul = document.getElementById(nodeId)
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
}

View File

@@ -0,0 +1,4 @@
function hideFunction(nodeId) {
var ul = document.getElementById(nodeId)
ul.className = (ul.className=="nodeOpenClass") ? "nodeCloseClass" : "nodeOpenClass"
}

BIN
Tester/www/root.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv test index</title>
<link rel="stylesheet" href="/stylesheet/style.css">
</head>
<body>
<h1>Webserv Test Index</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv test Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Something</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv test Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv in Test</h1>
<hr>
<p style="text-align:center">Time to submit something:</p>
<!-- <form action="./srcs/cgi-bin/upload_file.php" method="post" enctype="multipart/form-data"> -->
<form action="/uploaded" method="post" enctype="multipart/form-data">
<!-- <input type="hidden" name="upload_dir" value="./www/uploaded/"> -->
<input type="file" id="fileToUpload" name="myFile">
<input type="submit" value="Upload File">
</form>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv Test Deeper Index</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Deeper Index</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv test deeper Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Deeper Something</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Webserv test deeper Something</title>
</head>
<body>
<h1 style="text-align:center">Webserv Test Super Deep Something</h1>
<hr>
<p style="text-align:center">(˚3˚)</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!--
https://www.w3schools.com/howto/howto_html_file_upload_button.asp
https://www.filestack.com/fileschool/html/html-file-upload-tutorial-example/
https://www.rfc-editor.org/rfc/rfc9110#name-multipart-types
https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
-->
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="upload_file1">
<input type="file" name="upload_file2">
<input type="file" name="upload_file3">
<input type="submit">
</form>

View File

@@ -0,0 +1,10 @@
<!--
https://www.w3schools.com/howto/howto_html_file_upload_button.asp
https://www.filestack.com/fileschool/html/html-file-upload-tutorial-example/
https://www.rfc-editor.org/rfc/rfc9110#name-multipart-types
https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
-->
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="upload_file1">
<input type="submit">
</form>

29
compare.txt Normal file
View File

@@ -0,0 +1,29 @@
telnet> Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Type: text/html; charset=UTF-8
Content-Length: 199
<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 style="text-align:center">405 Method Not Allowed</h1><hr><p style="text-align:center">Le Webserv/0.1</p></body></html>HTTP/1.1 204 No Content
Server: Webserv/0.1
Connection: keep-alive
HTTP/1.1 404 Not Found
Server: Webserv/0.1
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Content-Length: 210
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">Check it UP 404 Not Found</h1>
<hr>
<p style=\"text-align:center\">Le Webserv/0.1</p>
</body>
</html>

View File

@@ -1,72 +1,109 @@
server {
# this is a comment
server_name our_server;
server_name server1;
listen 0.0.0.0:4040;
# client_body_limit asdfa;
# client_body_limit 400;
client_body_limit 5000;
# Max == 18446744073709551615 / 1024 == 18014398509481984
index index.html; # this is another comment
#index mdr.html; # this is another comment
root ./www/;
error_page 404 ./www/error_pages/error_404.html;
# error_page 404 ./www/error_pages/error_404.html;
# error_page 403 ./www/error_pages/error_404.html;
location /list {
autoindex on;
}
location /redirect {
redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
# redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
}
location /test {
index index1.html subdex.html;
}
location /test/index1.html {
index index1.html subdex.html;
}
location /hilarious_404/ {
redirect 301 https://berniesanders.com/404/;
}
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
location /stylesheet/style.css {
# root ./www/../;
root ./;
}
location /test/something.html {
# allow_methods DELETE;
}
# location /something/long/here {
# location /kapouet {
# root /tmp/www;
# }
location /test/test_deeper/ {
# allow_methods
autoindex on;
location / {
allow_methods GET;
root ./www/;
}
location /test/test_deeper/super_deep {
autoindex on;
}
# location /test/test_deeper/something.html {
# allow_methods DELETE;
# location /srcs/cgi-bin/ {
# root ./srcs/cgi-bin/;
# allow_methods POST;
# cgi_ext php;
# }
#
# location /list {
# autoindex on;
# }
# ok in theory if one were to go to /test they would get the index in www
# as opposed to the one in /website...
location /cgi-bin {
root ./srcs/cgi-bin/;
cgi_ext out php sh;
}
# location /upload {
# allow_methods POST;
# upload_dir ./www/user_files/;
# # root doesnt matter if used only with POST and no CGI
# }
#
# location /the_dump {
# allow_methods GET DELETE;
# root ./www/user_files;
# autoindex on;
# }
#
# location /redirect {
# redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
## redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
# }
# location /test {
# root /www;
# index index1.html subdex.html;
# root ./www/test/;
# }
#
# location /stylesheet {
# root ./stylesheet/;
# }
#
# location /test/index1.html {
# root ./www/test/index1.html;
# index index1.html subdex.html;
# }
#
# location /hilarious_404/ {
# redirect 301 https://berniesanders.com/404/;
# }
#
# location /test/something.html {
# # allow_methods DELETE;
# root ./www/test/something.html;
# }
#
# location /test/test_deeper/ {
## allow_methods
# root ./www/test/test_deeper/;
# index index1.html;
# }
#
# location /test/test_deeper/super_deep {
# root ./www/test/test_deeper/super_deep/;
# index something.html;
# }
#
#}
#
#server {
# server_name server2;
#
# listen 0.0.0.0:8080;
#
# index index.html;
# root ./www2/;
}

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1 style="text-align:center">404 Not Found</h1>
<hr>
<p style="text-align:center">Le Webserv/0.1</p>
</body>
</html>

117
main_test.sh Executable file
View File

@@ -0,0 +1,117 @@
#! /bin/bash
# import and run all tests
##########
# Colors
##########
_GREY='\033[30m'
_RED='\033[0;31m'
_GREEN='\033[32m'
_YELLOW='\033[33m'
_BLUE='\033[34m'
_PURPLE='\033[35m'
_CYAN='\033[36m'
_WHITE='\033[37m'
_END='\033[0m'
#test_file_names=("test_template.sh" "test_header.sh" "test_path.sh")
test_file_names=("test_method.sh" "test_header.sh" "test_path.sh" "test_valid_uri.sh" "test_delete.sh")
# Run this separately cuz of the content length subtlty
#test_file_names=("test_body.sh")
# kinda fucking useless, easier to test IRL
#test_file_names=("test_port.sh")
test_files=()
expected_result_files=()
fill_test_files()
{
for i in "${test_file_names[@]}"
do
test_files+=("./Tester/$i")
done
}
# needs to be invoked for each test_file!
# acutally kinda useless...
fill_expected_files()
{
for i in "${local_expected_test_files[@]}"
do
expected_result_files+=("./Tester/expected_results/$i")
done
}
test_all()
{
arg=${1}
make
rm -rf telnet.log
rm -rf webserv.log
rm -rf compare.txt
rm -rf expected.txt
fill_test_files
for i in "${test_files[@]}"
do
source $i
source ./Tester/telnet_test.sh
./webserv $config_file &>> webserv.log &
echo -e "${_GREEN}Running Telnet Test on '$test_name'${_END}"
sleep 1
run | telnet | tee compare.txt >> telnet.log
# run | telnet > compare.txt >> telnet.log
# run | telnet >> telnet.log
pkill webserv
echo -e "\n\n------\n" &>> webserv.log
fill_expected_files
expected_result_file="./Tester/expected_results/$file"
if [ "$arg" = "diff" ];
then
DIFF=$(diff -q compare.txt $expected_result_file)
if [ "$DIFF" == "" ];
then
echo "Good diff"
else
diff compare.txt $expected_result_file
fi
elif [ "$arg" = "create" ];
then
if ! test -f $expected_result_file;
then
cat compare.txt > $expected_result_file
fi
elif [ "$arg" = "replace" ];
then
cat compare.txt > $expected_result_file
fi
done
if [ "$arg" = "" ];
then
cat telnet.log
fi
}
test_all $1

View File

@@ -1,18 +1,25 @@
IN 42 SUBJECT AND/OR PRIORITY :
- CGI
- chunked request (response not mandatory it seems)
- fix need for index and autoindex config
- Ecrire des tests !
- "root" need to replace "location->path" part of "client.path"
replace up to the last "/" of the "location->path" part
(if its a folder this will be in fact the entire path)
----Priorité élevée------------------------
- curl --resolve, for testing hostname
curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040
- test limit de connexions sur listen()
- handle redirection (Work, but weird behavior need deeper test)
- upload files with config "upload_dir"
- _determine_location() review (New version to complete and test)
- replace atoi() with a better function to avoid overflow
like strtol : https://www32.cplusplus.com/reference/cstdlib/strtol/
-----------------------------
- 408 Request Timeout
- Ecrire des tests !
----Priorité modérée------------------------
- namespace utils ?
- change "std::string" to reference "std::string &" in most functions
and add "const" if apropriate.
- peut-être check si ip > 32bits
----Priorité faible------------------------
- idealy, we should not clear() raw_request after a response,
but just the part we actually parsed for this response.
I think in HTTP/1 the client could not send multiples request on the same connection one after the other without waiting for response.
So only for HTTP/2 adn HTTP/3 raw_request could contain more than the first request we handle.
- chunked request (need testing)
- client_body_limit 0 valeur special pour desactiver dans config
- gerer le champ "Accept" du client
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
@@ -20,14 +27,30 @@ IN 42 SUBJECT AND/OR PRIORITY :
little global timeout on epoll, like 100ms, then find client that actualy need to timeout
if (actual_time - client.last_action_time > 10000ms){timeout(client)}
- add headers "Date" and "Last-Modified" to response
- change "std::string" to reference "std::string &" in most functions
and add "const" if apropriate.
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
Valgrind error RESOLVED ! :
==847174== 1,314 bytes in 1 blocks are definitely lost in loss record 1 of 1
==847174== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==847174== by 0x49AA35D: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==847174== by 0x49ABB52: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==847174== by 0x426BC5: Webserv::_read_cgi_output(cgi_pipe_r_from_child&) (run_loop.cpp:39) // (LUKE: its an string.append() call)
==847174== by 0x427962: Webserv::run() (run_loop.cpp:153)
==847174== by 0x4052E9: main (main.cpp:38)
__________________________
--------------------------
----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.
RESOLVED, AGAIN :D !
--loop epoll()
EPOLLHUP on client fd 8
47067----loop epoll()
EPOLLHUP on client fd 8
47068----loop epoll()
EPOLLHUP on client fd 8
47069----loop epoll()
EPOLLHUP on client fd 8
47070----loop epoll()
EPOLLHUP on client fd 8
47071----loop epoll()
EPOLLHUP on client fd 8
47072----loop epoll()
EPOLLHUP on client fd 8
47073----loop epoll()

53
multipart_request.txt Normal file
View File

@@ -0,0 +1,53 @@
POST /upload HTTP/1.1
Host: localhost:4040
Connection: keep-alive
Content-Length: 364
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:4040
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeIV4xrEzThmNUcJf
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:4040/upload_form.html
Accept-Encoding: gzip, deflate, br
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
dnt: 1
sec-gpc: 1
------WebKitFormBoundaryeIV4xrEzThmNUcJf
Content-Disposition: form-data; name="upload_file"; filename=".gitignore"
Content-Type: text/plain
.DS_Store
Thumbs.db
*.o
*.d
*.swp
*.out
*.exe
*.stackdump
*.a
*.so
*.dSYM
.vscode
*.lnk
*.zip
builds
ubuntu_tester
ubuntu_cgi_tester
webserv
!**/webserv/
*.log
large.jpg
------WebKitFormBoundaryeIV4xrEzThmNUcJf--

1
siege.txt Normal file
View File

@@ -0,0 +1 @@
http://localhost:4040/cgi-bin/cgi_cpp_empty.out?fname=John&lname=Doe

View File

@@ -8,10 +8,17 @@
Client::Client()
: status(0),
header_complete(false),
read_body_size(0),
body_complete(false),
request_complete(false),
assigned_server(NULL),
assigned_location(NULL),
_fd(0),
cgi_state(CGI_NO_CGI),
cgi_pipe_w_to_child(-1),
cgi_pipe_r_from_child(-1),
cgi_pipe_w_to_parent(-1),
cgi_pipe_r_from_parent(-1),
cgi_pid(0),
_fd(-1),
_port(""),
_ip(""),
_lsocket(NULL)
@@ -22,9 +29,16 @@ Client::Client()
Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
: status(0),
header_complete(false),
read_body_size(0),
body_complete(false),
request_complete(false),
assigned_server(NULL),
assigned_location(NULL),
cgi_state(CGI_NO_CGI),
cgi_pipe_w_to_child(-1),
cgi_pipe_r_from_child(-1),
cgi_pipe_w_to_parent(-1),
cgi_pipe_r_from_parent(-1),
cgi_pid(0),
_fd(afd),
_port(aport),
_ip(aip),
@@ -41,7 +55,6 @@ Client::~Client() {
Client::Client( Client const & src )
: status ( src.status ),
header_complete ( src.header_complete ),
read_body_size ( src.read_body_size ),
assigned_server ( src.assigned_server ),
assigned_location ( src.assigned_location ),
_fd ( src._fd ),
@@ -71,27 +84,37 @@ Client & Client::operator=( Client const & rhs )
* 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
/* HTTP Headers :
https://www.iana.org/assignments/http-fields/http-fields.xhtml
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_headers(std::vector<ServerConfig> &servers)
{
clear_request(); // not mandatory
if (raw_request.find(CRLF CRLF) == NPOS)
return ;
header_complete = true;
clear_request_vars(); // not mandatory
_parse_request_line();
if (status)
return;
_parse_request_fields();
// DEBUG
print_client("headers");
// print_client("headers");
if (status)
return;
assigned_server = ::_determine_process_server(this, servers);
assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
assigned_server = _determine_process_server(this, servers);
assigned_location = _determine_location(*assigned_server, _request.abs_path);
_check_request_errors();
if (status)
return;
_parse_port_hostname(this->get_rq_headers("Host")); // use getter for headers because it works case insensitive
// DEBUG
// std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n";
// dont clear raw_request, we need it for future reparsing of body
// see call of parse_request() in _read_request()
@@ -100,58 +123,142 @@ print_client("headers");
void Client::parse_request_body()
{
std::cerr << "parse_request_body()\n";
size_t pos;
pos = raw_request.find(CRLF CRLF);
pos += std::string(CRLF CRLF).size();
_request.body = raw_request.substr(pos);
if (pos == NPOS)
{
std::cerr << "parse_request_body() bad call, header incomplete\n";
// QUESTION from hugo : don't we change the status here ?
// RESPONSE from luke : C'est vrai. Peut-être mettre un 500, c'etait plus du debug à la base.
// C'est seulement si on appelle la fonction au mauvais endroit, avant d'avoir un header complet, que ça arrive.
return;
}
if (!get_rq_headers("Transfer-Encoding").empty()
&& get_rq_headers("Transfer-Encoding") == "chunked")
{
// Chunked decoding WIP. How to test this ? dont know how to send chunks with telnet.
_parse_chunked_body(pos + CRLF_SIZE*2);
}
else if (raw_request.size() - pos >= std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10))
{
std::cerr << "Content-Length = " << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
if (get_rq_headers("Content-Type").find("multipart/form-data") != NPOS)
_parse_multipart_body(pos);
else
_request.body = raw_request.substr(pos + CRLF_SIZE*2);
body_complete = true;
}
std::cerr << "Content-Length = " << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
std::cerr << "raw_request.size() - pos = " << raw_request.size() - pos << "\n";
_request.body = raw_request.substr(pos);
std::cerr << "_request.body.size() = " << _request.body.size() << "\n";
///////////////
// Body checks
if (_request.body.size() > assigned_server->client_body_limit)
status = 413; // HTTP Client Errors
}
bool Client::fill_script_path(std::string script)
void Client::_parse_chunked_body(size_t pos)
{
size_t chunk_size = 1;
size_t chunk_field_end = 0;
char *endptr = NULL;
char *endptr_copy = NULL;
/* TODO: verify if last chunk in raw_request (to avoid multiples complete parsing)
but how ? with "raw_request.rfind("0" CRLF CRLF)", there no confirmation
that we have found the last last-chunk OR just some data */
_request.body = raw_request.substr(pos);
std::cerr << "______Chunked\n" << _request.body << "\n______\n";
pos = 0;
while (chunk_size != 0)
{
/* if (pos > _request.body.size())
{
std::cerr << "parse_request_body(), pos > size()\n";
// status = 400;
return;
} */
/*
if (pos == _request.body.size())
{
std::cerr << "parse_request_body(), will reread till last chunk\n";
return;
} */
/* endptr_copy = endptr; */
(void)endptr_copy;
chunk_size = std::strtoul(&_request.body[pos], &endptr, 16);
if (errno == ERANGE)
{
status = 413;
return ;
}
/* if (endptr == endptr_copy)
{
std::cerr << "parse_request_body(), no conversion possible\n";
return;
} */
chunk_field_end = _request.body.find(CRLF, pos);
if (chunk_field_end == NPOS)
{
std::cerr << "parse_request_body(), chunk_field no CRLF\n";
// status = 400;
return;
}
chunk_field_end += CRLF_SIZE;
_request.body.erase(pos, chunk_field_end);
pos += chunk_size + CRLF_SIZE;
}
_request.headers.erase("Transfer-Encoding");
body_complete = true;
}
void Client::fill_script_path(std::string &path, size_t pos)
{
size_t pos;
int len = script.size();
std::string path = this->get_rq_abs_path();
std::string tmp;
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;
_request.script.path = path.substr(0, pos);
_request.script.info = path.substr(pos);
}
void Client::clear()
{
clear_request();
header_complete = false;
read_body_size = 0;
assigned_server = NULL;
assigned_location = NULL;
clear_request_vars();
raw_request.clear();
response.clear();
status = 0;
header_complete = false;
body_complete = false;
request_complete = false;
assigned_server = NULL;
assigned_location = NULL;
clear_cgi_vars();
}
void Client::clear_request()
void Client::clear_request_vars()
{
clear_script();
_request.method = UNKNOWN;
_request.uri.clear();
_request.target.clear();
_request.version.clear();
_request.headers.clear();
_request.body.clear();
_request.multi_bodys.clear();
_request.abs_path.clear();
_request.query.clear();
_request.port.clear();
_request.hostname.clear();
}
void Client::clear_script()
@@ -160,32 +267,40 @@ void Client::clear_script()
_request.script.info.clear();
}
void Client::clear_cgi_vars()
{
cgi_state = CGI_NO_CGI;
cgi_pipe_w_to_child = -1;
cgi_pipe_r_from_child = -1;
cgi_pipe_w_to_parent = -1;
cgi_pipe_r_from_parent = -1;
cgi_pid = 0;
cgi_output.clear();
}
// debug
void Client::print_client(std::string message)
{
std::map<std::string, std::string>::iterator it;
std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n";
std::cerr << "\n=== DEBUG PRINT CLIENT ===\n";
std::cerr << message << ":\n----------\n\n" << "raw_request:\n__\n";
::print_special(raw_request);
std::cout << "\n__\n"
std::cerr << "\n__\n"
<< "get_cl_fd() : [" << get_cl_fd() << "]\n"
<< "get_cl_port() : [" << get_cl_port() << "]\n"
<< "get_cl_ip() : [" << get_cl_ip() << "]\n"
<< "get_rq_method_str() : [" << get_rq_method_str() << "]\n"
<< "get_rq_uri() : [" << get_rq_uri() << "]\n"
<< "get_rq_target() : [" << get_rq_target() << "]\n"
<< "get_rq_abs_path() : [" << get_rq_abs_path() << "]\n"
<< "get_rq_query() : [" << get_rq_query() << "]\n"
<< "get_rq_version() : [" << get_rq_version() << "]\n"
<< "get_rq_body() : [" << get_rq_body() << "]\n"
<< "get_rq_port() : [" << get_rq_port() << "]\n"
<< "get_rq_hostname() : [" << get_rq_hostname() << "]\n"
<< "get_rq_script_path() : [" << get_rq_script_path() << "]\n"
<< "get_rq_script_info() : [" << get_rq_script_info() << "]\n"
<< "headers :\n";
for (it = _request.headers.begin(); it != _request.headers.end(); it++)
std::cout << " " << it->first << ": [" << it->second << "]\n";
std::cout << "\n=== END PRINT CLIENT ===\n\n";
std::cerr << " " << it->first << ": [" << it->second << "]\n";
std::cerr << "\n=== END PRINT CLIENT ===\n\n";
}
/*********************************************
@@ -202,15 +317,14 @@ const listen_socket * Client::get_cl_lsocket() const { return _lsocket; }
http_method Client::get_rq_method() const { return _request.method; }
std::string Client::get_rq_method_str() const
{ return ::http_methods_to_str(_request.method); }
std::string Client::get_rq_uri() const { return _request.uri; }
std::string Client::get_rq_target() const { return _request.target; }
std::string Client::get_rq_abs_path() const { return _request.abs_path; }
std::string Client::get_rq_query() const { return _request.query; }
std::string Client::get_rq_version() const { return _request.version; }
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;
@@ -237,26 +351,29 @@ void Client::_parse_request_line()
{
std::cerr << "err _parse_first_line(): wrong number of elements (" << line.size() << " instead of 3)\n";
status = 400; // "bad request"
// if the header is fucked up, then this will be triggered, but for some reason in test_body.sh case another response 200 OK is sent first?
}
else
{
_request.method = str_to_http_method(line[0]);
_request.uri = line[1];
_parse_request_uri(line[1]);
_request.target = line[1];
_parse_request_target(line[1]);
_request.version = line[2];
}
}
void Client::_parse_request_uri( std::string uri )
void Client::_parse_request_target( std::string target )
{
size_t pos;
pos = uri.find("?");
if (pos != std::string::npos)
_request.query = uri.substr(pos + 1);
pos = target.find("?");
if (pos != NPOS)
_request.query = target.substr(pos + 1);
else
_request.query = "";
_request.abs_path = uri.substr(0, pos);
_request.abs_path = target.substr(0, pos);
if (_request.abs_path[_request.abs_path.size() - 1] == '/')
_request.abs_path.erase(_request.abs_path.size() - 1);
}
void Client::_parse_request_fields()
@@ -268,11 +385,11 @@ void Client::_parse_request_fields()
headers = raw_request;
// delete first line
pos = headers.find(CRLF);
if (pos != std::string::npos)
headers.erase(0, pos + std::string(CRLF).size());
if (pos != NPOS)
headers.erase(0, pos + CRLF_SIZE);
// delete body part
pos = headers.find(CRLF CRLF);
if (pos != std::string::npos)
if (pos != NPOS)
headers.erase(pos);
else {
std::cerr << "err _parse_request_fields(): request header doesn't end with empty line\n";
@@ -287,28 +404,12 @@ void Client::_parse_request_fields()
::str_map_key_tolower(_request.headers);
}
void Client::_parse_port_hostname(std::string host)
{
size_t pos;
if (host == "")
std::cerr << "no host\n";
pos = host.find(':');
// port :
if (pos == 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);
}
void Client::_check_request_errors()
{
//////////////////////
// /* Debug */ std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n";
// /* Debug */ std::cerr << "strtoul=" << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
// /* Debug */ std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n";
///////////////////////
// Request line checks
if (_request.method == UNKNOWN)
status = 501; // HTTP Client Errors
@@ -330,18 +431,116 @@ void Client::_check_request_errors()
response.append(CRLF CRLF);
}
if (status)
return;
/////////////////
//////////////////
// Headers checks
if (!this->get_rq_headers("Content-Length").empty()
&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
status = 413; // HTTP Client Errors
else if (!get_rq_headers("Content-Length").empty()
&& std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) > assigned_server->client_body_limit)
status = 413;
else if (!get_rq_headers("Transfer-Encoding").empty()
&& get_rq_headers("Transfer-Encoding") != "chunked" )
status = 501;
else if (!get_rq_headers("Content-Encoding").empty())
{
status = 415;
response.append("Accept-Encoding:"); // empty, no encoding accepted
response.append(CRLF);
}
return;
}
ServerConfig *Client::_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
{
/*
Behavior like this :
http://nginx.org/en/docs/http/request_processing.html
*/
std::string server_name = client->get_rq_headers("Host");
// /* Debug */ std::cerr << "server_name = " << server_name << "\n";
size_t pos = server_name.rfind(':');
if (pos != NPOS)
server_name.erase(pos);
// /* Debug */ std::cerr << "server_name = " << server_name << "\n";
std::vector<ServerConfig>::iterator it = servers.begin();
std::vector<ServerConfig>::iterator default_server = servers.end();
while (it != servers.end())
{
if (it->host == client->get_cl_lsocket()->host
&& it->port == client->get_cl_lsocket()->port)
{
if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
break;
else if (default_server == servers.end())
default_server = it;
}
++it;
}
if (it != servers.end())
return (&(*it));
else
return (&(*default_server));
}
// const?
const LocationConfig *Client::_determine_location(const ServerConfig &server, const std::string &path)
{
/* RULES ***
If a path coresponds exactly to a location, use that one
if no path coresponds then use the most correct one
most correct means the most precise branch that is still above
the point we are aiming for
New Rule for location paths, they never end in /
Sooo
If we get a url that ends in / ignore the last /
*/
// TODO: technically i think this is useless...
std::string uri = path;
if (uri[uri.size() - 1] == '/' && uri.size() != 1)
uri.erase(uri.size() - 1);
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
{
if (it->path.size() > uri.size())
continue ;
if (uri.compare(0, it->path.size(), it->path) == 0)
{
if (it->path.size() == uri.size())
return (&(*it));
else if (uri[it->path.size()] == '/')
return (&(*it));
// this works cuz only ever looking for a / burried in a longer path
}
}
return (&(server.locations.back()));
// /test/mdr
// /test/mdr/
// /test/mdrBST
/////// More stuff to check this still works with
// /test/test_
// /test/test_/
// /test/test_deeper
// /test/test_deeper/
// /test/test_deepei
// /test/test_deepei/
// /test/test_deeperi
// /test/test_deeper/super_deep/
// /test/aaaaaaaaaaa/super_deep/
}
/*********************************************
* OVERLOAD
*********************************************/

View File

@@ -3,6 +3,7 @@
# define CLIENT_HPP
# include <iostream>
# include <iomanip> // setw
# include <string>
# include <map>
# include <vector>
@@ -11,6 +12,7 @@
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
# include "utils.hpp"
# include "ServerConfig.hpp"
# include "colors.h"
struct Script
{
@@ -18,20 +20,34 @@ struct Script
std::string info;
};
struct MultipartBody
{
std::map<std::string, std::string> headers;
std::string body;
};
struct Request
{
http_method method;
std::string uri;
std::string target;
std::string abs_path;
std::string query;
std::string version;
std::map<std::string, std::string> headers;
std::string body;
std::string port;
std::string hostname;
std::vector<MultipartBody> multi_bodys;
struct Script script;
};
enum cgi_states
{
CGI_NO_CGI = 0,
CGI_WAIT_TO_EXEC,
CGI_READY_TO_EXEC,
CGI_OUTPUT_READING,
CGI_OUTPUT_COMPLETE
};
class Client
{
public:
@@ -45,10 +61,20 @@ class Client
std::string response;
unsigned int status;
bool header_complete;
size_t read_body_size;
bool body_complete;
bool request_complete;
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
const LocationConfig *assigned_location;
// CGI variables
cgi_states cgi_state;
int cgi_pipe_w_to_child;
int cgi_pipe_r_from_child;
int cgi_pipe_w_to_parent;
int cgi_pipe_r_from_parent;
pid_t cgi_pid;
std::string cgi_output;
// getters
int get_cl_fd() const;
const std::string & get_cl_port() const;
@@ -58,23 +84,25 @@ class Client
// requests getters
http_method get_rq_method() const;
std::string get_rq_method_str() const;
std::string get_rq_uri() const;
std::string get_rq_target() const;
std::string get_rq_abs_path() const;
std::string get_rq_query() const;
std::string get_rq_version() const;
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;
const std::vector<MultipartBody> &get_rq_multi_bodys() const;
const std::string get_rq_multi_bodys_headers(const std::string & key, std::vector<MultipartBody>::const_iterator body_it) const;
void parse_request_headers(std::vector<ServerConfig> &servers);
void parse_request_body();
void clear();
void clear_request();
void clear_request_vars();
void clear_cgi_vars();
void clear_script();
bool fill_script_path(std::string script);
void fill_script_path(std::string &path, size_t pos);
// DEBUG
void print_client(std::string message = "");
@@ -87,19 +115,19 @@ class Client
void _parse_request_line();
void _parse_request_fields();
void _parse_request_uri( std::string uri );
void _parse_port_hostname(std::string host);
void _parse_request_target( std::string target );
void _parse_chunked_body(size_t pos);
void _parse_multipart_body(size_t pos);
void _check_request_errors();
ServerConfig*
_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
const LocationConfig*
_determine_location(const ServerConfig &server, const std::string &path);
};
bool operator==(const Client& lhs, const Client& rhs);
bool operator==(const Client& lhs, int fd);
bool operator==(int fd, const Client& rhs);
// Temporary Global Scope. Probably move to Client in the future.
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path);
#endif

View File

@@ -0,0 +1,92 @@
#include "Client.hpp"
const std::vector<MultipartBody> &Client::get_rq_multi_bodys() const { return _request.multi_bodys; }
const std::string Client::get_rq_multi_bodys_headers(const std::string & key, std::vector<MultipartBody>::const_iterator body_it) const
{
std::map<std::string, std::string>::const_iterator it;
it = body_it->headers.find(::str_tolower(key));
if (it == body_it->headers.end())
return ""; // IF return reference compiler "warning: returning reference to local temporary"
return it->second;
}
void Client::_parse_multipart_body(size_t pos)
{
/*
** Parsing roughly like described in :
** https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
*/
MultipartBody new_body;
std::string boundary;
size_t start_pos;
size_t end_pos;
std::string tmp;
size_t tmp_pos;
size_t ret;
// Get boundary
boundary = get_rq_headers("Content-Type");
start_pos = boundary.find("boundary=");
if (start_pos == NPOS)
{
status = 400; std::cerr << "_parse_multipart_body() error 1\n";
return;
}
start_pos += sizeof("boundary=")-1;
boundary = boundary.substr(start_pos);
// Search boundary
start_pos = raw_request.find("--" + boundary, pos);
if (start_pos == NPOS || start_pos + sizeof("--")-1 + boundary.size() > raw_request.size())
{
status = 400; std::cerr << "_parse_multipart_body() error 2\n";
return;
}
start_pos += sizeof("--")-1 + boundary.size() + CRLF_SIZE;
while (1)
{
end_pos = raw_request.find("--" + boundary, start_pos);
if (end_pos == NPOS)
{
status = 400; std::cerr << "_parse_multipart_body() error 3\n";
return;
}
new_body.body = raw_request.substr(start_pos, end_pos - start_pos - CRLF_SIZE);
// Split headers from body
tmp_pos = new_body.body.find(CRLF CRLF);
if (tmp_pos != NPOS)
{
ret = ::parse_http_headers(new_body.body.substr(0, tmp_pos), new_body.headers);
::str_map_key_tolower(new_body.headers);
if (ret)
{
status = 400; std::cerr << "_parse_multipart_body() error 4\n";
return;
}
tmp_pos += CRLF_SIZE*2;
new_body.body.erase(0, tmp_pos);
}
else
{ // No headers case
tmp_pos = new_body.body.find(CRLF);
if (tmp_pos != 0)
{
status = 400; std::cerr << "_parse_multipart_body() error 5\n";
return;
}
}
_request.multi_bodys.push_back(new_body);
// Move start for next loop
start_pos = end_pos + sizeof("--")-1 + boundary.size();
if ( start_pos + 2 + CRLF_SIZE == raw_request.size()
&& raw_request[start_pos] == '-'
&& raw_request[start_pos+1] == '-')
break;
}
}

94
srcs/cgi-bin/Makefile Normal file
View File

@@ -0,0 +1,94 @@
# - - - - - - #
# #
# COLORS #
# #
# - - - - - - #
GRAY = "\e[0;30m"
RED = "\e[0;31m"
GREEN = "\e[0;32m"
YELLOW = "\e[0;33m"
BLUE = "\e[0;34m"
PURPLE = "\e[0;35m"
CYAN = "\e[0;36m"
WHITE = "\e[0;37m"
B_GRAY = "\e[1;30m"
B_RED = "\e[1;31m"
B_GREEN = "\e[1;32m"
B_YELLOW = "\e[1;33m"
B_BLUE = "\e[1;34m"
B_PURPLE = "\e[1;35m"
B_CYAN = "\e[1;36m"
B_WHITE = "\e[1;37m"
RESET = "\e[0m"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# . name = value \ . += append to a variable #
# VARIABLES . value . != set result of command #
# . name is case sensitive . ?= set if not already set #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
NAME = $(SRCS_X:.cpp=.out)
CXX = c++
CXXFLAGS = -Wall -Wextra #-Werror
CXXFLAGS += $(HEADERS_D:%=-I%)
CXXFLAGS += -std=c++98
CXXFLAGS += -g
VPATH = $(SRCS_D)
HEADERS_D = .
SRCS_D = .
SRCS = cgi_utils.cpp
SRCS_X = \
cgi_cpp.cpp \
cgi_cpp_bad_headers.cpp \
cgi_cpp_empty.cpp \
cgi_cpp_empty_lines.cpp \
cgi_cpp_len.cpp \
cgi_cpp_len_big.cpp \
cgi_cpp_len_small.cpp \
cgi_cpp_no_body.cpp \
cgi_cpp_no_headers.cpp \
cgi_cpp_only_crlf.cpp \
cgi_cpp_sleep.cpp \
cgi_cpp_status.cpp \
cgi_cpp_download.cpp \
OBJS_D = builds
OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o)
OBJS_X = $(SRCS_X:%.cpp=$(OBJS_D)/%.o)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# . target: prerequisites . $@ : target #
# RULES . recipe . $< : 1st prerequisite #
# . recipe . $^ : all prerequisites #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
all: $(NAME)
$(OBJS_D)/%.o: %.cpp | $(OBJS_D)
@echo $(B_GREEN)"compilation :" $@ $(RESET)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(OBJS_D):
mkdir $@
$(NAME): $(OBJS) $(OBJS_X)
$(NAME):
@echo $(B_YELLOW)"linkage :" $@ $(RESET)
$(CXX) $(OBJS) $(@:%.out=$(OBJS_D)/%.o) -o $@
clean:
rm -rf $(OBJS_D)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY : all clean fclean re

4
srcs/cgi-bin/cgi Executable file
View File

@@ -0,0 +1,4 @@
#! /bin/bash
echo "status: 100\r\n"
echo "\r\n\r\n"
echo "hiii"

View File

@@ -1,41 +0,0 @@
# include <iostream>
# include <string>
# include <sstream>
int main (int ac, char **av) {
std::string to_send;
std::string header;
std::string end_header = "\r\n\r\n";
std::string response;
std::stringstream strs;
header = "HTTP/1.1 200 OK\n";
header += "Content-Type: text/html; charset=UTF-8\n";
header += "Content-Length: ";
response = "<!DOCTYPE html>\n";
response += "<html>\n";
response += "<head>\n";
response += "<title>CGI</title>\n";
response += "</head>\n";
response += "<body>\n";
response += "<h2>CGI request :</h2>\n";
for (int i = 1; i < ac; i++)
{
response += "<p>";
response += av[i];
response += "</p>\n";
}
response += "</body>\n";
response += "</html>\n";
strs << response.size();
header += strs.str();
header += end_header;
to_send = header;
to_send += response;
std::cout << to_send;
return 0;
}

4
srcs/cgi-bin/cgi.sh Executable file
View File

@@ -0,0 +1,4 @@
#! /bin/bash
echo "status: 100\r\n"
echo "\r\n\r\n"
echo "hiii"

Binary file not shown.

25
srcs/cgi-bin/cgi_cpp.cpp Normal file
View File

@@ -0,0 +1,25 @@
# include "cgi_utils.hpp"
# include <unistd.h>
# include <cstdio>
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,24 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string http_status;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Bad-Headers: wrong";
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,55 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::string form_infos;
std::string path;
std::ifstream ifd;
std::stringstream buf;
size_t status;
std::cin >> rq_body;
(void)ac;
(void)av;
(void)env;
http_header = "Content-Type: image/jpeg" CRLF;
form_infos = get_form_infos(rq_body);
path = get_value("file", rq_body);
path = "./www/" + path;
status = ::eval_file_access(path, R_OK);
if (status)
{
std::cout << "Status: " << status << CRLF CRLF;
return 0;
}
ifd.open(path.c_str());
if (!ifd)
{
std::cout << "Status: " << 500 << CRLF CRLF;
return 0;
}
else
{
buf << ifd.rdbuf();
if (!ifd || !buf)
{
std::cout << "Status: " << 500 << CRLF CRLF;
return 0;
}
}
std::cout << http_header << CRLF << buf.str();
return 0;
}

View File

@@ -0,0 +1,12 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
(void)ac;
(void)av;
(void)env;
return 0;
}

View File

@@ -0,0 +1,23 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF CRLF CRLF CRLF CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,25 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
http_header += "Content-Length: " + itos(http_body.size()) + CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,25 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
http_header += "Content-Length: " + itos(http_body.size() + 100) + CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,24 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
http_header += "Content-Length: " + itos(http_body.size() - 100) + CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,22 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_status;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
(void)env;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
std::cout << http_header << CRLF;
return 0;
}

View File

@@ -0,0 +1,24 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string http_status;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = CRLF;
fill_body_basic(env, http_body, rq_body);
std::cout << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,15 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
(void)ac;
(void)av;
(void)env;
std::cout << CRLF CRLF;
return 0;
}

View File

@@ -0,0 +1,29 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string rq_body;
size_t time;
std::stringstream ss;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
fill_body_basic(env, http_body, rq_body);
ss << get_value("sleep", rq_body);
ss >> time;
sleep(time);
std::cout << http_header << CRLF << http_body;
return 0;
}

View File

@@ -0,0 +1,27 @@
# include "cgi_utils.hpp"
int main (int ac, char **av, char ** env)
{
std::string http_header;
std::string http_body;
std::string http_status;
std::string rq_body;
std::cin >> rq_body;
(void)ac;
(void)av;
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
fill_body_basic(env, http_body, rq_body);
http_status = get_value("Status", rq_body);
http_header += "Status: " + http_status + CRLF;
std::cout << http_header << CRLF CRLF << http_body;
return 0;
}

29
srcs/cgi-bin/cgi_second/cgi.php Executable file
View File

@@ -0,0 +1,29 @@
#! /usr/bin/php
<?php
echo "false: 300\r\n";
echo "server: Webserv/0.2\r\n";
echo "Status: 300\r\n";
echo "\r\n";
echo "BEGIN PHP-CGI\n-----------\n\n";
echo "AUTH_TYPE: " . getenv("AUTH_TYPE");
echo "\nCONTENT_LENGTH: " . getenv("CONTENT_LENGTH");
echo "\nCONTENT_TYPE: " . getenv("CONTENT_TYPE");
echo "\nGATEWAY_INTERFACE: " . getenv("GATEWAY_INTERFACE");
echo "\nPATH_INFO: " . getenv("PATH_INFO");
echo "\nPATH_TRANSLATED: " . getenv("PATH_TRANSLATED");
echo "\nQUERY_STRING: " . getenv("QUERY_STRING");
echo "\nREMOTE_ADDR: " . getenv("REMOTE_ADDR");
echo "\nREMOTE_HOST: " . getenv("REMOTE_HOST");
echo "\nREMOTE_IDENT: " . getenv("REMOTE_IDENT");
echo "\nREMOTE_USER: " . getenv("REMOTE_USER");
echo "\nREQUEST_METHOD: " . getenv("REQUEST_METHOD");
echo "\nSCRIPT_NAME: " . getenv("SCRIPT_NAME");
echo "\nSERVER_NAME: " . getenv("SERVER_NAME");
echo "\nSERVER_PORT: " . getenv("SERVER_PORT");
echo "\nSERVER_PROTOCOL: " . getenv("SERVER_PROTOCOL");
echo "\nSERVER_SOFTWARE: " . getenv("SERVER_SOFTWARE");
echo "\nREDIRECT_STATUS: " . getenv("REDIRECT_STATUS");
// echo $_POST['REQUEST_METHOD'];
echo "\n\n-----------\nEND PHP-CGI\n\n";
?>

View File

@@ -0,0 +1,3 @@
h1, h2, h3, p {
display: inline;
}

199
srcs/cgi-bin/cgi_utils.cpp Normal file
View File

@@ -0,0 +1,199 @@
#include "cgi_utils.hpp"
std::string str_tolower(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
std::string trim(std::string str, char del)
{
size_t pos;
// delete leadings del
pos = str.find_first_not_of(del);
if (pos == NPOS)
pos = str.size();
str = str.substr(pos);
// delete trailing del
pos = str.find_last_not_of(del);
if (pos != NPOS)
str = str.substr(0, pos + 1);
return str;
}
std::vector<std::string>
split(const std::string & input, std::string delim, char ctrim)
{
std::vector<std::string> split_str;
std::string tmp;
size_t start = 0;
size_t end = 0;
size_t len = 0;
while (end != NPOS)
{
end = input.find(delim, start);
len = end - start;
if (end == NPOS)
len = end;
tmp = input.substr(start, len);
if (ctrim != '\0')
tmp = trim(tmp, ctrim);
if (tmp.size() != 0)
split_str.push_back( tmp );
start = end + delim.size();
}
return split_str;
}
std::string itos(int n)
{
std::stringstream strs;
strs << n;
return ( strs.str() );
}
std::string parse_env(const std::string & env)
{
std::string ret = "";
char * ret_env;
ret_env = getenv(env.c_str());
if (ret_env != NULL)
ret = ret_env;
return ret;
}
std::string print_env(char **env, std::string tag)
{
std::string ret = "";
for (int i = 0; env[i] != NULL; ++i)
{
ret += "<" + tag + ">";
ret += env[i];
ret += "</" + tag + "><br>";
}
return ret;
}
std::string
print_form(std::string form, std::string tag_key, std::string tag_val)
{
std::vector<std::string> split_str;
std::vector<std::string> sub_split_str;
std::vector<std::string>::const_iterator it;
std::string ret = "";
std::string key;
split_str = split(form, "&");
for (it = split_str.begin(); it != split_str.end(); ++it)
{
sub_split_str = split(*it, "=");
key = sub_split_str[0];
if (key == "fname")
key = "first name";
else if (key == "lname")
key = "last name";
ret += "<br><" + tag_key + ">" + key + ": </" + tag_key + ">";
ret += "<" + tag_val + ">" + sub_split_str[1] + "</" + tag_val + ">";
}
return ret;
}
std::string get_form_infos(const std::string & rq_body)
{
std::string form_infos;
std::string method;
method = parse_env("REQUEST_METHOD");
if (method == "POST")
form_infos = rq_body;
else if (method == "GET")
form_infos = parse_env("QUERY_STRING");
return form_infos;
}
std::string get_value(const std::string & key, const std::string & rq_body)
{
std::string infos;
std::string ret;
size_t pos;
size_t end;
size_t len;
infos = get_form_infos(rq_body);
pos = str_tolower(infos).find(str_tolower(key));
if (pos == NPOS)
return "";
pos = infos.find_first_of("=", pos);
if (pos == NPOS)
return "";
pos++;
end = infos.find_first_of("&", pos);
if (end == NPOS)
end = infos.size();
len = end - pos;
ret = infos.substr(pos, len);
return ret;
}
void
fill_body_basic(char **env, std::string & http_body, const std::string & rq_body)
{
std::string rq_method = "not found";
std::string rq_query;
std::string form_infos;
rq_method = parse_env("REQUEST_METHOD");
rq_query = parse_env("QUERY_STRING");
if (rq_method == "POST")
form_infos = rq_body;
else if (rq_method == "GET")
form_infos = rq_query;
http_body = HTML_BODY_TOP;
http_body += "<br><h3>method used: </h3>";
http_body += "<p>" + rq_method + "</p>";
http_body += "<br><h3>form body: </h3>";
http_body += "<p>" + rq_body + "</p>";
http_body += "<br><h3>form query: </h3>";
http_body += "<p>" + rq_query + "</p>";
http_body += "<br><br><h3>output:</h3><br>";
http_body += print_form(form_infos, "p", "p");
http_body += "<br><br><h3>cgi_env_variables:</h3><br>";
http_body += print_env(env, "p");
http_body += HTML_BODY_BOTTOM;
}
size_t eval_file_access(const std::string &path, int mode)
{
if (::access(path.c_str(), F_OK) == -1)
{
std::perror("err access()");
return 404; // NOT_FOUND, file doesn't exist
}
if (::access(path.c_str(), mode) == -1)
{
std::perror("err access()");
return 403; // FORBIDDEN, file doesn't have access permission
}
return 0;
}

View File

@@ -0,0 +1,68 @@
#ifndef CGI_UTILS_HPP
# define CGI_UTILS_HPP
# include <iostream>
# include <string>
# include <sstream>
# include <fstream>
# include <vector>
# include <stdlib.h> // getenv
# include <algorithm> // transform
# include <unistd.h> // sleep, close, access
# define CR "\r"
# define LF "\n"
# define CRLF CR LF
# define CRLF_SIZE 2
# define NPOS std::string::npos
# define HTML_BODY_TOP "<!DOCTYPE html>"\
"<html>"\
" <head>"\
" <meta charset=\"UTF-8\">"\
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"\
" <title>CGI</title>"\
" <link href=\"./cgi_style.css\" type=\"text/css\" rel=\"stylesheet\">"\
" </head>"\
" <body>"\
" <h1>CGI</h1><br>"
# define HTML_BODY_BOTTOM " <br><h1>END CGI</h1>"\
" </body>"\
"</html>"
std::string
str_tolower(std::string str);
std::string
trim(std::string str, char del);
std::vector<std::string>
split(const std::string & input, std::string delim, char ctrim = '\0');
std::string
itos(int n);
std::string
parse_env(const std::string & env);
std::string
print_env(char **env, std::string tag = "p");
std::string
get_form_infos(const std::string & rq_body);
std::string
get_value(const std::string & key, const std::string & rq_body);
std::string
print_form(std::string form, std::string key = "p", std::string val = "p");
void
fill_body_basic(char **env, std::string & http_body, const std::string & rq_body);
size_t
eval_file_access(const std::string &path, int mode);
#endif

10
srcs/cgi-bin/tmp.php Executable file
View File

@@ -0,0 +1,10 @@
#! /usr/bin/php
<?php
if(isset($_FILE["fileToUpload"]))
echo "there is a file";
else
echo "\n\nHeader: something\r\n\r\nno file\r\n";
?>

56
srcs/cgi-bin/upload_file.php Executable file
View File

@@ -0,0 +1,56 @@
#! /usr/bin/php
<?php
$target_dir = "user_files/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
} else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The file ". htmlspecialchars( basename( $_FILES["fileToUpload"]["name"])). " has been uploaded.";
} else {
echo "Sorry, there was an error uploading your file.";
}
}
?>

67
srcs/cgi-bin/upload_file1.php Executable file
View File

@@ -0,0 +1,67 @@
#! /usr/bin/php
<?php
// $FileName=$_FILES['myFile']['filename'];
# $TmpName=$_FILES['myFile']['tmp_name'];
# move_uploaded_file($TmpName, $FileName);
# echo("File was uploaded successfully!");
// this part needs to be grabed from POST uri
$target_dir = "./www/user_files/";
//$target_dir = $_POST["upload_dir"];
//$target_dir = $_GET["upload_dir"];
//$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$target_file = $target_dir . basename($_FILES["myFile"]["name"]);
$uploadOk = 1;
$fileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
//$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
//if(isset($_POST["submit"])) {
// $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
// if($check !== false) {
// echo "File is an image - " . $check["mime"] . ".";
// $uploadOk = 1;
// } else {
// echo "File is not an image.";
// $uploadOk = 0;
// }
//}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
//if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
//&& $imageFileType != "gif" ) {
// echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
// $uploadOk = 0;
//}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The file ". htmlspecialchars( basename( $_FILES["fileToUpload"]["name"])). " has been uploaded.";
} else {
echo "Sorry, there was an error uploading your file.";
}
}
?>

View File

@@ -1,14 +1,3 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ConfigParser.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/07/11 23:01:41 by me #+# #+# */
/* Updated: 2022/08/03 17:32:33 by lperrey ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CONFIGPARSER_HPP
# define CONFIGPARSER_HPP
@@ -22,99 +11,45 @@
# include <exception> // exception, what
# include <stdexcept> // runtime_error, invalid_argument
# include <string> // string
# include <cstdlib> // atoi (athough it's already cover by <string>)
# include <cstdlib> // strtol, stroul
# include <iostream> // cout, cin
# include <fstream> // ifstream
//# include <unistd.h> // access()
# include <dirent.h> // opendir(), doesn't work...
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
# include <algorithm> // sort() in Post
class ConfigParser {
public:
public:
// canonical
ConfigParser();
~ConfigParser();
ConfigParser(const std::string &config_file);
ConfigParser(const char* path); // a string?
~ConfigParser();
void read_config(const std::string &config_file);
// ideally i wouldn't have one cuz it makes no sense, when would i use it?
// ConfigParser & operator=(const ConfigParser& rhs);
std::vector<ServerConfig> *parse() const;
// void parse(); // return void cuz throw exceptions.
std::vector<ServerConfig> * parse(); // const?
// std::vector<ServerConfig> parse(); // const?
void print_content() const;
// other parses?
private:
std::string _content;
// i thought if it were an instance of this class you could call
// private member functions from anywhere...
void _print_content() const;
ServerConfig _parse_server(size_t *start) const;
LocationConfig _parse_location(size_t *start) const;
void _set_server_values(ServerConfig *server, const std::string &key, std::string value) const;
void _set_location_values(LocationConfig *location, const std::string &key, std::string value) const;
// I don't love that this is here but...
// doesn't work use the operator overload
// bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b);
/* Extra */
std::string _pre_set_val_check(const std::string &key,
const std::string &value) const;
std::string _get_first_word(size_t *curr) const;
std::string _get_rest_of_line(size_t *curr) const;
private:
std::string _content;
// explicit?
// what exaclty does explicit do again?
ConfigParser(); // might need a path as arg?
// should this be in private since it always needs a path?
ServerConfig _parse_server(size_t *start);
LocationConfig _parse_location(size_t *start);
void _set_server_values(ServerConfig *server, const std::string key, std::string value);
void _set_location_values(LocationConfig *location, const std::string key, std::string value);
std::string _pre_set_val_check(const std::string key, \
const std::string value);
std::string _get_first_word(size_t *curr); // const?
std::string _get_rest_of_line(size_t *curr); // const?
// some sort of post processing...
void _post_processing(std::vector<ServerConfig> *servers);
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
/* Post Processing */
void _post_processing(std::vector<ServerConfig> *servers) const;
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
};
// no idea if it should go here...
//bool compareLocationConfigs(const LocationConfig &a,
// const LocationConfig &b);
// def needs work line a better name an do i even need this?
// should it be in Utils instead?
class MyException : public std::invalid_argument
{
MyException(const std::string str)
: std::invalid_argument(str) {}
};
#endif

View File

@@ -1,37 +1,16 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* LocationConfig.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef LOCATIONCONFIG_HPP
# define LOCATIONCONFIG_HPP
# include <map>
# include <vector>
# include <string>
# include <iostream>
# include <sys/stat.h> // stat()
# include <stdio.h> // printf(), gotta go
# include "utils.hpp"
// again, struct instead?
class LocationConfig
struct LocationConfig
{
public:
// canonic stuff?
std::string path; // /path and /path/ are fine
// i add trailing / if a dir
// i remove trailing / systematically
std::string root;
std::vector<std::string> index;
unsigned int allow_methods;
@@ -42,10 +21,8 @@ public:
int redirect_status; // only in location
std::string redirect_uri; // only 1 per location
// au pire you do location / { return 301 http://location; }
// and that's how you get the redirect from the root.
void print_all()
void print_all() const
{
std::cout << "\nPRINTING A LOCATION\n";
@@ -63,14 +40,13 @@ public:
std::cout << "------\n";
}
// works a lot better than using a compare function...
bool operator<(const LocationConfig& rhs) const
bool operator<(const LocationConfig& rhs) const
{
int comp_lhs = 0;
int comp_rhs = 0;
size_t tmp = 0;
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
while ((tmp = this->path.find_first_of("/", tmp)) != NPOS)
{
++tmp;
++comp_lhs;
@@ -78,7 +54,7 @@ public:
if (path[path.find_last_of("/") + 1] != '\0')
++comp_lhs;
tmp = 0;
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
while ((tmp = rhs.path.find_first_of("/", tmp)) != NPOS)
{
++tmp;
++comp_rhs;
@@ -86,19 +62,16 @@ public:
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
++comp_rhs;
return (comp_lhs < comp_rhs); // right comparison ? not <= ?
return (comp_lhs < comp_rhs);
};
bool operator==(const LocationConfig& rhs) const
{
if (path.compare(rhs.path) == 0)
return true;
return false;
}
};
#endif

View File

@@ -10,22 +10,16 @@
# include <string> // string
# include <iostream> // cout, cin
// a class that's all public? just so we have options?
class ServerConfig
struct ServerConfig
{
public:
// do i need some canonic stuff?
std::vector<std::string> server_name;
// we could shove default in here if we wanted to...
std::string host;
std::string port; // port needs to be something else... not quite an int
std::string port;
std::string root; // ./www/ or www work www/ and www work
// i do remove trailing / tho
std::string root;
unsigned int client_body_limit; // set to default max if none set
size_t client_body_limit;
std::vector<std::string> index;
std::map<int, std::string> error_pages;
@@ -33,7 +27,7 @@ public:
std::vector<LocationConfig> locations;
void print_all()
void print_all() const
{
std::cout << "PRINTING A FULL SERVER CONFIG\n\n";
@@ -44,14 +38,11 @@ public:
for (size_t i = 0; i < index.size(); i++)
std::cout << index[i] << " ";
std::cout << "\nerror_pages: ";
for(std::map<int, std::string>::iterator it = error_pages.begin(); \
for(std::map<int, std::string>::const_iterator it = error_pages.begin(); \
it != error_pages.end(); it++)
std::cout << it->first << "--" << it->second << " ";
// for (size_t i = 0; i < error_pages.size(); i++)
// std::cout << error_pages->first << "--" << error_pages->second << " ";
// std::cout << "skiping Locations for now...\n";
for (std::vector<LocationConfig>::iterator it = locations.begin(); it < locations.end(); it++)
for (std::vector<LocationConfig>::const_iterator it = locations.begin(); it < locations.end(); it++)
it->print_all();
std::cout << "client_body_limit: " << client_body_limit << '\n';

View File

@@ -1,27 +0,0 @@
// prolly get rid of this file...
#include "LocationConfig.hpp"
#include <string>
#include <algorithm>
// Ok so maybe it can't be a member functions?
bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b)
{
int len_a;
int len_b;
size_t tmp = 0;
// consider adding 1 to path that ends in a file not folder.
while ((tmp = a.path.find_first_of("/", tmp)) != std::string::npos)
++len_a;
tmp = 0;
while ((tmp = b.path.find_first_of("/", tmp)) != std::string::npos)
++len_b;
return (len_a < len_b); // right comparison ? not <= ?
}

View File

@@ -1,69 +1,54 @@
#include "ConfigParser.hpp"
std::string ConfigParser::_pre_set_val_check(const std::string key, \
const std::string value)
std::string ConfigParser::_pre_set_val_check(const std::string &key,
const std::string &value) const
{
// check key for ;
// check values for ; at end and right number of words depending on key
// std::cout << "pre check\n";
if (key.find_first_of(";") != std::string::npos)
throw std::invalid_argument("bad config file arguments 2");
if (key.find_first_of(";") != NPOS)
throw std::invalid_argument("bad config file arguments");
// there shouldn't be any tabs, right? not between values...
if (value.find_first_of("\t") != std::string::npos)
{
// std::cout << value << "\n";
if (value.find_first_of("\t") != NPOS)
throw std::invalid_argument("why would you put tabs between values");
}
size_t i = value.find_first_of(";");
// so you can't have no ;
// you can't have just ;
// and you can't have a ; not at the end or several ;
// in theory value_find_last_of should find the only ;
if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \
if (i == NPOS || (value.find_last_not_of(" \n")) != i \
|| value.compare(";") == 0)
throw std::invalid_argument("bad config file arguments 4");
throw std::invalid_argument("bad config file arguments");
return (value.substr(0, i));
}
// assumes curr is on a space or \t or \n
// get first word? next word? word?
std::string ConfigParser::_get_first_word(size_t *curr)
std::string ConfigParser::_get_first_word(size_t *curr) const
{
size_t start;
// are these checks excessive?
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
throw std::invalid_argument("bad config file arguments");
if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
if ((*curr = _content.find_first_of(" \t\n", start)) == NPOS)
throw std::invalid_argument("bad config file arguments");
std::string key = _content.substr(start, *curr - start);
return (key);
}
// also assumes curr is on a space \t or \n
std::string ConfigParser::_get_rest_of_line(size_t *curr)
std::string ConfigParser::_get_rest_of_line(size_t *curr) const
{
size_t start;
if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos)
if ((start = _content.find_first_not_of(" \t\n", *curr)) == NPOS)
throw std::invalid_argument("bad config file arguments");
if ((*curr = _content.find_first_of("\n", start)) == std::string::npos)
if ((*curr = _content.find_first_of("\n", start)) == NPOS)
throw std::invalid_argument("bad config file arguments");
std::string values = _content.substr(start, *curr - start);
@@ -71,9 +56,7 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
}
void ConfigParser::_print_content() const
void ConfigParser::print_content() const
{
std::cout << _content;
}

View File

@@ -1,44 +1,34 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ConfigParser.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/07/13 22:11:17 by me #+# #+# */
/* Updated: 2022/08/03 17:51:35 by lperrey ### ########.fr */
/* */
/* ************************************************************************** */
#include "ConfigParser.hpp"
// Default
ConfigParser::ConfigParser()
ConfigParser::ConfigParser() {};
ConfigParser::~ConfigParser() {};
ConfigParser::ConfigParser(const std::string &config_file)
{
std::cout << "Default Constructor\n";
// don't use yet, you have no idea what the defaults are
read_config(config_file);
}
ConfigParser::ConfigParser(const char* path)
void ConfigParser::read_config(const std::string &config_file)
{
std::cout << "Param Constructor\n";
std::ifstream file;
std::string buf;
size_t comment;
_content.clear();
file.open(path);
if (file.is_open())
file.open(config_file.c_str());
if (!file)
throw std::invalid_argument("failed to open config");
else
{
_content.clear();
while (!file.eof())
{
getline(file, buf);
// remove # comments here.
if ((comment = buf.find_first_of("#")) == std::string::npos)
if ((comment = buf.find_first_of("#")) == NPOS)
{
// remove empty lines, i think...
if ((buf.find_first_not_of(" \t")) != std::string::npos)
if ((buf.find_first_not_of(" \t")) != NPOS)
_content.append(buf + '\n');
}
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
@@ -48,68 +38,46 @@ ConfigParser::ConfigParser(const char* path)
_content.append(tmp + '\n');
}
}
file.close();
}
else
throw std::invalid_argument("failed to open config");
}
ConfigParser::~ConfigParser()
{
// do i need to destroy anything, won't it handle itself?
}
/*
ConfigParser & ConfigParser::operator=(const ConfigParser& rhs)
{
if (this == rhs) // * & ?
return (*this); // * ?
// make some stuff equal
return (*this);
}
*/
std::vector<ServerConfig> * ConfigParser::parse()
std::vector<ServerConfig> * ConfigParser::parse() const
{
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
// std::vector<ServerConfig> ret;
size_t start = 0;
size_t curr = _content.find_first_not_of(" \t\n", 0);
if (curr == std::string::npos)
if (curr == NPOS)
throw std::invalid_argument("empty config file");
while (curr != std::string::npos)
while (curr != NPOS)
{
if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos)
if ((start = _content.find_first_not_of(" \t\n", curr)) == NPOS)
throw std::invalid_argument("empty config file");
if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
if ((curr = _content.find_first_of(" \t\n", start)) == NPOS)
throw std::invalid_argument("empty config file");
std::string key = _content.substr(start, curr - start);
if (key != "server")
throw std::invalid_argument("bad config file arguments 1");
throw std::invalid_argument("bad config file arguments");
ret->push_back(_parse_server(&curr));
}
_post_processing(ret);
return (ret);
}
ServerConfig ConfigParser::_parse_server(size_t *start)
ServerConfig ConfigParser::_parse_server(size_t *start) const
{
ServerConfig ret;
size_t curr = _content.find_first_not_of(" \t\n", *start);
ret.client_body_limit = 0;
if (curr == std::string::npos || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax 1");
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
if (curr == NPOS || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax");
// are there other things to check for?
while (curr != std::string::npos) // here curr == { + 1
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
throw std::invalid_argument("bad config file syntax");
while (curr != NPOS) // here curr == { + 1
{
// so this moves curr to past the word...
std::string key = _get_first_word(&curr);
@@ -134,7 +102,7 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
LocationConfig ConfigParser::_parse_location(size_t *start)
LocationConfig ConfigParser::_parse_location(size_t *start) const
{
LocationConfig ret;
size_t curr = *start;
@@ -147,17 +115,16 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
ret.path = _get_first_word(&curr);
if (ret.path[0] != '/')
throw std::invalid_argument("Location path require a leading /");
// ret.path.insert(0, "/");
// in theory now curr should be right after the "path"
curr = _content.find_first_not_of(" \t\n", curr);
if (curr == std::string::npos || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax 2");
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
if (curr == NPOS || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax");
while (curr != std::string::npos)
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
throw std::invalid_argument("bad config file syntax");
while (curr != NPOS)
{
// so this moves curr to past the word...
std::string key = _get_first_word(&curr);
@@ -179,11 +146,9 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
// should i be sending pointers or references?
void ConfigParser::_set_server_values(ServerConfig *server, \
const std::string key, std::string value)
void ConfigParser::_set_server_values(ServerConfig *server,
const std::string &key, std::string value) const
{
// should i be sending pointers or references?
value = _pre_set_val_check(key, value);
std::vector<std::string> tmp_val = ::split(value, ' ');
@@ -193,20 +158,23 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
throw std::invalid_argument("missing value");
else if (key == "server_name" && server->server_name.empty())
{
for (std::vector<std::string>::iterator it = server->server_name.begin(); \
it < server->server_name.end(); it++)
for (size_t i = 0; i < size; i++)
{
if (it->compare(tmp_val[0]) == 0)
throw std::invalid_argument("server_name already exists");
for (std::vector<std::string>::const_iterator it = server->server_name.begin();
it < server->server_name.end(); it++)
{
if (it->compare(tmp_val[i]) == 0)
throw std::invalid_argument("server_name already exists");
}
server->server_name.push_back(tmp_val[i]);
}
server->server_name.push_back(tmp_val[0]);
}
else if (key == "listen" && size == 1 && server->host == "" \
else if (key == "listen" && size == 1 && server->host == ""
&& server->port == "")
{
if (tmp_val[0].find_first_of(":") == std::string::npos)
if (tmp_val[0].find_first_of(":") == NPOS)
{
if (!::isNumeric(tmp_val[0]))
if (!::is_numeric_btw(0, 65535, tmp_val[0]))
throw std::invalid_argument("bad port number");
server->host = "0.0.0.0";
server->port = tmp_val[0];
@@ -220,10 +188,10 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
throw std::invalid_argument("bad host ip");
for (size_t i = 0; i < ip.size(); i++)
{
if (!::isNumeric_btw(0, 255, ip[i]))
if (!::is_numeric_btw(0, 255, ip[i]))
throw std::invalid_argument("bad host ip");
}
if (!::isNumeric(tmp2[1]))
if (!::is_numeric_btw(0, 65535, tmp2[1]))
throw std::invalid_argument("bad port number");
server->host = tmp2[0];
server->port = tmp2[1];
@@ -236,26 +204,29 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
server->root = tmp_val[0];
}
else if (key == "client_body_limit" && size == 1 \
else if (key == "client_body_limit" && size == 1
&& server->client_body_limit == 0)
{
if (!::isNumeric(tmp_val[0]))
if (!::is_numeric(tmp_val[0]))
throw std::invalid_argument("client_body_limit not a number");
server->client_body_limit = atoi(tmp_val[0].c_str());
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
if (errno == ERANGE || server->client_body_limit > (ULONG_MAX / KB) )
throw std::invalid_argument("client_body_limit too big");
server->client_body_limit = server->client_body_limit * KB;
}
else if (key == "index")
{
for (unsigned long i = 0; i != tmp_val.size(); i++)
for (size_t i = 0; i != tmp_val.size(); i++)
server->index.push_back(tmp_val[i]);
}
else if (key == "error_page")
{
std::string path = tmp_val[tmp_val.size() - 1];
for (unsigned long i = 0; i != tmp_val.size() - 1; i++)
std::string path = tmp_val[size - 1];
for (size_t i = 0; i < size - 1; i++)
{
if (!(isNumeric_btw(400, 599, tmp_val[i])))
if (!(is_numeric_btw(400, 599, tmp_val[i])))
throw std::invalid_argument("invalid error code");
int status_code = atoi(tmp_val[i].c_str());
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
if (server->error_pages.find(status_code) != server->error_pages.end())
throw std::invalid_argument("redeclaring error page");
server->error_pages[status_code] = path;
@@ -266,11 +237,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
}
// should i be sending pointers or references?
void ConfigParser::_set_location_values(LocationConfig *location, \
const std::string key, std::string value)
void ConfigParser::_set_location_values(LocationConfig *location,
const std::string &key, std::string value) const
{
// should i be sending pointers or references?
value = _pre_set_val_check(key, value);
std::vector<std::string> tmp_val = ::split(value, ' ');
@@ -289,12 +258,12 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
location->autoindex = (tmp_val[0] == "on" ? true : false);
else if (key == "index")
{
for (unsigned long i = 0; i != tmp_val.size(); i++)
for (size_t i = 0; i < size; i++)
location->index.push_back(tmp_val[i]);
}
else if (key == "allow_methods" && location->allow_methods == 0)
{
for (unsigned long i = 0; i != tmp_val.size(); i++)
for (size_t i = 0; i < size; i++)
{
http_method m = ::str_to_http_method(tmp_val[i]);
if (m == UNKNOWN)
@@ -304,33 +273,31 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
}
else if (key == "cgi_ext")
{
for (size_t i = 0; i < tmp_val.size(); i++)
for (size_t i = 0; i < size; i++)
{
if (tmp_val[i][0] == '.')
throw std::invalid_argument("cgi_ext should not have a leading '.'");
location->cgi_ext.push_back(tmp_val[i]);
}
}
else if (key == "redirect" && location->redirect_status == 0 \
else if (key == "redirect" && location->redirect_status == 0
&& location->redirect_uri == "")
{
if (tmp_val.size() != 2)
if (size != 2)
throw std::invalid_argument("wrong number of values");
if (tmp_val[0] != "301" && tmp_val[0] != "302"
&& tmp_val[0] != "303" && tmp_val[0] != "307"
&& tmp_val[0] != "308")
throw std::invalid_argument("bad redirect status");
std::cout << tmp_val[1] << '\n';
if (tmp_val[1].compare(0, 7, "http://")
&& tmp_val[1].compare(0, 8, "https://"))
throw std::invalid_argument("bad redirect uri");
location->redirect_status = atoi(tmp_val[0].c_str());
location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10);
location->redirect_uri = tmp_val[1];
}
else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
{
// what checks to do?
// add trailing /
if (tmp_val[0][tmp_val[0].size() - 1] != '/')
tmp_val[0].push_back('/');

View File

@@ -3,31 +3,23 @@
#include "ConfigParser.hpp"
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers) const
{
std::vector<ServerConfig>::iterator it = servers->begin();
while (it != servers->end())
{
// host and port are Mandatory
if (it->host == "")
throw std::invalid_argument("Config file needs a host and port");
// root is mandatory
if (it->root == "")
throw std::invalid_argument("Config file needs a root");
// index is mandatory in Server
if (it->index.empty())
throw std::invalid_argument("Config file needs an Index");
if (it->client_body_limit == 0)
it->client_body_limit = 5000; // what is the recomended size?
// if error_pages is left empty, we'll use the defaults which
// i believe are set elsewhere...
it->client_body_limit = 1 * MB;
if (!_find_root_path_location(it->locations))
{
@@ -46,23 +38,37 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
while (it_l != it->locations.end())
{
if (it_l->path[it_l->path.size() - 1] == '/'
&& it_l->path.size() > 1)
it_l->path.erase(it_l->path.size() - 1);
if (it_l->root == "")
it_l->root = it->root;
if (it_l->allow_methods == UNKNOWN)
it_l->allow_methods = ANY_METHODS;
if (it_l->index.empty())
if (it_l->index.empty() && it_l->autoindex == false)
it_l->index = it->index;
// nothing to be done for cgi_ext, error_pages, redirect
if (path_is_valid(it_l->root + it_l->path) == 1 \
&& it_l->path[it_l->path.size() - 1] != '/')
it_l->path.push_back('/');
++it_l;
}
it_l = it->locations.begin();
while (it_l != it->locations.end())
{
std::vector<LocationConfig>::const_iterator tmp = it_l + 1;
while (tmp != it->locations.end())
{
if (it_l->path == tmp->path)
throw std::invalid_argument("Duplicate locations in config file");
++tmp;
}
++it_l;
}
std::sort(it->locations.begin(), it->locations.end());
std::reverse(it->locations.begin(), it->locations.end());
@@ -70,19 +76,15 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
}
}
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations) const
{
std::vector<LocationConfig>::iterator it = locations.begin();
std::vector<LocationConfig>::const_iterator it = locations.begin();
while (it != locations.end())
{
if (it->path.compare("/") == 0)
{
// std::cout << "in compare: " << it->path << " -- ";
return true;
}
++it;
}
return false;
}
}

View File

@@ -5,47 +5,37 @@
#include "Webserv.hpp"
#include "ConfigParser.hpp"
// debug
family_state g_family;
sem_t* g_shmem;
// debug end
int main(int ac, char **av)
{
// debug
init_semaphore();
std::vector<ServerConfig>* servers_config = NULL;
try
{
std::string config = (ac == 2 ? av[1] : "./default.config");
// like this just looks kinda gross, why bother creating an instance
// and not immediately parsing? like it servers no other purpose...
// what if i call parse directly in the constructor?
// oh because the constructor has no return, but still
// is there a better way?
ConfigParser configParser(config);
// configParser.print_content();
ConfigParser configParser(config.c_str());
configParser._print_content();
// i don't love that servers has to be a pointer...
std::vector<ServerConfig>* servers = configParser.parse();
// use an iterator you moron
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
{
// std::cout << it->server_name << " ";
it->print_all();
}
// Webserv serv(configParser.parse());
// is this better or worse than using
servers_config = configParser.parse();
Webserv serv;
// serv.init_virtual_servers();
serv.init_virtual_servers(servers);
delete servers;
serv.init_virtual_servers(servers_config);
delete servers_config;
servers_config = NULL;
serv.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << '\n';
delete servers_config;
}
return (0);
}

View File

@@ -1,14 +1,6 @@
#include "utils.hpp"
void throw_test()
{
static int i = 0;
++i;
if (i % 8 == 0)
throw std::bad_alloc();
}
// notice : the use of getline make it such as
// it doesn't identify multiple delim as one :
// " something \n else " -> 1 - something
@@ -38,11 +30,11 @@ std::vector<std::string>
size_t end = 0;
size_t len = 0;
while (end != std::string::npos)
while (end != NPOS)
{
end = input.find(delim, start);
len = end - start;
if (end == std::string::npos)
if (end == NPOS)
len = end;
tmp = input.substr(start, len);
if (ctrim != '\0')
@@ -60,31 +52,18 @@ std::string trim(std::string str, char del)
// delete leadings del
pos = str.find_first_not_of(del);
if (pos == std::string::npos)
if (pos == NPOS)
pos = str.size();
str = str.substr(pos);
// delete trailing del
pos = str.find_last_not_of(del);
if (pos != std::string::npos)
if (pos != NPOS)
str = str.substr(0, pos + 1);
return str;
}
//// trim a set of char
//std::string trim(std::string str, std::string del)
//{
// std::string new_str;
//
// while (new_str.compare(str) != 0)
// {
// for (size_t i = 0; i < del.size(); i++)
// trim(str, del[i]);
// }
// return str;
//}
std::string itos(int n)
{
std::stringstream strs;
@@ -93,7 +72,7 @@ std::string itos(int n)
return ( strs.str() );
}
bool isNumeric(std::string str)
bool is_numeric(std::string str)
{
for (size_t i = 0; i < str.length(); i++)
{
@@ -103,14 +82,14 @@ bool isNumeric(std::string str)
return true;
}
bool isNumeric_btw(int low, int high, std::string str)
bool is_numeric_btw(int low, int high, std::string str)
{
for (size_t i = 0; i < str.length(); i++)
{
if (std::isdigit(str[i]) == false)
return false;
}
int n = std::atoi(str.c_str());
int n = std::strtol(str.c_str(), NULL, 10);
if (n < low || n > high)
return false;
return true;
@@ -151,29 +130,40 @@ std::string http_methods_to_str(unsigned int methods)
# include <iostream>
// you could make this &path...
int path_is_valid(std::string path)
file_type eval_file_type(const std::string &path)
{
const char *tmp_path = path.c_str();
struct stat s;
if (stat(tmp_path, &s) == 0)
if (::stat(path.c_str(), &s) != -1)
{
if (S_ISREG(s.st_mode))
{
// std::cout << "is a file\n";
return (2);
}
return (IS_FILE);
else if (S_ISDIR(s.st_mode))
{
// std::cout << "is a Dir\n";
return (1);
}
return (IS_DIR);
}
// std::cout << "path is neither dir nor file\n";
return (0);
else
{
std::perror("err stat()");
}
return (IS_OTHER);
}
size_t eval_file_access(const std::string &path, int mode)
{
if (::access(path.c_str(), F_OK) == -1)
{
std::perror("err access()");
return 404; // NOT_FOUND, file doesn't exist
}
if (::access(path.c_str(), mode) == -1)
{
std::perror("err access()");
return 403; // FORBIDDEN, file doesn't have access permission
}
return 0;
}
void
replace_all_substr(
@@ -187,7 +177,7 @@ void
while (1)
{
pos = str.find(ori_substr, pos);
if (pos == std::string::npos)
if (pos == NPOS)
break;
str.replace(pos, ori_substr.size(), new_substr);
pos += new_substr.size();
@@ -201,8 +191,8 @@ std::string str_tolower(std::string str)
}
// identify a line in a string, by delim (ex. '\n')
// delete this line from the string
// and return the deleted line
// delete this line from the string (and the following nl sequence characters)
// and return the deleted line (without the followinf nl sequence characters)
std::string
extract_line(std::string & str, size_t pos, std::string delim)
{
@@ -212,18 +202,18 @@ std::string
size_t len;
begin = str.rfind(delim, pos);
if (begin == std::string::npos)
if (begin == NPOS)
begin = 0;
else
else if (begin < pos)
begin += delim.size();
end = str.find(delim, pos);
len = end;
if (end != std::string::npos)
if (end != NPOS)
len = end - begin;
del_str = str.substr(begin, len);
str.erase(begin, len);
str.erase(begin, len + delim.size());
return del_str;
}
@@ -245,7 +235,7 @@ size_t
std::vector<std::string> list;
std::vector<std::string>::iterator it;
std::vector<std::string>::iterator it_end;
size_t err = 0;
size_t err_count = 0;
size_t pos;
std::string key;
std::string val;
@@ -256,15 +246,15 @@ size_t
for (it = list.begin(); it != it_end; it++)
{
pos = (*it).find(':');
if (pos == std::string::npos)
if (pos == NPOS)
{
err++;
err_count++;
continue;
}
key = (*it).substr(0, pos);
if ( key.find(' ') != std::string::npos )
if ( key.find(' ') != NPOS )
{
err++;
err_count++;
continue;
}
// bad idea, in cgi we need to have the original value
@@ -273,7 +263,7 @@ size_t
val = ::trim(val, ' ');
fields.insert( std::pair<std::string, std::string>(key, val) );
}
return err;
return err_count;
}
void str_map_key_tolower(std::map<std::string, std::string> & mp)
@@ -294,6 +284,15 @@ void str_map_key_tolower(std::map<std::string, std::string> & mp)
}
// DEBUG
void throw_test()
{
static int i = 0;
++i;
if (i % 8 == 0)
throw std::bad_alloc();
}
void print_special(std::string str)
{
char c;
@@ -302,15 +301,60 @@ void print_special(std::string str)
{
c = str[i];
if (c == '\r')
std::cout << YELLOW << "\\r" << RESET;
std::cerr << YELLOW << "\\r" << RESET;
else if (c == '\n')
std::cout << YELLOW << "\\n" << RESET << "\n";
std::cerr << YELLOW << "\\n" << RESET << "\n";
else
std::cout << c;
fflush(stdout);
std::cerr << c;
std::fflush(stdout);
}
}
// https://stackoverflow.com/questions/5656530/how-to-use-shared-memory-with-linux-in-c
void* create_shared_memory(size_t size) {
// Our memory buffer will be readable and writable:
int protection = PROT_READ | PROT_WRITE;
// The buffer will be shared (meaning other processes can access it), but
// anonymous (meaning third-party processes cannot obtain an address for it),
// so only this process and its children will be able to use it:
int visibility = MAP_SHARED | MAP_ANONYMOUS;
// The remaining parameters to `mmap()` are not important for this use case,
// but the manpage for `mmap` explains their purpose.
return mmap(NULL, size, protection, visibility, -1, 0);
}
void init_semaphore()
{
int ret;
g_shmem = static_cast<sem_t*>(create_shared_memory(4));
ret = sem_init(g_shmem, 1, 1);
if (ret != 0)
{
perror("err sem_init()");
exit(EXIT_FAILURE);
}
}
void print_secure(std::string message)
{
sem_wait(g_shmem);
std::cerr << g_family << "| " << message;
sem_post(g_shmem);
}
void print_secure(int fd, std::string message)
{
sem_wait(g_shmem);
std::cerr << g_family << "| (" << std::setw(2) << fd << ") " << message;
sem_post(g_shmem);
}
// OVERLOADS
bool operator==(const listen_socket& lhs, int fd)
{ return lhs.fd == fd; }

View File

@@ -6,16 +6,48 @@
# include <map>
# include <string>
# include <sstream>
# include <cstdlib> // atoi
# include <iostream>
# include <iomanip> // setw
# include <cstdlib> // strtol, strtoul
# include <climits> // LONG_MAX
# include <cerrno> // errno
# include <sys/stat.h> // stat()
# include <cctype> // tolower
# include <algorithm> // transform
# include <cstdio> // fflush
# include "colors.h"
# include <cstdio> // perror, fflush
# include <unistd.h> // close, access
# include "colors.h" // for debug print_special
// debug
# include <sys/mman.h> // mmap (for shared memory)
# include <semaphore.h> // sem_init, sem_post, sem_wait
# define CR "\r"
# define LF "\n"
# define CRLF CR LF
# define CRLF_SIZE 2
# define NPOS std::string::npos
# define KB 1024
# define MB 1048576
/* Equivalent for end of http header size :
** std::string(CRLF CRLF).size();
** sizeof(CRLF CRLF) - 1;
** CRLF_SIZE*2
*/
enum family_state
{
PARENT,
CHILD
};
enum file_type
{
IS_OTHER,
IS_FILE,
IS_DIR
};
enum http_method
{
@@ -24,8 +56,6 @@ enum http_method
POST = 1 << 1,
DELETE = 1 << 2,
ANY_METHODS = 0b11111111,
// ALL_METHODS = 0b11111111,
// i would prefer this...
};
struct listen_socket
@@ -37,23 +67,56 @@ struct listen_socket
bool operator==(const listen_socket& lhs, int fd);
bool operator==(int fd, const listen_socket& rhs);
extern family_state g_family;
extern sem_t* g_shmem;
std::vector<std::string> split(std::string input, char delimiter);
std::vector<std::string> split_trim(std::string input, std::string delim = "\n", char ctrim = '\0');
bool isNumeric(std::string str);
bool isNumeric_btw(int low, int high, std::string str);
bool is_numeric(std::string str);
bool is_numeric_btw(int low, int high, std::string str);
std::string itos(int n);
std::string trim(std::string str, char del);
http_method str_to_http_method(std::string &str);
std::string http_methods_to_str(unsigned int methods);
int path_is_valid(std::string path);
file_type eval_file_type(const std::string &path);
size_t eval_file_access(const std::string &path, int mode);
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
std::string str_tolower(std::string str);
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n");
size_t parse_http_headers (std::string headers, std::map<std::string, std::string> & fields );
void str_map_key_tolower(std::map<std::string, std::string> & mp);
void throw_test();
// debug
void print_special(std::string str);
void throw_test();
void print_special(std::string str);
void* create_shared_memory(size_t size);
void init_semaphore();
void print_secure(int fd, std::string message);
void print_secure(std::string message);
/* Template */
template <typename T1, typename T2 >
void print_pair(const std::pair<T1,T2> p)
{
std::cout << p.first << ": ";
std::cout << p.second << "\n";
}
template <typename Key, typename T >
void print_map(const std::map<Key,T>& c)
{
typename std::map<Key,T>::const_iterator it = c.begin();
typename std::map<Key,T>::const_iterator it_end = c.end();
std::cout << " --print_map():\n";
std::cout << "map.size() = " << c.size() << "\n";
while (it != it_end)
{
print_pair(*it);
++it;
}
std::cout << " --\n";
}
#endif

View File

@@ -15,6 +15,7 @@
# include <cerrno> // errno
# include <unistd.h> // close, access
# include <iostream> // cout, cin
# include <iomanip> // setw
# include <cstring> // memset
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
@@ -23,23 +24,21 @@
# include <algorithm> // find
# include <string> // string
# include <cstdio> // perror, remove
# include <cstdlib> // atoi (athough it's already cover by <string>)
# include <cstdlib> // strtol, strtoul
# include <dirent.h> // opendir()
# include <locale> // isalpha, local
# include "Client.hpp"
# include "ServerConfig.hpp"
# include "utils.hpp"
# include "http_status.hpp"
# include "autoindex.hpp"
# include "colors.h"
extern bool g_run;
extern int g_last_signal;
void signal_handler(int signum);
// these might only be TMP
# define FAILURE -1
# define SUCCESS 1
# define MIME_TYPE_DEFAULT "application/octet-stream"
class Webserv
@@ -47,13 +46,7 @@ class Webserv
public:
// base.cpp
Webserv();
// Webserv(Webserv const &src);
// what should it take as arg, *, &, ?
// Webserv(std::vector<ServerConfig>& servers);
~Webserv();
// Webserv &operator=(Webserv const &rhs);
// init.cpp
void init_virtual_servers(std::vector<ServerConfig>* servers);
@@ -69,60 +62,104 @@ class Webserv
std::map<std::string, std::string> _mime_types;
// accept.cpp
void _accept_connection(listen_socket &lsocket);
void _accept_connection(listen_socket &lsocket);
std::map<std::string, std::string>
_extract_infos(struct sockaddr_in addr);
_extract_infos(struct sockaddr_in addr);
// request.cpp
void _request(Client *client);
int _read_request(Client *client);
void _request(Client *client);
int _read_request(Client *client);
// response.cpp
void _response(Client *client);
int _send_response(Client *client);
void _append_base_headers(Client *client);
void _construct_response(Client *client);
void _process_method(Client *client);
void _insert_status_line(Client *client);
void _error_html_response(Client *client);
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
// ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
// const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
std::string _determine_file_extension(const std::string &path) const;
void _response(Client *client);
int _send_response(Client *client);
void _append_base_headers(Client *client);
void _construct_response(Client *client);
void _process_method(Client *client, std::string &path);
std::string _replace_url_root(Client *client, std::string path);
void _insert_status_line(Client *client);
void _error_html_response(Client *client);
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
// method_get.cpp
void _get(Client *client);
void _get_file(Client *client, const std::string &path);
void _autoindex(Client *client, std::string &path);
void _get(Client *client, std::string &path);
void _get_file(Client *client, const std::string &path);
void _autoindex(Client *client, const std::string &path);
std::string _determine_file_extension(const std::string &path) const;
// method_post.cpp
void _post(Client *client);
void _post_file(Client *client, const std::string &path);
void _post(Client *client, const std::string &path);
void _upload_files(Client *client);
// method_delete.cpp
void _delete(Client *client);
void _delete_file(Client *client, const std::string &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 _delete(Client *client, const std::string &path);
void _delete_file(Client *client, const std::string &path);
// epoll_update.cpp
int _epoll_update(int fd, uint32_t events, int op);
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
// signal.cpp
void _handle_last_signal();
// close.cpp
void _close_client(int fd);
void _close_client_cgi_pipes(Client *client);
void _close_all_clients();
void _close_all_clients_fd();
void _close_all_clients_cgi_pipes();
void _close_all_listen_sockets();
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
void _handle_epoll_error_client(uint32_t events, int fd);
// init.cpp
void _bind(int socket_fd, in_port_t port, std::string host);
void _listen(int socket_fd, unsigned int max_connections);
void _init_http_status_map();
void _init_mime_types_map();
// timeout.cpp
void _timeout();
// cgi.cpp
bool _is_cgi(Client *client, std::string path);
size_t _cgi_pos(Client *client, std::string &path, size_t pos);
void _cgi_open_pipes(Client *client);
void _write_body_to_cgi(Client *client);
void _exec_cgi(Client *client);
void _set_env_vector(Client *client, std::vector<std::string> &env_vector);
void _set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector);
std::string _dup_env(std::string var, std::string val);
std::string _dup_env(std::string var, int i);
void _exec_script(Client *client, char *env[]);
void _check_script_output(Client *client, std::string & output);
void _check_script_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);
// signal.cpp
void _handle_last_signal();
// 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();
unsigned int
_check_script_fields(const std::string & output, unsigned int status);
void _check_fields_duplicates(Client *client, std::string & output);
void _add_script_body_length_header(std::string & output);
void _remove_body_leading_empty_lines(std::string & output);
// cgi_epoll.cpp
void _read_cgi_output(Client *client);
void _handle_epollerr_cgi_output(uint32_t events, Client *client);
void _handle_epollhup_cgi_output(uint32_t events, Client *client);
void _cgi_input_ready(Client *client);
void _handle_epollerr_cgi_input(uint32_t events, Client *client);
Client *_find_cgi_output_fd(int fd);
Client *_find_cgi_input_fd(int fd);
///////////////////////
class ExecFail : public std::exception
{
public :
virtual const char* what() const throw() {
return ("Exec CGI fail");
};
};
};
#endif
/*
HTTP Semantics:
https://www.rfc-editor.org/rfc/rfc9110.html
https://www.bortzmeyer.org/9110.html
https://www.bortzmeyer.org/cours-http-cnam.html
HTTP/1.1:
https://www.rfc-editor.org/rfc/rfc9112.html
https://www.bortzmeyer.org/9112.html
CGI:
https://www.rfc-editor.org/rfc/rfc3875.html
*/

View File

@@ -8,7 +8,7 @@ void Webserv::_accept_connection(listen_socket &lsocket)
int accepted_fd;
std::map<std::string, std::string> infos;
std::cerr << "accept()\n";
print_secure(" accept() socket (" + ::itos(lsocket.fd) + ")\n");
addr_len = sizeof addr;
accepted_fd = ::accept(lsocket.fd, (sockaddr*)&addr, &addr_len);
if (accepted_fd == -1)
@@ -18,11 +18,16 @@ void Webserv::_accept_connection(listen_socket &lsocket)
_handle_last_signal();
return ;
}
::fcntl(accepted_fd, F_SETFL, O_NONBLOCK);
if (::fcntl(accepted_fd, F_SETFL, O_NONBLOCK) == -1)
{
std::perror("err fcntl()");
if (::close(accepted_fd) == -1)
std::perror("err close()");
}
infos = _extract_infos(addr);
Client client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
_clients.push_back(client);
Client new_client(accepted_fd, &lsocket, infos["port"], infos["ip"]);
_clients.push_back(new_client);
_epoll_update(accepted_fd, EPOLLIN, EPOLL_CTL_ADD);
}

View File

@@ -2,8 +2,6 @@
#ifndef AUTOINDEX_HPP
# define AUTOINDEX_HPP
// # define HTML_ERROR(STATUS) "\r\n<!DOCTYPE html><html><head><title>"STATUS"</title></head><body><h1 style=\"text-align:center\">"STATUS"</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
# define AUTOINDEX_START \
"<!DOCTYPE html>"\
"<html>"\
@@ -27,6 +25,4 @@
"</body>"\
"</html>"
#endif
#endif

View File

@@ -5,7 +5,7 @@ Webserv::Webserv()
{
std::cerr << "Server init\n";
_epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ?
_epfd = ::epoll_create1(EPOLL_CLOEXEC);
if (_epfd == -1)
{
std::perror("err epoll_create1()");
@@ -19,30 +19,6 @@ Webserv::Webserv()
std::signal(SIGINT, signal_handler);
}
/* Webserv::Webserv(Webserv const &src)
{
} */
// we'll come back to this
/*
Webserv::Webserv(std::vector<ServerConfig>* servers)
: _servers(servers)
{
// talk to luke about what all this does
// the Param Constructor might need to do dif stuff
std::cout << "Server init\n";
_epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ?
if (_epfd == -1)
{
std::perror("err epoll_create1(): ");
throw std::runtime_error("Epoll init");
}
}
*/
Webserv::~Webserv()
{
close(_epfd);
@@ -50,8 +26,3 @@ Webserv::~Webserv()
_close_all_listen_sockets();
std::cerr << "Server destroyed\n";
}
/* Webserv & Webserv::operator=(Webserv const &rhs)
{
} */

446
srcs/webserv/cgi.cpp Normal file
View File

@@ -0,0 +1,446 @@
#include "Webserv.hpp"
/*
CGI RFC:
https://www.rfc-editor.org/rfc/rfc3875.html
*/
bool Webserv::_is_cgi(Client *client, std::string path)
{
std::string script_path;
size_t file_type;
size_t file_access = client->status;
size_t pos = 0;
print_secure(client->get_cl_fd(), "....._is_cgi()\n");
while (pos != NPOS)
{
pos = _cgi_pos(client, path, pos);
print_secure(client->get_cl_fd(), "....>_is_cgi()\n");
if (pos == NPOS)
break;
client->fill_script_path(path, pos);
script_path = client->get_rq_script_path();
file_type = ::eval_file_type(script_path);
if (file_type == IS_DIR) // but what if it's a symlink ?
continue;
if (file_type == IS_FILE)
{
file_access = ::eval_file_access( script_path, X_OK );
if (!file_access)
return true;
}
}
client->clear_script();
client->status = file_access; // 404 not_found OR 403 forbidden
return false;
}
size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
{
std::vector<std::string> v_ext;
std::vector<std::string>::const_iterator it;
std::vector<std::string>::const_iterator it_end;
size_t len;
std::locale loc; // for isalpha()
print_secure(client->get_cl_fd(), "....._cgi_pos()\n");
v_ext = client->assigned_location->cgi_ext;
if (v_ext.empty())
return NPOS;
it_end = client->assigned_location->cgi_ext.end();
while (pos < path.size())
{
if (path.compare(pos, 2, "./") == 0)
pos += 2;
pos = path.find('.', pos);
if (pos == NPOS)
return pos;
it = client->assigned_location->cgi_ext.begin();
for ( ; it != it_end; ++it)
{
len = (*it).size();
if (path.compare(pos + 1, len, *it) == 0)
if ( !std::isalpha(path[pos + 1 + len], loc) )
return pos + 1 + len;
}
pos++;
}
return NPOS;
}
void Webserv::_cgi_open_pipes(Client *client)
{
#define R 0
#define W 1
int pipe_fd[2];
print_secure(client->get_cl_fd(), "....._cgi_open_pipes()\n");
if (::pipe(pipe_fd) == -1)
{
std::perror("err pipe");
client->status = 500;
}
client->cgi_pipe_r_from_parent = pipe_fd[R];
print_secure(client->get_cl_fd(), "open pipe rfp [" + ::itos(client->cgi_pipe_r_from_parent) + "]\n");
client->cgi_pipe_w_to_child = pipe_fd[W];
print_secure(client->get_cl_fd(), "open pipe wtc [" + ::itos(client->cgi_pipe_w_to_child) + "]\n");
if (::pipe(pipe_fd) == -1)
{
std::perror("err pipe");
if (::close(client->cgi_pipe_r_from_parent) == -1)
std::perror("err close()");
if (::close(client->cgi_pipe_w_to_child) == -1)
std::perror("err close()");
client->status = 500;
}
client->cgi_pipe_r_from_child = pipe_fd[R];
print_secure(client->get_cl_fd(), "open pipe rfc [" + ::itos(client->cgi_pipe_r_from_child) + "]\n");
client->cgi_pipe_w_to_parent = pipe_fd[W];
print_secure(client->get_cl_fd(), "open pipe wtp [" + ::itos(client->cgi_pipe_w_to_parent) + "]\n");
// epoll add for writing body to child
_epoll_update(client->cgi_pipe_w_to_child, EPOLLOUT, EPOLL_CTL_ADD);
// stop monitoring client->fd until we can write body
_epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL);
client->cgi_state = CGI_WAIT_TO_EXEC;
}
void Webserv::_write_body_to_cgi(Client *client)
{
ssize_t ret;
std::string body = client->get_rq_body();
print_secure(client->get_cl_fd(), "....._write_body_to_cgi()\n");
ret = ::write(client->cgi_pipe_w_to_child, body.c_str(), body.size());
if (ret == -1)
{
std::perror("err write()");
_close_client_cgi_pipes(client);
client->status = 500;
client->cgi_state = CGI_NO_CGI;
}
}
void Webserv::_exec_cgi(Client *client)
{
char* env_cstr[19] = {NULL};
std::vector<std::string> env_vector;
env_vector.reserve(18);
int i = 0;
print_secure(client->get_cl_fd(), "....._exec_cgi()\n");
_set_env_vector(client, env_vector);
try {
_set_env_cstr(env_cstr, env_vector);
_exec_script(client, env_cstr);
while (env_cstr[i] != NULL)
delete[] env_cstr[i++];
return;
}
catch (const Webserv::ExecFail& e)
{
while (env_cstr[i] != NULL)
delete[] env_cstr[i++];
throw;
}
}
std::string Webserv::_dup_env(std::string var, std::string val = "")
{
std::string str;
str = var + "=" + val;
return (str);
}
std::string Webserv::_dup_env(std::string var, int i)
{
std::string str;
std::string val;
val = ::itos(i);
str = var + "=" + val;
return (str);
}
/*
https://www.rfc-editor.org/rfc/rfc3875#section-4.1
*/
void Webserv::_set_env_vector(Client *client, std::vector<std::string> &env_vector)
{
print_secure(client->get_cl_fd(), "....._set_env_vector()\n");
env_vector.push_back(_dup_env("AUTH_TYPE")); // authentification not supported
env_vector.push_back(_dup_env("CONTENT_LENGTH" , client->get_rq_body().size()));
env_vector.push_back(_dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type")));
env_vector.push_back(_dup_env("GATEWAY_INTERFACE" , "CGI/1.1")); // https://www.rfc-editor.org/rfc/rfc3875#section-4.1.4
env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info()));
env_vector.push_back(_dup_env("PATH_TRANSLATED")); // not used
env_vector.push_back(_dup_env("QUERY_STRING" , client->get_rq_query()));
env_vector.push_back(_dup_env("REMOTE_ADDR" , client->get_cl_ip()));
env_vector.push_back(_dup_env("REMOTE_HOST" , client->get_cl_ip())); // equal to REMOTE_ADDR or empty
env_vector.push_back(_dup_env("REMOTE_IDENT")); // authentification not supported
env_vector.push_back(_dup_env("REMOTE_USER")); // authentification not supported
env_vector.push_back(_dup_env("REQUEST_METHOD" , client->get_rq_method_str()));
env_vector.push_back(_dup_env("SCRIPT_NAME" , "/" + client->get_rq_script_path()));
env_vector.push_back(_dup_env("SERVER_NAME" , client->get_cl_lsocket()->host));
env_vector.push_back(_dup_env("SERVER_PORT" , client->get_cl_lsocket()->port));
env_vector.push_back(_dup_env("SERVER_PROTOCOL" , "HTTP/1.1"));
env_vector.push_back(_dup_env("SERVER_SOFTWARE" , "Webserv/0.1"));
env_vector.push_back(_dup_env("REDIRECT_STATUS" , "200"));
}
void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector)
{
std::vector<std::string>::const_iterator it = env_vector.begin();
std::vector<std::string>::const_iterator it_end = env_vector.end();
int i = 0;
while (it != it_end)
{
env_cstr[i] = new char[it->size()+1];
std::strcpy(env_cstr[i], it->c_str());
++it;
++i;
}
env_cstr[i] = NULL;
}
void Webserv::_exec_script(Client *client, char *env[])
{
pid_t pid;
char * const nll[1] = {NULL};
std::string path;
print_secure(client->get_cl_fd(), "....._exec_script()\n");
// pid = 1; // DEBUG, if no fork, no problem
pid = fork();
print_secure(client->get_cl_fd(), "fork()\n");
if (pid == -1)
{
std::perror("err fork()");
client->status = 500;
_close_client_cgi_pipes(client);
}
else if (pid == 0) // child
{
std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, SIG_DFL);
g_family = CHILD;
if (dup2(client->cgi_pipe_r_from_parent, STDIN_FILENO) == -1)
{
std::perror("err dup2() STDIN_FILENO");
throw ExecFail();
}
if (dup2(client->cgi_pipe_w_to_parent, STDOUT_FILENO) == -1)
{
std::perror("err dup2() STDOUT_FILENO");
if (::close(STDIN_FILENO) == -1) // Valgind debug, not essential
std::perror("err close");
throw ExecFail();
}
_close_all_clients_fd();
path = client->get_rq_script_path();
print_secure(client->get_cl_fd(), "execve:[" + path + "]\n");
if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing
{
std::perror("err execve()");
if (::close(STDIN_FILENO) == -1) // Valgind debug, not essential
std::perror("err close");
if (::close(STDOUT_FILENO) == -1) // Valgind debug, not essential
std::perror("err close");
throw ExecFail();
}
}
else //parent
{
//usleep(1 * 10000);
print_secure(client->get_cl_fd(), "close pipe rfp [" + ::itos(client->cgi_pipe_r_from_parent) + "]\n");
if (::close(client->cgi_pipe_r_from_parent) == -1)
std::perror("err close");
client->cgi_pipe_r_from_parent = -1;
print_secure(client->get_cl_fd(), "close pipe wtp [" + ::itos(client->cgi_pipe_w_to_parent) + "]\n");
if (::close(client->cgi_pipe_w_to_parent) == -1)
std::perror("err close");
client->cgi_pipe_w_to_parent = -1;
print_secure(client->get_cl_fd(), "close pipe wtc [" + ::itos(client->cgi_pipe_w_to_child) + "]\n");
if (::close(client->cgi_pipe_w_to_child) == -1)
std::perror("err close");
client->cgi_pipe_w_to_child = -1;
// add client->cgi_pipe_r_from_child to epoll,
_epoll_update(client->cgi_pipe_r_from_child, EPOLLIN, EPOLL_CTL_ADD);
// stop monitoring client->fd until the cgi-script as done is job
_epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL);
client->cgi_pid = pid;
client->cgi_state = CGI_OUTPUT_READING;
}
}
void Webserv::_check_script_output(Client *client, std::string & output)
{
size_t pos;
pos = client->cgi_output.find(CRLF CRLF);
print_secure(client->get_cl_fd(), "....._check_script_output()\n");
if (pos == 0 || pos == NPOS)
{
client->status = 500;;
return;
}
_check_script_status(client, output);
if (client->status >= 400 && client->status < 600)
return;
client->status = _check_script_fields(output, client->status);
_check_fields_duplicates(client, output);
_remove_body_leading_empty_lines(output);
_add_script_body_length_header(output);
}
void Webserv::_check_script_status(Client *client, std::string & output)
{
size_t pos;
int status_pos;
print_secure(client->get_cl_fd(), "....._check_script_status()\n");
pos = output.find("Status:");
if (pos != NPOS)
{
status_pos = pos + std::string("Status:").size();
client->status = std::strtoul(output.c_str() + status_pos, NULL, 10);
::extract_line(output, pos, CRLF);
}
else
client->status = 200;
}
unsigned int Webserv::_check_script_fields(const std::string & output, unsigned int status)
{
std::string headers;
std::string body;
size_t pos;
pos = output.find(CRLF CRLF);
if (pos == NPOS) // there is not empty line
return 500;
headers = output.substr(0, pos);
body = output.substr(pos + CRLF_SIZE * 2);
headers = str_tolower(headers);
pos = headers.find("content-type");
if (pos == NPOS) // there is no content-type field
{
if (!body.empty()) // there is body
return 500;
if (headers.find("location") == NPOS) // there is no location field
return 500;
}
else if (headers.find("location") != NPOS) // there is a location field
{
if (body.empty()) // there is no body
return 500;
}
return status;
}
void Webserv::_check_fields_duplicates(Client *client, std::string & output)
{
std::map<std::string, std::string> srv_fld; // server_field
std::map<std::string, std::string> scr_fld; // script_field
std::map<std::string, std::string>::iterator it_srv;
std::map<std::string, std::string>::iterator it_scr;
std::string tmp;
size_t pos;
print_secure(client->get_cl_fd(), "....._check_fields_duplicates()\n");
// put server headers in map
tmp = client->response;
pos = tmp.find(CRLF CRLF);
if (pos != NPOS)
tmp.erase(pos);
::parse_http_headers(tmp, srv_fld);
// put script headers in map
tmp = output;
pos = tmp.find(CRLF CRLF);
if (pos != NPOS)
tmp.erase(pos);
::parse_http_headers(tmp, scr_fld);
// compare both map to supress duplicates
for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++)
{
for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++)
{
if (str_tolower(it_srv->first) == str_tolower(it_scr->first))
{
pos = client->response.find(it_srv->first);
::extract_line(client->response, pos, CRLF);
}
}
}
}
void Webserv::_remove_body_leading_empty_lines(std::string & output)
{
size_t pos;
size_t pos_empty;
pos = output.find(CRLF CRLF);
if (pos == NPOS)
return;
pos += CRLF_SIZE * 2;
pos_empty = pos;
while (pos_empty == pos)
{
pos = output.find(CRLF, pos);
if (pos == pos_empty)
extract_line(output, pos, CRLF);
}
}
void Webserv::_add_script_body_length_header(std::string & output)
{
std::map<std::string, std::string> field;
std::map<std::string, std::string>::iterator it;
std::stringstream str_len;
std::string tmp;
size_t pos;
size_t len;
pos = output.find(CRLF CRLF);
if (pos != NPOS)
tmp = output.substr(pos + (CRLF_SIZE * 2));
len = tmp.size();
str_len << len;
// put script headers in map
tmp = output;
pos = tmp.find(CRLF CRLF);
if (pos != NPOS)
tmp.erase(pos);
::parse_http_headers(tmp, field);
// case insensitive search in map for "Content-Length"
tmp = "Content-Length";
for (it = field.begin(); it != field.end(); ++it)
{
if (str_tolower(it->first) == str_tolower(tmp))
{
pos = output.find(it->first);
::extract_line(output, pos, CRLF);
}
}
tmp += ": ";
tmp += str_len.str();
tmp += CRLF;
output.insert(0, tmp);
}

110
srcs/webserv/cgi_epoll.cpp Normal file
View File

@@ -0,0 +1,110 @@
#include "Webserv.hpp"
#define BUFSIZE 8192 // (8Ko)
void Webserv::_read_cgi_output(Client *client)
{
char buf[BUFSIZE];
ssize_t ret;
ret = ::read(client->cgi_pipe_r_from_child, buf, BUFSIZE);
if (ret == -1)
{
std::perror("err read(cgi_fd)");
client->status = 500;
client->cgi_state = CGI_NO_CGI;
}
else if (ret == 0)
std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n"; // :)
else
client->cgi_output.append(buf, ret);
}
void Webserv::_handle_epollerr_cgi_output(uint32_t events, Client *client)
{
(void)events;
client->status = 500;
client->cgi_state = CGI_NO_CGI;
// Common with EPOLLHUP
pid_t wait_ret;
wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
if (wait_ret == client->cgi_pid)
{
_epoll_update(client->cgi_pipe_r_from_child, 0, EPOLL_CTL_DEL);
print_secure(client->get_cl_fd(), "close pipe rfc [" + ::itos(client->cgi_pipe_r_from_child) + "]\n");
if (::close(client->cgi_pipe_r_from_child) == -1)
std::perror("err close()");
client->cgi_pipe_r_from_child = -1;
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
}
}
void Webserv::_handle_epollhup_cgi_output(uint32_t events, Client *client)
{
(void)events;
// Common with EPOLLERR
pid_t wait_ret;
wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
if (wait_ret == client->cgi_pid)
{
_epoll_update(client->cgi_pipe_r_from_child, 0, EPOLL_CTL_DEL);
print_secure(client->get_cl_fd(), "close pipe rfc [" + ::itos(client->cgi_pipe_r_from_child) + "]\n");
if (::close(client->cgi_pipe_r_from_child) == -1)
std::perror("err close()");
client->cgi_pipe_r_from_child = -1;
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
client->cgi_state = CGI_OUTPUT_COMPLETE;
}
}
void Webserv::_cgi_input_ready(Client *client)
{
client->cgi_state = CGI_READY_TO_EXEC;
_epoll_update(client->cgi_pipe_w_to_child, 0, EPOLL_CTL_DEL);
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
}
void Webserv::_handle_epollerr_cgi_input(uint32_t events, Client *client)
{
(void)events;
client->status = 500;
client->cgi_state = CGI_NO_CGI;
_close_client_cgi_pipes(client);
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
}
Client *Webserv::_find_cgi_output_fd(int fd)
{
std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_pipe_r_from_child == fd)
return (&(*it));
++it;
}
return (NULL);
}
Client *Webserv::_find_cgi_input_fd(int fd)
{
std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_pipe_w_to_child == fd)
return (&(*it));
++it;
}
return (NULL);
}

Some files were not shown because too many files have changed in this diff Show More