merge, the whole thing is cleaner
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
NAME = webserv
|
NAME = webserv
|
||||||
CXX = clang++
|
CXX = c++
|
||||||
|
|
||||||
CXXFLAGS = -Wall -Wextra #-Werror
|
CXXFLAGS = -Wall -Wextra #-Werror
|
||||||
CXXFLAGS += $(HEADERS_D:%=-I%)
|
CXXFLAGS += $(HEADERS_D:%=-I%)
|
||||||
|
|||||||
@@ -1,29 +1,35 @@
|
|||||||
|
|
||||||
server {
|
server {
|
||||||
|
|
||||||
# this is a comment
|
# this is a comment
|
||||||
|
|
||||||
server_name our_server;
|
server_name server1;
|
||||||
|
|
||||||
listen 0.0.0.0:4040;
|
listen 0.0.0.0:4040;
|
||||||
|
|
||||||
# client_body_limit asdfa;
|
# client_body_limit asdfa;
|
||||||
client_body_limit 3000000;
|
client_body_limit 5000;
|
||||||
|
# Max == 18446744073709551615 / 1024 == 18014398509481984
|
||||||
|
|
||||||
index index.html; # this is another comment
|
index index.html; # this is another comment
|
||||||
|
#index mdr.html; # this is another comment
|
||||||
|
|
||||||
root ./www/;
|
root ./www/;
|
||||||
|
|
||||||
error_page 404 ./www/error_pages/error_404.html;
|
error_page 404 ./www/error_pages/error_404.html;
|
||||||
|
|
||||||
|
|
||||||
# something to do with /upload
|
|
||||||
|
|
||||||
|
location / {
|
||||||
|
allow_methods GET;
|
||||||
|
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 {
|
location /list {
|
||||||
autoindex on;
|
autoindex on;
|
||||||
@@ -31,18 +37,18 @@ server {
|
|||||||
|
|
||||||
location /cgi-bin {
|
location /cgi-bin {
|
||||||
root ./srcs/cgi-bin/;
|
root ./srcs/cgi-bin/;
|
||||||
cgi_ext cpp php sh;
|
cgi_ext out php sh;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /upload {
|
location /upload {
|
||||||
allow_methods POST;
|
allow_methods POST;
|
||||||
autoindex on;
|
autoindex on;
|
||||||
upload_dir ./www/user_files/; # TODO: append a '/' if there is none ?
|
upload_dir ./www/user_files/;
|
||||||
# root doesn’t matter if used only with POST and no CGI
|
# root doesn’t matter if used only with POST and no CGI
|
||||||
}
|
}
|
||||||
|
|
||||||
location /the_dump {
|
location /the_dump {
|
||||||
allow_methods GET;
|
allow_methods GET DELETE;
|
||||||
root ./www/user_files;
|
root ./www/user_files;
|
||||||
autoindex on;
|
autoindex on;
|
||||||
}
|
}
|
||||||
@@ -89,5 +95,13 @@ server {
|
|||||||
# allow_methods DELETE;
|
# allow_methods DELETE;
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name server2;
|
||||||
|
|
||||||
|
listen 0.0.0.0:4040;
|
||||||
|
|
||||||
|
index index.html;
|
||||||
|
root ./www2/;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
# import and run all tests
|
# import and run all tests
|
||||||
|
|
||||||
#test_files=("test_template.sh" "test_header.sh" "test_body.sh")
|
#test_files=("test_template.sh" "test_header.sh" "test_body.sh")
|
||||||
#test_files=("test_template.sh" "test_header.sh" "test_path.sh")
|
test_files=("test_template.sh" "test_header.sh" "test_path.sh")
|
||||||
test_files=("test_path.sh")
|
#test_files=("test_path.sh")
|
||||||
|
|
||||||
|
|
||||||
test_all()
|
test_all()
|
||||||
|
|||||||
14
memo.txt
14
memo.txt
@@ -1,25 +1,21 @@
|
|||||||
IN 42 SUBJECT AND/OR PRIORITY :
|
IN 42 SUBJECT AND/OR PRIORITY :
|
||||||
- CGI (TODO HUGO)
|
- CGI (TODO HUGO)
|
||||||
- chunked request (need testing)
|
|
||||||
|
|
||||||
- Need to test normal body parsing
|
- Need to test normal body parsing (Verif avec HUGO)
|
||||||
- upload files testing and adjustements
|
|
||||||
- https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
|
||||||
Config en Ko (value * 2^10) serait plus commode que en octet
|
|
||||||
Et 0 valeur special pour desactiver
|
|
||||||
|
|
||||||
- Ecrire des tests !
|
- Ecrire des tests !
|
||||||
- handle redirection (Work, but weird behavior need deeper test)
|
- handle redirection (Work, but weird behavior need deeper test)
|
||||||
|
|
||||||
- check return 0 de send()
|
|
||||||
- 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
|
||||||
- test limit de connexions sur listen()
|
- test limit de connexions sur listen()
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
Si ce n'est pas deja fait :
|
Si ce n'est pas deja fait :
|
||||||
- dans config, check erreur si port > 16bits
|
peut-être check si ip > 32bits
|
||||||
(peut-être check si ip > 32bits)
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
- chunked request (need testing)
|
||||||
|
- client_body_limit 0 valeur special pour desactiver dans config
|
||||||
- gerer le champ "Accept" du client
|
- gerer le champ "Accept" du client
|
||||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||||
|
|||||||
@@ -183,8 +183,11 @@ void Client::_parse_chunked_body(size_t pos)
|
|||||||
/* endptr_copy = endptr; */
|
/* endptr_copy = endptr; */
|
||||||
(void)endptr_copy;
|
(void)endptr_copy;
|
||||||
chunk_size = std::strtoul(&_request.body[pos], &endptr, 16);
|
chunk_size = std::strtoul(&_request.body[pos], &endptr, 16);
|
||||||
/* if (chunk_size == LONG_MAX && errno == ERANGE)
|
if (errno == ERANGE)
|
||||||
status = 413; */
|
{
|
||||||
|
status = 413;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
/* if (endptr == endptr_copy)
|
/* if (endptr == endptr_copy)
|
||||||
{
|
{
|
||||||
std::cerr << "parse_request_body(), no conversion possible\n";
|
std::cerr << "parse_request_body(), no conversion possible\n";
|
||||||
@@ -215,18 +218,13 @@ void Client::fill_script_path(std::string &path, size_t pos)
|
|||||||
{
|
{
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
|
|
||||||
/*DEBUG*/ std::cout << "\n" << B_PURPLE << "debug path dot" << RESET << "\npath:[" << path << "]\n" << "&path[pos]:[" << &path[pos] << "]\n";
|
|
||||||
if (path[0] == '.')
|
if (path[0] == '.')
|
||||||
{
|
{
|
||||||
path.erase(0, 1);
|
path.erase(0, 1);
|
||||||
pos--;
|
pos--;
|
||||||
}
|
}
|
||||||
/*DEBUG*/ std::cout << "path:[" << path << "]\n" << "&path[pos]:[" << &path[pos] << "]\n";
|
|
||||||
|
|
||||||
_request.script.path = path.substr(0, pos);
|
_request.script.path = path.substr(0, pos);
|
||||||
/*DEBUG*/ std::cout << "script_path:[" << _request.script.path << "]\n";
|
|
||||||
_request.script.info = path.substr(pos);
|
_request.script.info = path.substr(pos);
|
||||||
/*DEBUG*/ std::cout << "script_info:[" << _request.script.info << "]\n" << B_PURPLE << "end debug path dot" << RESET << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::clear()
|
void Client::clear()
|
||||||
@@ -412,7 +410,7 @@ void Client::_parse_port_hostname(std::string host)
|
|||||||
|
|
||||||
void Client::_check_request_errors()
|
void Client::_check_request_errors()
|
||||||
{
|
{
|
||||||
std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n";
|
std::cerr << "Content-Length=" << get_rq_headers("Content-Length") << "\n";
|
||||||
std::cerr << "strtoul=" << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
std::cerr << "strtoul=" << std::strtoul(get_rq_headers("Content-Length").c_str(), NULL, 10) << "\n";
|
||||||
std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n";
|
std::cerr << "client_body_limit=" << assigned_server->client_body_limit << "\n";
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
|||||||
Binary file not shown.
114
srcs/cgi-bin/cgi_cpp.cpp
Normal file
114
srcs/cgi-bin/cgi_cpp.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# include <iostream>
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
# include <vector>
|
||||||
|
# include <stdlib.h> // getenv
|
||||||
|
|
||||||
|
# define CR "\r"
|
||||||
|
# define LF "\n"
|
||||||
|
# define CRLF CR LF
|
||||||
|
# define NPOS std::string::npos
|
||||||
|
|
||||||
|
std::string trim(std::string str, char del)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
// delete leadings del
|
||||||
|
pos = str.find_first_not_of(del);
|
||||||
|
if (pos == NPOS)
|
||||||
|
pos = str.size();
|
||||||
|
str = str.substr(pos);
|
||||||
|
|
||||||
|
// delete trailing del
|
||||||
|
pos = str.find_last_not_of(del);
|
||||||
|
if (pos != NPOS)
|
||||||
|
str = str.substr(0, pos + 1);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
split(const std::string & input, std::string delim, char ctrim = '\0')
|
||||||
|
{
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::string tmp;
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
while (end != NPOS)
|
||||||
|
{
|
||||||
|
end = input.find(delim, start);
|
||||||
|
len = end - start;
|
||||||
|
if (end == NPOS)
|
||||||
|
len = end;
|
||||||
|
tmp = input.substr(start, len);
|
||||||
|
if (ctrim != '\0')
|
||||||
|
tmp = trim(tmp, ctrim);
|
||||||
|
if (tmp.size() != 0)
|
||||||
|
split_str.push_back( tmp );
|
||||||
|
start = end + delim.size();
|
||||||
|
}
|
||||||
|
return split_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int ac, char **av, char **en)
|
||||||
|
{
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::vector<std::string> sub_split_str;
|
||||||
|
std::vector<std::string>::const_iterator it;
|
||||||
|
char * tmp;
|
||||||
|
std::string input;
|
||||||
|
std::string http_header;
|
||||||
|
std::string http_body;
|
||||||
|
std::ostringstream strs;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
std::cin >> input;
|
||||||
|
|
||||||
|
http_header = "Content-Type: text/html; charset=UTF-8" CRLF;
|
||||||
|
http_header += "Content-Length: ";
|
||||||
|
|
||||||
|
http_body = "\
|
||||||
|
<!DOCTYPE html>\
|
||||||
|
<html>\
|
||||||
|
<head>\
|
||||||
|
<title>CGI</title>\
|
||||||
|
</head>\
|
||||||
|
<body>\
|
||||||
|
<h2>cgi</h2>\
|
||||||
|
";
|
||||||
|
|
||||||
|
http_body += "<h3>";
|
||||||
|
tmp = getenv("REQUEST_METHOD");
|
||||||
|
if (tmp != NULL)
|
||||||
|
http_body += tmp;
|
||||||
|
else
|
||||||
|
http_body = "method not foud";
|
||||||
|
http_body += "</h3>";
|
||||||
|
|
||||||
|
split_str = split(input, "&");
|
||||||
|
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||||
|
{
|
||||||
|
sub_split_str = split(*it, "=");
|
||||||
|
http_body += "<h3>";
|
||||||
|
http_body += sub_split_str[0];
|
||||||
|
http_body += "</h3>";
|
||||||
|
http_body += "<p>";
|
||||||
|
http_body += sub_split_str[1];
|
||||||
|
http_body += "</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
http_body += "\
|
||||||
|
</body>\
|
||||||
|
</html>\
|
||||||
|
";
|
||||||
|
|
||||||
|
strs << http_body.size();
|
||||||
|
http_header += strs.str();
|
||||||
|
http_header += CRLF CRLF;
|
||||||
|
|
||||||
|
std::cout << http_header << CRLF CRLF << http_body;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
133
srcs/cgi-bin/cgi_cpp_content_length.cpp
Normal file
133
srcs/cgi-bin/cgi_cpp_content_length.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# include <iostream>
|
||||||
|
# include <string>
|
||||||
|
# include <sstream>
|
||||||
|
# include <vector>
|
||||||
|
# include <stdlib.h> // getenv
|
||||||
|
|
||||||
|
# define CR "\r"
|
||||||
|
# define LF "\n"
|
||||||
|
# define CRLF CR LF
|
||||||
|
# define NPOS std::string::npos
|
||||||
|
|
||||||
|
std::string trim(std::string str, char del)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
// delete leadings del
|
||||||
|
pos = str.find_first_not_of(del);
|
||||||
|
if (pos == NPOS)
|
||||||
|
pos = str.size();
|
||||||
|
str = str.substr(pos);
|
||||||
|
|
||||||
|
// delete trailing del
|
||||||
|
pos = str.find_last_not_of(del);
|
||||||
|
if (pos != NPOS)
|
||||||
|
str = str.substr(0, pos + 1);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
split(const std::string & input, std::string delim, char ctrim = '\0')
|
||||||
|
{
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::string tmp;
|
||||||
|
size_t start = 0;
|
||||||
|
size_t end = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
while (end != NPOS)
|
||||||
|
{
|
||||||
|
end = input.find(delim, start);
|
||||||
|
len = end - start;
|
||||||
|
if (end == NPOS)
|
||||||
|
len = end;
|
||||||
|
tmp = input.substr(start, len);
|
||||||
|
if (ctrim != '\0')
|
||||||
|
tmp = trim(tmp, ctrim);
|
||||||
|
if (tmp.size() != 0)
|
||||||
|
split_str.push_back( tmp );
|
||||||
|
start = end + delim.size();
|
||||||
|
}
|
||||||
|
return split_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int ac, char **av, char **en) {
|
||||||
|
std::vector<std::string> split_str;
|
||||||
|
std::vector<std::string> sub_split_str;
|
||||||
|
std::vector<std::string>::const_iterator it;
|
||||||
|
char * tmp;
|
||||||
|
std::string output;
|
||||||
|
std::ostringstream strs;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
std::cout << "Content-Type: text/html; charset=UTF-8" << CRLF CRLF;
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "<!DOCTYPE html>"
|
||||||
|
<< "<html>"
|
||||||
|
<< "<head>"
|
||||||
|
<< " <title>CGI</title>"
|
||||||
|
<< "</head>"
|
||||||
|
<< "<body>"
|
||||||
|
<< " <h2>cgi</h2>"
|
||||||
|
<< " <h3>";
|
||||||
|
|
||||||
|
tmp = getenv("REQUEST_METHOD");
|
||||||
|
if (tmp != NULL)
|
||||||
|
output = tmp;
|
||||||
|
else
|
||||||
|
output = "method not foud";
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< output
|
||||||
|
<< " </h3>"
|
||||||
|
<< " <h3>http-request-body-message content :</h3>";
|
||||||
|
|
||||||
|
|
||||||
|
std::cin >> output;
|
||||||
|
split_str = split(output, "&");
|
||||||
|
output.clear();
|
||||||
|
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||||
|
{
|
||||||
|
sub_split_str = split(*it, "=");
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "<p>"
|
||||||
|
<< sub_split_str[0]
|
||||||
|
<< " : "
|
||||||
|
<< sub_split_str[1]
|
||||||
|
<< "</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = getenv("QUERY_STRING");
|
||||||
|
if (tmp == NULL)
|
||||||
|
std::cout << "query not foud";
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< " <h3>http-uri-query content :</h3>";
|
||||||
|
|
||||||
|
output = tmp;
|
||||||
|
split_str = split(output, "&");
|
||||||
|
output.clear();
|
||||||
|
for (it = split_str.begin(); it != split_str.end(); ++it)
|
||||||
|
{
|
||||||
|
sub_split_str = split(*it, "=");
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "<h3>"
|
||||||
|
<< sub_split_str[0]
|
||||||
|
<< "</h3>"
|
||||||
|
<< "<p>"
|
||||||
|
<< sub_split_str[1]
|
||||||
|
<< "</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "</body>"
|
||||||
|
<< "</html>";
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -14,8 +14,6 @@
|
|||||||
# include <cstdlib> // strtol, stroul
|
# include <cstdlib> // strtol, stroul
|
||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
# include <fstream> // ifstream
|
# include <fstream> // ifstream
|
||||||
//# include <unistd.h> // access()
|
|
||||||
# include <dirent.h> // opendir(), doesn't work...
|
|
||||||
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
||||||
# include <algorithm> // sort() in Post
|
# include <algorithm> // sort() in Post
|
||||||
|
|
||||||
@@ -23,43 +21,37 @@ class ConfigParser {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ConfigParser(const char* path); // a string?
|
ConfigParser();
|
||||||
|
|
||||||
// might not need this either, ask Luke
|
|
||||||
~ConfigParser();
|
~ConfigParser();
|
||||||
|
ConfigParser(const std::string &config_file);
|
||||||
|
|
||||||
|
void read_config(const std::string &config_file);
|
||||||
|
|
||||||
std::vector<ServerConfig> * parse(); // const?
|
std::vector<ServerConfig> * parse(); // const?
|
||||||
|
|
||||||
|
|
||||||
// i thought if it were an instance of this class you could call
|
// i thought if it were an instance of this class you could call
|
||||||
// private member functions from anywhere...
|
// private member functions from anywhere... // QUESTION : Wut ?
|
||||||
void print_content() const;
|
void print_content() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _content;
|
std::string _content;
|
||||||
|
|
||||||
// not sure i even need this...
|
|
||||||
ConfigParser();
|
|
||||||
|
|
||||||
|
|
||||||
ServerConfig _parse_server(size_t *start);
|
ServerConfig _parse_server(size_t *start);
|
||||||
LocationConfig _parse_location(size_t *start);
|
LocationConfig _parse_location(size_t *start);
|
||||||
|
|
||||||
|
|
||||||
void _set_server_values(ServerConfig *server, const std::string key, std::string value);
|
void _set_server_values(ServerConfig *server, const std::string key, std::string value);
|
||||||
void _set_location_values(LocationConfig *location, const std::string key, std::string value);
|
void _set_location_values(LocationConfig *location, const std::string key, std::string value);
|
||||||
|
|
||||||
|
|
||||||
/* Extra */
|
/* Extra */
|
||||||
std::string _pre_set_val_check(const std::string key, \
|
std::string _pre_set_val_check(const std::string key,
|
||||||
const std::string value);
|
const std::string value);
|
||||||
|
|
||||||
std::string _get_first_word(size_t *curr); // const?
|
std::string _get_first_word(size_t *curr); // const?
|
||||||
std::string _get_rest_of_line(size_t *curr); // const?
|
std::string _get_rest_of_line(size_t *curr); // const?
|
||||||
|
|
||||||
|
|
||||||
/* Post Processing */
|
/* Post Processing */
|
||||||
void _post_processing(std::vector<ServerConfig> *servers);
|
void _post_processing(std::vector<ServerConfig> *servers);
|
||||||
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
|
bool _find_root_path_location(std::vector<LocationConfig> locations) const;
|
||||||
|
|||||||
@@ -1,31 +1,14 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* LocationConfig.hpp :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
|
||||||
/* Updated: 2022/08/15 19:37:34 by erlazo ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef LOCATIONCONFIG_HPP
|
#ifndef LOCATIONCONFIG_HPP
|
||||||
# define LOCATIONCONFIG_HPP
|
# define LOCATIONCONFIG_HPP
|
||||||
|
|
||||||
# include <map>
|
|
||||||
# include <vector>
|
# include <vector>
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <iostream>
|
|
||||||
# include <sys/stat.h> // stat()
|
|
||||||
|
|
||||||
# include "utils.hpp"
|
# include "utils.hpp"
|
||||||
|
|
||||||
// again, struct instead?
|
struct LocationConfig
|
||||||
class LocationConfig
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
std::string path; // /path and /path/ are fine
|
std::string path; // /path and /path/ are fine
|
||||||
// i remove trailing / systematically
|
// i remove trailing / systematically
|
||||||
std::string root;
|
std::string root;
|
||||||
@@ -91,13 +74,4 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,9 @@
|
|||||||
# include <string> // string
|
# include <string> // string
|
||||||
# include <iostream> // cout, cin
|
# include <iostream> // cout, cin
|
||||||
|
|
||||||
// a class that's all public? just so we have options?
|
struct ServerConfig
|
||||||
class ServerConfig
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
std::vector<std::string> server_name;
|
std::vector<std::string> server_name;
|
||||||
// we could shove default in here if we wanted to...
|
|
||||||
|
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string port;
|
std::string port;
|
||||||
@@ -24,9 +20,7 @@ public:
|
|||||||
std::string root; // ./www/ or www work www/ and www work
|
std::string root; // ./www/ or www work www/ and www work
|
||||||
// i do remove trailing / tho
|
// i do remove trailing / tho
|
||||||
|
|
||||||
size_t client_body_limit; // set to default max if none set
|
size_t client_body_limit;
|
||||||
// 413 (Request Entity Too Large) if exceeded
|
|
||||||
// default is 1m 1 000 000 ?
|
|
||||||
|
|
||||||
std::vector<std::string> index;
|
std::vector<std::string> index;
|
||||||
std::map<int, std::string> error_pages;
|
std::map<int, std::string> error_pages;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "ConfigParser.hpp"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
// should i be sending & references?
|
// should i be sending & references?
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
|
|
||||||
#include "ConfigParser.hpp"
|
#include "ConfigParser.hpp"
|
||||||
|
|
||||||
// Default
|
ConfigParser::ConfigParser() {};
|
||||||
// possibly remove...
|
ConfigParser::~ConfigParser() {};
|
||||||
ConfigParser::ConfigParser()
|
|
||||||
|
ConfigParser::ConfigParser(const std::string &config_file)
|
||||||
{
|
{
|
||||||
std::cout << "Default Constructor\n";
|
read_config(config_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigParser::ConfigParser(const char* path)
|
void ConfigParser::read_config(const std::string &config_file)
|
||||||
{
|
{
|
||||||
std::ifstream file;
|
std::ifstream file;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
size_t comment;
|
size_t comment;
|
||||||
|
|
||||||
_content.clear();
|
file.open(config_file.c_str());
|
||||||
file.open(path);
|
if (!file)
|
||||||
if (file.is_open())
|
throw std::invalid_argument("failed to open config");
|
||||||
|
else
|
||||||
{
|
{
|
||||||
|
_content.clear();
|
||||||
while (!file.eof())
|
while (!file.eof())
|
||||||
{
|
{
|
||||||
getline(file, buf);
|
getline(file, buf);
|
||||||
@@ -35,19 +38,9 @@ ConfigParser::ConfigParser(const char* path)
|
|||||||
_content.append(tmp + '\n');
|
_content.append(tmp + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw std::invalid_argument("failed to open config");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// might not need this...
|
|
||||||
ConfigParser::~ConfigParser()
|
|
||||||
{
|
|
||||||
// do i need to destroy anything, won't it handle itself?
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// const?
|
// const?
|
||||||
std::vector<ServerConfig> * ConfigParser::parse()
|
std::vector<ServerConfig> * ConfigParser::parse()
|
||||||
{
|
{
|
||||||
@@ -156,7 +149,7 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
|||||||
|
|
||||||
|
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
void ConfigParser::_set_server_values(ServerConfig *server,
|
||||||
const std::string key, std::string value)
|
const std::string key, std::string value)
|
||||||
{
|
{
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
@@ -181,7 +174,7 @@ 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 == "")
|
&& server->port == "") // QUESTION LUKE : C'est quoi cette condition ? Si listen est vide ? Je comprends pas trop.
|
||||||
{
|
{
|
||||||
if (tmp_val[0].find_first_of(":") == NPOS)
|
if (tmp_val[0].find_first_of(":") == NPOS)
|
||||||
{
|
{
|
||||||
@@ -222,6 +215,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
if (!::isNumeric(tmp_val[0]))
|
if (!::isNumeric(tmp_val[0]))
|
||||||
throw std::invalid_argument("client_body_limit not a number");
|
throw std::invalid_argument("client_body_limit not a number");
|
||||||
server->client_body_limit = 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) )
|
||||||
|
throw std::invalid_argument("client_body_limit too big");
|
||||||
|
server->client_body_limit = server->client_body_limit * KB;
|
||||||
}
|
}
|
||||||
else if (key == "index")
|
else if (key == "index")
|
||||||
{
|
{
|
||||||
@@ -231,7 +227,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
else if (key == "error_page")
|
else if (key == "error_page")
|
||||||
{
|
{
|
||||||
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 (!(isNumeric_btw(400, 599, tmp_val[i])))
|
||||||
throw std::invalid_argument("invalid error code");
|
throw std::invalid_argument("invalid error code");
|
||||||
@@ -247,7 +243,7 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
|||||||
|
|
||||||
|
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
void ConfigParser::_set_location_values(LocationConfig *location, \
|
void ConfigParser::_set_location_values(LocationConfig *location,
|
||||||
const std::string key, std::string value)
|
const std::string key, std::string value)
|
||||||
{
|
{
|
||||||
// should i be sending pointers or references?
|
// should i be sending pointers or references?
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
|||||||
throw std::invalid_argument("Config file needs an Index");
|
throw std::invalid_argument("Config file needs an Index");
|
||||||
|
|
||||||
if (it->client_body_limit == 0)
|
if (it->client_body_limit == 0)
|
||||||
it->client_body_limit = 5000; // what is the recomended size?
|
it->client_body_limit = 1 * MB;
|
||||||
|
|
||||||
|
|
||||||
// if error_pages is left empty, we'll use the defaults which
|
// if error_pages is left empty, we'll use the defaults which
|
||||||
|
|||||||
@@ -11,15 +11,9 @@ int main(int ac, char **av)
|
|||||||
{
|
{
|
||||||
std::string config = (ac == 2 ? av[1] : "./default.config");
|
std::string config = (ac == 2 ? av[1] : "./default.config");
|
||||||
|
|
||||||
// like this just looks kinda gross, why bother creating an instance
|
|
||||||
// and not immediately parsing? like it servers no other purpose...
|
|
||||||
// what if i call parse directly in the constructor?
|
|
||||||
// oh because the constructor has no return, but still
|
|
||||||
// is there a better way?
|
|
||||||
|
|
||||||
ConfigParser configParser(config.c_str());
|
ConfigParser configParser(config);
|
||||||
|
// configParser.print_content();
|
||||||
configParser.print_content();
|
|
||||||
|
|
||||||
// i don't love that servers has to be a pointer...
|
// i don't love that servers has to be a pointer...
|
||||||
std::vector<ServerConfig>* servers = configParser.parse();
|
std::vector<ServerConfig>* servers = configParser.parse();
|
||||||
|
|||||||
@@ -171,18 +171,19 @@ file_type eval_file_type(const std::string &path)
|
|||||||
return (IS_OTHER);
|
return (IS_OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t eval_file_mode(std::string path, int mode)
|
// rename in "eval_file_access" ?
|
||||||
|
size_t eval_file_mode(const std::string &path, int mode)
|
||||||
{
|
{
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
if (::access(path.c_str(), F_OK) == -1)
|
||||||
{
|
{
|
||||||
std::perror("err access()");
|
std::perror("err access()");
|
||||||
return 404; // NOT_FOUND, file doesn't exist
|
return 404; // NOT_FOUND, file doesn't exist
|
||||||
}
|
}
|
||||||
|
|
||||||
if (access(path.c_str(), mode) == -1)
|
if (::access(path.c_str(), mode) == -1)
|
||||||
{
|
{
|
||||||
std::perror("err access()");
|
std::perror("err access()");
|
||||||
return 403; // FORBIDDEN, file doesn't have execution permission
|
return 403; // FORBIDDEN, file doesn't have access permission
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -226,7 +227,7 @@ std::string
|
|||||||
begin = str.rfind(delim, pos);
|
begin = str.rfind(delim, pos);
|
||||||
if (begin == NPOS)
|
if (begin == NPOS)
|
||||||
begin = 0;
|
begin = 0;
|
||||||
else
|
else if (begin < pos)
|
||||||
begin += delim.size();
|
begin += delim.size();
|
||||||
|
|
||||||
end = str.find(delim, pos);
|
end = str.find(delim, pos);
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
# define CRLF CR LF
|
# define CRLF CR LF
|
||||||
# define CRLF_SIZE 2
|
# define CRLF_SIZE 2
|
||||||
# define NPOS std::string::npos
|
# define NPOS std::string::npos
|
||||||
|
# define KB 1024
|
||||||
|
# define MB 1048576
|
||||||
|
|
||||||
/* Equivalent for end of http header size :
|
/* Equivalent for end of http header size :
|
||||||
** std::string(CRLF CRLF).size();
|
** std::string(CRLF CRLF).size();
|
||||||
@@ -65,15 +67,15 @@ std::string trim(std::string str, char del);
|
|||||||
http_method str_to_http_method(std::string &str);
|
http_method str_to_http_method(std::string &str);
|
||||||
std::string http_methods_to_str(unsigned int methods);
|
std::string http_methods_to_str(unsigned int methods);
|
||||||
file_type eval_file_type(const std::string &path);
|
file_type eval_file_type(const std::string &path);
|
||||||
size_t eval_file_mode(std::string path, int mode);
|
size_t eval_file_mode(const std::string &path, int mode);
|
||||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||||
std::string str_tolower(std::string str);
|
std::string str_tolower(std::string str);
|
||||||
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
std::string extract_line(std::string & str, size_t pos = 0, std::string delim = "\n");
|
||||||
std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n");
|
std::string get_line (std::string str, size_t pos = 0, std::string delim = "\n");
|
||||||
size_t parse_http_headers (std::string headers, std::map<std::string, std::string> & fields );
|
size_t parse_http_headers (std::string headers, std::map<std::string, std::string> & fields );
|
||||||
void str_map_key_tolower(std::map<std::string, std::string> & mp);
|
void str_map_key_tolower(std::map<std::string, std::string> & mp);
|
||||||
void throw_test();
|
|
||||||
// debug
|
// debug
|
||||||
|
void throw_test();
|
||||||
void print_special(std::string str);
|
void print_special(std::string str);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ class Webserv
|
|||||||
void _check_script_output(Client *client, std::string & output);
|
void _check_script_output(Client *client, std::string & output);
|
||||||
void _check_script_status(Client *client, std::string & output);
|
void _check_script_status(Client *client, std::string & output);
|
||||||
void _check_script_fields(Client *client, std::string & output);
|
void _check_script_fields(Client *client, std::string & output);
|
||||||
|
void _add_script_body_length_header(std::string & output);
|
||||||
|
void _remove_body_leading_empty_lines(std::string & output);
|
||||||
// epoll_update.cpp
|
// epoll_update.cpp
|
||||||
int _epoll_update(int fd, uint32_t events, int op);
|
int _epoll_update(int fd, uint32_t events, int op);
|
||||||
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
int _epoll_update(int fd, uint32_t events, int op, void *ptr);
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ 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;
|
size_t file_mode = client->status;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
||||||
while (pos != NPOS)
|
while (pos != NPOS)
|
||||||
{
|
{
|
||||||
pos = _cgi_pos(client, path, pos);
|
pos = _cgi_pos(client, path, pos);
|
||||||
/*DEBUG*/ std::cout << "pos:" << pos << "\n&path[pos]:" << &path[pos] << "\n" << B_YELLOW << "fin debug _cgi_pos()\n\n" << RESET;
|
|
||||||
if (pos == NPOS)
|
if (pos == NPOS)
|
||||||
break;
|
break;
|
||||||
client->fill_script_path(path, pos);
|
client->fill_script_path(path, pos);
|
||||||
@@ -27,7 +26,6 @@ bool Webserv::_is_cgi(Client *client, std::string path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
client->clear_script();
|
client->clear_script();
|
||||||
client->clear_script();
|
|
||||||
client->status = file_mode; // 404 not_found OR 403 forbidden
|
client->status = file_mode; // 404 not_found OR 403 forbidden
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -40,25 +38,20 @@ 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()
|
||||||
|
|
||||||
/*DEBUG*/ it = client->assigned_location->cgi_ext.begin(); std::cout << B_YELLOW << "\nDEBUG _cgi_pos()\n" << RESET << "vector_ext.size():[" << client->assigned_location->cgi_ext.size() << "]\n\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;
|
||||||
/*DEBUG*/ std::cout << "ext:[" << *it << "]\n" << "path:[" << path << "]\n\n";
|
|
||||||
it_end = client->assigned_location->cgi_ext.end();
|
it_end = client->assigned_location->cgi_ext.end();
|
||||||
while (pos < path.size())
|
while (pos < path.size())
|
||||||
{
|
{
|
||||||
/*DEBUG*/ std::cout << "\nwhile\n";
|
|
||||||
if (path.compare(pos, 2, "./") == 0)
|
if (path.compare(pos, 2, "./") == 0)
|
||||||
pos += 2;
|
pos += 2;
|
||||||
/*DEBUG*/ std::cout << "&path[pos]:[" << &path[pos] << "]\n";
|
|
||||||
pos = path.find('.', pos);
|
pos = path.find('.', pos);
|
||||||
if (pos == NPOS)
|
if (pos == NPOS)
|
||||||
return pos;
|
return pos;
|
||||||
it = client->assigned_location->cgi_ext.begin();
|
it = client->assigned_location->cgi_ext.begin();
|
||||||
for ( ; it != it_end; ++it)
|
for ( ; it != it_end; ++it)
|
||||||
{
|
{
|
||||||
/*DEBUG*/ std::cout << " for\n"; std::cout << " &path[pos]:[" << &path[pos] << "]\n" << " *it:[" << *it << "]\n" << " (*it).size():[" << (*it).size() << "]\n" << " path.substr(pos, (*it).size()):[" << path.substr(pos + 1, (*it).size()) << "]\n\n";
|
|
||||||
len = (*it).size();
|
len = (*it).size();
|
||||||
if (path.compare(pos + 1, len, *it) == 0)
|
if (path.compare(pos + 1, len, *it) == 0)
|
||||||
if ( !std::isalpha(path[pos + 1 + len], loc) )
|
if ( !std::isalpha(path[pos + 1 + len], loc) )
|
||||||
@@ -147,8 +140,6 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
std::string body = client->get_rq_body();
|
std::string body = client->get_rq_body();
|
||||||
int fd_in[2];
|
int fd_in[2];
|
||||||
int fd_out[2];
|
int fd_out[2];
|
||||||
int save_in = dup(STDIN_FILENO);
|
|
||||||
int save_out = dup(STDOUT_FILENO);
|
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
||||||
pipe(fd_in);
|
pipe(fd_in);
|
||||||
@@ -157,21 +148,21 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
std::cerr << "fork crashed" << std::endl;
|
std::cerr << "fork crashed" << std::endl;
|
||||||
else if (pid == 0)
|
else if (pid == 0) // child
|
||||||
{
|
{
|
||||||
close(FD_WR_TO_CHLD);
|
close(FD_WR_TO_CHLD);
|
||||||
close(FD_RD_FR_CHLD);
|
close(FD_RD_FR_CHLD);
|
||||||
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
|
||||||
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
|
||||||
path = "." + client->get_rq_script_path();
|
path = "." + client->get_rq_script_path();
|
||||||
/*DEBUG*/std::cerr << "execve\n" << "path:[" << path << "]\n";
|
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
|
||||||
execve(path.c_str(), nll, env);
|
execve(path.c_str(), nll, env);
|
||||||
// for tests execve crash :
|
// for tests execve crash :
|
||||||
//execve("wrong", nll, env);
|
//execve("wrong", nll, env);
|
||||||
std::cerr << "execve crashed.\n";
|
std::cerr << "execve crashed.\n";
|
||||||
// TODO HUGO : check errno
|
// TODO HUGO : check errno
|
||||||
}
|
}
|
||||||
else
|
else //parent
|
||||||
{
|
{
|
||||||
close(FD_RD_FR_PRNT);
|
close(FD_RD_FR_PRNT);
|
||||||
close(FD_WR_TO_PRNT);
|
close(FD_WR_TO_PRNT);
|
||||||
@@ -189,9 +180,7 @@ std::string Webserv::_exec_script(Client *client, char **env)
|
|||||||
}
|
}
|
||||||
if (script_output.empty())
|
if (script_output.empty())
|
||||||
script_output = "Status: 500\r\n\r\n";
|
script_output = "Status: 500\r\n\r\n";
|
||||||
|
|
||||||
dup2(save_in, STDIN_FILENO);
|
|
||||||
dup2(save_out, STDOUT_FILENO);
|
|
||||||
return script_output;
|
return script_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +188,8 @@ void Webserv::_check_script_output(Client *client, std::string & output)
|
|||||||
{
|
{
|
||||||
_check_script_status(client, output);
|
_check_script_status(client, output);
|
||||||
_check_script_fields(client, output);
|
_check_script_fields(client, output);
|
||||||
|
_remove_body_leading_empty_lines(output);
|
||||||
|
_add_script_body_length_header(output);
|
||||||
// _check_script_empty_lines(client, output);
|
// _check_script_empty_lines(client, output);
|
||||||
// _check_script_space_colons(client, output);
|
// _check_script_space_colons(client, output);
|
||||||
// _check_script_new_lines(client, output);
|
// _check_script_new_lines(client, output);
|
||||||
@@ -255,3 +246,58 @@ void Webserv::_check_script_fields(Client *client, std::string & output)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Webserv::_remove_body_leading_empty_lines(std::string & output)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
size_t pos_empty;
|
||||||
|
|
||||||
|
pos = output.find(CRLF CRLF);
|
||||||
|
if (pos == NPOS)
|
||||||
|
return;
|
||||||
|
pos += CRLF_SIZE * 2;
|
||||||
|
pos_empty = pos;
|
||||||
|
while (pos_empty == pos)
|
||||||
|
{
|
||||||
|
pos = output.find(CRLF, pos);
|
||||||
|
if (pos == pos_empty)
|
||||||
|
extract_line(output, pos, CRLF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Webserv::_add_script_body_length_header(std::string & output)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> field;
|
||||||
|
std::map<std::string, std::string>::iterator it;
|
||||||
|
std::stringstream str_len;
|
||||||
|
std::string tmp;
|
||||||
|
size_t pos;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
pos = output.find(CRLF CRLF);
|
||||||
|
if (pos != NPOS)
|
||||||
|
tmp = output.substr(pos + (CRLF_SIZE * 2));
|
||||||
|
len = tmp.size();
|
||||||
|
str_len << len;
|
||||||
|
|
||||||
|
// put script headers in map
|
||||||
|
tmp = output;
|
||||||
|
pos = tmp.find(CRLF CRLF);
|
||||||
|
if (pos != NPOS)
|
||||||
|
tmp.erase(pos);
|
||||||
|
::parse_http_headers(tmp, field);
|
||||||
|
// case insensitive search in map for "Content-Length"
|
||||||
|
tmp = "Content-Length";
|
||||||
|
for (it = field.begin(); it != field.end(); ++it)
|
||||||
|
{
|
||||||
|
if (str_tolower(it->first) == str_tolower(tmp))
|
||||||
|
{
|
||||||
|
pos = output.find(it->first);
|
||||||
|
::extract_line(output, pos, CRLF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp += ": ";
|
||||||
|
tmp += str_len.str();
|
||||||
|
tmp += CRLF;
|
||||||
|
output.insert(0, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
void Webserv::_delete(Client *client, const std::string &path)
|
void Webserv::_delete(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||||
*/
|
*/
|
||||||
_delete_file(client, path);
|
_delete_file(client, path);
|
||||||
@@ -12,24 +11,21 @@ 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)
|
||||||
{
|
{
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
std::cout << "_delete_file()\n";
|
||||||
{
|
client->status = ::eval_file_mode(path, W_OK);
|
||||||
std::perror("err access()");
|
if (client->status)
|
||||||
client->status = 404;
|
return;
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(path.c_str(), W_OK) == -1)
|
if (std::remove(path.c_str()) == -1)
|
||||||
{
|
|
||||||
std::perror("err access()");
|
|
||||||
client->status = 403;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remove(path.c_str()) == -1)
|
|
||||||
{
|
{
|
||||||
std::perror("err remove()");
|
std::perror("err remove()");
|
||||||
client->status = 500;
|
if (errno == ENOTEMPTY || errno == EEXIST)
|
||||||
|
client->status = 403;
|
||||||
|
else
|
||||||
|
client->status = 500;
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->status = 204;
|
||||||
|
client->response.append(CRLF);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ std::string Webserv::_replace_url_root(Client *client, std::string path)
|
|||||||
// const?
|
// const?
|
||||||
void Webserv::_get(Client *client, std::string &path)
|
void Webserv::_get(Client *client, std::string &path)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-get
|
||||||
// Index/Autoindex block
|
*/
|
||||||
|
std::cout << "_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] != '/')
|
||||||
@@ -34,12 +35,14 @@ void Webserv::_get(Client *client, std::string &path)
|
|||||||
}
|
}
|
||||||
if (client->assigned_location->autoindex == true)
|
if (client->assigned_location->autoindex == true)
|
||||||
_autoindex(client, path);
|
_autoindex(client, path);
|
||||||
|
else
|
||||||
|
client->status = 404;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_get_file(client, path);
|
_get_file(client, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
# define MAX_FILESIZE 1 * MB // unused
|
||||||
void Webserv::_get_file(Client *client, const std::string &path)
|
void Webserv::_get_file(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -52,21 +55,11 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
|||||||
|
|
||||||
std::cout << "_get_file()\n";
|
std::cout << "_get_file()\n";
|
||||||
|
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
client->status = ::eval_file_mode(path, R_OK);
|
||||||
{
|
if (client->status)
|
||||||
std::perror("err access()");
|
return;
|
||||||
client->status = 404;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(path.c_str(), R_OK) == -1)
|
ifd.open(path.c_str());
|
||||||
{
|
|
||||||
std::perror("err access()");
|
|
||||||
client->status = 403;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
ifd.open(path.c_str(), std::ios::ate);
|
|
||||||
if (!ifd)
|
if (!ifd)
|
||||||
{
|
{
|
||||||
std::cerr << path << ": ifd.open fail" << '\n';
|
std::cerr << path << ": ifd.open fail" << '\n';
|
||||||
@@ -74,17 +67,6 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* std::streampos size = ifd.tellg();
|
|
||||||
// WIP Low priority : Chunk or not chunk (if filesize too big)
|
|
||||||
if (size > MAX_FILESIZE)
|
|
||||||
{
|
|
||||||
// Then chunk
|
|
||||||
client->status = 500; // WIP temp
|
|
||||||
std::cerr << "File too large for non chunk body\n";
|
|
||||||
return ;
|
|
||||||
} */
|
|
||||||
|
|
||||||
ifd.seekg(0, std::ios::beg);
|
|
||||||
buf << ifd.rdbuf();
|
buf << ifd.rdbuf();
|
||||||
if (!ifd || !buf)
|
if (!ifd || !buf)
|
||||||
{
|
{
|
||||||
@@ -100,6 +82,19 @@ void Webserv::_get_file(Client *client, const std::string &path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// WIP Low priority : Chunk or not chunk (if filesize > MAX_FILESIZE)
|
||||||
|
// WITH flag "std::ios::ate" for open()
|
||||||
|
std::streampos size = ifd.tellg();
|
||||||
|
ifd.seekg(0, std::ios::beg);
|
||||||
|
if (size > MAX_FILESIZE)
|
||||||
|
{
|
||||||
|
// Chunked GET here
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// const?
|
// const?
|
||||||
void Webserv::_autoindex(Client *client, const std::string &path)
|
void Webserv::_autoindex(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,10 +5,9 @@
|
|||||||
void Webserv::_post(Client *client, const std::string &path)
|
void Webserv::_post(Client *client, const std::string &path)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
WIP
|
|
||||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||||
*/
|
*/
|
||||||
(void)path;
|
(void)path; // unused, we use "assigned_location->upload_dir" instead
|
||||||
std::cout << "_post()\n";
|
std::cout << "_post()\n";
|
||||||
std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n";
|
std::cerr << "upload_dir = " << client->assigned_location->upload_dir << "\n";
|
||||||
|
|
||||||
@@ -31,12 +30,17 @@ void Webserv::_post(Client *client, const std::string &path)
|
|||||||
// TODO : Loop for multi body
|
// TODO : Loop for multi body
|
||||||
void Webserv::_upload_files(Client *client)
|
void Webserv::_upload_files(Client *client)
|
||||||
{
|
{
|
||||||
|
std::cout << "_upload_files()\n";
|
||||||
std::ofstream ofd;
|
std::ofstream ofd;
|
||||||
std::vector<MultipartBody>::const_iterator body_it = client->get_rq_multi_bodys().begin();
|
std::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;
|
bool file_existed = false;
|
||||||
|
|
||||||
|
client->status = ::eval_file_mode(client->assigned_location->upload_dir, W_OK);
|
||||||
|
if (client->status)
|
||||||
|
return;
|
||||||
|
|
||||||
while (body_it != client->get_rq_multi_bodys().end())
|
while (body_it != client->get_rq_multi_bodys().end())
|
||||||
{
|
{
|
||||||
@@ -72,13 +76,13 @@ void Webserv::_upload_files(Client *client)
|
|||||||
path.append(filename);
|
path.append(filename);
|
||||||
|
|
||||||
|
|
||||||
if (access(path.c_str(), F_OK) == -1)
|
if (::access(path.c_str(), F_OK) == -1)
|
||||||
file_existed = false;
|
file_existed = false;
|
||||||
else
|
else
|
||||||
file_existed = true;
|
file_existed = true;
|
||||||
|
|
||||||
// How to determine status 403 for file that dont already exist ? access() on the upload_dir ?
|
// 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()");
|
||||||
client->status = 403;
|
client->status = 403;
|
||||||
@@ -103,10 +107,15 @@ void Webserv::_upload_files(Client *client)
|
|||||||
ofd.close();
|
ofd.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->status = 204;
|
||||||
|
client->response.append(CRLF);
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9110.html#name-204-no-content
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (file_existed) // with multi body it doesn't make much sense
|
if (file_existed) // with multi body it doesn't make much sense
|
||||||
{
|
{
|
||||||
// client->status = 200;
|
client->status = 200;
|
||||||
client->status = 204; // DEBUG 204
|
|
||||||
client->response.append("Location: ");
|
client->response.append("Location: ");
|
||||||
client->response.append("/index.html"); // WIP
|
client->response.append("/index.html"); // WIP
|
||||||
client->response.append(CRLF);
|
client->response.append(CRLF);
|
||||||
@@ -115,12 +124,11 @@ void Webserv::_upload_files(Client *client)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// client->status = 201;
|
client->status = 201;
|
||||||
client->status = 204; // DEBUG 204
|
|
||||||
client->response.append("Location: ");
|
client->response.append("Location: ");
|
||||||
client->response.append("/index.html"); // WIP
|
client->response.append("/index.html"); // WIP
|
||||||
client->response.append(CRLF);
|
client->response.append(CRLF);
|
||||||
client->response.append(CRLF);
|
client->response.append(CRLF);
|
||||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
#include "parsing_message_http.hpp"
|
|
||||||
|
|
||||||
std::string
|
|
||||||
parse_http_body(std::string message)
|
|
||||||
{
|
|
||||||
std::string body;
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
pos = message.find(CRLF CRLF);
|
|
||||||
pos += std::string(CRLF CRLF).size();
|
|
||||||
// TODO: copying just like that might fail in case of binary or images
|
|
||||||
body = message.substr(pos);
|
|
||||||
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PARSING_MESSAGE_HTTP_HPP
|
|
||||||
# define PARSING_MESSAGE_HTTP_HPP
|
|
||||||
|
|
||||||
# include <iostream>
|
|
||||||
# include <string>
|
|
||||||
# include <vector>
|
|
||||||
# include <map>
|
|
||||||
# include "utils.hpp"
|
|
||||||
|
|
||||||
std::map<std::string, std::string>
|
|
||||||
parse_http_headers (
|
|
||||||
std::string headers,
|
|
||||||
std::map<std::string, std::string> fields )
|
|
||||||
|
|
||||||
std::string
|
|
||||||
parse_http_body(std::string message);
|
|
||||||
|
|
||||||
// http message structure :
|
|
||||||
//
|
|
||||||
// start-line
|
|
||||||
// request-line
|
|
||||||
// method SP target SP version
|
|
||||||
// response-line
|
|
||||||
// version SP status SP reason
|
|
||||||
// header-fields
|
|
||||||
// name ":" SP value
|
|
||||||
// CRLF
|
|
||||||
// body
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include "parsing_request.hpp"
|
|
||||||
|
|
||||||
@@ -47,6 +47,8 @@ int Webserv::_send_response(Client *client)
|
|||||||
if (client->status >= 400)
|
if (client->status >= 400)
|
||||||
_error_html_response(client);
|
_error_html_response(client);
|
||||||
|
|
||||||
|
/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
|
||||||
std::cerr << "client->response.size() = " << client->response.size() << "\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)
|
||||||
@@ -55,6 +57,11 @@ int Webserv::_send_response(Client *client)
|
|||||||
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||||
return SEND_CLOSE;
|
return SEND_CLOSE;
|
||||||
}
|
}
|
||||||
|
if (ret == 0) // actually never happen ?
|
||||||
|
{
|
||||||
|
std::cerr << "SEND RET 0 for client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
|
||||||
|
return SEND_CLOSE;
|
||||||
|
}
|
||||||
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
||||||
|
|
||||||
return SEND_COMPLETE;
|
return SEND_COMPLETE;
|
||||||
@@ -83,8 +90,16 @@ void Webserv::_construct_response(Client *client)
|
|||||||
if (_is_cgi(client, path))
|
if (_is_cgi(client, path))
|
||||||
{
|
{
|
||||||
script_output = _exec_cgi(client);
|
script_output = _exec_cgi(client);
|
||||||
|
|
||||||
|
///*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(script_output); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
///*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
|
||||||
_check_script_output(client, script_output);
|
_check_script_output(client, script_output);
|
||||||
client->response += script_output;
|
client->response += script_output;
|
||||||
|
|
||||||
|
/*DEBUG*/ std::cout << B_YELLOW "inside cgi" RESET "\n";
|
||||||
|
///*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_process_method(client, path);
|
_process_method(client, path);
|
||||||
@@ -157,12 +172,17 @@ void Webserv::_append_body(Client *client, const std::string &body, const std::s
|
|||||||
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
ServerConfig *_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
Behavior like this :
|
||||||
http://nginx.org/en/docs/http/request_processing.html
|
http://nginx.org/en/docs/http/request_processing.html
|
||||||
_determine_process_server() should be complete.
|
|
||||||
TODO : test it
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::string const &server_name = client->get_rq_headers("Host");
|
std::string server_name = client->get_rq_headers("Host");
|
||||||
|
std::cerr << "server_name = " << server_name << "\n";
|
||||||
|
size_t pos = server_name.rfind(':');
|
||||||
|
if (pos != NPOS)
|
||||||
|
server_name.erase(pos);
|
||||||
|
std::cerr << "server_name = " << server_name << "\n";
|
||||||
|
|
||||||
std::vector<ServerConfig>::iterator it = servers.begin();
|
std::vector<ServerConfig>::iterator it = servers.begin();
|
||||||
std::vector<ServerConfig>::iterator default_server = servers.end();
|
std::vector<ServerConfig>::iterator default_server = servers.end();
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,82 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title></title>
|
<title></title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid red;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
form > * {
|
||||||
|
display: flex;
|
||||||
|
margin: 5px auto 5px 5px;
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
margin: 0px 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form method="get">
|
|
||||||
|
<form method="get" action="/cgi-bin/cgi_cpp.out">
|
||||||
|
|
||||||
|
<p><mark>get</mark> form</p>
|
||||||
|
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||||
|
<label for="fname">First name:</label><br>
|
||||||
|
<input type="text" id="fname" name="fname" value="John"><br>
|
||||||
|
<label for="lname">Last name:</label><br>
|
||||||
|
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||||
|
|
||||||
<input type="submit" value="submit">
|
<input type="submit" value="submit">
|
||||||
</form>
|
</form>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<form method="post" action="/cgi-bin/cgi_cpp.out">
|
||||||
|
|
||||||
|
<p><mark>post</mark> form</p>
|
||||||
|
<p>to <mark>/cgi-bin/cgi_cpp.out</mark></p>
|
||||||
|
<label for="fname">First name:</label><br>
|
||||||
|
<input type="text" id="fname" name="fname" value="John"><br>
|
||||||
|
<label for="lname">Last name:</label><br>
|
||||||
|
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||||
|
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<form method="get" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||||
|
|
||||||
|
<p><mark>get</mark> form</p>
|
||||||
|
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||||
|
<label for="fname">First name:</label><br>
|
||||||
|
<input type="text" id="fname" name="fname" value="John"><br>
|
||||||
|
<label for="lname">Last name:</label><br>
|
||||||
|
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||||
|
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<form method="post" action="/cgi-bin/cgi_cpp_content_length.out">
|
||||||
|
|
||||||
|
<p><mark>post</mark> form</p>
|
||||||
|
<p>to <mark>/cgi-bin/cgi_cpp_content_length.out</mark></p>
|
||||||
|
<label for="fname">First name:</label><br>
|
||||||
|
<input type="text" id="fname" name="fname" value="John"><br>
|
||||||
|
<label for="lname">Last name:</label><br>
|
||||||
|
<input type="text" id="lname" name="lname" value="Doe"><br><br>
|
||||||
|
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<form method="post">
|
|
||||||
<input type="submit" value="submit">
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
11
www2/index.html
Normal file
11
www2/index.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Le Webserv</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 style="text-align:center">( ͡◉ ͜ ʖ ͡◉) Le index du serveur 2 ! </h1>
|
||||||
|
<hr>
|
||||||
|
<p style="text-align:center">˚ಎ˚</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user