Merge branch 'master' of bitbucket.org:LuckyLaszlo/webserv
This commit is contained in:
7
Makefile
7
Makefile
@@ -22,10 +22,11 @@ SRCS_D = srcs \
|
||||
SRCS = main.cpp \
|
||||
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
|
||||
accept.cpp request.cpp response.cpp \
|
||||
method_get.cpp method_post.cpp method_delete.cpp \
|
||||
run_loop.cpp \
|
||||
ConfigParser.cpp \
|
||||
ConfigParserUtils.cpp \
|
||||
ConfigParserPost.cpp \
|
||||
parser.cpp \
|
||||
extraConfig.cpp \
|
||||
postProcessing.cpp \
|
||||
utils.cpp \
|
||||
cgi_script.cpp \
|
||||
Client.cpp \
|
||||
|
||||
@@ -10,14 +10,44 @@ server {
|
||||
# client_body_limit 400;
|
||||
|
||||
index index.html; # this is another comment
|
||||
|
||||
root ./www/;
|
||||
|
||||
# If not explicitly set, ConfigParser need to genererate a location block
|
||||
# like this for path "/" (based on field "root" and "index" of the server)
|
||||
location / {
|
||||
root ./www/;
|
||||
index index.html;
|
||||
location /test {
|
||||
index index1.html;
|
||||
}
|
||||
|
||||
allow_methods GET;
|
||||
# /stylesheet/ alone doesn't work, i mean we don't have wildcards...
|
||||
location /stylesheet/style.css {
|
||||
# root ./www/../;
|
||||
root ./;
|
||||
}
|
||||
|
||||
location /test/something.html {
|
||||
# allow_methods DELETE;
|
||||
}
|
||||
|
||||
# location /something/long/here {
|
||||
# }
|
||||
|
||||
location /test/test_deeper/ {
|
||||
# allow_methods
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location /test/test_deeper/super_deep {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
# location /test/test_deeper/something.html {
|
||||
# allow_methods DELETE;
|
||||
# }
|
||||
|
||||
|
||||
# ok in theory if one were to go to /test they would get the index in www
|
||||
# as opposed to the one in /website...
|
||||
# location /test {
|
||||
# root /www;
|
||||
# }
|
||||
|
||||
}
|
||||
|
||||
16
memo.txt
16
memo.txt
@@ -1,13 +1,25 @@
|
||||
IN 42 SUBJECT, PRIORITY :
|
||||
- chunked request (response not mandatory it seems)
|
||||
- CGI
|
||||
- index (default file directory)
|
||||
- Ecrire des tests !
|
||||
|
||||
- handle redirection (weird behavior, to fix)
|
||||
- upload files with config "upload_repo"
|
||||
-----------------------------
|
||||
- autoindex (done, need test)
|
||||
--------------
|
||||
- replace atoi() with a better function
|
||||
- 408 Request Timeout
|
||||
- gerer le champ "Accept" du client
|
||||
- gerer les ".." dans un URL (verifier que l'on ne sort pas du dossier "root")
|
||||
- do correct handling of special character in url (/rfc2119_files/errata.js.t%C3%A9l%C3%A9chargement -> /rfc2119_files/errata.js.téléchargement)
|
||||
- handle redirection
|
||||
- maybe add a "last_action_time" in Client for timeout handling
|
||||
little global timeout on epoll, like 100ms, then find client that actualy need to timeout
|
||||
if (actual_time - client.last_action_time > 10000ms){timeout(client)}
|
||||
- add headers "Date" and "Last-Modified" to response
|
||||
- change "std::string" to reference "std::string &" in most functions
|
||||
and add "const" if apropriate.
|
||||
- http_method en mode binary flags. "std::vector<http_method> allow_methods" -> "unsigned int allow_methods;"
|
||||
- Dans le parsing, trier les "locations" par ordre de precision.
|
||||
Compter les "/" dans le chemin, les locations avec le plus de "/" seront en premier dans le vector.
|
||||
- Il faut vérifier le path de la requête, voir si le serveur est bien censé délivrer cette ressource et si le client y a accès, avant d'appeler le CGI.
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
|
||||
#include "Client.hpp"
|
||||
|
||||
char Client::buf[MAX_FILESIZE+1];
|
||||
|
||||
/*********************************************
|
||||
* COPLIENS
|
||||
*********************************************/
|
||||
|
||||
Client::Client() : fd(0), body_size(0), status(0) {
|
||||
Client::Client()
|
||||
: fd(0),
|
||||
lsocket(NULL),
|
||||
status(0),
|
||||
header_complete(false),
|
||||
read_body_size(0),
|
||||
assigned_server(NULL),
|
||||
assigned_location(NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,9 +60,12 @@ void Client::parse_request()
|
||||
void Client::clear()
|
||||
{
|
||||
clear_request();
|
||||
header_complete = false;
|
||||
read_body_size = 0;
|
||||
assigned_server = NULL;
|
||||
assigned_location = NULL;
|
||||
raw_request.clear();
|
||||
response.clear();
|
||||
body_size = 0;
|
||||
status = 0;
|
||||
}
|
||||
|
||||
@@ -128,6 +137,8 @@ void Client::_parse_request_headers( std::vector<std::string> list )
|
||||
|
||||
void Client::_parse_request_body( size_t pos )
|
||||
{
|
||||
// TODO : a revoir avec une std::string,
|
||||
// pour ne pas avoir le probleme d'un '0' qui marque la fin des données
|
||||
std::string body = &raw_request[pos];
|
||||
|
||||
_request.body = body;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include "utils.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
|
||||
struct Request
|
||||
{
|
||||
@@ -17,7 +18,6 @@ struct Request
|
||||
std::string body;
|
||||
};
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
@@ -27,12 +27,16 @@ class Client
|
||||
//Client &operator=(Client const &rhs);
|
||||
|
||||
int fd;
|
||||
const listen_socket *lsocket;
|
||||
|
||||
std::string raw_request;
|
||||
std::string response;
|
||||
static char buf[MAX_FILESIZE+1];
|
||||
size_t body_size;
|
||||
unsigned int status;
|
||||
listen_socket *lsocket;
|
||||
|
||||
bool header_complete;
|
||||
size_t read_body_size;
|
||||
ServerConfig *assigned_server; // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *assigned_location;
|
||||
|
||||
// const functions ?
|
||||
http_method get_method();
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
# include <iostream> // cout, cin
|
||||
# include <fstream> // ifstream
|
||||
//# include <unistd.h> // access()
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include <dirent.h> // opendir(), doesn't work...
|
||||
# include <sys/stat.h> // stat(), replaces opendir() don't bother with ERRNO ?
|
||||
# include <algorithm> // sort() in Post
|
||||
|
||||
class ConfigParser {
|
||||
|
||||
@@ -51,6 +52,13 @@ public:
|
||||
// private member functions from anywhere...
|
||||
void _print_content() const;
|
||||
|
||||
|
||||
// I don't love that this is here but...
|
||||
// doesn't work use the operator overload
|
||||
// bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::string _content;
|
||||
|
||||
@@ -82,11 +90,20 @@ private:
|
||||
|
||||
void _post_processing(std::vector<ServerConfig> *servers);
|
||||
|
||||
bool _find_root_path_location(std::vector<LocationConfig> locations); // const?
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
// no idea if it should go here...
|
||||
//bool compareLocationConfigs(const LocationConfig &a,
|
||||
// const LocationConfig &b);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// def needs work line a better name an do i even need this?
|
||||
// should it be in Utils instead?
|
||||
class MyException : public std::invalid_argument
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
|
||||
// make certain servers default
|
||||
// fill out empty settings
|
||||
// if special settings are empty throw
|
||||
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port should already be set
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// is that a good default?
|
||||
if (it->root == "")
|
||||
it->root = "/";
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
// autoindex should already be false by default right?
|
||||
|
||||
// what do we do if Allow methods is left empty?
|
||||
// all ?
|
||||
if (it->allow_methods.empty())
|
||||
throw std::invalid_argument("No methods specified");
|
||||
|
||||
|
||||
// what to do if index is left empty? index.html?
|
||||
// ok but i still need to check index, no idea how...
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
// check that path is feasible...
|
||||
// opendir?
|
||||
DIR* dir = opendir(it_l->path.c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("location dir could not be opened");
|
||||
|
||||
if (it_l->client_body_limit == 0)
|
||||
it_l->client_body_limit = 5000; // what is the recomended size?
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
// fill out allow methods from server?
|
||||
if (it_l->allow_methods.empty())
|
||||
it_l->allow_methods = it->allow_methods;
|
||||
|
||||
// fill out index from Server?
|
||||
// or do a bunch of checks on what is in there...
|
||||
|
||||
// same for redirect status i think
|
||||
|
||||
// maybe do something for Cgi_info?
|
||||
|
||||
++it_l;
|
||||
}
|
||||
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
// do the defaults at the end?
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: lperrey <lperrey@student.42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/07/23 16:08:00 by me #+# #+# */
|
||||
/* Updated: 2022/08/02 14:06:07 by lperrey ### ########.fr */
|
||||
/* Updated: 2022/08/04 19:32:40 by erlazo ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
# include <map>
|
||||
# include <vector>
|
||||
# include <string>
|
||||
# include <iostream>
|
||||
# include <sys/stat.h> // stat()
|
||||
|
||||
# include <stdio.h> // printf(), gotta go
|
||||
|
||||
|
||||
# include "utils.hpp"
|
||||
|
||||
// again, struct instead?
|
||||
class LocationConfig
|
||||
@@ -24,22 +30,64 @@ class LocationConfig
|
||||
public:
|
||||
// canonic stuff?
|
||||
|
||||
std::string path;
|
||||
|
||||
int client_body_limit;
|
||||
std::string root;
|
||||
std::string path; // /path and /path/ are fine
|
||||
// i add trailing / if a dir
|
||||
std::string root;
|
||||
std::vector<std::string> index;
|
||||
std::vector<http_method> allow_methods;
|
||||
std::map<std::string, std::string> cgi_info;
|
||||
unsigned int allow_methods;
|
||||
std::vector<std::string> cgi_ext; // php not .php
|
||||
bool autoindex;
|
||||
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// wait no there can only be 1 and i think it might have to be in
|
||||
// location only...
|
||||
int redirect_status;
|
||||
std::string redirect_uri;
|
||||
std::string upload_repo; // might change name...
|
||||
|
||||
int redirect_status;
|
||||
std::string redirect_uri;
|
||||
// au pire you do location / { return 301 http://location; }
|
||||
// and that's how you get the redirect from the root.
|
||||
|
||||
void print_all()
|
||||
{
|
||||
std::cout << "\nPRINTING A LOCATION\n";
|
||||
|
||||
std::cout << "Path: " << path << '\n';
|
||||
std::cout << "root: " << root << '\n';
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
|
||||
std::cout << "Skipping index...\n";
|
||||
|
||||
std::cout << "Location allow_methods: ";
|
||||
std::cout << ::http_methods_to_str(allow_methods) << "\n";
|
||||
|
||||
std::cout << "Skipping redirect status etc...\n";
|
||||
|
||||
std::cout << "------\n";
|
||||
}
|
||||
|
||||
// works a lot better than using a compare function...
|
||||
bool operator<(const LocationConfig& rhs) const
|
||||
{
|
||||
int comp_lhs = 0;
|
||||
int comp_rhs = 0;
|
||||
size_t tmp = 0;
|
||||
|
||||
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
|
||||
{
|
||||
++tmp;
|
||||
++comp_lhs;
|
||||
}
|
||||
if (path[path.find_last_of("/") + 1] != '\0')
|
||||
++comp_lhs;
|
||||
tmp = 0;
|
||||
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
{
|
||||
++tmp;
|
||||
++comp_rhs;
|
||||
}
|
||||
if (rhs.path[rhs.path.find_last_of("/") + 1] != '\0')
|
||||
++comp_rhs;
|
||||
|
||||
return (comp_lhs < comp_rhs); // right comparison ? not <= ?
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -14,48 +14,24 @@
|
||||
class ServerConfig
|
||||
{
|
||||
public:
|
||||
|
||||
// do i need some canonic stuff?
|
||||
|
||||
|
||||
// there can be several
|
||||
std::vector<std::string> server_name;
|
||||
// we could shove default in here if we wanted to...
|
||||
|
||||
// there can only be 1 per server...
|
||||
std::string host;
|
||||
std::string port; // port needs to be something else... not quite an int
|
||||
// should a Server be able to handle several?
|
||||
|
||||
// there can only be one.
|
||||
std::string root;
|
||||
std::string root; // ./www/ or www work www/ and www work
|
||||
// i do remove trailing / tho
|
||||
|
||||
unsigned int client_body_limit; // set to default max if none set
|
||||
|
||||
// might be the only one we let slide if bad input...
|
||||
bool autoindex;
|
||||
|
||||
// we will check the index in the post processing with access() ?
|
||||
std::vector<std::string> index;
|
||||
std::map<int, std::string> error_pages;
|
||||
|
||||
// i'm tempted to do something diff for storing method types...
|
||||
// fuck it, you can only call allow_methods once in Server
|
||||
// once more in each location.
|
||||
std::vector<http_method> allow_methods;
|
||||
|
||||
std::vector<LocationConfig> locations;
|
||||
|
||||
// not convinced we need these...
|
||||
// struct timeval send_timeout;
|
||||
// struct timeval recv_timeout;
|
||||
|
||||
|
||||
// fuck maybe i do need return here...
|
||||
// wait if i can call several times, shouldn't it be a map?
|
||||
// i think actually there can only be 1 and it can only be in a location?
|
||||
// int redirect_status;
|
||||
// std::string redirect_uri;
|
||||
|
||||
void print_all()
|
||||
{
|
||||
@@ -73,15 +49,12 @@ public:
|
||||
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 << "\nallow_methods: ";
|
||||
for (size_t i = 0; i < allow_methods.size(); i++)
|
||||
std::cout << allow_methods[i] << " ";
|
||||
std::cout << "\nskiping Locations for now...\n";
|
||||
std::cout << "also skiping send_timeout and recv\n";
|
||||
std::cout << "autoindex: " << autoindex << '\n';
|
||||
|
||||
// std::cout << "skiping Locations for now...\n";
|
||||
for (std::vector<LocationConfig>::iterator it = locations.begin(); it < locations.end(); it++)
|
||||
it->print_all();
|
||||
|
||||
std::cout << "client_body_limit: " << client_body_limit << '\n';
|
||||
// std::cout << "redirect_status: " << redirect_status << '\n';
|
||||
// std::cout << "redirect_uri: " << redirect_uri << '\n';
|
||||
std::cout << "host: " << host << '\n';
|
||||
std::cout << "port: " << port << '\n';
|
||||
|
||||
|
||||
27
srcs/config/compareLocationConfigs.cpp
Normal file
27
srcs/config/compareLocationConfigs.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
// prolly get rid of this file...
|
||||
|
||||
|
||||
#include "LocationConfig.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
// Ok so maybe it can't be a member functions?
|
||||
bool compareLocationConfigs(const LocationConfig &a, const LocationConfig &b)
|
||||
{
|
||||
int len_a;
|
||||
int len_b;
|
||||
size_t tmp = 0;
|
||||
|
||||
// consider adding 1 to path that ends in a file not folder.
|
||||
|
||||
|
||||
while ((tmp = a.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
++len_a;
|
||||
tmp = 0;
|
||||
while ((tmp = b.path.find_first_of("/", tmp)) != std::string::npos)
|
||||
++len_b;
|
||||
return (len_a < len_b); // right comparison ? not <= ?
|
||||
}
|
||||
@@ -82,6 +82,9 @@ std::string ConfigParser::_get_rest_of_line(size_t *curr)
|
||||
return (values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_print_content() const
|
||||
{
|
||||
std::cout << _content;
|
||||
@@ -31,8 +31,6 @@ ConfigParser::ConfigParser(const char* path)
|
||||
file.open(path);
|
||||
if (file.is_open())
|
||||
{
|
||||
// are there more throws i need to add in case of errors, what would
|
||||
// those errors be?
|
||||
while (!file.eof())
|
||||
{
|
||||
getline(file, buf);
|
||||
@@ -45,7 +43,7 @@ ConfigParser::ConfigParser(const char* path)
|
||||
}
|
||||
else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment)
|
||||
{
|
||||
// is there a comment at the end of the line
|
||||
// check for comment at the end of the line
|
||||
std::string tmp = buf.substr(0, comment - 1);
|
||||
_content.append(tmp + '\n');
|
||||
}
|
||||
@@ -53,7 +51,7 @@ ConfigParser::ConfigParser(const char* path)
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("open config");
|
||||
throw std::invalid_argument("failed to open config");
|
||||
}
|
||||
|
||||
ConfigParser::~ConfigParser()
|
||||
@@ -85,10 +83,11 @@ std::vector<ServerConfig> * ConfigParser::parse()
|
||||
throw std::invalid_argument("empty config file");
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// why no checks here
|
||||
// if not here do i need them elsewhere?
|
||||
start = _content.find_first_not_of(" \t\n", curr);
|
||||
curr = _content.find_first_of(" \t\n", start);
|
||||
if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos)
|
||||
throw std::invalid_argument("empty config file");
|
||||
|
||||
if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos)
|
||||
throw std::invalid_argument("empty config file");
|
||||
std::string key = _content.substr(start, curr - start);
|
||||
if (key != "server")
|
||||
throw std::invalid_argument("bad config file arguments 1");
|
||||
@@ -104,13 +103,12 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
|
||||
size_t curr = _content.find_first_not_of(" \t\n", *start);
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.autoindex = false;
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 1");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
// are there other things to check for?
|
||||
while (curr != std::string::npos) // here curr == { + 1
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
@@ -142,9 +140,15 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
size_t curr = *start;
|
||||
// start is after the 1st word aka "location"
|
||||
|
||||
ret.client_body_limit = 0;
|
||||
ret.autoindex = false;
|
||||
ret.redirect_status = 0;
|
||||
ret.allow_methods = 0;
|
||||
|
||||
ret.path = _get_first_word(&curr);
|
||||
// are you sure about this?
|
||||
if (ret.path[0] != '/')
|
||||
ret.path.insert(0, "/");
|
||||
// throw std::invalid_argument("Location path require a leading /");
|
||||
// in theory now curr should be right after the "path"
|
||||
|
||||
curr = _content.find_first_not_of(" \t\n", curr);
|
||||
@@ -152,9 +156,9 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
if (curr == std::string::npos || _content[curr] != '{')
|
||||
throw std::invalid_argument("bad config file syntax 2");
|
||||
|
||||
curr = _content.find_first_of(" \t\n", curr + 1);
|
||||
// if (curr == std::string::npos) // are there other things to check for?
|
||||
// throw std::invalid_argument("bad config file syntax");
|
||||
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos)
|
||||
throw std::invalid_argument("bad config file syntax");
|
||||
// are there other things to check for?
|
||||
while (curr != std::string::npos)
|
||||
{
|
||||
// so this moves curr to past the word...
|
||||
@@ -177,8 +181,6 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
const std::string key, std::string value)
|
||||
{
|
||||
@@ -189,11 +191,12 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
|
||||
if (size < 1)
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "server_name" && size == 1)
|
||||
else if (key == "server_name" && server->server_name.empty())
|
||||
{
|
||||
for (size_t i = 0; i < server->server_name.size(); i++)
|
||||
for (std::vector<std::string>::iterator it = server->server_name.begin(); \
|
||||
it < server->server_name.end(); it++)
|
||||
{
|
||||
if (server->server_name[i].compare(tmp_val[0]) == 0)
|
||||
if (it->compare(tmp_val[0]) == 0)
|
||||
throw std::invalid_argument("server_name already exists");
|
||||
}
|
||||
server->server_name.push_back(tmp_val[0]);
|
||||
@@ -229,19 +232,11 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
}
|
||||
else if (key == "root" && size == 1 && server->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
server->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "autoindex" && size == 1)
|
||||
{
|
||||
// autoindex is a bool, there's no good way for me to see if it has
|
||||
// bet set already
|
||||
server->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& server->client_body_limit == 0)
|
||||
{
|
||||
@@ -252,22 +247,9 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
else if (key == "index")
|
||||
{
|
||||
// i think you can call index several times...
|
||||
// should i be doing an access?
|
||||
// since index is at the root, but root might not yet be defined
|
||||
// will check index later in post
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
server->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && server->allow_methods.empty())
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
server->allow_methods.push_back(m);
|
||||
}
|
||||
}
|
||||
else if (key == "error_page")
|
||||
{
|
||||
|
||||
@@ -292,22 +274,8 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
|
||||
server->error_pages[status_code] = path;
|
||||
}
|
||||
}
|
||||
/* else if (key == "recv_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
// what is tv_sec and do i need it?
|
||||
// ok so i don't fully understand this part but ok, keep for now...
|
||||
server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
else if (key == "send_timeout" && size == 1 && server->server_name == "")
|
||||
{
|
||||
server->send_timeout.tv_sec = atoi(tmp_val[0].c_str());
|
||||
}
|
||||
*/ else
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,48 +291,43 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
throw std::invalid_argument("missing value");
|
||||
else if (key == "root" && size == 1 && location->root == "")
|
||||
{
|
||||
DIR* dir = opendir(tmp_val[0].c_str());
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
else
|
||||
throw std::invalid_argument("root dir could not be opened");
|
||||
// remove trailing /
|
||||
if (tmp_val[0][tmp_val[0].size() - 1] == '/')
|
||||
tmp_val[0].erase(tmp_val[0].size() - 1, 1);
|
||||
location->root = tmp_val[0];
|
||||
}
|
||||
else if (key == "client_body_limit" && size == 1 \
|
||||
&& location->client_body_limit == 0)
|
||||
else if (key == "autoindex" && size == 1)
|
||||
{
|
||||
if (!::isNumeric(tmp_val[0]))
|
||||
throw std::invalid_argument("client_body_limit not a number");
|
||||
location->client_body_limit = atoi(tmp_val[0].c_str());
|
||||
location->autoindex = (tmp_val[0] == "on" ? true : false);
|
||||
std::cout << "in parser " << location->path << " autoindex: " << location->autoindex << '\n';
|
||||
}
|
||||
else if (key == "index")
|
||||
{
|
||||
// what about index /index.html; aka at the root? not handle?
|
||||
// you can definitely call Index several times, i think
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
location->index.push_back(tmp_val[i]);
|
||||
}
|
||||
else if (key == "allow_methods" && location->allow_methods.empty())
|
||||
else if (key == "allow_methods" && location->allow_methods == 0)
|
||||
{
|
||||
for (unsigned long i = 0; i != tmp_val.size(); i++)
|
||||
{
|
||||
http_method m = ::str_to_http_method(tmp_val[i]);
|
||||
if (m == UNKNOWN)
|
||||
throw std::invalid_argument("not a valid method");
|
||||
location->allow_methods.push_back(m);
|
||||
location->allow_methods |= m;
|
||||
}
|
||||
}
|
||||
else if (key == "cgi_info")
|
||||
else if (key == "cgi_ext")
|
||||
{
|
||||
// you can call cgi_info several times i think.
|
||||
// ok wtf is all this even doing, figure that out
|
||||
unsigned long i = value.find_first_of(" ");
|
||||
if (i == std::string::npos)
|
||||
throw std::invalid_argument("bad config file arguments 8");
|
||||
// ok why an int now, we gotta be more consistent!
|
||||
int j = value.find_first_not_of(" ", i);
|
||||
location->cgi_info[value.substr(0, i)] = value.substr(j, value.length());
|
||||
for (size_t i = 0; i < tmp_val.size(); i++)
|
||||
{
|
||||
if (tmp_val[i][0] == '.')
|
||||
throw std::invalid_argument("cgi_ext should not have a leading '.'");
|
||||
location->cgi_ext.push_back(tmp_val[i]);
|
||||
}
|
||||
}
|
||||
else if (key == "return" && location->redirect_status == 0 \
|
||||
else if (key == "redirect" && location->redirect_status == 0 \
|
||||
&& location->redirect_uri == "")
|
||||
{
|
||||
// actually i think there can only be one per location...
|
||||
@@ -372,20 +335,26 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
|
||||
if (tmp_val.size() != 2)
|
||||
throw std::invalid_argument("wrong number of values");
|
||||
// and tmp_val[0] should be a number and tmp_val[1] a string?
|
||||
if (!(::isNumeric(tmp_val[0])))
|
||||
throw std::invalid_argument("value not a number");
|
||||
if (tmp_val[0] != "301" && tmp_val[0] != "302"
|
||||
&& tmp_val[0] != "303" && tmp_val[0] != "307"
|
||||
&& tmp_val[0] != "308")
|
||||
throw std::invalid_argument("bad redirect status");
|
||||
// double check this
|
||||
// it means we aren't allowing internal redirects.
|
||||
if (tmp_val[1].compare(0, 7, "http://")
|
||||
|| tmp_val[1].compare(0, 8, "https://"))
|
||||
throw std::invalid_argument("bad redirect uri");
|
||||
|
||||
// somehow check that tmp_val[1] is a string? or valid? how?
|
||||
// something about using access() to see if
|
||||
location->redirect_status = atoi(tmp_val[0].c_str());
|
||||
location->redirect_uri = tmp_val[1];
|
||||
}
|
||||
else
|
||||
else if (key == "upload_repo" && size == 1 && location->upload_repo == "")
|
||||
{
|
||||
// means either you didn't write the right key, or the value is
|
||||
// missing, or the value has already been filled.
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
// what checks to do?
|
||||
location->upload_repo = tmp_val[0];
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("bad key value pair");
|
||||
}
|
||||
|
||||
|
||||
98
srcs/config/postProcessing.cpp
Normal file
98
srcs/config/postProcessing.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
|
||||
|
||||
#include "ConfigParser.hpp"
|
||||
|
||||
void ConfigParser::_post_processing(std::vector<ServerConfig> *servers)
|
||||
{
|
||||
std::vector<ServerConfig>::iterator it = servers->begin();
|
||||
|
||||
while (it != servers->end())
|
||||
{
|
||||
// host and port are Mandatory
|
||||
if (it->host == "")
|
||||
throw std::invalid_argument("Config file needs a host and port");
|
||||
|
||||
// root is mandatory
|
||||
if (it->root == "")
|
||||
throw std::invalid_argument("Config file needs a root");
|
||||
|
||||
// index is mandatory in Server
|
||||
if (it->index.empty())
|
||||
throw std::invalid_argument("Config file needs an Index");
|
||||
|
||||
if (it->client_body_limit == 0)
|
||||
it->client_body_limit = 5000; // what is the recomended size?
|
||||
|
||||
|
||||
// if error_pages is left empty, we'll use the defaults which
|
||||
// i believe are set elsewhere...
|
||||
|
||||
|
||||
if (!_find_root_path_location(it->locations))
|
||||
{
|
||||
LocationConfig tmp;
|
||||
|
||||
tmp.path = "/";
|
||||
tmp.root = it->root;
|
||||
tmp.index = it->index;
|
||||
tmp.allow_methods = ANY_METHODS;
|
||||
tmp.autoindex = false;
|
||||
tmp.redirect_status = 0;
|
||||
it->locations.push_back(tmp);
|
||||
}
|
||||
|
||||
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
|
||||
|
||||
while (it_l != it->locations.end())
|
||||
{
|
||||
if (it_l->root == "")
|
||||
it_l->root = it->root;
|
||||
|
||||
if (it_l->allow_methods == UNKNOWN)
|
||||
it_l->allow_methods = ANY_METHODS;
|
||||
|
||||
if (it_l->index.empty())
|
||||
it_l->index = it->index;
|
||||
|
||||
// same for redirect status i think
|
||||
|
||||
// maybe do something for Cgi_ext?
|
||||
|
||||
// std::cout << "In Post, Root + Path: " << it_l->root + it_l->path << '\n';
|
||||
/* if (path_is_valid(it_l->root + it_l->path) == 0)
|
||||
{
|
||||
//either we throw or we erase
|
||||
throw std::invalid_argument("location path is invalid");
|
||||
}
|
||||
*/
|
||||
if (path_is_valid(it_l->root + it_l->path) == 1 \
|
||||
&& it_l->path[it_l->path.size() - 1] != '/')
|
||||
it_l->path.push_back('/');
|
||||
|
||||
|
||||
++it_l;
|
||||
}
|
||||
std::sort(it->locations.begin(), it->locations.end());
|
||||
// std::reverse(it->locations.begin(), it->locations.end());
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigParser::_find_root_path_location(std::vector<LocationConfig> locations)
|
||||
{
|
||||
std::vector<LocationConfig>::iterator it = locations.begin();
|
||||
|
||||
while (it != locations.end())
|
||||
{
|
||||
if (it->path.compare("/") == 0)
|
||||
{
|
||||
// std::cout << "in compare: " << it->path << " -- ";
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ int main(int ac, char **av)
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
void throw_test()
|
||||
{
|
||||
static int i = 0;
|
||||
++i;
|
||||
if (i % 8 == 0)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
std::vector<std::string> split(std::string input, char delimiter)
|
||||
{
|
||||
std::vector<std::string> answer;
|
||||
@@ -60,6 +68,9 @@ http_method str_to_http_method(std::string &str)
|
||||
return POST;
|
||||
else if (str == "DELETE")
|
||||
return DELETE;
|
||||
else if (str == "ALL_METHODS") // for Eric: why this test ? can we delete it?
|
||||
return ANY_METHODS;
|
||||
// would prefere ALL_METHODS
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -85,6 +96,32 @@ std::string http_methods_to_str(unsigned int methods)
|
||||
return (str);
|
||||
}
|
||||
|
||||
# include <iostream>
|
||||
|
||||
// you could make this &path...
|
||||
int path_is_valid(std::string path)
|
||||
{
|
||||
const char *tmp_path = path.c_str();
|
||||
struct stat s;
|
||||
|
||||
if (stat(tmp_path, &s) == 0)
|
||||
{
|
||||
if (S_ISREG(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a file\n";
|
||||
return (2);
|
||||
}
|
||||
else if (S_ISDIR(s.st_mode))
|
||||
{
|
||||
// std::cout << "is a Dir\n";
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
// std::cout << "path is neither dir nor file\n";
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr)
|
||||
{
|
||||
if (ori_substr.empty())
|
||||
|
||||
@@ -6,20 +6,12 @@
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <cstdlib> // atoi
|
||||
# include <sys/stat.h> // stat()
|
||||
|
||||
# define CR "\r"
|
||||
# define LF "\n"
|
||||
# define CRLF CR LF
|
||||
|
||||
// enum http_method
|
||||
// {
|
||||
// UNKNOWN = 0b00000000,
|
||||
// GET = 0b00000001,
|
||||
// POST = 0b00000010,
|
||||
// DELETE = 0b00000100,
|
||||
// ANY_METHODS = 0b11111111,
|
||||
// };
|
||||
|
||||
enum http_method
|
||||
{
|
||||
UNKNOWN = 0b0,
|
||||
@@ -27,6 +19,8 @@ enum http_method
|
||||
POST = 1 << 1,
|
||||
DELETE = 1 << 2,
|
||||
ANY_METHODS = 0b11111111,
|
||||
// ALL_METHODS = 0b11111111,
|
||||
// i would prefer this...
|
||||
};
|
||||
|
||||
struct listen_socket
|
||||
@@ -45,6 +39,8 @@ std::string itos(int n);
|
||||
std::string trim(std::string str, char c);
|
||||
http_method str_to_http_method(std::string &str);
|
||||
std::string http_methods_to_str(unsigned int methods);
|
||||
int path_is_valid(std::string path);
|
||||
void replace_all_substr(std::string &str, const std::string &ori_substr, const std::string &new_substr);
|
||||
void throw_test();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
# include <string> // string
|
||||
# include <cstdio> // perror, remove
|
||||
# include <cstdlib> // atoi (athough it's already cover by <string>)
|
||||
# include <dirent.h> // opendir()
|
||||
|
||||
# include "Client.hpp"
|
||||
# include "ServerConfig.hpp"
|
||||
# include "utils.hpp"
|
||||
# include "http_status.hpp"
|
||||
# include "autoindex.hpp"
|
||||
|
||||
extern bool g_run;
|
||||
extern int g_last_signal;
|
||||
@@ -38,6 +40,8 @@ void signal_handler(int signum);
|
||||
# define FAILURE -1
|
||||
# define SUCCESS 1
|
||||
|
||||
# define MIME_TYPE_DEFAULT "application/octet-stream"
|
||||
|
||||
class Webserv
|
||||
{
|
||||
public:
|
||||
@@ -68,29 +72,29 @@ class Webserv
|
||||
void _accept_connection(listen_socket &lsocket);
|
||||
// request.cpp
|
||||
void _request(Client *client);
|
||||
void _read_request(Client *client);
|
||||
int _read_request(Client *client);
|
||||
// response.cpp
|
||||
void _response(Client *client);
|
||||
void _send_response(Client *client, ServerConfig &server);
|
||||
|
||||
int _send_response(Client *client);
|
||||
void _append_base_headers(Client *client);
|
||||
void _construct_response(Client *client, ServerConfig &server);
|
||||
void _process_method(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _construct_response(Client *client);
|
||||
void _process_method(Client *client);
|
||||
void _insert_status_line(Client *client);
|
||||
void _error_html_response(Client *client, ServerConfig &server);
|
||||
void _append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension = "");
|
||||
|
||||
void _get(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _error_html_response(Client *client);
|
||||
void _append_body(Client *client, const std::string &body, const std::string &file_extension = "");
|
||||
ServerConfig *_determine_process_server(Client *client); // cant be const cause of error_pages.operator[]
|
||||
const LocationConfig *_determine_location(const ServerConfig &server, const std::string &path) const;
|
||||
std::string _determine_file_extension(const std::string &path) const;
|
||||
// method_get.cpp
|
||||
void _get(Client *client);
|
||||
void _get_file(Client *client, const std::string &path);
|
||||
|
||||
void _post(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
void _autoindex(Client *client, std::string &path);
|
||||
// method_post.cpp
|
||||
void _post(Client *client);
|
||||
void _post_file(Client *client, const std::string &path);
|
||||
|
||||
void _delete(Client *client, ServerConfig &server, LocationConfig &location);
|
||||
// method_delete.cpp
|
||||
void _delete(Client *client);
|
||||
void _delete_file(Client *client, const std::string &path);
|
||||
|
||||
ServerConfig &_determine_process_server(Client *client);
|
||||
LocationConfig &_determine_location(ServerConfig &server, std::string &path);
|
||||
// cgi_script.cpp
|
||||
bool _is_cgi(Client *client);
|
||||
void _exec_cgi(Client *client);
|
||||
|
||||
32
srcs/webserv/autoindex.hpp
Normal file
32
srcs/webserv/autoindex.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#ifndef AUTOINDEX_HPP
|
||||
# define AUTOINDEX_HPP
|
||||
|
||||
// # define HTML_ERROR(STATUS) "\r\n<!DOCTYPE html><html><head><title>"STATUS"</title></head><body><h1 style=\"text-align:center\">"STATUS"</h1><hr><p style=\"text-align:center\">Le Webserv/0.1</p></body></html>"
|
||||
|
||||
# define AUTOINDEX_START \
|
||||
"<!DOCTYPE html>"\
|
||||
"<html>"\
|
||||
"<head>"\
|
||||
"<title>Index of "
|
||||
|
||||
# define AUTOINDEX_MID1 \
|
||||
"</title>"\
|
||||
"</head>"\
|
||||
"<body>" \
|
||||
"<h1>Index of "
|
||||
|
||||
# define AUTOINDEX_MID2 \
|
||||
"</h1>"\
|
||||
"<hr>"\
|
||||
"<pre>"
|
||||
|
||||
# define AUTOINDEX_END \
|
||||
"</pre>"\
|
||||
"<hr>"\
|
||||
"</body>"\
|
||||
"</html>"
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -30,6 +30,13 @@
|
||||
# define S201 "201 Created"
|
||||
# define S204 "204 No Content"
|
||||
|
||||
# define S301 "301 Moved Permanently"
|
||||
# define S302 "302 Found"
|
||||
# define S303 "303 See Other"
|
||||
# define S304 "304 Not Modified" // unused
|
||||
# define S307 "307 Temporary Redirect"
|
||||
# define S308 "308 Permanent Redirect"
|
||||
|
||||
# define S400 "400 Bad Request"
|
||||
# define S403 "403 Forbidden"
|
||||
# define S404 "404 Not Found"
|
||||
@@ -38,5 +45,6 @@
|
||||
|
||||
# define S500 "500 Internal Server Error"
|
||||
# define S501 "501 Not Implemented"
|
||||
# define S505 "505 HTTP Version Not Supported"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,132 +89,151 @@ void Webserv::_listen(int socket_fd, unsigned int max_connections)
|
||||
|
||||
void Webserv::_init_http_status_map()
|
||||
{
|
||||
_http_status[200] = S200;
|
||||
_http_status[201] = S201;
|
||||
_http_status[204] = S204;
|
||||
/* "map.insert()" over "map.operator[]" :
|
||||
** http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=93
|
||||
*/
|
||||
typedef std::map<int, std::string>::value_type status_pair;
|
||||
|
||||
_http_status[400] = S400;
|
||||
_http_status[403] = S403;
|
||||
_http_status[404] = S404;
|
||||
_http_status[405] = S405;
|
||||
_http_status[413] = S413;
|
||||
// _http_status.insert(std::make_pair(200, S200)); // equivalent
|
||||
_http_status.insert(status_pair(200, S200));
|
||||
_http_status.insert(status_pair(201, S201));
|
||||
_http_status.insert(status_pair(204, S204));
|
||||
|
||||
_http_status[500] = S500;
|
||||
_http_status[501] = S501;
|
||||
_http_status.insert(status_pair(301, S301));
|
||||
_http_status.insert(status_pair(302, S302));
|
||||
_http_status.insert(status_pair(303, S303));
|
||||
_http_status.insert(status_pair(304, S304));
|
||||
_http_status.insert(status_pair(307, S307));
|
||||
_http_status.insert(status_pair(308, S308));
|
||||
|
||||
_http_status.insert(status_pair(400, S400));
|
||||
_http_status.insert(status_pair(403, S403));
|
||||
_http_status.insert(status_pair(404, S404));
|
||||
_http_status.insert(status_pair(405, S405));
|
||||
_http_status.insert(status_pair(413, S413));
|
||||
|
||||
_http_status.insert(status_pair(500, S500));
|
||||
_http_status.insert(status_pair(501, S501));
|
||||
}
|
||||
|
||||
void Webserv::_init_mime_types_map()
|
||||
{
|
||||
_mime_types[""] = "application/octet-stream";
|
||||
/* From :
|
||||
** http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
*/
|
||||
typedef std::map<std::string, std::string>::value_type mime_pair;
|
||||
|
||||
_mime_types["html"] = "text/html";
|
||||
_mime_types["htm"] = "text/html";
|
||||
_mime_types["shtml"] = "text/html";
|
||||
_mime_types["css"] = "text/css";
|
||||
_mime_types["xml"] = "text/xml";
|
||||
_mime_types["gif"] = "image/gif";
|
||||
_mime_types["jpeg"] = "image/jpeg";
|
||||
_mime_types["jpg"] = "image/jpeg";
|
||||
_mime_types["js"] = "application/javascript";
|
||||
_mime_types["atom"] = "application/atom+xml";
|
||||
_mime_types["rss"] = "application/rss+xml";
|
||||
_mime_types.insert(mime_pair("", MIME_TYPE_DEFAULT));
|
||||
|
||||
_mime_types["mml"] = "text/mathml";
|
||||
_mime_types["txt"] = "text/plain";
|
||||
_mime_types["jad"] = "text/vnd.sun.j2me.app-descriptor";
|
||||
_mime_types["wml"] = "text/vnd.wap.wml";
|
||||
_mime_types["htc"] = "text/x-component";
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("html", "text/html"));
|
||||
_mime_types.insert(mime_pair("htm", "text/html"));
|
||||
_mime_types.insert(mime_pair("shtml", "text/html"));
|
||||
_mime_types.insert(mime_pair("css", "text/css"));
|
||||
_mime_types.insert(mime_pair("xml", "text/xml"));
|
||||
_mime_types.insert(mime_pair("gif", "image/gif"));
|
||||
_mime_types.insert(mime_pair("jpeg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("jpg", "image/jpeg"));
|
||||
_mime_types.insert(mime_pair("js", "application/javascript"));
|
||||
_mime_types.insert(mime_pair("atom", "application/atom+xml"));
|
||||
_mime_types.insert(mime_pair("rss", "application/rss+xml"));
|
||||
|
||||
_mime_types["png"] = "image/png";
|
||||
_mime_types["tif"] = "image/tiff";
|
||||
_mime_types["tiff"] = "image/tiff";
|
||||
_mime_types["wbmp"] = "image/vnd.wap.wbmp";
|
||||
_mime_types["ico"] = "image/x-icon";
|
||||
_mime_types["jng"] = "image/x-jng";
|
||||
_mime_types["bmp"] = "image/x-ms-bmp";
|
||||
_mime_types["svg"] = "image/svg+xml";
|
||||
_mime_types["svgz"] = "image/svg+xml";
|
||||
_mime_types["webp"] = "image/webp";
|
||||
_mime_types.insert(mime_pair("mml", "text/mathml"));
|
||||
_mime_types.insert(mime_pair("txt", "text/plain"));
|
||||
_mime_types.insert(mime_pair("jad", "text/vnd.sun.j2me.app-descriptor"));
|
||||
_mime_types.insert(mime_pair("wml", "text/vnd.wap.wml"));
|
||||
_mime_types.insert(mime_pair("htc", "text/x-component"));
|
||||
|
||||
_mime_types["woff"] = "application/font-woff";
|
||||
_mime_types["jar"] = "application/java-archive";
|
||||
_mime_types["war"] = "application/java-archive";
|
||||
_mime_types["ear"] = "application/java-archive";
|
||||
_mime_types["json"] = "application/json";
|
||||
_mime_types["hqx"] = "application/mac-binhex40";
|
||||
_mime_types["doc"] = "application/msword";
|
||||
_mime_types["pdf"] = "application/pdf";
|
||||
_mime_types["ps"] = "application/postscript";
|
||||
_mime_types["eps"] = "application/postscript";
|
||||
_mime_types["ai"] = "application/postscript";
|
||||
_mime_types["rtf"] = "application/rtf";
|
||||
_mime_types["m3u8"] = "application/vnd.apple.mpegurl";
|
||||
_mime_types["xls"] = "application/vnd.ms-excel";
|
||||
_mime_types["eot"] = "application/vnd.ms-fontobject";
|
||||
_mime_types["ppt"] = "application/vnd.ms-powerpoint";
|
||||
_mime_types["wmlc"] = "application/vnd.wap.wmlc";
|
||||
_mime_types["kml"] = "application/vnd.google-earth.kml+xml";
|
||||
_mime_types["kmz"] = "application/vnd.google-earth.kmz";
|
||||
_mime_types["7z"] = "application/x-7z-compressed";
|
||||
_mime_types["cco"] = "application/x-cocoa";
|
||||
_mime_types["jardiff"] = "application/x-java-archive-diff";
|
||||
_mime_types["jnlp"] = "application/x-java-jnlp-file";
|
||||
_mime_types["run"] = "application/x-makeself";
|
||||
_mime_types["pl"] = "application/x-perl";
|
||||
_mime_types["pm"] = "application/x-perl";
|
||||
_mime_types["prc"] = "application/x-pilot";
|
||||
_mime_types["pdb"] = "application/x-pilot";
|
||||
_mime_types["rar"] = "application/x-rar-compressed";
|
||||
_mime_types["rpm"] = "application/x-redhat-package-manager";
|
||||
_mime_types["sea"] = "application/x-sea";
|
||||
_mime_types["swf"] = "application/x-shockwave-flash";
|
||||
_mime_types["sit"] = "application/x-stuffit";
|
||||
_mime_types["tcl"] = "application/x-tcl";
|
||||
_mime_types["tk"] = "application/x-tcl";
|
||||
_mime_types["der"] = "application/x-x509-ca-cert";
|
||||
_mime_types["pem"] = "application/x-x509-ca-cert";
|
||||
_mime_types["crt"] = "application/x-x509-ca-cert";
|
||||
_mime_types["xpi"] = "application/x-xpinstall";
|
||||
_mime_types["xhtml"] = "application/xhtml+xml";
|
||||
_mime_types["xspf"] = "application/xspf+xml";
|
||||
_mime_types["zip"] = "application/zip";
|
||||
_mime_types.insert(mime_pair("png", "image/png"));
|
||||
_mime_types.insert(mime_pair("tif", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("tiff", "image/tiff"));
|
||||
_mime_types.insert(mime_pair("wbmp", "image/vnd.wap.wbmp"));
|
||||
_mime_types.insert(mime_pair("ico", "image/x-icon"));
|
||||
_mime_types.insert(mime_pair("jng", "image/x-jng"));
|
||||
_mime_types.insert(mime_pair("bmp", "image/x-ms-bmp"));
|
||||
_mime_types.insert(mime_pair("svg", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("svgz", "image/svg+xml"));
|
||||
_mime_types.insert(mime_pair("webp", "image/webp"));
|
||||
|
||||
_mime_types["bin"] = "application/octet-stream";
|
||||
_mime_types["exe"] = "application/octet-stream";
|
||||
_mime_types["dll"] = "application/octet-stream";
|
||||
_mime_types["deb"] = "application/octet-stream";
|
||||
_mime_types["dmg"] = "application/octet-stream";
|
||||
_mime_types["iso"] = "application/octet-stream";
|
||||
_mime_types["img"] = "application/octet-stream";
|
||||
_mime_types["msi"] = "application/octet-stream";
|
||||
_mime_types["msp"] = "application/octet-stream";
|
||||
_mime_types["msm"] = "application/octet-stream";
|
||||
_mime_types.insert(mime_pair("woff", "application/font-woff"));
|
||||
_mime_types.insert(mime_pair("jar", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("war", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("ear", "application/java-archive"));
|
||||
_mime_types.insert(mime_pair("json", "application/json"));
|
||||
_mime_types.insert(mime_pair("hqx", "application/mac-binhex40"));
|
||||
_mime_types.insert(mime_pair("doc", "application/msword"));
|
||||
_mime_types.insert(mime_pair("pdf", "application/pdf"));
|
||||
_mime_types.insert(mime_pair("ps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("eps", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("ai", "application/postscript"));
|
||||
_mime_types.insert(mime_pair("rtf", "application/rtf"));
|
||||
_mime_types.insert(mime_pair("m3u8", "application/vnd.apple.mpegurl"));
|
||||
_mime_types.insert(mime_pair("xls", "application/vnd.ms-excel"));
|
||||
_mime_types.insert(mime_pair("eot", "application/vnd.ms-fontobject"));
|
||||
_mime_types.insert(mime_pair("ppt", "application/vnd.ms-powerpoint"));
|
||||
_mime_types.insert(mime_pair("wmlc", "application/vnd.wap.wmlc"));
|
||||
_mime_types.insert(mime_pair("kml", "application/vnd.google-earth.kml+xml"));
|
||||
_mime_types.insert(mime_pair("kmz", "application/vnd.google-earth.kmz"));
|
||||
_mime_types.insert(mime_pair("7z", "application/x-7z-compressed"));
|
||||
_mime_types.insert(mime_pair("cco", "application/x-cocoa"));
|
||||
_mime_types.insert(mime_pair("jardiff", "application/x-java-archive-diff"));
|
||||
_mime_types.insert(mime_pair("jnlp", "application/x-java-jnlp-file"));
|
||||
_mime_types.insert(mime_pair("run", "application/x-makeself"));
|
||||
_mime_types.insert(mime_pair("pl", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("pm", "application/x-perl"));
|
||||
_mime_types.insert(mime_pair("prc", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("pdb", "application/x-pilot"));
|
||||
_mime_types.insert(mime_pair("rar", "application/x-rar-compressed"));
|
||||
_mime_types.insert(mime_pair("rpm", "application/x-redhat-package-manager"));
|
||||
_mime_types.insert(mime_pair("sea", "application/x-sea"));
|
||||
_mime_types.insert(mime_pair("swf", "application/x-shockwave-flash"));
|
||||
_mime_types.insert(mime_pair("sit", "application/x-stuffit"));
|
||||
_mime_types.insert(mime_pair("tcl", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("tk", "application/x-tcl"));
|
||||
_mime_types.insert(mime_pair("der", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("pem", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("crt", "application/x-x509-ca-cert"));
|
||||
_mime_types.insert(mime_pair("xpi", "application/x-xpinstall"));
|
||||
_mime_types.insert(mime_pair("xhtml", "application/xhtml+xml"));
|
||||
_mime_types.insert(mime_pair("xspf", "application/xspf+xml"));
|
||||
_mime_types.insert(mime_pair("zip", "application/zip"));
|
||||
|
||||
_mime_types["docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
_mime_types["xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
_mime_types["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
_mime_types.insert(mime_pair("bin", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("exe", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dll", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("deb", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("dmg", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("iso", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("img", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msi", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msp", "application/octet-stream"));
|
||||
_mime_types.insert(mime_pair("msm", "application/octet-stream"));
|
||||
|
||||
_mime_types["mid"] = "audio/midi";
|
||||
_mime_types["midi"] = "audio/midi";
|
||||
_mime_types["kar"] = "audio/midi";
|
||||
_mime_types["mp3"] = "audio/mpeg";
|
||||
_mime_types["ogg"] = "audio/ogg";
|
||||
_mime_types["m4a"] = "audio/x-m4a";
|
||||
_mime_types["ra"] = "audio/x-realaudio";
|
||||
_mime_types.insert(mime_pair("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"));
|
||||
_mime_types.insert(mime_pair("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||
_mime_types.insert(mime_pair("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"));
|
||||
|
||||
_mime_types["3gpp"] = "video/3gpp";
|
||||
_mime_types["3gp"] = "video/3gpp";
|
||||
_mime_types["ts"] = "video/mp2t";
|
||||
_mime_types["mp4"] = "video/mp4";
|
||||
_mime_types["mpeg"] = "video/mpeg";
|
||||
_mime_types["mpg"] = "video/mpeg";
|
||||
_mime_types["mov"] = "video/quicktime";
|
||||
_mime_types["webm"] = "video/webm";
|
||||
_mime_types["flv"] = "video/x-flv";
|
||||
_mime_types["m4v"] = "video/x-m4v";
|
||||
_mime_types["mng"] = "video/x-mng";
|
||||
_mime_types["asx"] = "video/x-ms-asf";
|
||||
_mime_types["asf"] = "video/x-ms-asf";
|
||||
_mime_types["wmv"] = "video/x-ms-wmv";
|
||||
_mime_types["avi"] = "video/x-msvideo";
|
||||
_mime_types.insert(mime_pair("mid", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("midi", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("kar", "audio/midi"));
|
||||
_mime_types.insert(mime_pair("mp3", "audio/mpeg"));
|
||||
_mime_types.insert(mime_pair("ogg", "audio/ogg"));
|
||||
_mime_types.insert(mime_pair("m4a", "audio/x-m4a"));
|
||||
_mime_types.insert(mime_pair("ra", "audio/x-realaudio"));
|
||||
|
||||
_mime_types.insert(mime_pair("3gpp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("3gp", "video/3gpp"));
|
||||
_mime_types.insert(mime_pair("ts", "video/mp2t"));
|
||||
_mime_types.insert(mime_pair("mp4", "video/mp4"));
|
||||
_mime_types.insert(mime_pair("mpeg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mpg", "video/mpeg"));
|
||||
_mime_types.insert(mime_pair("mov", "video/quicktime"));
|
||||
_mime_types.insert(mime_pair("webm", "video/webm"));
|
||||
_mime_types.insert(mime_pair("flv", "video/x-flv"));
|
||||
_mime_types.insert(mime_pair("m4v", "video/x-m4v"));
|
||||
_mime_types.insert(mime_pair("mng", "video/x-mng"));
|
||||
_mime_types.insert(mime_pair("asx", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("asf", "video/x-ms-asf"));
|
||||
_mime_types.insert(mime_pair("wmv", "video/x-ms-wmv"));
|
||||
_mime_types.insert(mime_pair("avi", "video/x-msvideo"));
|
||||
}
|
||||
|
||||
40
srcs/webserv/method_delete.cpp
Normal file
40
srcs/webserv/method_delete.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_delete(Client *client)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
std::string path = client->get_path();
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
189
srcs/webserv/method_get.cpp
Normal file
189
srcs/webserv/method_get.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
void Webserv::_get(Client *client)
|
||||
{
|
||||
/* RULES **
|
||||
|
||||
if path is a valid dir check if index is specified and serve that
|
||||
if no index and autoindex, server that
|
||||
if file, server that!
|
||||
|
||||
Where does cgi fit in in all this ???
|
||||
|
||||
|
||||
*/
|
||||
std::string path = client->get_path();
|
||||
|
||||
// this might not be the best thing, a voir
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
std::cerr << "path = " << path << "\n";
|
||||
|
||||
// path = root + location.path
|
||||
// we will tack on an index if there is a valid one
|
||||
// or autoindex if allowed
|
||||
// or let _get_file sort out the error otherwise.
|
||||
|
||||
if (path_is_valid(path) == 1)
|
||||
{
|
||||
// std::cout << "path is valid\n";
|
||||
if (path[path.size() - 1] != '/')
|
||||
path.push_back('/');
|
||||
for (size_t i = 0; i < client->assigned_location->index.size(); i++)
|
||||
{
|
||||
// std::cout << "location path: " << client->assigned_location->path << '\n';
|
||||
// std::cout << "location index: " << client->assigned_location->index[i] << '\n';
|
||||
// std::cout << "path with index: " << path + assigned_location->index[i] << '\n';
|
||||
if (path_is_valid(path + client->assigned_location->index[i]) == 2)
|
||||
{
|
||||
// std::cout << "found a valid index\n";
|
||||
path.append(client->assigned_location->index[i]);
|
||||
_get_file(client, path);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (client->assigned_location->autoindex == true)
|
||||
{
|
||||
_autoindex(client, path);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
// else
|
||||
// _get_file(client, path);
|
||||
// what about cgi ???
|
||||
|
||||
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
if (_is_cgi(client))
|
||||
{
|
||||
_exec_cgi(client);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
# define MAX_FILESIZE 1000000 // (1Mo)
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
/*
|
||||
std::ios::binary
|
||||
https://gcc.gnu.org/onlinedocs/libstdc++/manual/fstreams.html#std.io.filestreams.binary
|
||||
tldr : its seems to not be so simple to do read/write of binary file in a portable way.
|
||||
*/
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
std::stringstream buf;
|
||||
|
||||
std::cout << "made it to get_file\n";
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::ate);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::streampos size = ifd.tellg();
|
||||
|
||||
// WIP : 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();
|
||||
if (!ifd || !buf)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
std::string file_ext = _determine_file_extension(path);
|
||||
_append_body(client, buf.str(), file_ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// i only sort of need &path...
|
||||
// def can improve but works for now...
|
||||
//void Webserv::_autoindex(Client *client, LocationConfig &location, std::string &path)
|
||||
void Webserv::_autoindex(Client *client, std::string &path)
|
||||
{
|
||||
// std::cout << "made it to _autoindex\n";
|
||||
|
||||
(void)path;
|
||||
std::string dir_list;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
// std::cout << "location root: " << client->assigned_location->root << " location path: "
|
||||
// << client->assigned_location->path << '\n';
|
||||
|
||||
// if ((dir = opendir (path.c_str())) != NULL)
|
||||
if ((dir = opendir ((client->assigned_location->root + client->assigned_location->path).c_str())) != NULL)
|
||||
{
|
||||
/* print all the files and directories within directory */
|
||||
dir_list.append(AUTOINDEX_START);
|
||||
dir_list.append(client->assigned_location->path);
|
||||
dir_list.append(AUTOINDEX_MID1);
|
||||
dir_list.append(client->assigned_location->path);
|
||||
dir_list.append(AUTOINDEX_MID2);
|
||||
while ((ent = readdir (dir)) != NULL)
|
||||
{
|
||||
if (strcmp(".", ent->d_name) == 0)
|
||||
continue ;
|
||||
dir_list.append("<a href=\"");
|
||||
dir_list.append(client->assigned_location->path.c_str());
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("\">");
|
||||
dir_list.append(ent->d_name);
|
||||
dir_list.append("</a>");
|
||||
dir_list.append("\r\n"); // is this right?
|
||||
}
|
||||
|
||||
// <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);
|
||||
// std::cout << "\n\n" << dir_list << '\n';
|
||||
closedir (dir);
|
||||
_append_body(client, dir_list, "html");
|
||||
}
|
||||
else
|
||||
{
|
||||
// in theory not possible cuz we already checked...
|
||||
/* could not open directory */
|
||||
// perror ("");
|
||||
std::cout << "could not open dir\n";
|
||||
return ;
|
||||
}
|
||||
}
|
||||
66
srcs/webserv/method_post.cpp
Normal file
66
srcs/webserv/method_post.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
|
||||
void Webserv::_post(Client *client)
|
||||
{
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
std::string path = client->get_path();
|
||||
path.insert(0, client->assigned_location->root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Content-Length useless at this point ?
|
||||
// Maybe usefull in _read_request() for rejecting too big content.
|
||||
// Need to _determine_process_server() as soon as possible,
|
||||
// like in _read_request() for stopping read if body is too big ?
|
||||
ofd << client->get_body();
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,38 @@
|
||||
#include "Webserv.hpp"
|
||||
|
||||
#define BUFSIZE 8192
|
||||
#define MAX_HEADER_SIZE 42000 // arbitrary
|
||||
|
||||
enum read_return
|
||||
{
|
||||
READ_IN_PROGRESS,
|
||||
READ_COMPLETE,
|
||||
READ_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_request(Client *client)
|
||||
{
|
||||
_read_request(client);
|
||||
int ret = _read_request(client);
|
||||
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == READ_CLOSE)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
}
|
||||
else if (ret == READ_COMPLETE)
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_read_request(Client *client)
|
||||
int Webserv::_read_request(Client *client) // Messy, Need refactoring
|
||||
{
|
||||
char buf[BUFSIZE+1];
|
||||
char buf[BUFSIZE];
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "call recv()" << "\n" ;
|
||||
ret = ::recv(client->fd, buf, BUFSIZE, 0);
|
||||
std::cerr << "recv() on fd(" << client->fd << ") returned = " << ret << "\n" ;
|
||||
if (ret == -1)
|
||||
@@ -22,23 +41,65 @@ void Webserv::_read_request(Client *client)
|
||||
std::perror("err recv()");
|
||||
std::cerr << "client ptr =" << client << "\n"; // DEBUG
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
return READ_CLOSE;
|
||||
}
|
||||
if (ret == 0) // Not sure what to do in case of 0. Just close ?
|
||||
if (ret == 0)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
std::cerr << "recv() read 0, then close client" << "\n"; // DEBUG
|
||||
return READ_CLOSE;
|
||||
}
|
||||
/*
|
||||
if (ret == BUFSIZE)
|
||||
// send error like "request too long" to client
|
||||
*/
|
||||
|
||||
buf[ret] = '\0';
|
||||
client->raw_request.append(buf);
|
||||
client->parse_request();
|
||||
client->raw_request.append(buf, ret);
|
||||
if (!client->header_complete)
|
||||
{
|
||||
if (client->raw_request.find(CRLF CRLF) != std::string::npos)
|
||||
{
|
||||
// std::cerr << "Raw_request :\n|||||||||||||||||||||||||||||\n " << client->raw_request << "\n|||||||||||||||||||||||||||||\n"; // DEBUG
|
||||
client->header_complete = true;
|
||||
client->parse_request(); // TODO : split function to avoid useless parsing ?
|
||||
if (client->status) // WIP, need to change client->parse_request() for status update
|
||||
return READ_COMPLETE;
|
||||
client->assigned_server = _determine_process_server(client);
|
||||
client->assigned_location = _determine_location(*client->assigned_server, client->get_path());
|
||||
if (client->get_version().compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
|
||||
{ // TODO : move in Client parsing ?
|
||||
client->status = 505;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
if (!client->get_headers("Content-Length").empty()
|
||||
&& ::atoi(client->get_headers("Content-Length").c_str()) > (int)client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
else if (client->raw_request.size() > MAX_HEADER_SIZE)
|
||||
{
|
||||
client->status = 400;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
else if (client->header_complete)
|
||||
{
|
||||
client->read_body_size += ret;
|
||||
if (client->read_body_size > client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
if ((int)client->read_body_size >= ::atoi(client->get_headers("Content-Length").c_str()))
|
||||
{
|
||||
client->parse_request(); // reparse for the body
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
_epoll_update(client->fd, EPOLLOUT, EPOLL_CTL_MOD);
|
||||
|
||||
if (client->header_complete && client->get_headers("Content-Type").empty() && client->get_headers("Content-Length").empty() )
|
||||
{
|
||||
return READ_COMPLETE;
|
||||
}
|
||||
|
||||
return READ_IN_PROGRESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,60 @@
|
||||
|
||||
#include "Webserv.hpp"
|
||||
|
||||
enum send_return
|
||||
{
|
||||
SEND_IN_PROGRESS, // unused
|
||||
SEND_COMPLETE,
|
||||
SEND_CLOSE,
|
||||
};
|
||||
|
||||
void Webserv::_response(Client *client)
|
||||
{
|
||||
client->status = 200; // default value
|
||||
int ret = _send_response(client);
|
||||
|
||||
ServerConfig &server = _determine_process_server(client);
|
||||
_send_response(client, server);
|
||||
if (g_last_signal)
|
||||
_handle_last_signal();
|
||||
|
||||
if (ret == SEND_CLOSE)
|
||||
{
|
||||
_close_client(client->fd);
|
||||
}
|
||||
else if (ret == SEND_COMPLETE)
|
||||
{
|
||||
if (client->get_headers("Connection") == "close")
|
||||
_close_client(client->fd);
|
||||
else
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_send_response(Client *client, ServerConfig &server)
|
||||
int Webserv::_send_response(Client *client)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
std::cerr << "send()\n";
|
||||
|
||||
_append_base_headers(client);
|
||||
_construct_response(client, server);
|
||||
if (!client->status)
|
||||
_construct_response(client);
|
||||
_insert_status_line(client);
|
||||
if (client->status >= 400)
|
||||
_error_html_response(client, server);
|
||||
_error_html_response(client);
|
||||
|
||||
std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
|
||||
ret = ::send(client->fd, client->response.c_str(), client->response.size(), 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
return SEND_CLOSE;
|
||||
}
|
||||
std::cerr << "ret send() = " << ret << "\n"; // DEBUG
|
||||
|
||||
// Body send (WIP for working binary files)
|
||||
if (client->body_size)
|
||||
{
|
||||
ret = ::send(client->fd, client->buf, client->body_size, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
std::perror("err send()");
|
||||
std::cerr << "client.fd =" << client->fd << "\n"; // DEBUG
|
||||
_close_client(client->fd);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->get_headers("Connection") == "close")
|
||||
_close_client(client->fd);
|
||||
else
|
||||
{
|
||||
_epoll_update(client->fd, EPOLLIN, EPOLL_CTL_MOD);
|
||||
client->clear();
|
||||
}
|
||||
return SEND_COMPLETE;
|
||||
}
|
||||
|
||||
void Webserv::_append_base_headers(Client *client)
|
||||
@@ -64,38 +67,53 @@ void Webserv::_append_base_headers(Client *client)
|
||||
client->response.append("Connection: keep-alive" CRLF);
|
||||
}
|
||||
|
||||
void Webserv::_construct_response(Client *client, ServerConfig &server)
|
||||
void Webserv::_construct_response(Client *client)
|
||||
{
|
||||
// TODO : Move this in read(), stop read if content too large
|
||||
if (client->get_body().size() > server.client_body_limit)
|
||||
if (client->get_body().size() > client->assigned_server->client_body_limit)
|
||||
{
|
||||
client->status = 413;
|
||||
return;
|
||||
}
|
||||
LocationConfig &location = _determine_location(server, client->get_path());
|
||||
_process_method(client, server, location);
|
||||
/* if (client->assigned_location->redirect_status)
|
||||
{
|
||||
// (for codes 301, 302, 303, 307, and 308)
|
||||
client->status = client->assigned_location->redirect_status;
|
||||
client->response.append("Location: ");
|
||||
client->response.append(client->assigned_location->redirect_uri);
|
||||
client->response.append(CRLF);
|
||||
} */
|
||||
if (client->get_path().find("redirect_test") != std::string::npos) // Test block
|
||||
{ // Weird behavior. The web browser seems to wait for a complete response until timeout.
|
||||
// (for codes 301, 302, 303, 307, and 308)
|
||||
client->status = 307;
|
||||
client->response.append("Location: ");
|
||||
client->response.append("https://www.rfc-editor.org/rfc/rfc3875#section-3.3");
|
||||
client->response.append(CRLF);
|
||||
client->response.append(CRLF);
|
||||
return ;
|
||||
}
|
||||
_process_method(client);
|
||||
}
|
||||
|
||||
void Webserv::_process_method(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
void Webserv::_process_method(Client *client)
|
||||
{
|
||||
unsigned int allow_methods = ANY_METHODS; // TEMP VARIABLE
|
||||
// after update in ConfigParser, use the "allow_methods" of location.
|
||||
// TODO in ConfigParser : by default if no field in config file, "allow_methods" must be set to ANY_METHODS
|
||||
|
||||
std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
|
||||
std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug
|
||||
if (client->get_method() == UNKNOWN)
|
||||
{
|
||||
client->status = 501;
|
||||
}
|
||||
else if (allow_methods & client->get_method())
|
||||
else if (client->assigned_location->allow_methods & client->get_method())
|
||||
{
|
||||
switch (client->get_method())
|
||||
{
|
||||
case (GET):
|
||||
_get(client, server, location); break;
|
||||
_get(client); break;
|
||||
case (POST):
|
||||
_post(client, server, location); break;
|
||||
_post(client); break;
|
||||
case (DELETE):
|
||||
_delete(client, server, location); break;
|
||||
_delete(client); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -104,7 +122,7 @@ void Webserv::_process_method(Client *client, ServerConfig &server, LocationConf
|
||||
{
|
||||
client->status = 405;
|
||||
client->response.append("Allow: ");
|
||||
client->response.append(::http_methods_to_str(allow_methods));
|
||||
client->response.append(::http_methods_to_str(client->assigned_location->allow_methods));
|
||||
client->response.append(CRLF);
|
||||
}
|
||||
}
|
||||
@@ -119,241 +137,43 @@ void Webserv::_insert_status_line(Client *client)
|
||||
client->response.insert(0, status_line);
|
||||
}
|
||||
|
||||
void Webserv::_error_html_response(Client *client, ServerConfig &server)
|
||||
void Webserv::_error_html_response(Client *client)
|
||||
{
|
||||
if (server.error_pages[client->status].empty())
|
||||
if (!client->assigned_server || client->assigned_server->error_pages[client->status].empty())
|
||||
{
|
||||
std::string html_page = HTML_ERROR;
|
||||
::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
|
||||
_append_body(client, html_page.c_str(), html_page.size(), "html");
|
||||
_append_body(client, html_page, "html");
|
||||
}
|
||||
else
|
||||
_get_file(client, server.error_pages[client->status]);
|
||||
_get_file(client, client->assigned_server->error_pages[client->status]);
|
||||
}
|
||||
|
||||
#define INDEX "index.html" // temp wip
|
||||
void Webserv::_get(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
void Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
std::string path = client->get_path();
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
|
||||
if (path == "/") // TODO : index and autoindex
|
||||
path.append(INDEX);
|
||||
path.insert(0, location.root);
|
||||
|
||||
std::cerr << "path = " << path << "\n";
|
||||
|
||||
// TMP HUGO
|
||||
//
|
||||
if (_is_cgi(client))
|
||||
{
|
||||
_exec_cgi(client);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// END TMP HUGO
|
||||
|
||||
_get_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_get_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ifstream ifd; // For chunk, ifstream directly in struct CLient for multiples read without close() ?
|
||||
// char buf[MAX_FILESIZE+1];
|
||||
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.open(path.c_str(), std::ios::binary | std::ios::ate); // std::ios::binary (binary for files like images ?)
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
client->response.append("Content-Type: ");
|
||||
if (mime_type.empty())
|
||||
client->response.append(MIME_TYPE_DEFAULT);
|
||||
else
|
||||
{
|
||||
std::streampos size = ifd.tellg();
|
||||
|
||||
// WIP : 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";
|
||||
ifd.close();
|
||||
return ;
|
||||
}
|
||||
|
||||
ifd.seekg(0, std::ios::beg);
|
||||
ifd.read(client->buf, size);
|
||||
if (!ifd)
|
||||
{
|
||||
std::cerr << path << ": ifd.read fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 200;
|
||||
client->buf[ifd.gcount()] = '\0';
|
||||
|
||||
std::string file_ext = "";
|
||||
size_t dot_pos = path.rfind(".");
|
||||
std::cerr << "dot_pos = " << dot_pos << "\n";
|
||||
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
|
||||
file_ext = path.substr(dot_pos + 1);
|
||||
std::cerr << "file_ext = " << file_ext << "\n";
|
||||
|
||||
client->body_size = ifd.gcount();
|
||||
// WIP, pass empty body argument because append to string mess up binary file
|
||||
_append_body(client, "", client->body_size, file_ext);
|
||||
}
|
||||
|
||||
ifd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_append_body(Client *client, const char *body, size_t body_size, const std::string &file_extension)
|
||||
{
|
||||
/*
|
||||
TODO : determine Content-Type
|
||||
how ? read the body ?
|
||||
or before in other way (like based and file extension) and pass here as argument ?
|
||||
http://nginx.org/en/docs/http/ngx_http_core_module.html#types
|
||||
Need to look "conf/mime.types" of nginx. Maybe make a map<> based on that.
|
||||
*/
|
||||
const std::string &mime_type = _mime_types[file_extension];
|
||||
client->response.append("Content-Type: ");
|
||||
client->response.append(mime_type);
|
||||
if (mime_type.find("text/") != std::string::npos)
|
||||
client->response.append("; charset=UTF-8");
|
||||
client->response.append(CRLF);
|
||||
}
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body_size);
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
client->response.append("Content-Length: ");
|
||||
std::string tmp = ::itos(body.size());
|
||||
client->response.append(tmp);
|
||||
client->response.append(CRLF);
|
||||
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
client->response.append(CRLF);
|
||||
client->response.append(body);
|
||||
}
|
||||
|
||||
void Webserv::_post(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-post
|
||||
*/
|
||||
std::string path = client->get_path();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_post_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_post_file(Client *client, const std::string &path)
|
||||
{
|
||||
std::ofstream ofd;
|
||||
|
||||
bool file_existed;
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
file_existed = false;
|
||||
else
|
||||
file_existed = true;
|
||||
|
||||
// How to determine status 403 for file that dont already exist ?
|
||||
if (file_existed && access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
ofd.open(path.c_str(), std::ios::binary | std::ios::trunc);
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.open fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used body.size() so Content-Length useless at this point ?
|
||||
// Maybe usefull in _read_request() for rejecting too big content.
|
||||
// Need to _determine_process_server() as soon as possible,
|
||||
// like in _read_request() for stopping read if body is too big ?
|
||||
ofd.write(client->get_body().c_str(), client->get_body().size());
|
||||
if (!ofd)
|
||||
{
|
||||
std::cerr << path << ": ofd.write fail" << '\n';
|
||||
client->status = 500;
|
||||
}
|
||||
else if (file_existed)
|
||||
{
|
||||
client->status = 200;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = 201;
|
||||
// WIP https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.3-4
|
||||
}
|
||||
|
||||
ofd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Webserv::_delete(Client *client, ServerConfig &server, LocationConfig &location)
|
||||
{
|
||||
(void)server; // To remove from arg if we determine its useless
|
||||
/*
|
||||
WIP
|
||||
https://www.rfc-editor.org/rfc/rfc9110.html#name-delete
|
||||
*/
|
||||
std::string path = client->get_path();
|
||||
path.insert(0, location.root);
|
||||
|
||||
/* CGI Here ? */
|
||||
|
||||
_delete_file(client, path);
|
||||
}
|
||||
|
||||
void Webserv::_delete_file(Client *client, const std::string &path)
|
||||
{
|
||||
if (access(path.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 404;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (access(path.c_str(), W_OK) == -1)
|
||||
{
|
||||
std::perror("err access()");
|
||||
client->status = 403;
|
||||
return ;
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
{
|
||||
std::perror("err remove()");
|
||||
client->status = 500;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
ServerConfig &Webserv::_determine_process_server(Client *client)
|
||||
ServerConfig *Webserv::_determine_process_server(Client *client)
|
||||
{
|
||||
/*
|
||||
http://nginx.org/en/docs/http/request_processing.html
|
||||
@@ -378,29 +198,71 @@ ServerConfig &Webserv::_determine_process_server(Client *client)
|
||||
++it;
|
||||
}
|
||||
if (it != _servers.end())
|
||||
return (*it);
|
||||
return (&(*it));
|
||||
else
|
||||
return (*default_server);
|
||||
return (&(*default_server));
|
||||
}
|
||||
|
||||
LocationConfig &Webserv::_determine_location(ServerConfig &server, std::string &path)
|
||||
const LocationConfig *Webserv::_determine_location(const ServerConfig &server, const std::string &path) const
|
||||
{
|
||||
/*
|
||||
Assume there is at least one location in vector<LocationConfig> for path "/"
|
||||
TODO in ConfigParser :
|
||||
If no location block in config file, one need to be generated
|
||||
for path "/", and filled with fields "root" and "index" based on parent server block
|
||||
std::cout << "determin location path sent: " << path << '\n';
|
||||
|
||||
|
||||
/// NO FUCKING IDEA WHY BUT...
|
||||
// basically if 2 strings are identical to a point, compare from
|
||||
// longer one or it'll freak out cuz of \0 or something idk
|
||||
//// Basically: str.compare() from the larger string...
|
||||
|
||||
/* RULES ***
|
||||
|
||||
If a path coresponds exactly to a location, use that one
|
||||
if no path coresponds then use the most correct one
|
||||
most correct means the most precise branch that is still above
|
||||
the point we are aiming for
|
||||
|
||||
*/
|
||||
|
||||
std::vector<LocationConfig>::iterator it = server.locations.begin();
|
||||
while (it != server.locations.end())
|
||||
std::vector<LocationConfig>::const_iterator best;
|
||||
std::cout << "\nMade it to weird location picker case.\n";
|
||||
|
||||
for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
|
||||
{
|
||||
if (it->path.compare(0, path.size(), path))
|
||||
break;
|
||||
++it;
|
||||
std::cout << it->path << " -- ";
|
||||
// if (rit->path.size() > path.size())
|
||||
/* if ((rit->path[rit->path.size() - 1] == '/' ? rit->path.size() : rit->path.size() - 1) > path.size())
|
||||
{
|
||||
std::cout << "skipping this one\n";
|
||||
continue ;
|
||||
}
|
||||
*/
|
||||
// OK I REALLY DON"T LOVE THIS PART, BUT IT DOES WORK FOR NOW...
|
||||
// if (it->path[it->path.size() - 1] == '/'
|
||||
// && it->path.compare(0, it->path.size(), path + "/") == 0)
|
||||
// HOLD ON THIS MIGHT BE GOOD, BUT I COULD USE SOME HELP...
|
||||
if (it->path[it->path.size() - 1] == '/'
|
||||
&& it->path.compare(0, it->path.size() - 1, path) == 0)
|
||||
{
|
||||
best = it;
|
||||
std::cout << "Picked a best! 1\n";
|
||||
}
|
||||
// int comp = path.compare(0, rit->path.size(), rit->path);
|
||||
//int comp = rit->path.compare(0, rit->path.size() - 1, path);
|
||||
// std::cout << "rit path size: " << rit->path.size() << " comp: " << comp << '\n';
|
||||
// if (rit->path.compare(0, rit->path.size(), path) == 0)
|
||||
// if (comp == 0)
|
||||
if (path.compare(0, it->path.size(), it->path) == 0)
|
||||
{
|
||||
best = it;
|
||||
std::cout << "Picked a best! 2\n";
|
||||
}
|
||||
}
|
||||
if (it != server.locations.end())
|
||||
return (*it);
|
||||
else
|
||||
return (server.locations.front());
|
||||
return (&(*best));
|
||||
}
|
||||
|
||||
std::string Webserv::_determine_file_extension(const std::string &path) const
|
||||
{
|
||||
size_t dot_pos = path.rfind(".");
|
||||
if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
|
||||
return ( path.substr(dot_pos + 1) );
|
||||
return (std::string(""));
|
||||
}
|
||||
|
||||
@@ -34,17 +34,34 @@ void Webserv::run()
|
||||
i = 0;
|
||||
while (i < nfds)
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
++i;
|
||||
if (!g_run)
|
||||
try
|
||||
{
|
||||
// TODO : handle EPOLLERR and EPOLLHUP
|
||||
it_socket = std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd);
|
||||
if (it_socket != _listen_sockets.end() && events[i].events & EPOLLIN)
|
||||
_accept_connection(*it_socket);
|
||||
else if (events[i].events & EPOLLIN)
|
||||
_request( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
else if (events[i].events & EPOLLOUT)
|
||||
_response( &(*std::find(_clients.begin(), _clients.end(), events[i].data.fd)) );
|
||||
++i;
|
||||
if (!g_run)
|
||||
break;
|
||||
}
|
||||
catch (const std::bad_alloc& e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
_close_all_clients();
|
||||
/* Swap to free the memory
|
||||
From : http://www.uml.org.cn/c%2B%2B/pdf/EffectiveSTL.pdf#page=66 */
|
||||
std::vector<Client>().swap(_clients);
|
||||
break;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
stylesheet/style.css
Normal file
4
stylesheet/style.css
Normal file
@@ -0,0 +1,4 @@
|
||||
h1 {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -8,4 +8,4 @@
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
14
www/test/index1.html
Normal file
14
www/test/index1.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test index</title>
|
||||
<!-- <link rel="stylesheet" href="stylesheet/style.css"> -->
|
||||
<!-- <link rel="stylesheet" href="/stylesheet/style.css"> -->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>Webserv Test Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/something.html
Normal file
11
www/test/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/index1.html
Normal file
11
www/test/test_deeper/index1.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv Test Deeper Index</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Index</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/something.html
Normal file
11
www/test/test_deeper/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Deeper Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
11
www/test/test_deeper/super_deep/something.html
Normal file
11
www/test/test_deeper/super_deep/something.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Webserv test deeper Something</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center">Webserv Test Super Deep Something</h1>
|
||||
<hr>
|
||||
<p style="text-align:center">(˚3˚)</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user