15 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
58 changed files with 1067 additions and 707 deletions

View File

@@ -1,14 +1,13 @@
NAME = webserv NAME = webserv
CXX = c++ CXX = clang++
CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS = -Wall -Wextra -Werror
CXXFLAGS += $(HEADERS_D:%=-I%) CXXFLAGS += $(HEADERS_D:%=-I%)
CXXFLAGS += -std=c++98 CXXFLAGS += -std=c++98
CXXFLAGS += -g CXXFLAGS += -g
#CXXFLAGS += -fno-limit-debug-info CXXFLAGS += -MMD -MP # header dependencie
CXXFLAGS += -MMD -MP #header dependencie # for debug
#CXXFLAGS += -O3
VPATH = $(SRCS_D) VPATH = $(SRCS_D)
@@ -25,11 +24,9 @@ SRCS = main.cpp \
accept.cpp request.cpp response.cpp \ accept.cpp request.cpp response.cpp \
method_get.cpp method_post.cpp method_delete.cpp \ method_get.cpp method_post.cpp method_delete.cpp \
run_loop.cpp timeout.cpp \ run_loop.cpp timeout.cpp \
parser.cpp \ parser.cpp extraConfig.cpp postProcessing.cpp \
extraConfig.cpp \
postProcessing.cpp \
utils.cpp \ utils.cpp \
cgi.cpp \ cgi.cpp cgi_epoll.cpp \
Client.cpp Client_multipart_body.cpp \ Client.cpp Client_multipart_body.cpp \
OBJS_D = builds OBJS_D = builds
@@ -46,14 +43,16 @@ DEPS = $(OBJS:.o=.d) #header dependencie
all: $(NAME) all: $(NAME)
$(OBJS_D)/%.o: %.cpp | $(OBJS_D) $(OBJS_D)/%.o: %.cpp | $(OBJS_D)
$(CXX) $(CXXFLAGS) -c $< -o $@ $(CXX) -c $< -o $@ $(CXXFLAGS)
printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)" printf "$(_CYAN)\r\33[2K\rCompling $@$(_END)"
$(OBJS_D): $(OBJS_D):
mkdir $@ mkdir $@
$(NAME): $(OBJS) $(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)" echo "$(_GREEN)\r\33[2K\r$(NAME) created 😎$(_END)"
# CGI # CGI
@@ -68,11 +67,11 @@ cgire:
clean: clean:
rm -rf $(OBJS_D) rm -rf $(OBJS_D)
echo "$(_RED).o Files Deleted 😱$(_END)" echo "$(_CYAN).o Files Deleted 🤫$(_END)"
fclean: clean fclean: clean
rm -f $(NAME) rm -f $(NAME)
echo "$(_RED)$(NAME) Deleted 😱$(_END)" echo "$(_CYAN)$(NAME) Deleted 🤫$(_END)"
re: fclean all re: fclean all

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

@@ -22,15 +22,13 @@ HTTP/1.1 200 OK
Server: Webserv/0.1 Server: Webserv/0.1
Connection: keep-alive Connection: keep-alive
Content-Type: text/html; charset=UTF-8 Content-Type: text/html; charset=UTF-8
Content-Length: 290 Content-Length: 227
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Webserv test index</title> <title>Webserv test index</title>
<!-- <link rel="stylesheet" href="stylesheet/style.css"> -->
<link rel="stylesheet" href="/stylesheet/style.css"> <link rel="stylesheet" href="/stylesheet/style.css">
</head> </head>
<body> <body>
<h1>Webserv Test Index</h1> <h1>Webserv Test Index</h1>
@@ -42,15 +40,13 @@ HTTP/1.1 200 OK
Server: Webserv/0.1 Server: Webserv/0.1
Connection: keep-alive Connection: keep-alive
Content-Type: text/html; charset=UTF-8 Content-Type: text/html; charset=UTF-8
Content-Length: 290 Content-Length: 227
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Webserv test index</title> <title>Webserv test index</title>
<!-- <link rel="stylesheet" href="stylesheet/style.css"> -->
<link rel="stylesheet" href="/stylesheet/style.css"> <link rel="stylesheet" href="/stylesheet/style.css">
</head> </head>
<body> <body>
<h1>Webserv Test Index</h1> <h1>Webserv Test Index</h1>
@@ -58,13 +54,13 @@ Content-Length: 290
<p style="text-align:center">(˚3˚)</p> <p style="text-align:center">(˚3˚)</p>
</body> </body>
</html> </html>
HTTP/1.1 HTTP/1.1 200 OK
Server: Webserv/0.1 Server: Webserv/0.1
Connection: keep-alive Connection: keep-alive
Content-Type: text/html; charset=UTF-8 Content-Type: text/html; charset=UTF-8
Content-Length: 1277 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> <!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/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/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/kermit.ico">kermit.ico</a>
@@ -72,14 +68,14 @@ Content-Length: 1277
<a style="font-size:1.5em" href="/list/Cagneyc_intro.gif">Cagneyc_intro.gif</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/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/root.png">root.png</a>
<a style="font-size:1.5em" href="/list/test">test</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/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/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/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/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/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/error_pages">error_pages/</a>
<a style="font-size:1.5em" href="/list/rfc2119_files">rfc2119_files</a> <a style="font-size:1.5em" href="/list/rfc2119_files">rfc2119_files/</a>
</pre><hr></body></html>HTTP/1.1 404 Not Found </pre><hr></body></html>HTTP/1.1 404 Not Found
Server: Webserv/0.1 Server: Webserv/0.1
Connection: keep-alive Connection: keep-alive

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

View File

@@ -25,6 +25,16 @@ server {
root ./Tester/www/; 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/ { location /srcs/cgi-bin/ {
root ./srcs/cgi-bin/; root ./srcs/cgi-bin/;
allow_methods POST; allow_methods POST;

View File

@@ -25,7 +25,7 @@ body+="this is a message this is a message this is a message this is a message t
#header_cycle=("Content-Length: 17\n" "\n") #header_cycle=("Content-Length: 17\n" "\n")
#header_cycle=("Content-Length: 17\n") #header_cycle=("Content-Length: 17\n")
#header_cycle=() #header_cycle=()
#header+="Content-Length: 8\n" header+="Content-Length: 8\n"
run_this_test() run_this_test()
{ {

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

View File

@@ -17,7 +17,6 @@ httpz=("HTTP/1.1")
# the parts we will send to webserv # the parts we will send to webserv
# let main.sh handle the l1
@@ -26,8 +25,6 @@ header+="Nonsense: fu\n"
#header_cycle=("Transfer-encoding: fu" "Content-encoding: fu") #header_cycle=("Transfer-encoding: fu" "Content-encoding: fu")
#header+="Transfer-encoding: fu\n"
#header+="Content-endcoding: fu\n"
body= body=

View File

@@ -16,7 +16,7 @@ httpz=("HTTP/1.1")
l1="${methods[0]} ${paths[0]} ${httpz[0]}" l1="${methods[0]} ${paths[0]} ${httpz[0]}"
header="Host: $host" header="Host: $host\n"
body= body=

View File

@@ -45,8 +45,7 @@ run_this_test()
files=() files=()
file="expected_path_test.txt" file="expected_port_test.txt"
files+=("expected_path_test.txt")
#files+=("expected_path_root_test.txt") #files+=("expected_path_root_test.txt")
#files+=("expected_path_testnoslash_test.txt") #files+=("expected_path_testnoslash_test.txt")
#files+=("expected_path_testslash_test.txt") #files+=("expected_path_testslash_test.txt")

View File

@@ -1,4 +1,29 @@
telnet> Trying 127.0.0.1... telnet> Trying 127.0.0.1...
telnet> ?Invalid command Connected to localhost.
telnet> ?Invalid command Escape character is '^]'.
telnet> telnet> telnet> 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

@@ -11,109 +11,99 @@ server {
client_body_limit 5000; client_body_limit 5000;
# Max == 18446744073709551615 / 1024 == 18014398509481984 # Max == 18446744073709551615 / 1024 == 18014398509481984
index index.html; # this is another comment index index.html; # this is another comment
#index mdr.html; # this is another comment #index mdr.html; # this is another comment
root ./www/; root ./www/;
error_page 404 ./www/error_pages/error_404.html; # error_page 404 ./www/error_pages/error_404.html;
# error_page 403 ./www/error_pages/error_404.html;
# location /kapouet {
# root /tmp/www;
# }
location / { location / {
allow_methods GET; allow_methods GET;
root ./www/; root ./www/;
} }
location /srcs/cgi-bin/ { # location /srcs/cgi-bin/ {
root ./srcs/cgi-bin/; # root ./srcs/cgi-bin/;
allow_methods POST; # allow_methods POST;
cgi_ext php; # cgi_ext php;
} # }
#
# location /list {
# autoindex on;
# }
location /list {
autoindex on;
}
location /cgi-bin { location /cgi-bin {
root ./srcs/cgi-bin/; root ./srcs/cgi-bin/;
cgi_ext out php sh; cgi_ext out php sh;
} }
location /upload { # location /upload {
allow_methods POST; # allow_methods POST;
# autoindex on; # upload_dir ./www/user_files/;
# root ./www/; # # root doesnt matter if used only with POST and no CGI
# index upload_form_single.html; # }
#
# upload_dir ./www/user_files/; # location /the_dump {
# root doesnt matter if used only with POST and no CGI # allow_methods GET DELETE;
} # root ./www/user_files;
# autoindex on;
location /uploaded { # }
allow_methods GET; #
autoindex on; # location /redirect {
# upload_dir ./www/user_files/; # redirect 307 https://fr.wikipedia.org/wiki/Ketchup;
root ./www/user_files; ## redirect 307 https://www.youtube.com/watch?v=rG6b8gjMEkw;
# 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 {
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;
}
# location /test/test_deeper/something.html {
# allow_methods DELETE;
# } # }
} # location /test {
# index index1.html subdex.html;
server { # root ./www/test/;
server_name server2; # }
#
listen 0.0.0.0:4040; # location /stylesheet {
# root ./stylesheet/;
index index.html; # }
root ./www2/; #
# 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

@@ -21,11 +21,13 @@ _END='\033[0m'
#test_file_names=("test_template.sh" "test_header.sh" "test_path.sh") #test_file_names=("test_template.sh" "test_header.sh" "test_path.sh")
#test_file_names=("test_method.sh" "test_header.sh" "test_path.sh") test_file_names=("test_method.sh" "test_header.sh" "test_path.sh" "test_valid_uri.sh" "test_delete.sh")
test_file_names=("test_port.sh")
# Run this separately cuz of the content length subtlty
#test_file_names=("test_body.sh") #test_file_names=("test_body.sh")
#test_file_names=("test_valid_uri.sh")
#test_file_names=("test_path.sh") # kinda fucking useless, easier to test IRL
#test_file_names=("test_port.sh")
test_files=() test_files=()
expected_result_files=() expected_result_files=()
@@ -54,7 +56,6 @@ test_all()
{ {
arg=${1} arg=${1}
c=0
make make
@@ -102,8 +103,6 @@ test_all()
cat compare.txt > $expected_result_file cat compare.txt > $expected_result_file
fi fi
c+=1
done done
if [ "$arg" = "" ]; if [ "$arg" = "" ];

View File

@@ -1,7 +1,4 @@
----Priorité élevée------------------------ ----Priorité élevée------------------------
- CGI (TODO HUGO)
- Need to test normal body parsing (Verif avec HUGO)
- curl --resolve, for testing hostname - curl --resolve, for testing hostname
curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040 curl -v --resolve server1:4040:127.0.0.1 --resolve server2:4040:127.0.0.1 server1:4040
@@ -10,34 +7,17 @@
- handle redirection (Work, but weird behavior need deeper test) - handle redirection (Work, but weird behavior need deeper test)
- Ecrire des tests ! - Ecrire des tests !
- cgi_cpp_status.cpp with POST dont show error page. Normal or not ?
For non blocking CGI :
// We could maybe,
// add FD_RD_FR_CHLD to epoll,
// return to the main loop,
// read FD_RD_FR_CHLD each time epoll say its ready,
// then try waitpid() with WNOHANG after each read.
// when waitpid() tell us its finish (or maybe when epoll return EPOLLHUP)
// then actually parse the script_output and send it to the client.
- check status in autoindex
- merge changes from hugo5 to master (attention a pas casse svp :clown:)
----Priorité modérée------------------------ ----Priorité modérée------------------------
- namespace utils ? - namespace utils ?
- change "std::string" to reference "std::string &" in most functions - change "std::string" to reference "std::string &" in most functions
and add "const" if apropriate. and add "const" if apropriate.
- peut-être check si ip > 32bits - peut-être check si ip > 32bits
----Priorité faible------------------------ ----Priorité faible------------------------
- idealy, we should not clear() raw_request after a response, - idealy, we should not clear() raw_request after a response,
but just the part we actually parsed for this response. but just the part we actually parsed for this response.
I think the client could send multiples request on the same connection one after the other without waiting for response. 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 raw_request could contain more than the first request we handle. So only for HTTP/2 adn HTTP/3 raw_request could contain more than the first request we handle.
- chunked request (need testing) - chunked request (need testing)
- client_body_limit 0 valeur special pour desactiver dans config - client_body_limit 0 valeur special pour desactiver dans config
- gerer le champ "Accept" du client - gerer le champ "Accept" du client
@@ -47,8 +27,6 @@ and add "const" if apropriate.
little global timeout on epoll, like 100ms, then find client that actualy need to timeout 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)} if (actual_time - client.last_action_time > 10000ms){timeout(client)}
- add headers "Date" and "Last-Modified" to response - 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. - 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 ! : Valgrind error RESOLVED ! :
@@ -56,7 +34,7 @@ Valgrind error RESOLVED ! :
==847174== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==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 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 0x49ABB52: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append(char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==847174== by 0x426BC5: Webserv::_read_cgi_output(cgi_pipe_rfd&) (run_loop.cpp:39) // (LUKE: its an string.append() call) ==847174== by 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 0x427962: Webserv::run() (run_loop.cpp:153)
==847174== by 0x4052E9: main (main.cpp:38) ==847174== by 0x4052E9: main (main.cpp:38)

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

@@ -12,9 +12,13 @@ Client::Client()
request_complete(false), request_complete(false),
assigned_server(NULL), assigned_server(NULL),
assigned_location(NULL), assigned_location(NULL),
cgi_pipe_rfd(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), cgi_pid(0),
_fd(0), _fd(-1),
_port(""), _port(""),
_ip(""), _ip(""),
_lsocket(NULL) _lsocket(NULL)
@@ -29,7 +33,11 @@ Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string a
request_complete(false), request_complete(false),
assigned_server(NULL), assigned_server(NULL),
assigned_location(NULL), assigned_location(NULL),
cgi_pipe_rfd(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), cgi_pid(0),
_fd(afd), _fd(afd),
_port(aport), _port(aport),
@@ -87,7 +95,7 @@ void Client::parse_request_headers(std::vector<ServerConfig> &servers)
if (raw_request.find(CRLF CRLF) == NPOS) if (raw_request.find(CRLF CRLF) == NPOS)
return ; return ;
header_complete = true; header_complete = true;
clear_request(); // not mandatory clear_request_vars(); // not mandatory
_parse_request_line(); _parse_request_line();
if (status) if (status)
@@ -104,7 +112,6 @@ void Client::parse_request_headers(std::vector<ServerConfig> &servers)
_check_request_errors(); _check_request_errors();
if (status) if (status)
return; return;
_parse_port_hostname(get_rq_headers("Host")); // use getter for headers because it works case insensitive
// DEBUG // DEBUG
// std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n"; // std::cerr << get_rq_method_str() << " " << get_rq_target() << " " << get_rq_version() << "\n";
@@ -229,7 +236,7 @@ void Client::fill_script_path(std::string &path, size_t pos)
void Client::clear() void Client::clear()
{ {
clear_request(); clear_request_vars();
raw_request.clear(); raw_request.clear();
response.clear(); response.clear();
status = 0; status = 0;
@@ -238,12 +245,10 @@ void Client::clear()
request_complete = false; request_complete = false;
assigned_server = NULL; assigned_server = NULL;
assigned_location = NULL; assigned_location = NULL;
cgi_pipe_rfd = 0; clear_cgi_vars();
cgi_pid = 0;
cgi_output.clear();
} }
void Client::clear_request() void Client::clear_request_vars()
{ {
clear_script(); clear_script();
_request.method = UNKNOWN; _request.method = UNKNOWN;
@@ -254,8 +259,6 @@ void Client::clear_request()
_request.multi_bodys.clear(); _request.multi_bodys.clear();
_request.abs_path.clear(); _request.abs_path.clear();
_request.query.clear(); _request.query.clear();
_request.port.clear();
_request.hostname.clear();
} }
void Client::clear_script() void Client::clear_script()
@@ -264,14 +267,25 @@ void Client::clear_script()
_request.script.info.clear(); _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 // debug
void Client::print_client(std::string message) void Client::print_client(std::string message)
{ {
std::map<std::string, std::string>::iterator it; std::map<std::string, std::string>::iterator it;
std::cout << "\n=== DEBUG PRINT CLIENT ===\n"; std::cerr << "\n=== DEBUG PRINT CLIENT ===\n";
std::cout << message << ":\n----------\n\n" << "raw_request:\n__\n"; std::cerr << message << ":\n----------\n\n" << "raw_request:\n__\n";
::print_special(raw_request); ::print_special(raw_request);
std::cout << "\n__\n" std::cerr << "\n__\n"
<< "get_cl_fd() : [" << get_cl_fd() << "]\n" << "get_cl_fd() : [" << get_cl_fd() << "]\n"
<< "get_cl_port() : [" << get_cl_port() << "]\n" << "get_cl_port() : [" << get_cl_port() << "]\n"
<< "get_cl_ip() : [" << get_cl_ip() << "]\n" << "get_cl_ip() : [" << get_cl_ip() << "]\n"
@@ -281,14 +295,12 @@ std::cout << "\n=== DEBUG PRINT CLIENT ===\n";
<< "get_rq_query() : [" << get_rq_query() << "]\n" << "get_rq_query() : [" << get_rq_query() << "]\n"
<< "get_rq_version() : [" << get_rq_version() << "]\n" << "get_rq_version() : [" << get_rq_version() << "]\n"
<< "get_rq_body() : [" << get_rq_body() << "]\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_path() : [" << get_rq_script_path() << "]\n"
<< "get_rq_script_info() : [" << get_rq_script_info() << "]\n" << "get_rq_script_info() : [" << get_rq_script_info() << "]\n"
<< "headers :\n"; << "headers :\n";
for (it = _request.headers.begin(); it != _request.headers.end(); it++) for (it = _request.headers.begin(); it != _request.headers.end(); it++)
std::cout << " " << it->first << ": [" << it->second << "]\n"; std::cerr << " " << it->first << ": [" << it->second << "]\n";
std::cout << "\n=== END PRINT CLIENT ===\n\n"; std::cerr << "\n=== END PRINT CLIENT ===\n\n";
} }
/********************************************* /*********************************************
@@ -310,8 +322,6 @@ std::string Client::get_rq_abs_path() const { return _request.abs_path; }
std::string Client::get_rq_query() const { return _request.query; } std::string Client::get_rq_query() const { return _request.query; }
std::string Client::get_rq_version() const { return _request.version; } std::string Client::get_rq_version() const { return _request.version; }
std::string Client::get_rq_body() const { return _request.body; } 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_path()const { return _request.script.path; }
std::string Client::get_rq_script_info()const { return _request.script.info; } std::string Client::get_rq_script_info()const { return _request.script.info; }
@@ -394,31 +404,11 @@ void Client::_parse_request_fields()
::str_map_key_tolower(_request.headers); ::str_map_key_tolower(_request.headers);
} }
// TODO : I think its now useless. Probably to delete.
void Client::_parse_port_hostname(std::string host)
{
size_t pos;
if (host == "")
std::cerr << "no host\n";
pos = host.find(':');
// port :
if (pos == NPOS)
_request.port = "4040"; // TODO: make equal to default port in config
else
_request.port = host.substr(pos);
if (_request.port == ":")
_request.port = "";
// hostname :
_request.hostname = host.substr(0, pos);
}
void Client::_check_request_errors() void Client::_check_request_errors()
{ {
/* Debug */ std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n"; // /* 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 << "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"; // /* Debug */ std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n";
/////////////////////// ///////////////////////
// Request line checks // Request line checks
if (_request.method == UNKNOWN) if (_request.method == UNKNOWN)
@@ -518,7 +508,6 @@ If we get a url that ends in / ignore the last /
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++) for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
{ {
// std::cout << it->path << " -- ";
if (it->path.size() > uri.size()) if (it->path.size() > uri.size())
continue ; continue ;

View File

@@ -3,6 +3,7 @@
# define CLIENT_HPP # define CLIENT_HPP
# include <iostream> # include <iostream>
# include <iomanip> // setw
# include <string> # include <string>
# include <map> # include <map>
# include <vector> # include <vector>
@@ -35,11 +36,18 @@ struct Request
std::map<std::string, std::string> headers; std::map<std::string, std::string> headers;
std::string body; std::string body;
std::vector<MultipartBody> multi_bodys; std::vector<MultipartBody> multi_bodys;
std::string port;
std::string hostname;
struct Script script; 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 class Client
{ {
public: public:
@@ -55,10 +63,15 @@ class Client
bool header_complete; bool header_complete;
bool body_complete; bool body_complete;
bool request_complete; bool request_complete;
// size_t read_body_size; // unused for now
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[] ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
const LocationConfig *assigned_location; const LocationConfig *assigned_location;
int cgi_pipe_rfd;
// 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; pid_t cgi_pid;
std::string cgi_output; std::string cgi_output;
@@ -76,8 +89,6 @@ class Client
std::string get_rq_query() const; std::string get_rq_query() const;
std::string get_rq_version() const; std::string get_rq_version() const;
std::string get_rq_body() 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_path() const;
std::string get_rq_script_info() const; std::string get_rq_script_info() const;
std::string get_rq_headers(const std::string & key) const; std::string get_rq_headers(const std::string & key) const;
@@ -88,7 +99,8 @@ class Client
void parse_request_headers(std::vector<ServerConfig> &servers); void parse_request_headers(std::vector<ServerConfig> &servers);
void parse_request_body(); void parse_request_body();
void clear(); void clear();
void clear_request(); void clear_request_vars();
void clear_cgi_vars();
void clear_script(); void clear_script();
void fill_script_path(std::string &path, size_t pos); void fill_script_path(std::string &path, size_t pos);
// DEBUG // DEBUG
@@ -104,7 +116,6 @@ class Client
void _parse_request_line(); void _parse_request_line();
void _parse_request_fields(); void _parse_request_fields();
void _parse_request_target( std::string target ); void _parse_request_target( std::string target );
void _parse_port_hostname(std::string host);
void _parse_chunked_body(size_t pos); void _parse_chunked_body(size_t pos);
void _parse_multipart_body(size_t pos); void _parse_multipart_body(size_t pos);
void _check_request_errors(); void _check_request_errors();

View File

@@ -36,8 +36,6 @@ void Client::_parse_multipart_body(size_t pos)
} }
start_pos += sizeof("boundary=")-1; start_pos += sizeof("boundary=")-1;
boundary = boundary.substr(start_pos); boundary = boundary.substr(start_pos);
std::cerr << "boundary =|" << boundary << "|\n";
// Search boundary // Search boundary
start_pos = raw_request.find("--" + boundary, pos); start_pos = raw_request.find("--" + boundary, pos);
@@ -48,7 +46,7 @@ void Client::_parse_multipart_body(size_t pos)
} }
start_pos += sizeof("--")-1 + boundary.size() + CRLF_SIZE; start_pos += sizeof("--")-1 + boundary.size() + CRLF_SIZE;
while (1) // TODO : test loop for multi body while (1)
{ {
end_pos = raw_request.find("--" + boundary, start_pos); end_pos = raw_request.find("--" + boundary, start_pos);
if (end_pos == NPOS) if (end_pos == NPOS)
@@ -56,9 +54,6 @@ void Client::_parse_multipart_body(size_t pos)
status = 400; std::cerr << "_parse_multipart_body() error 3\n"; status = 400; std::cerr << "_parse_multipart_body() error 3\n";
return; return;
} }
/* // Maye useful for multi body (remove "start_pos - CRLF_SIZE" if used)
end_pos = raw_request.rfind(CRLF, end_pos); if (end_pos == NPOS) {status = 400; return; } */
new_body.body = raw_request.substr(start_pos, end_pos - start_pos - CRLF_SIZE); new_body.body = raw_request.substr(start_pos, end_pos - start_pos - CRLF_SIZE);
// Split headers from body // Split headers from body
@@ -74,7 +69,6 @@ void Client::_parse_multipart_body(size_t pos)
} }
tmp_pos += CRLF_SIZE*2; tmp_pos += CRLF_SIZE*2;
new_body.body.erase(0, tmp_pos); new_body.body.erase(0, tmp_pos);
// ::print_map(new_body.headers);
} }
else else
{ // No headers case { // No headers case
@@ -94,10 +88,5 @@ void Client::_parse_multipart_body(size_t pos)
&& raw_request[start_pos] == '-' && raw_request[start_pos] == '-'
&& raw_request[start_pos+1] == '-') && raw_request[start_pos+1] == '-')
break; break;
/* ::print_special(raw_request);
std::cerr << "start_pos = " << start_pos << "\n";
std::cerr << "raw_request.size() = " << raw_request.size() << "\n";
std::cerr << raw_request.substr(start_pos); */
} }
} }

View File

@@ -92,4 +92,3 @@ fclean: clean
re: fclean all re: fclean all
.PHONY : all clean fclean re .PHONY : all clean fclean re

View File

@@ -24,7 +24,7 @@ int main (int ac, char **av, char ** env)
path = get_value("file", rq_body); path = get_value("file", rq_body);
path = "./www/" + path; path = "./www/" + path;
status = ::eval_file_read(path); status = ::eval_file_access(path, R_OK);
if (status) if (status)
{ {
std::cout << "Status: " << status << CRLF CRLF; std::cout << "Status: " << status << CRLF CRLF;

View File

@@ -181,7 +181,7 @@ void
http_body += HTML_BODY_BOTTOM; http_body += HTML_BODY_BOTTOM;
} }
size_t eval_file_read(const std::string &path) size_t eval_file_access(const std::string &path, int mode)
{ {
if (::access(path.c_str(), F_OK) == -1) if (::access(path.c_str(), F_OK) == -1)
{ {
@@ -189,7 +189,7 @@ size_t eval_file_read(const std::string &path)
return 404; // NOT_FOUND, file doesn't exist return 404; // NOT_FOUND, file doesn't exist
} }
if (::access(path.c_str(), R_OK) == -1) if (::access(path.c_str(), mode) == -1)
{ {
std::perror("err access()"); std::perror("err access()");
return 403; // FORBIDDEN, file doesn't have access permission return 403; // FORBIDDEN, file doesn't have access permission

View File

@@ -62,7 +62,7 @@ void
fill_body_basic(char **env, std::string & http_body, const std::string & rq_body); fill_body_basic(char **env, std::string & http_body, const std::string & rq_body);
size_t size_t
eval_file_read(const std::string &path); eval_file_access(const std::string &path, int mode);
#endif #endif

View File

@@ -14,47 +14,41 @@
# include <cstdlib> // strtol, stroul # include <cstdlib> // strtol, stroul
# include <iostream> // cout, cin # include <iostream> // cout, cin
# include <fstream> // ifstream # include <fstream> // ifstream
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
# include <algorithm> // sort() in Post # include <algorithm> // sort() in Post
class ConfigParser { class ConfigParser {
public: public:
ConfigParser(); ConfigParser();
~ConfigParser(); ~ConfigParser();
ConfigParser(const std::string &config_file); ConfigParser(const std::string &config_file);
void read_config(const std::string &config_file); void read_config(const std::string &config_file);
std::vector<ServerConfig> * parse(); // const? std::vector<ServerConfig> *parse() const;
void print_content() const;
// i thought if it were an instance of this class you could call private:
// private member functions from anywhere... // QUESTION : Wut ? std::string _content;
void print_content() const;
ServerConfig _parse_server(size_t *start) const;
LocationConfig _parse_location(size_t *start) const;
private: void _set_server_values(ServerConfig *server, const std::string &key, std::string value) const;
std::string _content; void _set_location_values(LocationConfig *location, const std::string &key, std::string value) const;
/* Extra */
std::string _pre_set_val_check(const std::string &key,
const std::string &value) const;
ServerConfig _parse_server(size_t *start); std::string _get_first_word(size_t *curr) const;
LocationConfig _parse_location(size_t *start); std::string _get_rest_of_line(size_t *curr) const;
void _set_server_values(ServerConfig *server, const std::string key, std::string value); /* Post Processing */
void _set_location_values(LocationConfig *location, const std::string key, std::string value); void _post_processing(std::vector<ServerConfig> *servers) const;
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
/* Extra */
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?
/* Post Processing */
void _post_processing(std::vector<ServerConfig> *servers);
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
}; };

View File

@@ -17,8 +17,7 @@ struct ServerConfig
std::string host; std::string host;
std::string port; std::string port;
std::string root; // ./www/ or www work www/ and www work std::string root;
// i do remove trailing / tho
size_t client_body_limit; size_t client_body_limit;
@@ -42,10 +41,7 @@ struct ServerConfig
for(std::map<int, std::string>::const_iterator it = error_pages.begin(); \ for(std::map<int, std::string>::const_iterator it = error_pages.begin(); \
it != error_pages.end(); it++) it != error_pages.end(); it++)
std::cout << it->first << "--" << it->second << " "; std::cout << it->first << "--" << it->second << " ";
// for (size_t i = 0; i < error_pages.size(); i++)
// std::cout << error_pages->first << "--" << error_pages->second << " ";
// std::cout << "skiping Locations for now...\n";
for (std::vector<LocationConfig>::const_iterator it = locations.begin(); it < locations.end(); it++) for (std::vector<LocationConfig>::const_iterator it = locations.begin(); it < locations.end(); it++)
it->print_all(); it->print_all();

View File

@@ -1,16 +1,13 @@
#include "ConfigParser.hpp" #include "ConfigParser.hpp"
// should i be sending & references? std::string ConfigParser::_pre_set_val_check(const std::string &key,
// const? const std::string &value) const
std::string ConfigParser::_pre_set_val_check(const std::string key,
const std::string value)
{ {
// check key for ; // check key for ;
// check values for ; at end and right number of words depending on key // check values for ; at end and right number of words depending on key
// std::cout << "pre check\n";
if (key.find_first_of(";") != NPOS) if (key.find_first_of(";") != NPOS)
throw std::invalid_argument("bad config file arguments"); throw std::invalid_argument("bad config file arguments");
@@ -30,9 +27,8 @@ std::string ConfigParser::_pre_set_val_check(const std::string key,
return (value.substr(0, i)); return (value.substr(0, i));
} }
// const?
// assumes curr is on a space or \t or \n // assumes curr is on a space or \t or \n
std::string ConfigParser::_get_first_word(size_t *curr) std::string ConfigParser::_get_first_word(size_t *curr) const
{ {
size_t start; size_t start;
@@ -45,9 +41,8 @@ std::string ConfigParser::_get_first_word(size_t *curr)
return (key); return (key);
} }
// const?
// also assumes curr is on a space \t or \n // also assumes curr is on a space \t or \n
std::string ConfigParser::_get_rest_of_line(size_t *curr) std::string ConfigParser::_get_rest_of_line(size_t *curr) const
{ {
size_t start; size_t start;

View File

@@ -41,8 +41,7 @@ void ConfigParser::read_config(const std::string &config_file)
} }
} }
// const? std::vector<ServerConfig> * ConfigParser::parse() const
std::vector<ServerConfig> * ConfigParser::parse()
{ {
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>(); std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
@@ -67,7 +66,7 @@ std::vector<ServerConfig> * ConfigParser::parse()
return (ret); return (ret);
} }
ServerConfig ConfigParser::_parse_server(size_t *start) ServerConfig ConfigParser::_parse_server(size_t *start) const
{ {
ServerConfig ret; ServerConfig ret;
size_t curr = _content.find_first_not_of(" \t\n", *start); size_t curr = _content.find_first_not_of(" \t\n", *start);
@@ -78,7 +77,6 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS) if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
throw std::invalid_argument("bad config file syntax"); throw std::invalid_argument("bad config file syntax");
// are there other things to check for?
while (curr != NPOS) // here curr == { + 1 while (curr != NPOS) // here curr == { + 1
{ {
// so this moves curr to past the word... // so this moves curr to past the word...
@@ -104,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; LocationConfig ret;
size_t curr = *start; size_t curr = *start;
@@ -148,11 +146,9 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
// should i be sending pointers or references?
void ConfigParser::_set_server_values(ServerConfig *server, void ConfigParser::_set_server_values(ServerConfig *server,
const std::string key, std::string value) const std::string &key, std::string value) const
{ {
// should i be sending pointers or references?
value = _pre_set_val_check(key, value); value = _pre_set_val_check(key, value);
std::vector<std::string> tmp_val = ::split(value, ' '); std::vector<std::string> tmp_val = ::split(value, ' ');
@@ -174,11 +170,11 @@ void ConfigParser::_set_server_values(ServerConfig *server,
} }
} }
else if (key == "listen" && size == 1 && server->host == "" else if (key == "listen" && size == 1 && server->host == ""
&& server->port == "") // QUESTION LUKE : C'est quoi cette condition ? Si listen est vide ? Je comprends pas trop. && server->port == "")
{ {
if (tmp_val[0].find_first_of(":") == NPOS) if (tmp_val[0].find_first_of(":") == NPOS)
{ {
if (!::isNumeric_btw(0, 65535, tmp_val[0])) if (!::is_numeric_btw(0, 65535, tmp_val[0]))
throw std::invalid_argument("bad port number"); throw std::invalid_argument("bad port number");
server->host = "0.0.0.0"; server->host = "0.0.0.0";
server->port = tmp_val[0]; server->port = tmp_val[0];
@@ -192,10 +188,10 @@ void ConfigParser::_set_server_values(ServerConfig *server,
throw std::invalid_argument("bad host ip"); throw std::invalid_argument("bad host ip");
for (size_t i = 0; i < ip.size(); i++) 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"); throw std::invalid_argument("bad host ip");
} }
if (!::isNumeric_btw(0, 65535, tmp2[1])) if (!::is_numeric_btw(0, 65535, tmp2[1]))
throw std::invalid_argument("bad port number"); throw std::invalid_argument("bad port number");
server->host = tmp2[0]; server->host = tmp2[0];
server->port = tmp2[1]; server->port = tmp2[1];
@@ -206,13 +202,12 @@ void ConfigParser::_set_server_values(ServerConfig *server,
// remove trailing / // remove trailing /
if (tmp_val[0][tmp_val[0].size() - 1] == '/') if (tmp_val[0][tmp_val[0].size() - 1] == '/')
tmp_val[0].erase(tmp_val[0].size() - 1, 1); tmp_val[0].erase(tmp_val[0].size() - 1, 1);
// tmp_val[0].push_back('/');
server->root = tmp_val[0]; server->root = tmp_val[0];
} }
else if (key == "client_body_limit" && size == 1 else if (key == "client_body_limit" && size == 1
&& server->client_body_limit == 0) && server->client_body_limit == 0)
{ {
if (!::isNumeric(tmp_val[0])) if (!::is_numeric(tmp_val[0]))
throw std::invalid_argument("client_body_limit not a number"); throw std::invalid_argument("client_body_limit not a number");
server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10); server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10);
if (errno == ERANGE || server->client_body_limit > (ULONG_MAX / KB) ) if (errno == ERANGE || server->client_body_limit > (ULONG_MAX / KB) )
@@ -229,7 +224,7 @@ void ConfigParser::_set_server_values(ServerConfig *server,
std::string path = tmp_val[size - 1]; std::string path = tmp_val[size - 1];
for (size_t i = 0; i < size - 1; i++) 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"); throw std::invalid_argument("invalid error code");
int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10); int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10);
if (server->error_pages.find(status_code) != server->error_pages.end()) if (server->error_pages.find(status_code) != server->error_pages.end())
@@ -242,11 +237,9 @@ void ConfigParser::_set_server_values(ServerConfig *server,
} }
// should i be sending pointers or references?
void ConfigParser::_set_location_values(LocationConfig *location, void ConfigParser::_set_location_values(LocationConfig *location,
const std::string key, std::string value) const std::string &key, std::string value) const
{ {
// should i be sending pointers or references?
value = _pre_set_val_check(key, value); value = _pre_set_val_check(key, value);
std::vector<std::string> tmp_val = ::split(value, ' '); std::vector<std::string> tmp_val = ::split(value, ' ');
@@ -259,7 +252,6 @@ void ConfigParser::_set_location_values(LocationConfig *location,
// remove trailing / // remove trailing /
if (tmp_val[0][tmp_val[0].size() - 1] == '/') if (tmp_val[0][tmp_val[0].size() - 1] == '/')
tmp_val[0].erase(tmp_val[0].size() - 1, 1); tmp_val[0].erase(tmp_val[0].size() - 1, 1);
// tmp_val[0].push_back('/');
location->root = tmp_val[0]; location->root = tmp_val[0];
} }
else if (key == "autoindex" && size == 1) else if (key == "autoindex" && size == 1)
@@ -306,7 +298,6 @@ void ConfigParser::_set_location_values(LocationConfig *location,
} }
else if (key == "upload_dir" && size == 1 && location->upload_dir == "") else if (key == "upload_dir" && size == 1 && location->upload_dir == "")
{ {
// what checks to do?
// add trailing / // add trailing /
if (tmp_val[0][tmp_val[0].size() - 1] != '/') if (tmp_val[0][tmp_val[0].size() - 1] != '/')
tmp_val[0].push_back('/'); tmp_val[0].push_back('/');

View File

@@ -3,21 +3,18 @@
#include "ConfigParser.hpp" #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(); std::vector<ServerConfig>::iterator it = servers->begin();
while (it != servers->end()) while (it != servers->end())
{ {
// host and port are Mandatory
if (it->host == "") if (it->host == "")
throw std::invalid_argument("Config file needs a host and port"); throw std::invalid_argument("Config file needs a host and port");
// root is mandatory
if (it->root == "") if (it->root == "")
throw std::invalid_argument("Config file needs a root"); throw std::invalid_argument("Config file needs a root");
// index is mandatory in Server
if (it->index.empty()) if (it->index.empty())
throw std::invalid_argument("Config file needs an Index"); throw std::invalid_argument("Config file needs an Index");

View File

@@ -5,42 +5,37 @@
#include "Webserv.hpp" #include "Webserv.hpp"
#include "ConfigParser.hpp" #include "ConfigParser.hpp"
// debug
family_state g_family;
sem_t* g_shmem;
// debug end
int main(int ac, char **av) int main(int ac, char **av)
{ {
// debug
init_semaphore();
std::vector<ServerConfig>* servers_config = NULL;
try try
{ {
std::string config = (ac == 2 ? av[1] : "./default.config"); std::string config = (ac == 2 ? av[1] : "./default.config");
ConfigParser configParser(config); ConfigParser configParser(config);
// configParser.print_content(); // configParser.print_content();
// i don't love that servers has to be a pointer... servers_config = configParser.parse();
std::vector<ServerConfig>* servers = configParser.parse();
// use an iterator you moron
for (std::vector<ServerConfig>::iterator it = servers->begin(); it < servers->end(); it++)
{
(void)0;
// std::cout << it->server_name << " ";
// it->print_all();
}
// Webserv serv(configParser.parse());
// is this better or worse than using
Webserv serv; Webserv serv;
serv.init_virtual_servers(servers_config);
// serv.init_virtual_servers(); delete servers_config;
serv.init_virtual_servers(servers); servers_config = NULL;
delete servers;
serv.run(); serv.run();
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << e.what() << '\n'; std::cerr << e.what() << '\n';
delete servers_config;
} }
return (0); return (0);
} }

View File

@@ -1,14 +1,6 @@
#include "utils.hpp" #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 // notice : the use of getline make it such as
// it doesn't identify multiple delim as one : // it doesn't identify multiple delim as one :
// " something \n else " -> 1 - something // " something \n else " -> 1 - something
@@ -72,19 +64,6 @@ std::string trim(std::string str, char del)
return str; 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::string itos(int n)
{ {
std::stringstream strs; std::stringstream strs;
@@ -93,7 +72,7 @@ std::string itos(int n)
return ( strs.str() ); return ( strs.str() );
} }
bool isNumeric(std::string str) bool is_numeric(std::string str)
{ {
for (size_t i = 0; i < str.length(); i++) for (size_t i = 0; i < str.length(); i++)
{ {
@@ -103,7 +82,7 @@ bool isNumeric(std::string str)
return true; 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++) for (size_t i = 0; i < str.length(); i++)
{ {
@@ -155,7 +134,7 @@ file_type eval_file_type(const std::string &path)
{ {
struct stat s; struct stat s;
if (stat(path.c_str(), &s) != -1) if (::stat(path.c_str(), &s) != -1)
{ {
if (S_ISREG(s.st_mode)) if (S_ISREG(s.st_mode))
return (IS_FILE); return (IS_FILE);
@@ -305,6 +284,15 @@ void str_map_key_tolower(std::map<std::string, std::string> & mp)
} }
// DEBUG // DEBUG
void throw_test()
{
static int i = 0;
++i;
if (i % 8 == 0)
throw std::bad_alloc();
}
void print_special(std::string str) void print_special(std::string str)
{ {
char c; char c;
@@ -313,15 +301,60 @@ void print_special(std::string str)
{ {
c = str[i]; c = str[i];
if (c == '\r') if (c == '\r')
std::cout << YELLOW << "\\r" << RESET; std::cerr << YELLOW << "\\r" << RESET;
else if (c == '\n') else if (c == '\n')
std::cout << YELLOW << "\\n" << RESET << "\n"; std::cerr << YELLOW << "\\n" << RESET << "\n";
else else
std::cout << c; std::cerr << c;
fflush(stdout); 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) bool operator==(const listen_socket& lhs, int fd)
{ return lhs.fd == fd; } { return lhs.fd == fd; }

View File

@@ -7,6 +7,7 @@
# include <string> # include <string>
# include <sstream> # include <sstream>
# include <iostream> # include <iostream>
# include <iomanip> // setw
# include <cstdlib> // strtol, strtoul # include <cstdlib> // strtol, strtoul
# include <climits> // LONG_MAX # include <climits> // LONG_MAX
# include <cerrno> // errno # include <cerrno> // errno
@@ -17,6 +18,10 @@
# include <unistd.h> // close, access # include <unistd.h> // close, access
# include "colors.h" // for debug print_special # 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 CR "\r"
# define LF "\n" # define LF "\n"
# define CRLF CR LF # define CRLF CR LF
@@ -31,6 +36,12 @@
** CRLF_SIZE*2 ** CRLF_SIZE*2
*/ */
enum family_state
{
PARENT,
CHILD
};
enum file_type enum file_type
{ {
IS_OTHER, IS_OTHER,
@@ -45,8 +56,6 @@ enum http_method
POST = 1 << 1, POST = 1 << 1,
DELETE = 1 << 2, DELETE = 1 << 2,
ANY_METHODS = 0b11111111, ANY_METHODS = 0b11111111,
// ALL_METHODS = 0b11111111,
// i would prefer this...
}; };
struct listen_socket struct listen_socket
@@ -58,10 +67,13 @@ struct listen_socket
bool operator==(const listen_socket& lhs, int fd); bool operator==(const listen_socket& lhs, int fd);
bool operator==(int fd, const listen_socket& rhs); 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(std::string input, char delimiter);
std::vector<std::string> split_trim(std::string input, std::string delim = "\n", char ctrim = '\0'); std::vector<std::string> split_trim(std::string input, std::string delim = "\n", char ctrim = '\0');
bool isNumeric(std::string str); bool is_numeric(std::string str);
bool isNumeric_btw(int low, int high, std::string str); bool is_numeric_btw(int low, int high, std::string str);
std::string itos(int n); std::string itos(int n);
std::string trim(std::string str, char del); std::string trim(std::string str, char del);
http_method str_to_http_method(std::string &str); http_method str_to_http_method(std::string &str);
@@ -76,8 +88,11 @@ size_t parse_http_headers (std::string headers, std::map<std::string, std::
void str_map_key_tolower(std::map<std::string, std::string> & mp); void str_map_key_tolower(std::map<std::string, std::string> & mp);
// debug // debug
void throw_test(); void throw_test();
void print_special(std::string str); 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 */

View File

@@ -15,6 +15,7 @@
# include <cerrno> // errno # include <cerrno> // errno
# include <unistd.h> // close, access # include <unistd.h> // close, access
# include <iostream> // cout, cin # include <iostream> // cout, cin
# include <iomanip> // setw
# include <cstring> // memset # include <cstring> // memset
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname # include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
# include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa # include <arpa/inet.h> // htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
@@ -38,10 +39,6 @@ extern bool g_run;
extern int g_last_signal; extern int g_last_signal;
void signal_handler(int signum); void signal_handler(int signum);
// these might only be TMP
# define FAILURE -1
# define SUCCESS 1
# define MIME_TYPE_DEFAULT "application/octet-stream" # define MIME_TYPE_DEFAULT "application/octet-stream"
class Webserv class Webserv
@@ -49,13 +46,7 @@ class Webserv
public: public:
// base.cpp // base.cpp
Webserv(); Webserv();
// Webserv(Webserv const &src);
// what should it take as arg, *, &, ?
// Webserv(std::vector<ServerConfig>& servers);
~Webserv(); ~Webserv();
// Webserv &operator=(Webserv const &rhs);
// init.cpp // init.cpp
void init_virtual_servers(std::vector<ServerConfig>* servers); void init_virtual_servers(std::vector<ServerConfig>* servers);
@@ -105,11 +96,10 @@ class Webserv
void _handle_last_signal(); void _handle_last_signal();
// close.cpp // close.cpp
void _close_client(int fd); void _close_client(int fd);
void _close_client_cgi_pipes(Client *client);
void _close_all_clients(); void _close_all_clients();
void _close_all_clients_fd(); void _close_all_clients_fd();
void _close_all_clients_cgi_fd(); void _close_all_clients_cgi_pipes();
void _close_cgi_pipe_rfd(int fd);
void _close_all_cgi_pipe_rfd();
void _close_all_listen_sockets(); void _close_all_listen_sockets();
void _reopen_lsocket(std::vector<listen_socket>::iterator it); 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_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
@@ -124,6 +114,8 @@ class Webserv
// cgi.cpp // cgi.cpp
bool _is_cgi(Client *client, std::string path); bool _is_cgi(Client *client, std::string path);
size_t _cgi_pos(Client *client, std::string &path, size_t pos); 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 _exec_cgi(Client *client);
void _set_env_vector(Client *client, std::vector<std::string> &env_vector); 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); void _set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector);
@@ -132,15 +124,19 @@ class Webserv
void _exec_script(Client *client, char *env[]); void _exec_script(Client *client, char *env[]);
void _check_script_output(Client *client, std::string & output); void _check_script_output(Client *client, std::string & output);
void _check_script_status(Client *client, std::string & output); void _check_script_status(Client *client, std::string & output);
size_t _check_script_fields(const std::string & output, size_t status); unsigned int
_check_script_fields(const std::string & output, unsigned int status);
void _check_fields_duplicates(Client *client, std::string & output); void _check_fields_duplicates(Client *client, std::string & output);
void _add_script_body_length_header(std::string & output); void _add_script_body_length_header(std::string & output);
void _remove_body_leading_empty_lines(std::string & output); void _remove_body_leading_empty_lines(std::string & output);
// cgi_epoll.cpp
Client *_find_cgi_fd(int cgi_fd); void _read_cgi_output(Client *client);
void _read_cgi_output(Client *client); void _handle_epollerr_cgi_output(uint32_t events, Client *client);
void _handle_epoll_error_cgi_fd(uint32_t events, Client *client); void _handle_epollhup_cgi_output(uint32_t events, Client *client);
void _cgi_epollhup(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);
/////////////////////// ///////////////////////

View File

@@ -8,7 +8,7 @@ void Webserv::_accept_connection(listen_socket &lsocket)
int accepted_fd; int accepted_fd;
std::map<std::string, std::string> infos; std::map<std::string, std::string> infos;
std::cerr << "accept()\n"; print_secure(" accept() socket (" + ::itos(lsocket.fd) + ")\n");
addr_len = sizeof addr; addr_len = sizeof addr;
accepted_fd = ::accept(lsocket.fd, (sockaddr*)&addr, &addr_len); accepted_fd = ::accept(lsocket.fd, (sockaddr*)&addr, &addr_len);
if (accepted_fd == -1) if (accepted_fd == -1)
@@ -18,7 +18,12 @@ void Webserv::_accept_connection(listen_socket &lsocket)
_handle_last_signal(); _handle_last_signal();
return ; 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); infos = _extract_infos(addr);
Client new_client(accepted_fd, &lsocket, infos["port"], infos["ip"]); Client new_client(accepted_fd, &lsocket, infos["port"], infos["ip"]);

View File

@@ -5,7 +5,7 @@ Webserv::Webserv()
{ {
std::cerr << "Server init\n"; std::cerr << "Server init\n";
_epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ? _epfd = ::epoll_create1(EPOLL_CLOEXEC);
if (_epfd == -1) if (_epfd == -1)
{ {
std::perror("err epoll_create1()"); std::perror("err epoll_create1()");
@@ -19,30 +19,6 @@ Webserv::Webserv()
std::signal(SIGINT, signal_handler); 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() Webserv::~Webserv()
{ {
close(_epfd); close(_epfd);
@@ -50,8 +26,3 @@ Webserv::~Webserv()
_close_all_listen_sockets(); _close_all_listen_sockets();
std::cerr << "Server destroyed\n"; std::cerr << "Server destroyed\n";
} }
/* Webserv & Webserv::operator=(Webserv const &rhs)
{
} */

View File

@@ -9,12 +9,14 @@ bool Webserv::_is_cgi(Client *client, std::string path)
{ {
std::string script_path; std::string script_path;
size_t file_type; size_t file_type;
size_t file_mode = client->status; size_t file_access = client->status;
size_t pos = 0; size_t pos = 0;
print_secure(client->get_cl_fd(), "....._is_cgi()\n");
while (pos != NPOS) while (pos != NPOS)
{ {
pos = _cgi_pos(client, path, pos); pos = _cgi_pos(client, path, pos);
print_secure(client->get_cl_fd(), "....>_is_cgi()\n");
if (pos == NPOS) if (pos == NPOS)
break; break;
client->fill_script_path(path, pos); client->fill_script_path(path, pos);
@@ -24,13 +26,13 @@ bool Webserv::_is_cgi(Client *client, std::string path)
continue; continue;
if (file_type == IS_FILE) if (file_type == IS_FILE)
{ {
file_mode = ::eval_file_access( script_path, X_OK ); file_access = ::eval_file_access( script_path, X_OK );
if (!file_mode) if (!file_access)
return true; return true;
} }
} }
client->clear_script(); client->clear_script();
client->status = file_mode; // 404 not_found OR 403 forbidden client->status = file_access; // 404 not_found OR 403 forbidden
return false; return false;
} }
@@ -42,6 +44,7 @@ size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
size_t len; size_t len;
std::locale loc; // for isalpha() std::locale loc; // for isalpha()
print_secure(client->get_cl_fd(), "....._cgi_pos()\n");
v_ext = client->assigned_location->cgi_ext; v_ext = client->assigned_location->cgi_ext;
if (v_ext.empty()) if (v_ext.empty())
return NPOS; return NPOS;
@@ -66,6 +69,60 @@ size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos)
return NPOS; 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) void Webserv::_exec_cgi(Client *client)
{ {
char* env_cstr[19] = {NULL}; char* env_cstr[19] = {NULL};
@@ -73,6 +130,7 @@ void Webserv::_exec_cgi(Client *client)
env_vector.reserve(18); env_vector.reserve(18);
int i = 0; int i = 0;
print_secure(client->get_cl_fd(), "....._exec_cgi()\n");
_set_env_vector(client, env_vector); _set_env_vector(client, env_vector);
try { try {
_set_env_cstr(env_cstr, env_vector); _set_env_cstr(env_cstr, env_vector);
@@ -108,25 +166,25 @@ std::string Webserv::_dup_env(std::string var, int i)
return (str); return (str);
} }
// TODO : verifier que les variables sont corrects
/* /*
https://www.rfc-editor.org/rfc/rfc3875#section-4.1 https://www.rfc-editor.org/rfc/rfc3875#section-4.1
*/ */
void Webserv::_set_env_vector(Client *client, std::vector<std::string> &env_vector) void Webserv::_set_env_vector(Client *client, std::vector<std::string> &env_vector)
{ {
env_vector.push_back(_dup_env("AUTH_TYPE")); // authentification not supporte 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_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("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("GATEWAY_INTERFACE" , "CGI/1.1")); // https://www.rfc-editor.org/rfc/rfc3875#section-4.1.4
env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info())); // LUKE: To Check env_vector.push_back(_dup_env("PATH_INFO" , client->get_rq_script_info()));
env_vector.push_back(_dup_env("PATH_TRANSLATED")); // not supported // LUKE: Why not supported ? 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("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_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_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_IDENT")); // authentification not supported
env_vector.push_back(_dup_env("REMOTE_USER")); // 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("REQUEST_METHOD" , client->get_rq_method_str()));
env_vector.push_back(_dup_env("SCRIPT_NAME" , "/" + client->get_rq_script_path())); // LUKE: To Check env_vector.push_back(_dup_env("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_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_PORT" , client->get_cl_lsocket()->port));
env_vector.push_back(_dup_env("SERVER_PROTOCOL" , "HTTP/1.1")); env_vector.push_back(_dup_env("SERVER_PROTOCOL" , "HTTP/1.1"));
@@ -139,6 +197,7 @@ void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vect
std::vector<std::string>::const_iterator it = env_vector.begin(); std::vector<std::string>::const_iterator it = env_vector.begin();
std::vector<std::string>::const_iterator it_end = env_vector.end(); std::vector<std::string>::const_iterator it_end = env_vector.end();
int i = 0; int i = 0;
while (it != it_end) while (it != it_end)
{ {
env_cstr[i] = new char[it->size()+1]; env_cstr[i] = new char[it->size()+1];
@@ -152,87 +211,90 @@ void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vect
void Webserv::_exec_script(Client *client, char *env[]) void Webserv::_exec_script(Client *client, char *env[])
{ {
#define RD 0
#define WR 1
#define FD_WR_TO_CHLD fd_in[WR]
#define FD_WR_TO_PRNT fd_out[WR]
#define FD_RD_FR_CHLD fd_out[RD]
#define FD_RD_FR_PRNT fd_in[RD]
pid_t pid; pid_t pid;
char * const nll[1] = {NULL}; char * const nll[1] = {NULL};
std::string script_output;
std::string body = client->get_rq_body();
int fd_in[2];
int fd_out[2];
std::string path; std::string path;
::pipe(fd_in); print_secure(client->get_cl_fd(), "....._exec_script()\n");
::pipe(fd_out); // pid = 1; // DEBUG, if no fork, no problem
pid = fork(); pid = fork();
print_secure(client->get_cl_fd(), "fork()\n");
if (pid == -1) if (pid == -1)
{
std::perror("err fork()"); std::perror("err fork()");
client->status = 500;
_close_client_cgi_pipes(client);
}
else if (pid == 0) // child else if (pid == 0) // child
{ {
std::signal(SIGPIPE, SIG_DFL); std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, 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(); _close_all_clients_fd();
::close(_epfd);
::close(FD_WR_TO_CHLD);
::close(FD_RD_FR_CHLD);
if (dup2(FD_RD_FR_PRNT, STDIN_FILENO) == -1)
{
std::perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
throw ExecFail();
}
if (dup2(FD_WR_TO_PRNT, STDOUT_FILENO) == -1)
{
std::perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
throw ExecFail();
}
::close(FD_RD_FR_PRNT); path = client->get_rq_script_path();
::close(FD_WR_TO_PRNT); print_secure(client->get_cl_fd(), "execve:[" + path + "]\n");
path = client->get_rq_script_path(); // Wut ? Only relative path ?
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing if (::execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing
{ {
std::perror("err execve()"); std::perror("err execve()");
::close(STDIN_FILENO); // Valgind debug, not essential if (::close(STDIN_FILENO) == -1) // Valgind debug, not essential
::close(STDOUT_FILENO); // Valgind debug, not essential std::perror("err close");
if (::close(STDOUT_FILENO) == -1) // Valgind debug, not essential
std::perror("err close");
throw ExecFail(); throw ExecFail();
} }
} }
else //parent else //parent
{ {
::close(FD_RD_FR_PRNT); //usleep(1 * 10000);
::close(FD_WR_TO_PRNT);
::write(FD_WR_TO_CHLD, body.c_str(), body.size()); // move this before the fork ?
::close(FD_WR_TO_CHLD);
// add FD_RD_FR_CHLD to epoll, print_secure(client->get_cl_fd(), "close pipe rfp [" + ::itos(client->cgi_pipe_r_from_parent) + "]\n");
_epoll_update(FD_RD_FR_CHLD, EPOLLIN, EPOLL_CTL_ADD); 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 // stop monitoring client->fd until the cgi-script as done is job
_epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL); _epoll_update(client->get_cl_fd(), 0, EPOLL_CTL_DEL);
client->cgi_pipe_rfd = FD_RD_FR_CHLD;
client->cgi_pid = pid; client->cgi_pid = pid;
client->cgi_state = CGI_OUTPUT_READING;
} }
} }
#define STATUS_500 std::string("Status: 500" CRLF CRLF);
void Webserv::_check_script_output(Client *client, std::string & output) void Webserv::_check_script_output(Client *client, std::string & output)
{ {
size_t pos; size_t pos;
pos = client->cgi_output.find(CRLF CRLF); pos = client->cgi_output.find(CRLF CRLF);
print_secure(client->get_cl_fd(), "....._check_script_output()\n");
if (pos == 0 || pos == NPOS) if (pos == 0 || pos == NPOS)
{ {
client->status = 500;; client->status = 500;;
@@ -241,14 +303,10 @@ void Webserv::_check_script_output(Client *client, std::string & output)
_check_script_status(client, output); _check_script_status(client, output);
if (client->status >= 400 && client->status < 600) if (client->status >= 400 && client->status < 600)
return; return;
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script status]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
client->status = _check_script_fields(output, client->status); client->status = _check_script_fields(output, client->status);
_check_fields_duplicates(client, output); _check_fields_duplicates(client, output);
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script fields]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
_remove_body_leading_empty_lines(output); _remove_body_leading_empty_lines(output);
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script empty lines]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
_add_script_body_length_header(output); _add_script_body_length_header(output);
//*DEBUG*/ std::cout << "\n" B_PURPLE "[script content length]:" RESET "\n"; ::print_special(output); std::cout << B_PURPLE "-----------" RESET "\n\n";
} }
void Webserv::_check_script_status(Client *client, std::string & output) void Webserv::_check_script_status(Client *client, std::string & output)
@@ -256,6 +314,7 @@ void Webserv::_check_script_status(Client *client, std::string & output)
size_t pos; size_t pos;
int status_pos; int status_pos;
print_secure(client->get_cl_fd(), "....._check_script_status()\n");
pos = output.find("Status:"); pos = output.find("Status:");
if (pos != NPOS) if (pos != NPOS)
{ {
@@ -267,7 +326,7 @@ void Webserv::_check_script_status(Client *client, std::string & output)
client->status = 200; client->status = 200;
} }
size_t Webserv::_check_script_fields(const std::string & output, size_t status) unsigned int Webserv::_check_script_fields(const std::string & output, unsigned int status)
{ {
std::string headers; std::string headers;
std::string body; std::string body;
@@ -304,6 +363,7 @@ void Webserv::_check_fields_duplicates(Client *client, std::string & output)
std::string tmp; std::string tmp;
size_t pos; size_t pos;
print_secure(client->get_cl_fd(), "....._check_fields_duplicates()\n");
// put server headers in map // put server headers in map
tmp = client->response; tmp = client->response;
pos = tmp.find(CRLF CRLF); pos = tmp.find(CRLF CRLF);
@@ -384,4 +444,3 @@ void Webserv::_add_script_body_length_header(std::string & output)
tmp += CRLF; tmp += CRLF;
output.insert(0, tmp); 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);
}

View File

@@ -9,10 +9,10 @@ void Webserv::_close_client(int fd)
{ {
if (*it == fd) if (*it == fd)
{ {
// _epoll_update(fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG print_secure(it->get_cl_fd(), "close client fd " + ::itos(fd) + "\n");
std::cerr << "close fd " << fd << "\n";
if (::close(fd) == -1) if (::close(fd) == -1)
std::perror("err close()"); std::perror("err close()");
_close_client_cgi_pipes(&(*it));
_clients.erase(it); _clients.erase(it);
break; break;
} }
@@ -20,6 +20,36 @@ void Webserv::_close_client(int fd)
} }
} }
void Webserv::_close_client_cgi_pipes(Client *client)
{
if (client->cgi_state)
{ // No need to reset the fd to -1 normaly
print_secure(client->get_cl_fd(), "close pipe wtc [" + ::itos(client->cgi_pipe_w_to_child) + "]\n");
if (client->cgi_pipe_w_to_child != -1)
if (::close(client->cgi_pipe_w_to_child) == -1)
std::perror("err close()");
client->cgi_pipe_w_to_child = -1;
print_secure(client->get_cl_fd(), "close pipe rfc [" + ::itos(client->cgi_pipe_r_from_child) + "]\n");
if (client->cgi_pipe_r_from_child != -1)
if (::close(client->cgi_pipe_r_from_child) == -1)
std::perror("err close()");
client->cgi_pipe_r_from_child = -1;
print_secure(client->get_cl_fd(), "close pipe wtp [" + ::itos(client->cgi_pipe_w_to_parent) + "]\n");
if (client->cgi_pipe_w_to_parent != -1)
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 rfp [" + ::itos(client->cgi_pipe_r_from_parent) + "]\n");
if (client->cgi_pipe_r_from_parent != -1)
if (::close(client->cgi_pipe_r_from_parent) == -1)
std::perror("err close()");
client->cgi_pipe_r_from_parent = -1;
}
}
void Webserv::_close_all_clients() void Webserv::_close_all_clients()
{ {
_close_all_clients_fd(); _close_all_clients_fd();
@@ -28,32 +58,25 @@ void Webserv::_close_all_clients()
void Webserv::_close_all_clients_fd() void Webserv::_close_all_clients_fd()
{ {
_close_all_clients_cgi_fd(); _close_all_clients_cgi_pipes();
std::vector<Client>::iterator it = _clients.begin(); std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end(); std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end) while (it != it_end)
{ {
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG print_secure(it->get_cl_fd(), "close client fd " + ::itos(it->get_cl_fd()) + "\n");
std::cerr << "close fd " << it->get_cl_fd() << "\n";
if (::close(it->get_cl_fd()) == -1) if (::close(it->get_cl_fd()) == -1)
std::perror("err close()"); std::perror("err close()");
++it; ++it;
} }
} }
void Webserv::_close_all_clients_cgi_fd() void Webserv::_close_all_clients_cgi_pipes()
{ {
std::vector<Client>::iterator it = _clients.begin(); std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end(); std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end) while (it != it_end)
{ {
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG _close_client_cgi_pipes(&(*it));
if (it->cgi_pipe_rfd)
{
std::cerr << "close cgi-fd " << it->cgi_pipe_rfd << "\n";
if (::close(it->cgi_pipe_rfd) == -1)
std::perror("err close()");
}
++it; ++it;
} }
} }
@@ -63,19 +86,18 @@ void Webserv::_close_all_listen_sockets()
{ // TODO : change like clients (clear in place of pop_back) { // TODO : change like clients (clear in place of pop_back)
while (!_listen_sockets.empty()) while (!_listen_sockets.empty())
{ {
// _epoll_update(_listen_sockets.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG std::cerr << "close lsocket fd " << _listen_sockets.back().fd << "\n";
std::cerr << "close fd " << _listen_sockets.back().fd << "\n";
if (::close(_listen_sockets.back().fd) == -1) if (::close(_listen_sockets.back().fd) == -1)
std::perror("err close()"); std::perror("err close()");
_listen_sockets.pop_back(); _listen_sockets.pop_back();
} }
} }
void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
{
/* /*
** Many common code with init_virtual_servers(). Could refactor it. ** Many common code with init_virtual_servers(). Could refactor it.
*/ */
void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
{
int ret; int ret;
std::cerr << "close lsocket " << it->fd << "\n"; std::cerr << "close lsocket " << it->fd << "\n";
@@ -91,9 +113,8 @@ void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
} }
it->fd = ret; it->fd = ret;
// HUGO ADD // From : https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
// allow socket descriptor to be reuseable // allow socket descriptor to be reuseable
// I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
int on = 1; int on = 1;
if (setsockopt(it->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) if (setsockopt(it->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{ {
@@ -101,7 +122,6 @@ void Webserv::_reopen_lsocket(std::vector<listen_socket>::iterator it)
_listen_sockets.erase(it); _listen_sockets.erase(it);
return; return;
} }
// HUGO ADD END
try { try {
_bind(it->fd, std::strtoul(it->port.c_str(), NULL, 10), it->host); _bind(it->fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
@@ -136,8 +156,5 @@ void Webserv::_handle_epoll_error_client(uint32_t events, int fd)
std::cerr << "EPOLLERR on client fd " << fd << "\n"; // DEBUG std::cerr << "EPOLLERR on client fd " << fd << "\n"; // DEBUG
if (events & EPOLLHUP) if (events & EPOLLHUP)
std::cerr << "EPOLLHUP on client fd " << fd << "\n"; // DEBUG std::cerr << "EPOLLHUP on client fd " << fd << "\n"; // DEBUG
if (std::find(_clients.begin(), _clients.end(), fd) != _clients.end()) _close_client(fd);
std::cerr << "Found the client in _clients" << "\n"; // DEBUG
_close_client(fd); // " EPOLLHUP on client fd 8 " en boucle quand on mattraque un peu les CGI, donc il ne le trouve pas dans _clients. Etrange.
} }

View File

@@ -35,18 +35,14 @@ void Webserv::init_virtual_servers(std::vector<ServerConfig>* servers)
new_socket.port = it->port; new_socket.port = it->port;
_listen_sockets.push_back(new_socket); _listen_sockets.push_back(new_socket);
// HUGO ADD // From : https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
//
// allow socket descriptor to be reuseable // allow socket descriptor to be reuseable
// I just copied it from https://www.ibm.com/docs/en/i/7.2?topic=designs-example-nonblocking-io-select
int on = 1; int on = 1;
if (setsockopt(new_socket.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) if (setsockopt(new_socket.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{ {
::perror("err setsockopt()"); ::perror("err setsockopt()");
throw std::runtime_error("Socket init"); throw std::runtime_error("Socket init");
} }
//
// HUGO ADD END
_bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host); _bind(new_socket.fd, std::strtoul(it->port.c_str(), NULL, 10), it->host);
_listen(new_socket.fd, 42); // 42 arbitrary _listen(new_socket.fd, 42); // 42 arbitrary
@@ -69,7 +65,6 @@ void Webserv::_bind(int socket_fd, in_port_t port, std::string host)
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = ::htons(port); addr.sin_port = ::htons(port);
addr.sin_addr.s_addr = ::htonl(::inet_addr(host.c_str())); addr.sin_addr.s_addr = ::htonl(::inet_addr(host.c_str()));
// addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ?
if (::bind(socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) if (::bind(socket_fd, (const sockaddr*)&addr, sizeof addr) == -1)
{ {

View File

@@ -11,7 +11,7 @@ void Webserv::_delete(Client *client, const std::string &path)
void Webserv::_delete_file(Client *client, const std::string &path) void Webserv::_delete_file(Client *client, const std::string &path)
{ {
std::cout << "_delete_file()\n"; std::cerr << "_delete_file()\n";
client->status = ::eval_file_access(path, W_OK); client->status = ::eval_file_access(path, W_OK);
if (client->status) if (client->status)
return; return;

View File

@@ -2,13 +2,12 @@
#include "Webserv.hpp" #include "Webserv.hpp"
#define MAX_FILESIZE 1 * MB // unused #define MAX_FILESIZE 1 * MB // unused
// const?
/* /*
https://www.rfc-editor.org/rfc/rfc9110.html#name-get https://www.rfc-editor.org/rfc/rfc9110.html#name-get
*/ */
void Webserv::_get(Client *client, std::string &path) void Webserv::_get(Client *client, std::string &path)
{ {
std::cout << "_get()\n"; print_secure(client->get_cl_fd(), "_get()\n");
if (eval_file_type(path) == IS_DIR) if (eval_file_type(path) == IS_DIR)
{ {
if (path[path.size() - 1] != '/') if (path[path.size() - 1] != '/')
@@ -19,7 +18,7 @@ void Webserv::_get(Client *client, std::string &path)
{ {
path.append(client->assigned_location->index[i]); path.append(client->assigned_location->index[i]);
_get_file(client, path); _get_file(client, path);
std::cerr << "Added an index\n"; //debug print_secure(client->get_cl_fd(), "Added an index\n");
return ; return ;
} }
} }
@@ -42,8 +41,7 @@ void Webserv::_get_file(Client *client, const std::string &path)
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ? std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
std::stringstream buf; std::stringstream buf;
std::cout << "_get_file()\n"; print_secure(client->get_cl_fd(), "_get_file()\n");
client->status = ::eval_file_access(path, R_OK); client->status = ::eval_file_access(path, R_OK);
if (client->status) if (client->status)
return; return;
@@ -84,16 +82,19 @@ if (size > MAX_FILESIZE)
*/ */
// const? /*
<a href="http://nginx.org/">nginx.org</a>.<br/>
<a href="/test/test_deeper/index1.html">index1.html</a>
<a href="/test/test_deeper/..">..</a>
*/
void Webserv::_autoindex(Client *client, const std::string &path) void Webserv::_autoindex(Client *client, const std::string &path)
{ {
std::cout << "_autoindex()\n"; std::cerr << "_autoindex()\n";
std::string dir_list; std::string dir_list;
DIR *dir; DIR *dir;
struct dirent *ent; struct dirent *ent;
// std::cout << "Path in auto is: " << path << '\n';
if ( (dir = opendir(path.c_str()) ) != NULL) if ( (dir = opendir(path.c_str()) ) != NULL)
{ {
dir_list.append(AUTOINDEX_START); dir_list.append(AUTOINDEX_START);
@@ -117,13 +118,6 @@ void Webserv::_autoindex(Client *client, const std::string &path)
dir_list.append("</a>"); dir_list.append("</a>");
dir_list.append("\n"); dir_list.append("\n");
} }
// <a href="http://nginx.org/">nginx.org</a>.<br/>
// <a href="/test/test_deeper/index1.html">index1.html</a>
// apparently this is more than good enough!
// <a href="/test/test_deeper/..">..</a>
dir_list.append(AUTOINDEX_END); dir_list.append(AUTOINDEX_END);
closedir (dir); closedir (dir);
client->status = 200; client->status = 200;

View File

@@ -1,24 +1,21 @@
#include "Webserv.hpp" #include "Webserv.hpp"
void Webserv::_post(Client *client, const std::string &path)
{
/* /*
https://www.rfc-editor.org/rfc/rfc9110.html#name-post https://www.rfc-editor.org/rfc/rfc9110.html#name-post
*/ */
void Webserv::_post(Client *client, const std::string &path)
{
(void)path; // unused, we use "assigned_location->upload_dir" instead (void)path; // unused, we use "assigned_location->upload_dir" instead
std::cout << "_post()\n"; std::cerr << "_post()\n";
std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n";
if (client->get_rq_abs_path() != client->assigned_location->path) if (client->get_rq_abs_path() != client->assigned_location->path)
client->status = 404; // 404 ? J'ai un doute. client->status = 404; // 404 or other status better ?
else if (client->assigned_location->upload_dir.empty()) else if (client->assigned_location->upload_dir.empty())
client->status = 404; // 404 ? J'ai un doute. client->status = 404; // 404 or other status better ?
else if (client->get_rq_multi_bodys().empty()) else if (client->get_rq_multi_bodys().empty())
{ {
client->status = 415; client->status = 415; // Unsupported Media Type
client->response.append("Accept: multipart/form-data"); // empty, no encoding accepted client->response.append("Accept: multipart/form-data"); // empty, no encoding accepted
client->response.append(CRLF); client->response.append(CRLF);
} }
@@ -26,17 +23,18 @@ void Webserv::_post(Client *client, const std::string &path)
_upload_files(client); _upload_files(client);
} }
// #include <ctime> // TODO: could add date to DEFAULT_NAME to avoid overwriting files
#define DEFAULT_NAME "unnamed_file" #define DEFAULT_NAME "unnamed_file"
// TODO : Loop for multi body
void Webserv::_upload_files(Client *client) void Webserv::_upload_files(Client *client)
{ {
std::cout << "_upload_files()\n"; std::cerr << "_upload_files()\n";
std::ofstream ofd; std::ofstream ofd;
std::vector<MultipartBody>::const_iterator body_it = client->get_rq_multi_bodys().begin(); std::vector<MultipartBody>::const_iterator body_it = client->get_rq_multi_bodys().begin();
std::string path; std::string path;
std::string filename; std::string filename;
size_t pos; size_t pos;
bool file_existed = false; bool file_existed = false;
static int i_name = 0;
client->status = ::eval_file_access(client->assigned_location->upload_dir, W_OK); client->status = ::eval_file_access(client->assigned_location->upload_dir, W_OK);
if (client->status) if (client->status)
@@ -58,21 +56,18 @@ void Webserv::_upload_files(Client *client)
{ {
filename = filename.substr(pos + sizeof("filename=")-1); filename = filename.substr(pos + sizeof("filename=")-1);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
// A l'arrache pour enlever les " // Pour enlever les "
filename.erase(0, 1); filename.erase(0, 1);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
filename.erase(filename.size()-1, 1); filename.erase(filename.size()-1, 1);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
if (filename.empty())
filename = DEFAULT_NAME;
}
else
{
filename = DEFAULT_NAME;
} }
if (pos == NPOS || filename.empty())
filename = DEFAULT_NAME + ::itos(i_name++);
std::cerr << "filename ="<< filename << "\n"; std::cerr << "filename ="<< filename << "\n";
path = client->assigned_location->upload_dir; // Assume there a final '/' path = client->assigned_location->upload_dir;
path.append(filename); path.append(filename);
@@ -81,7 +76,6 @@ void Webserv::_upload_files(Client *client)
else else
file_existed = true; file_existed = true;
// How to determine status 403 for file that dont already exist ? access() on the upload_dir ?
if (file_existed && ::access(path.c_str(), W_OK) == -1) if (file_existed && ::access(path.c_str(), W_OK) == -1)
{ {
std::perror("err access()"); std::perror("err access()");

View File

@@ -25,8 +25,6 @@ void Webserv::_request(Client *client)
} }
else if (ret == READ_COMPLETE) else if (ret == READ_COMPLETE)
{ {
if (client->body_complete && client->get_rq_multi_bodys().empty()) // DEBUG
std::cerr << "______BODY\n" << client->get_rq_body() << "\n______\n"; // DEBUG
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD); _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
client->request_complete = true; client->request_complete = true;
} }
@@ -38,28 +36,18 @@ int Webserv::_read_request(Client *client)
ssize_t ret; ssize_t ret;
ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0); ret = ::recv(client->get_cl_fd(), buf, BUFSIZE, 0);
std::cerr << "recv() on fd(" << client->get_cl_fd() << ") returned = " << ret << "\n" ; print_secure(client->get_cl_fd(), "recv() ret = " + ::itos(ret) + "\n");
if (ret == -1) if (ret == -1)
{ {
std::perror("err recv()"); std::perror("err recv()");
return READ_CLOSE; return READ_CLOSE;
} }
if (ret == 0) if (ret == 0)
{
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
return READ_CLOSE; return READ_CLOSE;
}
client->raw_request.append(buf, ret); client->raw_request.append(buf, ret);
// ::print_special(client->raw_request);
// std::cerr << "__raw_request__\n" << client->raw_request << "\n______\n"; // DEBUG
print_special(client->raw_request);
std::cerr << "client header complete: " << client->header_complete << "\n"; // DEBUG
if (!client->header_complete) if (!client->header_complete)
{ {
std::cout << "Header not complete\n"; // debug
client->parse_request_headers(_servers); client->parse_request_headers(_servers);
if (client->status) if (client->status)
return READ_COMPLETE; return READ_COMPLETE;
@@ -78,8 +66,6 @@ int Webserv::_read_request(Client *client)
} }
if (client->header_complete) if (client->header_complete)
{ {
std::cerr << "Client header Complete\n";
// client->read_body_size += ret; // Not accurate, part of body could have been read with headers, unused for now
client->parse_request_body(); client->parse_request_body();
if (client->status || client->body_complete) if (client->status || client->body_complete)
return READ_COMPLETE; return READ_COMPLETE;

View File

@@ -12,6 +12,7 @@ void Webserv::_response(Client *client)
{ {
int ret = _send_response(client); int ret = _send_response(client);
print_secure(client->get_cl_fd(), "....._response()\n");
if (g_last_signal) if (g_last_signal)
_handle_last_signal(); _handle_last_signal();
@@ -22,7 +23,7 @@ void Webserv::_response(Client *client)
else if (ret == SEND_COMPLETE) else if (ret == SEND_COMPLETE)
{ {
if (client->get_rq_headers("Connection") == "close" if (client->get_rq_headers("Connection") == "close"
|| client->status == 400 // TODO: Refactoring || client->status == 400
|| client->status == 408 || client->status == 408
|| client->status == 413) || client->status == 413)
_close_client(client->get_cl_fd()); _close_client(client->get_cl_fd());
@@ -38,48 +39,42 @@ int Webserv::_send_response(Client *client)
{ {
ssize_t ret; ssize_t ret;
std::cerr << "send()\n"; print_secure(client->get_cl_fd(), "....._send_response()\n");
if (client->response.empty()) if (client->response.empty())
{ {
_append_base_headers(client); _append_base_headers(client);
if (!client->status) print_secure(client->get_cl_fd(), "....>_send_response()\n");
{
_construct_response(client);
if (client->cgi_pipe_rfd)
return SEND_IN_PROGRESS;
}
} }
else if (!client->cgi_output.empty())
if (!client->status)
{ {
// /*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n"; _construct_response(client);
// /*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(script_output); std::cout << B_PURPLE "-----------" RESET "\n\n"; if (client->cgi_state == CGI_WAIT_TO_EXEC
_check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output; || client->cgi_state == CGI_OUTPUT_READING)
if (client->status < 400) return SEND_IN_PROGRESS;
client->response += client->cgi_output; print_secure(client->get_cl_fd(), "....>_send_response()\n");
// /*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
} }
_insert_status_line(client); _insert_status_line(client);
print_secure(client->get_cl_fd(), "....>_send_response()\n");
if (client->status >= 400) if (client->status >= 400)
{
_error_html_response(client); _error_html_response(client);
print_secure(client->get_cl_fd(), "....>_send_response()\n");
}
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n"; print_secure(client->get_cl_fd(), "send()\n");
// /* Debug */ std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0); ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
if (ret == -1) if (ret == -1)
{ {
std::perror("err send()"); std::perror("err send()");
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
return SEND_CLOSE; return SEND_CLOSE;
} }
if (ret == 0) // actually never happen ? if (ret == 0)
{ {
std::cerr << "SEND RET 0 for client.fd =" << client->get_cl_fd() << "\n"; // DEBUG std::cerr << "send() ret == 0 never happens ?" << "\n"; // Debug
return SEND_CLOSE; return SEND_CLOSE;
} }
// /* Debug */ std::cerr << "ret send() = " << ret << "\n"; // DEBUG
return SEND_COMPLETE; return SEND_COMPLETE;
} }
@@ -88,8 +83,9 @@ void Webserv::_append_base_headers(Client *client)
{ {
client->response.append("Server: Webserv/0.1" CRLF); client->response.append("Server: Webserv/0.1" CRLF);
print_secure(client->get_cl_fd(), "....._append_base_headers()\n");
if (client->get_rq_headers("Connection") == "close" if (client->get_rq_headers("Connection") == "close"
|| client->status == 400 // TODO: Refactoring || client->status == 400
|| client->status == 408 || client->status == 408
|| client->status == 413) || client->status == 413)
client->response.append("Connection: close" CRLF); client->response.append("Connection: close" CRLF);
@@ -101,21 +97,34 @@ void Webserv::_construct_response(Client *client)
{ {
std::string path; std::string path;
std::string script_output; std::string script_output;
print_secure(client->get_cl_fd(), "....._construct_response()\n");
path = _replace_url_root(client, client->get_rq_abs_path()); path = _replace_url_root(client, client->get_rq_abs_path());
if (_is_cgi(client, path)) print_secure(client->get_cl_fd(), "....>_construct_response()\n");
if (client->cgi_state == CGI_READY_TO_EXEC)
{ {
_write_body_to_cgi(client);
print_secure(client->get_cl_fd(), "....>_construct_response()\n");
if (client->status)
return;
_exec_cgi(client); _exec_cgi(client);
return;
} }
_process_method(client, path); else if (client->cgi_state == CGI_OUTPUT_COMPLETE)
{
_check_script_output(client, client->cgi_output);
print_secure(client->get_cl_fd(), "....>_construct_response()\n");
if (client->status < 400 || client->status >= 600)
client->response += client->cgi_output;
}
else if (_is_cgi(client, path))
_cgi_open_pipes(client);
else
_process_method(client, path);
} }
void Webserv::_process_method(Client *client, std::string &path) void Webserv::_process_method(Client *client, std::string &path)
{ {
// std::cerr << "allow_methods = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug print_secure(client->get_cl_fd(), "....._process_method()\n");
std::cerr << "Path again: " << path << '\n'; // debug
switch (client->get_rq_method()) switch (client->get_rq_method())
{ {
case (GET): case (GET):
@@ -131,13 +140,11 @@ void Webserv::_process_method(Client *client, std::string &path)
std::string Webserv::_replace_url_root(Client *client, std::string path) std::string Webserv::_replace_url_root(Client *client, std::string path)
{ {
// /* Debug */ std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug print_secure(client->get_cl_fd(), "....._replace_url_root()\n");
// /* Debug */ std::cerr << "path before = " << path << "\n"; // DEBUG
if (client->assigned_location->path == "/") if (client->assigned_location->path == "/")
path.insert(0, client->assigned_location->root); path.insert(0, client->assigned_location->root);
else else
path.replace(0, client->assigned_location->path.size(), client->assigned_location->root); path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
// /* Debug */ std::cerr << "path after = " << path << "\n"; // DEBUG
return path; return path;
} }
@@ -148,6 +155,8 @@ void Webserv::_insert_status_line(Client *client)
{ {
std::string status_line; std::string status_line;
std::string status = _http_status[client->status]; std::string status = _http_status[client->status];
print_secure(client->get_cl_fd(), "....._insert_status_line()\n");
if (status.empty()) if (status.empty())
status = ::itos(client->status); status = ::itos(client->status);
@@ -159,8 +168,9 @@ void Webserv::_insert_status_line(Client *client)
void Webserv::_error_html_response(Client *client) void Webserv::_error_html_response(Client *client)
{ {
std::cout << "_error_html_response()\n";
print_secure(client->get_cl_fd(), "....._error_html_response()\n");
std::cerr << "_error_html_response()\n";
if (client->assigned_server if (client->assigned_server
&& !client->assigned_server->error_pages[client->status].empty() && !client->assigned_server->error_pages[client->status].empty()
&& ::eval_file_access(client->assigned_server->error_pages[client->status], R_OK) == 0 ) && ::eval_file_access(client->assigned_server->error_pages[client->status], R_OK) == 0 )
@@ -174,6 +184,7 @@ void Webserv::_error_html_response(Client *client)
status = "Error " + ::itos(client->status); status = "Error " + ::itos(client->status);
std::string html_page = HTML_ERROR; std::string html_page = HTML_ERROR;
::replace_all_substr(html_page, STATUS_PLACEHOLDER, status); ::replace_all_substr(html_page, STATUS_PLACEHOLDER, status);
print_secure(client->get_cl_fd(), "....>_error_html_response()\n");
_append_body(client, html_page, "html"); _append_body(client, html_page, "html");
} }
} }
@@ -182,6 +193,7 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s
{ {
const std::string &mime_type = _mime_types[file_extension]; const std::string &mime_type = _mime_types[file_extension];
print_secure(client->get_cl_fd(), "....._append_body()\n");
client->response.append("Content-Type: "); client->response.append("Content-Type: ");
if (mime_type.empty()) if (mime_type.empty())
client->response.append(MIME_TYPE_DEFAULT); client->response.append(MIME_TYPE_DEFAULT);

View File

@@ -4,98 +4,6 @@
#define MAX_EVENTS 42 // arbitrary #define MAX_EVENTS 42 // arbitrary
#define TIMEOUT 3000 #define TIMEOUT 3000
#define BUFSIZE 8192 // (8Ko)
#define STATUS_500 std::string("Status: 500" CRLF CRLF);
void Webserv::_read_cgi_output(Client *client)
{
char buf[BUFSIZE];
ssize_t ret;
std::cerr << "_read_cgi_output()" << "\n";
std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
std::cerr << "client fd = " << client->get_cl_fd() << "\n";
std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n";
ret = ::read(client->cgi_pipe_rfd, buf, BUFSIZE);
std::cerr << "cgi read ret = " << ret << "\n";
if (ret == -1)
{
std::perror("err read(cgi_fd)");
client->cgi_output = STATUS_500;
}
else if (ret == 0)
std::cerr << "Madame s'il vous plait, du Ketchup pour mon hamburger" << " (AKA:ret=0)" << "\n";
else
{
std::cerr << "NORMAL BEHAVIOR I THINK!\n"; // debug
client->cgi_output.append(buf, ret);
}
}
void Webserv::_handle_epoll_error_cgi_fd(uint32_t events, Client *client)
{
(void)events;
std::cerr << "cgi EPOLLERR" << "\n";
std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
std::cerr << "client fd = " << client->get_cl_fd() << "\n";
std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n";
client->cgi_output = STATUS_500;
pid_t wait_ret;
wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
std::cerr << "cgi EPOLLERR waitpid ret = " << wait_ret << "\n";
if (wait_ret == client->cgi_pid)
{
if (client->cgi_pipe_rfd)
{
if (::close(client->cgi_pipe_rfd) == -1)
std::perror("err close()");
}
client->cgi_pipe_rfd = 0;
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
}
}
void Webserv::_cgi_epollhup(uint32_t events, Client *client)
{
(void)events;
(void)client;
/* std::cerr << "cgi EPOLLHUP" << "\n";
std::cerr << "cgi_pid = " << client->cgi_pid << "\n";
std::cerr << "client fd = " << client->get_cl_fd() << "\n";
std::cerr << "cgi fd = " << client->cgi_pipe_rfd << "\n"; */
pid_t wait_ret;
wait_ret = ::waitpid(client->cgi_pid, NULL, WNOHANG);
// std::cerr << "cgi EPOLLHUP waitpid ret = " << wait_ret << "\n";
if (wait_ret == client->cgi_pid)
{
if (client->cgi_pipe_rfd)
{
if (::close(client->cgi_pipe_rfd) == -1)
std::perror("err close()");
}
client->cgi_pipe_rfd = 0;
_epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
}
}
Client *Webserv::_find_cgi_fd(int cgi_fd)
{
std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_pipe_rfd == cgi_fd)
return (&(*it));
++it;
}
return (NULL);
}
void Webserv::run() void Webserv::run()
{ {
std::cerr << "Server started\n"; std::cerr << "Server started\n";
@@ -104,14 +12,23 @@ void Webserv::run()
int i; int i;
int count_loop = 0; int count_loop = 0;
std::vector<listen_socket>::iterator it_lsocket; std::vector<listen_socket>::iterator it_lsocket;
Client *client_cgi = NULL; std::vector<Client>::iterator it_client;
Client *client_cgi_input = NULL;
Client *client_cgi_output = NULL;
std::signal(SIGPIPE, signal_handler);
std::signal(SIGINT, signal_handler);
g_run = true; g_run = true;
g_family = PARENT;
while (g_run) while (g_run)
{ {
std::cerr << ++count_loop << "----loop epoll() ";
sem_wait(g_shmem); // protect output here
std::cout << g_family << "|" << ++count_loop << "-----loop epoll() ";
nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT); nfds = ::epoll_wait(_epfd, events, MAX_EVENTS, TIMEOUT);
std::cerr << "(nfds=" << nfds << ")\n"; std::cerr << "(nfds=" << nfds << ")" << std::endl;
sem_post(g_shmem); // protect output here
if (nfds == -1) if (nfds == -1)
{ {
int errno_copy = errno; int errno_copy = errno;
@@ -119,7 +36,11 @@ void Webserv::run()
if (errno_copy == EINTR) if (errno_copy == EINTR)
g_run = false; g_run = false;
else else
{
std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, SIG_DFL);
throw std::runtime_error("Epoll wait"); throw std::runtime_error("Epoll wait");
}
} }
else if (nfds == 0 && !_clients.empty()) else if (nfds == 0 && !_clients.empty())
_timeout(); _timeout();
@@ -128,7 +49,10 @@ void Webserv::run()
{ {
try { try {
it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd); it_lsocket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
client_cgi = _find_cgi_fd(events[i].data.fd); it_client = std::find(_clients.begin(), _clients.end(), events[i].data.fd);
client_cgi_input = _find_cgi_input_fd(events[i].data.fd);
client_cgi_output = _find_cgi_output_fd(events[i].data.fd);
if (it_lsocket != _listen_sockets.end()) if (it_lsocket != _listen_sockets.end())
{ {
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
@@ -136,23 +60,34 @@ void Webserv::run()
else if (events[i].events & EPOLLIN) else if (events[i].events & EPOLLIN)
_accept_connection(*it_lsocket); _accept_connection(*it_lsocket);
} }
else if (client_cgi) else if (it_client != _clients.end())
{
if (events[i].events & EPOLLERR)
_handle_epoll_error_cgi_fd(events[i].events, client_cgi);
else if (events[i].events & EPOLLIN)
_read_cgi_output(client_cgi);
else if ( (events[i].events & EPOLLHUP) && !(events[i].events & EPOLLIN) )
_cgi_epollhup(events[i].events, client_cgi);
}
else if (std::find(_clients.begin(), _clients.end(), events[i].data.fd) != _clients.end()) // TODO: save the it in var to avoid multiples find()
{ {
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
_handle_epoll_error_client(events[i].events, events[i].data.fd); _handle_epoll_error_client(events[i].events, events[i].data.fd);
else if (events[i].events & EPOLLIN) else if (events[i].events & EPOLLIN)
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); _request( &(*it_client) );
else if (events[i].events & EPOLLOUT) else if (events[i].events & EPOLLOUT)
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) ); _response( &(*it_client) );
}
else if (client_cgi_input)
{
if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
_handle_epollerr_cgi_input(events[i].events, client_cgi_input);
else if (events[i].events & EPOLLOUT)
_cgi_input_ready(client_cgi_input);
}
else if (client_cgi_output)
{
if (events[i].events & EPOLLERR)
_handle_epollerr_cgi_output(events[i].events, client_cgi_output);
else if (events[i].events & EPOLLIN)
_read_cgi_output(client_cgi_output);
else if ( (events[i].events & EPOLLHUP) && !(events[i].events & EPOLLIN) )
_handle_epollhup_cgi_output(events[i].events, client_cgi_output);
}
else
{
std::cerr << "NOTHING FOR FD = " << events[i].data.fd << "\n"; // DEBUG
} }
++i; ++i;
if (!g_run) if (!g_run)
@@ -177,4 +112,6 @@ void Webserv::run()
} }
} }
} }
std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, SIG_DFL);
} }

View File

@@ -14,16 +14,10 @@ void Webserv::_handle_last_signal()
if (g_last_signal == SIGPIPE) if (g_last_signal == SIGPIPE)
{ {
std::cerr << "SIGPIPE\n"; std::cerr << "SIGPIPE\n";
// if (_actual_client)
// {
// _close_client(_actual_client->fd);
// _actual_client = NULL;
// }
} }
else if (g_last_signal == SIGINT) else if (g_last_signal == SIGINT)
{ {
g_run = false; g_run = false;
// maybe a throw here instead of "g_run" ?
} }
g_last_signal = 0; g_last_signal = 0;
} }

View File

@@ -12,12 +12,7 @@ void Webserv::_timeout()
std::cerr << "timeout request fd " << it->get_cl_fd() << "\n"; std::cerr << "timeout request fd " << it->get_cl_fd() << "\n";
it->status = 408; it->status = 408;
_epoll_update(it->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD); _epoll_update(it->get_cl_fd(), EPOLLOUT, EPOLL_CTL_MOD);
// DEBUG, close without repsonse 408
/* _close_client(it->get_cl_fd());
it = _clients.begin(); */
} }
// else // DEBUG
++it; ++it;
} }
} }

47
temp_memo_debug_fork.txt Normal file
View File

@@ -0,0 +1,47 @@
/*
//// RECREATE EPOLL //
std::cerr << "epfd from parent = " << _epfd << "\n";
if (::close(_epfd) == -1)
std::perror("err close");
_epfd = ::epoll_create1(EPOLL_CLOEXEC); // (EPOLL_CLOEXEC) for CGI fork ?
if (_epfd == -1)
{
std::perror("err epoll_create1()");
}
std::vector<listen_socket>::iterator it_ls = _listen_sockets.begin();
std::vector<listen_socket>::iterator it_ls_end = _listen_sockets.end();
while (it_ls != it_ls_end)
{
_epoll_update(it_ls->fd, EPOLLIN, EPOLL_CTL_ADD);
++it_ls;
}
std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
if (it->cgi_state == CGI_WAIT_TO_EXEC)
_epoll_update(it->cgi_pipe_w_to_child, EPOLLOUT, EPOLL_CTL_ADD);
else if (it->cgi_state == CGI_OUTPUT_READING)
_epoll_update(it->cgi_pipe_r_from_child, EPOLLIN, EPOLL_CTL_ADD);
else if (client->request_complete)
_epoll_update(it->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD);
else
_epoll_update(it->get_cl_fd(), EPOLLIN, EPOLL_CTL_ADD);
++it;
}
//// */
//// DEBUG, MINIMAL ACTIONS //
// ::sleep(1); // rescue team
client->status = 442; // rescue_team
_close_client_cgi_pipes(client);// rescue_team
// _epoll_update(client->get_cl_fd(), EPOLLOUT, EPOLL_CTL_ADD); // rescue_team
client->cgi_state = CGI_NO_CGI; // rescue_team
return; // rescue_team
////

View File

@@ -6,4 +6,3 @@ http://localhost:4040/test
http://localhost:4040/test/test_deeper/ http://localhost:4040/test/test_deeper/
http://localhost:4040/test/test_deeper/super_deep/ http://localhost:4040/test/test_deeper/super_deep/
http://localhost:4040/test/index1.html http://localhost:4040/test/index1.html

1
urls_cgi.txt Normal file
View File

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

BIN
www/Kermit_the_Frog.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -279,18 +279,13 @@
<br> <br>
<form method="get" action="/cgi-bin/cgi_cpp_download.out"> <form method="get" action="/cgi-bin/cgi_cpp_download.out">
<h1>WIP</h1>
<p><mark>get</mark> form</p> <p><mark>get</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp_download.out</mark></p> <p>to <mark>/cgi-bin/cgi_cpp_download.out</mark></p>
<label for="fdownload">download file:</label><br> <label for="fdownload">download file:</label><br>
<input type="file" id="fupload" name="fupload"> <input type="file" id="fupload" name="fupload">
<select name="file"> <select name="file">
<option value="Cagneyc_intro.gif" >Cagneyc_intro.gif </option> <option value="Kermit_the_Frog.jpg" selected >Kermit_the_Frog.jpg</option>
<option value="file.md" >file.md </option> <option value="kermit_tea.jpg" >kermit_tea.jpg</option>
<option value="index.html" >index.html </option>
<option value="kermit.ico" >kermit.ico </option>
<option value="punpun.png" selected>punpun.png </option>
<option value="subject.pdf" >subject.pdf </option>
<option value="Van_Eyck_Portrait_Arnolfini.jpg" >Van_Eyck_Portrait_Arnolfini.jpg</option> <option value="Van_Eyck_Portrait_Arnolfini.jpg" >Van_Eyck_Portrait_Arnolfini.jpg</option>
<option value="directory" >directory </option> <option value="directory" >directory </option>
<option value="DOESNT_EXIST.BAD" >DOESNT_EXIST.BAD </option> <option value="DOESNT_EXIST.BAD" >DOESNT_EXIST.BAD </option>
@@ -528,18 +523,6 @@
<h3>expectation:</h3> <h3>expectation:</h3>
<p>error 500</p> <p>error 500</p>
</form> </form>
<br>
<form method="post" action="/cgi-bin/cgi_cpp_upload.out" enctype="multipart/form-data">
<h1>WIP</h1>
<p><mark>post</mark> form</p>
<p>to <mark>/cgi-bin/cgi_cpp_upload.out</mark></p>
<label for="fupload">Upload file:</label><br>
<input type="file" id="fupload" name="fupload">
<input type="submit" value="submit">
<h3>expectation:</h3>
<p>upload file in ./www/user_files/</p>
</form>
</div> </div>
</body> </body>

BIN
www/kermit_greaser.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
www/kermit_le_lait.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
www/kermit_study.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
www/kermit_tea.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

1
www/to_delete.txt Normal file
View File

@@ -0,0 +1 @@
we will delete

BIN
www/user_files/pepe.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB