well, restructured things a bit, but mostly started trying to sort LocationConfigs, it is sort of going well, good ideas, implementations is still a bit lacking...

This commit is contained in:
Me
2022-08-04 01:09:40 +02:00
parent b0c524a8bd
commit 3d46505411
13 changed files with 363 additions and 122 deletions

View File

@@ -23,9 +23,9 @@ SRCS = main.cpp \
base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \
accept.cpp request.cpp response.cpp \
run_loop.cpp \
ConfigParser.cpp \
ConfigParserUtils.cpp \
ConfigParserPost.cpp \
parser.cpp \
extraConfig.cpp \
postProcessing.cpp \
utils.cpp \
cgi_script.cpp \
Client.cpp \

View File

@@ -10,22 +10,42 @@ server {
# client_body_limit 400;
index index.html; # this is another comment
root /website;
# root goop;
root www;
# root www/test; # also works
# root /www; # stat does not like this kind of definition
# root /usr; # because it's looking at / aka absolute path
# root ~/Programming/42/group_webserv/www; # didn't like thise either
# root /home/me/Programming/42/group_webserv; # but this is fine
location /test {
root www/test;
index index.html;
}
# 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/;
root www;
index index.html;
allow_methods DELETE;
}
allow_methods GET;
allow_methods GET POST;
location /something/long/here {
}
location /something/long/here/but/more {
}
# 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;
}
# location /test {
# root /www;
# }
}

View File

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

View File

@@ -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?
}

View File

@@ -16,7 +16,9 @@
# include <map>
# include <vector>
# include <string>
# include <iostream>
# include "utils.hpp"
// again, struct instead?
class LocationConfig
@@ -24,12 +26,18 @@ class LocationConfig
public:
// canonic stuff?
// i thought this might fix the "non static member function"
// shit i'm getting with the comparator...
LocationConfig() {}
~LocationConfig() {}
std::string path;
int client_body_limit;
std::string root;
std::vector<std::string> index;
std::vector<http_method> allow_methods;
unsigned int allow_methods;
std::map<std::string, std::string> cgi_info;
// wait if i can call several times, shouldn't it be a map?
@@ -40,10 +48,57 @@ public:
// 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 << "client_body_limit: " << client_body_limit << '\n';
std::cout << "root: " << root << '\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
{
size_t len_lhs = 0;
size_t len_rhs = 0;
size_t tmp = 0;
// consider adding 1 to path that ends in a file not folder.
while ((tmp = this->path.find_first_of("/", tmp)) != std::string::npos)
{
std::cout << "tmp_lhs: " << tmp << "\n";
++tmp;
++len_lhs;
}
tmp = 0;
while ((tmp = rhs.path.find_first_of("/", tmp)) != std::string::npos)
{
std::cout << "tmp_rhs: " << tmp << "\n";
++tmp;
++len_rhs;
}
std::cout << "len_lhs: " << len_lhs << " len_rhs: " << len_rhs << (len_lhs < len_rhs) << "\n";
return (len_lhs < len_rhs); // right comparison ? not <= ?
};
};
// ok it needs to go somewhere else
#endif

View File

@@ -32,17 +32,15 @@ public:
int client_body_limit; // set to default max if none set
// might be the only one we let slide if bad input...
// might be the only one we let slide if bad input... It remains false...
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;
unsigned int allow_methods;
std::vector<LocationConfig> locations;
@@ -74,10 +72,12 @@ public:
// 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 << ::http_methods_to_str(allow_methods) << "\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 << "autoindex: " << autoindex << '\n';
std::cout << "client_body_limit: " << client_body_limit << '\n';
// std::cout << "redirect_status: " << redirect_status << '\n';

View 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 <= ?
}

View File

@@ -105,6 +105,7 @@ ServerConfig ConfigParser::_parse_server(size_t *start)
ret.client_body_limit = 0;
ret.autoindex = false;
ret.allow_methods = 0;
if (curr == std::string::npos || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax 1");
@@ -144,6 +145,7 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
ret.client_body_limit = 0;
ret.redirect_status = 0;
ret.allow_methods = 0;
ret.path = _get_first_word(&curr);
// in theory now curr should be right after the "path"
@@ -176,9 +178,6 @@ LocationConfig ConfigParser::_parse_location(size_t *start)
}
void ConfigParser::_set_server_values(ServerConfig *server, \
const std::string key, std::string value)
{
@@ -189,11 +188,15 @@ 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++)
// fuck should i be using an iterator?
// 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 (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,12 +232,36 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
}
else if (key == "root" && size == 1 && server->root == "")
{
DIR* dir = opendir(tmp_val[0].c_str());
// consider using lstat if you don't? want symlinks included?
// don't bother with include <errno.h> for now.
// std::cout << tmp_val[0] << '\n';
// should i also check that it's not a file? no i think S_ISDIR does that right?
const char* folder = tmp_val[0].c_str();
struct stat sb;
if (stat(folder, &sb) == 0 && S_ISDIR(sb.st_mode))
{
// std::cout << "is a Dir\n";
server->root = tmp_val[0];
}
else
throw std::invalid_argument("root dir could not be opened");
/* DIR* dir = opendir(tmp_val[0].c_str());
if (dir)
closedir(dir);
else
throw std::invalid_argument("root dir could not be opened");
server->root = tmp_val[0];
{
switch (errno) {
case EACCES: printf("Permission denied\n"); break;
case ENOENT: printf("Directory does not exist\n"); break;
case ENOTDIR: printf("is not a directory\n"); break;
default:
throw std::invalid_argument("root dir could not be opened 1");
}
}
*/
}
else if (key == "autoindex" && size == 1)
{
@@ -258,14 +285,14 @@ void ConfigParser::_set_server_values(ServerConfig *server, \
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())
else if (key == "allow_methods" && server->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");
server->allow_methods.push_back(m);
server->allow_methods |= m;
}
}
else if (key == "error_page")
@@ -323,12 +350,16 @@ 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);
const char* folder = tmp_val[0].c_str();
struct stat sb;
if (stat(folder, &sb) == 0 && S_ISDIR(sb.st_mode))
{
// std::cout << "is a Dir\n";
location->root = tmp_val[0];
}
else
throw std::invalid_argument("root dir could not be opened");
location->root = tmp_val[0];
}
else if (key == "client_body_limit" && size == 1 \
&& location->client_body_limit == 0)
@@ -343,14 +374,14 @@ void ConfigParser::_set_location_values(LocationConfig *location, \
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")

View File

@@ -0,0 +1,161 @@
#include "ConfigParser.hpp"
// technically doesn't belong to ConfigParser or any other class
// adding static in front doesn't work...
/*
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 <= ?
}
*/
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 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");
if (it->client_body_limit == 0)
it->client_body_limit = 5000; // what is the recomended size?
// autoindex is False by Default
// if Allow methodes not specified we set to ALL
if (it->allow_methods == UNKNOWN) // in this case that means nothing...
it->allow_methods = ALL_METHODS;
if (it->index.empty())
throw std::invalid_argument("Config file needs an Index");
// if error_pages is left empty, we'll use the defaults which
// i believe are set elsewhere...
// actually do this at the end, once we know if there aren't any locations
// with path /
/* if (!_find_root_path_location(it->locations))
{
LocationConfig tmp;
tmp.path = "/";
tmp.client_body_limit = 5000; // figur out correct amount
tmp.root = it->root;
tmp.index = it->index;
tmp.allow_methods = it->allow_methods;
tmp.redirect_status = 0;
it->locations.push_back(tmp);
}
*/
std::vector<LocationConfig>::iterator it_l = it->locations.begin();
// first check locations we have
while (it_l != it->locations.end())
{
// opendir() doesn't work for some reason...
// this doens't work yet cuz the path needs to be relative and stat doesn't like / before...
/* const char* folder = it_l->path.c_str();
struct stat sb;
if (stat(folder, &sb) == 0 && S_ISDIR(sb.st_mode))
{
// std::cout << "is a Dir\n";
// it_l->path = it_lpath;
// yea nothing happens, i guess i can change how the if conditions work...
}
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;
// if Allow methodes not specified we set to Server methods
if (it_l->allow_methods == UNKNOWN) // in this case that means nothing...
it_l->allow_methods = it->allow_methods;
// fill out index from Server?
// or do a bunch of checks on what is in there...
if (it_l->index.empty())
it_l->index = it->index; // right?
// same for redirect status i think
// maybe do something for Cgi_info?
++it_l;
}
// Then put locations in order...
// may change how the order is set later
// ok we can sort in order and reverse...
std::cout << "made it to sorting...\n";
// std::sort(it->locations.begin(), it->locations.end(), compareLocationConfigs);
std::sort(it->locations.begin(), it->locations.end());
// for some reason no need to reverse...
// std::reverse(it->locations.begin(), it->locations.end());
++it;
}
// do the defaults at the end?
}
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;
}

View File

@@ -60,6 +60,8 @@ http_method str_to_http_method(std::string &str)
return POST;
else if (str == "DELETE")
return DELETE;
else if (str == "ALL_METHODS")
return ALL_METHODS;
return UNKNOWN;
}

View File

@@ -7,6 +7,7 @@
# include <sstream>
# include <cstdlib> // atoi
// enum http_method
// {
// UNKNOWN = 0b00000000,
@@ -33,4 +34,5 @@ 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);
#endif

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

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