diff --git a/.gitignore b/.gitignore index 01a1610..7a5f6c1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ Thumbs.db *.lnk *.zip +builds + ubuntu_tester ubuntu_cgi_tester webserv diff --git a/Makefile b/Makefile index d517856..52f06ca 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ NAME = webserv CXX = c++ CXXFLAGS = -Wall -Wextra #-Werror -CXXFLAGS += $(HEADERS) +CXXFLAGS += $(HEADERS_I) CXXFLAGS += -std=c++98 CXXFLAGS += -g CXXFLAGS += -MMD -MP #header dependencie @@ -12,18 +12,25 @@ CXXFLAGS += -MMD -MP #header dependencie #SHELL = /bin/zsh VPATH = $(SRCS_D) -HEADERS = $(HEADERS_D:%=-I%) -HEADERS_D = headers -#HEADERS = Webserv.hpp \ +HEADERS_I = $(HEADERS_D:%=-I%) +HEADERS_D = srcs \ + headers +HEADERS_F = Webserv.hpp \ + ConfigParser.hpp \ + ServerConfig.hpp \ + LocationConfig.hpp \ Client.hpp \ - Server.hpp + MethodType.hpp \ + utils.hpp SRCS_D = srcs srcs/webserv SRCS = main.cpp \ ft_itoa.cpp \ base.cpp init.cpp close.cpp epoll_update.cpp signal.cpp \ accept.cpp request.cpp response.cpp \ - run_loop.cpp + run_loop.cpp \ + ConfigParser.cpp \ + utils.cpp OBJS_D = builds OBJS = $(SRCS:%.cpp=$(OBJS_D)/%.o) diff --git a/big.config b/big.config new file mode 100644 index 0000000..5139c18 --- /dev/null +++ b/big.config @@ -0,0 +1,122 @@ +server { + server_name webserv; + listen 0.0.0.0:4242; + + root ./www/html; + + allow_methods GET; + + autoindex on; + index index.html index2.html; + client_body_limit 4096; + + error_page 404 405 ./www/html/error/error.html; + error_page 500 ./www/html/error/error2.html; + + location /board { + allow_methods GET; + root ./www/html; + } + + location /board/content { + allow_methods GET POST DELETE; + root ./www/html/contents; + index board.html; + cgi_info .php php-cgi; + } + + location /cgi { + allow_methods GET POST; + cgi_info php php-fpm; + } +} + +server { + server_name another_webserv; + listen 0.0.0.0:4242; + + root ./www/html; + + allow_methods GET; + + client_body_limit 256; + + location /board { + allow_methods GET; + root ./www/html; + } + + location /board/content { + allow_methods GET POST DELETE; + root ./www/html/contents; + } + + location /cgi { + allow_methods GET POST; + cgi_info php php-fpm; + } +} + +server { + server_name webserv; + listen 0.0.0.0:4243; + + root ./www/html; + index index.html; + + allow_methods GET; + + autoindex off; + client_body_limit 256 ; + recv_timeout 6; + send_timeout 6; + + location /board { + allow_methods GET DELETE; + root ./www/html; + } + + location /board/post { + allow_methods POST; + root ./www/html/contents; + } + + location /cgi { + allow_methods GET; + cgi_info cgi_tester hello world; + } +} +server { + server_name webserv; + listen 0.0.0.0:4243; + + root ./www/html; + index index.html; + + allow_methods GET; + + autoindex off; + client_body_limit 256 ; + recv_timeout 6; + send_timeout 6; + + location /board { + allow_methods GET DELETE; + root ./www/html; + } + + location /board/post { + allow_methods POST; + root ./www/html/contents; + } + + location /cgi { + allow_methods GET; + cgi_info cgi_tester hello world; + } +} + +server { + listen 0.0.0.0:8000; + return 301 https://profile.intra.42.fr/; +} diff --git a/default.config b/default.config new file mode 100644 index 0000000..395acfc --- /dev/null +++ b/default.config @@ -0,0 +1,29 @@ +server { + +# this is a comment + + server_name our_server; + + listen 0.0.0.0:4040; + + + index index.html; # this is another comment + root ./www/; + + allow_methods GET; +} + +server { + +# this is a comment + + server_name our_server; + + listen 0.0.0.0:4047; + + + index index.html; # this is another comment + root ./www/; + + allow_methods GET; +} diff --git a/headers/Webserv.hpp b/headers/Webserv.hpp index a122b91..4e9c307 100644 --- a/headers/Webserv.hpp +++ b/headers/Webserv.hpp @@ -4,56 +4,81 @@ # include # include -# include -# include -# include -# include // stringstream -# include // errno -# include // perror -# include // close, access -# include // cout, cin -# include // memset -# include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname -# include // sockaddr_in -# include // htonl, htons, ntohl, ntohs, inet_addr +# include // exception, what +# include // runtime_error, invalid_argument # include // epoll # include // fcntl # include // waitpid # include // signal -# include // itoa -char *ft_itoa(int n); # include // ifstream +# include // stringstream +# include // errno +# include // close, access +# include // cout, cin +# include // memset +# include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname +# include // htonl, htons, ntohl, ntohs, inet_addr +# include // sockaddr_in +// # include // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h' +# include // find +# include // string +# include // perror +# include // atoi (athough it's already cover by ) +char *ft_itoa(int n); # include "Client.hpp" -# include "Server.hpp" +# include "ServerConfig.hpp" -//# define BUFSIZE 8192 -//# define TIMEOUT 10 * 1000 -//# define MAX_EVENTS 42 // arbitrary -//# define MSG_TEST "Le Webserv / 20 =D\n" +// TODO: A virer +# include "ConfigParser.hpp" +# include "LocationConfig.hpp" +# include "MethodType.hpp" +# include "utils.hpp" +// TODO: A virer extern bool g_run; extern int g_last_signal; void signal_handler(int signum); +/* enum // WIP test +{ + SERVER_FD = 1, + CLIENT_FD +}; + +struct s // WIP test +{ + int fd; + Client *ptr; +}; + */ + +// these might only be TMP +# define FAILURE -1 +# define SUCCESS 1 + class Webserv { public: // base.cpp Webserv(); // Webserv(Webserv const &src); + + // what should it take as arg, *, &, ? +// Webserv(std::vector& servers); + ~Webserv(); // Webserv &operator=(Webserv const &rhs); // init.cpp - void init_virtual_servers(); // ADD config param + void init_virtual_servers(std::vector* servers); // run_loop.cpp void run(); private: int _epfd; - int _socket_fd; // temp, to replace with std::vector - // std::vector _servers; + std::vector _listen_sockets; + std::vector _servers; std::vector _clients; // accept.cpp @@ -76,7 +101,7 @@ class Webserv void _close_client(int fd); void _close_all_clients(); // init.cpp - void _bind(int socket_fd, in_port_t port); + void _bind(int socket_fd, in_port_t port, std::string host); void _listen(int socket_fd, unsigned int max_connections); // TMP HUGO TEST CGI diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp new file mode 100644 index 0000000..d7a24a3 --- /dev/null +++ b/srcs/ConfigParser.cpp @@ -0,0 +1,486 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConfigParser.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: lperrey +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/13 22:11:17 by me #+# #+# */ +/* Updated: 2022/07/30 23:07:42 by lperrey ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ConfigParser.hpp" + + +/***** Stuf to rework + + +need to figure out why return std::vector * rather than just simple + not a pointer... + is there a good reason? + + +*/ + + + + +// Default +ConfigParser::ConfigParser() +{ + std::cout << "Default Constructor\n"; + // don't use yet, you have no idea what the defaults are +} + +//ConfigParser::ConfigParser(std::string &path) +//ConfigParser::ConfigParser(char & path) +ConfigParser::ConfigParser(const char* path) +{ + std::cout << "Param Constructor\n"; + + std::ifstream file; + std::string buf; + size_t comment; + + _content.clear(); + 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); + // remove # comments here. + if ((comment = buf.find_first_of("#")) == std::string::npos) + { + // remove empty lines, i think... + if ((buf.find_first_not_of(" \t")) != std::string::npos) + _content.append(buf + '\n'); + } + else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment) + { +// else if (comment > 0 && (buf.find_first_not_of(" \t")) != std::string::npos) + // is there a comment at the end of the line + std::string tmp = buf.substr(0, comment - 1); + _content.append(tmp + '\n'); + } + } + file.close(); + } + else + throw std::invalid_argument("open config"); +} + +ConfigParser::~ConfigParser() +{ + // do i need to destroy anything, won't it handle itself? +} + +/* +ConfigParser & ConfigParser::operator=(const ConfigParser& rhs) +{ + if (this == rhs) // * & ? + return (*this); // * ? + + // make some stuff equal + return (*this); +} +*/ + + +std::vector * ConfigParser::parse() +{ + std::vector * ret = new std::vector(); +// std::vector ret; + + size_t start = 0; + size_t curr = _content.find_first_not_of(" \t\n", 0); + + if (curr == std::string::npos) + 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); + std::string key = _content.substr(start, curr - start); + if (key != "server") + throw std::invalid_argument("bad config file arguments 1"); +// Server server = parse_server(&curr); +// ret->push_back(server); + // why not this? + ret->push_back(_parse_server(&curr)); + } + return (ret); +} + +// might need new names for Prev and Curr, not super descriptive... +ServerConfig ConfigParser::_parse_server(size_t *start) +{ + ServerConfig ret; + size_t curr = _content.find_first_not_of(" \t\n", *start); + + 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"); + while (curr != std::string::npos) // here curr == { + 1 + { + // so this moves curr to past the word... + std::string key = _get_first_word(&curr); + // now curr is on space after 1st word. + if (key == "}") + { + // why +1 curr is already after it no? + *start = _content.find_first_not_of(" \t\n", curr + 1); + break ; + } + else if (key == "location") + { + // this does assume we have locations in Server... + // could change the name but it's so clear... + ret.locations.push_back(_parse_location(&curr)); + } + else + { + std::string values = _get_rest_of_line(&curr); + // curr now should be \n + // checking for ; in _set_value, check key and value + _set_server_values(&ret, key, values); // handles the throws + } + } + return (ret); +} + + + +LocationConfig ConfigParser::_parse_location(size_t *start) +{ + LocationConfig ret; + size_t curr = *start; + // start is after the 1st word aka "location" + + ret.path = _get_first_word(&curr); + // in theory now curr should be right after the "path" + + curr = _content.find_first_not_of(" \t\n", curr); + + + if (curr == std::string::npos || _content[curr] != '{') + throw std::invalid_argument("bad config file syntax 2"); + + 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"); + while (curr != std::string::npos) + { + // so this moves curr to past the word... + std::string key = _get_first_word(&curr); + // now curr is on space after 1st word. + if (key == "}") + { + *start = curr; + break ; + } + else + { + std::string values = _get_rest_of_line(&curr); + // curr now should be \n + // checking for ; in _set_value, check key and value + + _set_location_values(&ret, key, values); //handles the throws + } + } + return (ret); +} + + + +// ok you need to think through these throws, when will each occur? + + + +void ConfigParser::_set_server_values(ServerConfig *server, \ + const std::string key, std::string value) +{ + + // check key for ; + // check values for ; at end and right number of words depending on key + + if (key.find_first_of(";") != std::string::npos) + throw std::invalid_argument("bad config file arguments 2"); + + // there shouldn't be any tabs, right? not between values... + if (value.find_first_of("\t") != std::string::npos) + { + std::cout << value << "\n"; + throw std::invalid_argument("bad config file arguments 3"); + } + + size_t i = value.find_first_of(";"); + // so you can't have no ; + // you can't have just ; + // and you can't have a ; not at the end or several ; + // in theory value_find_last_of should find the only ; + if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ + || value.compare(";") == 0) + throw std::invalid_argument("bad config file arguments 4"); + + + // we Trim value. + // is this valid? + // would it be better to shove the result directly in tmp_val? + // like call substr in split? + //value = value.substr(0, i - 1); + value = value.substr(0, i); + + std::vector tmp_val = split(value, ' '); + size_t size = tmp_val.size(); + + // would if if be more optimized? + if (size < 1) + throw std::invalid_argument("missing value"); + else if (key == "server_name" && size == 1) + { + server->server_name = tmp_val[0]; + } + else if (key == "listen" && size == 1) + { + if (tmp_val[0].find_first_of(":") == std::string::npos) + { + // why not store as vector [4] ? + server->host = "0.0.0.0"; + server->port = tmp_val[0]; + } + else + { + // maybe do this differently? + std::vector tmp2 = split(tmp_val[0], ':'); + // i might take issue with this, will see + if (server->host != "" && server->host != tmp2[0]) + throw std::invalid_argument("bad listen"); + server->host = tmp2[0]; + server->port = tmp2[1]; + } + } + else if (key == "root" && size == 1) + { + server->root = tmp_val[0]; + } + else if (key == "autoindex" && size == 1) + { + server->autoindex = (tmp_val[0] == "on" ? true : false); + } + else if (key == "client_body_limit" && size == 1) + { + server->client_body_limit = atoi(tmp_val[0].c_str()); + } + else if (key == "recv_timeout" && size == 1) + { + // 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->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); + } +/* else + { + throw std::invalid_argument("should only have 1 value"); + // yea ok but it could also be something else like too many + // args + + } +*/ + else if (key == "index") + { + // could run more tests on value content but meh... + for (unsigned long i = 0; i != tmp_val.size(); i++) + server->index.push_back(tmp_val[i]); + } + else if (key == "allow_methods") + { + // might do something different here + // like change how methods are stored? + for (unsigned long i = 0; i != tmp_val.size(); i++) + server->allow_methods.push_back(_str_to_method_type(tmp_val[i])); + } + else if (key == "return") + { + // could run more checks here too + // like tmp_val.size() must be 2 + // and tmp_val[0] should be a number and tmp_val[1] a string? + server->redirect_status = atoi(tmp_val[0].c_str()); + server->redirect_uri = tmp_val[1]; + } + else if (key == "error_page") + { + // something more complicated? + // like make sure ints then 1 string? + std::string path = tmp_val[tmp_val.size() - 1]; + for (unsigned long i = 0; i != tmp_val.size() - 1; i++) + { + int status_code = atoi(tmp_val[i].c_str()); + // yea IDK i might not want to store this like that... + if (server->error_pages.find(status_code) != server->error_pages.end()) + continue ; + server->error_pages[status_code] = path; + } + } + else + { + throw std::invalid_argument("wrong number of values"); + } +} + +// again not sure i want an int ret +void ConfigParser::_set_location_values(LocationConfig *location, \ + const std::string key, std::string value) +{ + // check key for ; + // check values for ; at end and right number of words depending on key + + if (key.find_first_of(";") != std::string::npos) + throw std::invalid_argument("bad config file arguments 5"); + + // there shouldn't be any tabs, right? not between values... + if (value.find_first_of("\t") != std::string::npos) + throw std::invalid_argument("bad config file arguments 6"); + + size_t i = value.find_first_of(";"); + // so you can't have no ; + // you can't have just ; + // and you can't have a ; not at the end or several ; + // in theory value_find_last_of should find the only ; + if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ + || value.compare(";") == 0) + throw std::invalid_argument("bad config file arguments 7"); + + + // we Trim value. + // is this valid? + // could do like above? + value = value.substr(0, i); + + std::vector tmp_val = ::split(value, ' '); + size_t size = tmp_val.size(); + + if (size < 1) + throw std::invalid_argument("missing value"); + else if (key == "root" && size == 1) + { + location->root = tmp_val[0]; + } + else if (key == "client_body_limit" && size == 1) + { + location->client_body_limit = atoi(tmp_val[0].c_str()); + } +/* else + { + throw std::invalid_argument("should only have 1 argument"); + } +*/ + else if (key == "index") + { + for (unsigned long i = 0; i != tmp_val.size(); i++) + location->index.push_back(tmp_val[i]); + } + else if (key == "allow_methods") + { + for (unsigned long i = 0; i != tmp_val.size(); i++) + location->allow_methods.push_back(_str_to_method_type(tmp_val[i])); + } + else if (key == "cgi_info") + { +// 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()); + } + else + { + throw std::invalid_argument("bad config file arguments 9"); + } +} + +// assumes curr is on a space or \t or \n +// get first word? next word? word? +std::string ConfigParser::_get_first_word(size_t *curr) +{ + size_t start; + +// are these checks excessive? + if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + if ((*curr = _content.find_first_of(" \t\n", start)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + + std::string key = _content.substr(start, *curr - start); + + return (key); +} + +// also assumes curr is on a space \t or \n +std::string ConfigParser::_get_rest_of_line(size_t *curr) +{ + size_t start; + + if ((start = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + +// std::cout << "start + 4 = " << _content.substr(start, 4) << "\n"; +// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n"; + + + if ((*curr = _content.find_first_of("\n", start)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + + std::string values = _content.substr(start, *curr - start); + +// std::cout << "curr + 4 = " << _content.substr(*curr, 4) << "\n"; +// std::cout << "rest of Line values: " << values << "\n"; + + return (values); +} + + +MethodType ConfigParser::_str_to_method_type(std::string str) +{ + if (str == "GET") + return GET; + else if (str == "POST") + return POST; + else if (str == "DELETE") + return DELETE; + return INVALID; +} + + + + +void ConfigParser::_print_content() const +{ + std::cout << _content; +} + + + + +// I might need to make my own Exceptions to throw... + + + + + + + + diff --git a/srcs/ConfigParser.hpp b/srcs/ConfigParser.hpp new file mode 100644 index 0000000..81b1cdf --- /dev/null +++ b/srcs/ConfigParser.hpp @@ -0,0 +1,97 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConfigParser.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/11 23:01:41 by me #+# #+# */ +/* Updated: 2022/07/27 19:27:57 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONFIGPARSER_HPP +# define CONFIGPARSER_HPP + +# include "Webserv.hpp" // easier to just do this? +# include "ServerConfig.hpp" +// add includes properly + + +// This is gonna be temporary cuz i don't konw if i like it +#define MAX_REQUEST_SIZE 2048 +#define MAX_URI_SIZE 64 +#define BSIZE 1024 + +/* +// this can't be here... +enum MethodType +{ + GET, + POST, + DELETE, + INVALID, +}; +*/ + + +class ConfigParser { + +public: + + // canonical + + ConfigParser(const char* path); // a string? + ~ConfigParser(); + + // ideally i wouldn't have one cuz it makes no sense, when would i use it? +// ConfigParser & operator=(const ConfigParser& rhs); + +// void parse(); // return void cuz throw exceptions. + std::vector * parse(); // const? +// std::vector parse(); // const? + + // other parses? + +// i thought if it were an instance of this class you could call +// private member functions from anywhere... + void _print_content() const; + +private: + std::string _content; + + // explicit? + // what exaclty does explicit do again? + ConfigParser(); // might need a path as arg? + // should this be in private since it always needs a path? + + + ServerConfig _parse_server(size_t *start); + LocationConfig _parse_location(size_t *start); + + + void _set_server_values(ServerConfig *server, const std::string key, std::string value); + void _set_location_values(LocationConfig *location, const std::string key, std::string value); + + + std::string _get_first_word(size_t *curr); // const? + std::string _get_rest_of_line(size_t *curr); // const? + + + // why static? it's an enum... + static MethodType _str_to_method_type(std::string str); + + // just for testing purposes + + +}; + + + + + +#endif + + + + diff --git a/srcs/LocationConfig.hpp b/srcs/LocationConfig.hpp new file mode 100644 index 0000000..72ab187 --- /dev/null +++ b/srcs/LocationConfig.hpp @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* LocationConfig.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/23 16:08:00 by me #+# #+# */ +/* Updated: 2022/07/25 20:09:48 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef LOCATIONCONFIG_HPP +# define LOCATIONCONFIG_HPP + +// includes + // add includes properly +# include +# include +# include +# include "Webserv.hpp" + +// again, struct instead? +class LocationConfig +{ +public: + // canonic stuff? + + + int client_body_limit; + std::string path; + std::string root; + std::vector index; + std::vector allow_methods; + std::map cgi_info; + + +}; + + + + +#endif + + + + + + + + diff --git a/srcs/MethodType.hpp b/srcs/MethodType.hpp new file mode 100644 index 0000000..9c815f5 --- /dev/null +++ b/srcs/MethodType.hpp @@ -0,0 +1,15 @@ + + +#ifndef METHODTYPE_HPP +# define METHODTYPE_HPP + +enum MethodType +{ + GET, + POST, + DELETE, + INVALID, +}; + + +#endif diff --git a/srcs/ServerConfig.hpp b/srcs/ServerConfig.hpp new file mode 100644 index 0000000..a08d8a1 --- /dev/null +++ b/srcs/ServerConfig.hpp @@ -0,0 +1,104 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ServerConfig.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/23 15:55:16 by me #+# #+# */ +/* Updated: 2022/07/23 16:19:43 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SERVERCONFIG_HPP +# define SERVERCONFIG_HPP + +// add includes properly... +# include "Webserv.hpp" +# include "MethodType.hpp" +//# include "ConfigParser.hpp" +# include "LocationConfig.hpp" + +// a class that's all public? just so we have options? +class ServerConfig +{ +public: + + // i mean i guess i need some canonic stuff? + // although maybe if i make it a struct i can barebones it? + + + std::string server_name; + std::string root; + + std::vector index; + std::map error_pages; + + // i'm tempted to do something diff for storing method types... + std::vector allow_methods; + + std::vector locations; + + // might do something diff + struct timeval send_timeout; + struct timeval recv_timeout; + + int client_body_limit; + bool autoindex; + + // not sure what these look like in config file + int redirect_status; + std::string redirect_uri; + + // is this the best way? + std::string host; + std::string port; + + + // do i need a print all for testing? + + + void print_all() + { + std::cout << "PRINTING A FULL SERVER CONFIG\n\n"; + + std::cout << "Server_name: " << server_name << '\n'; + std::cout << "root: " << root << '\n'; + std::cout << "index: "; + for (size_t i = 0; i < index.size(); i++) + std::cout << index[i] << " "; + std::cout << "\nerror_pages: "; + for(std::map::iterator it = error_pages.begin(); \ + it != error_pages.end(); it++) + std::cout << it->first << "--" << it->second << " "; +// for (size_t i = 0; i < error_pages.size(); i++) +// std::cout << error_pages->first << "--" << error_pages->second << " "; + std::cout << "\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 << "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'; + + std::cout << "\n----------\n"; + } + + +}; + + + + +#endif + + + + + + + diff --git a/srcs/main.cpp b/srcs/main.cpp index aa5b1ab..4c580f3 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -4,18 +4,47 @@ #include #include "Webserv.hpp" -int main(void) +int main(int ac, char **av) { try { + std::string config = (ac == 2 ? av[1] : "./default.config"); + + // like this just looks kinda gross, why bother creating an instance + // and not immediately parsing? like it servers no other purpose... + // what if i call parse directly in the constructor? + // oh because the constructor has no return, but still + // is there a better way? + + ConfigParser configParser(config.c_str()); + + configParser._print_content(); + + // i don't love that servers has to be a pointer... + std::vector* servers = configParser.parse(); + + // use an iterator you moron + for (std::vector::iterator it = servers->begin(); it < servers->end(); it++) + { + // std::cout << it->server_name << " "; + it->print_all(); + } + + +// Webserv serv(configParser.parse()); + // is this better or worse than using + Webserv serv; - serv.init_virtual_servers(); + // serv.init_virtual_servers(); + serv.init_virtual_servers(servers); + delete servers; serv.run(); } catch (std::exception& e) { std::cout << e.what() << '\n'; } + return (0); } diff --git a/srcs/utils.cpp b/srcs/utils.cpp new file mode 100644 index 0000000..b66cd62 --- /dev/null +++ b/srcs/utils.cpp @@ -0,0 +1,19 @@ + + +#include "Webserv.hpp" + + + +std::vector split(std::string input, char delimiter) +{ + std::vector answer; + std::stringstream ss(input); + std::string temp; + + while (getline(ss, temp, delimiter)) + answer.push_back(temp); + + return answer; +} + + diff --git a/srcs/utils.hpp b/srcs/utils.hpp new file mode 100644 index 0000000..3f3b48c --- /dev/null +++ b/srcs/utils.hpp @@ -0,0 +1,10 @@ + + + +#ifndef UTILS_HPP +# define UTILS_HPP + + +std::vector split(std::string input, char delimiter); + +#endif diff --git a/srcs/webserv/base.cpp b/srcs/webserv/base.cpp index 57b2072..6267ec9 100644 --- a/srcs/webserv/base.cpp +++ b/srcs/webserv/base.cpp @@ -21,9 +21,29 @@ Webserv::Webserv() } */ +// we'll come back to this +/* +Webserv::Webserv(std::vector* servers) +: _servers(servers) +{ + // talk to luke about what all this does + // the Param Constructor might need to do dif stuff + std::cout << "Server init\n"; + + _epfd = ::epoll_create1(0); // (EPOLL_CLOEXEC) for CGI fork ? + if (_epfd == -1) + { + std::perror("err epoll_create1(): "); + throw std::runtime_error("Epoll init"); + } +} +*/ + + Webserv::~Webserv() { - close(_socket_fd); + //close(_socket_fd); + // TODO : CLOSE ALL _listen_sockets close(_epfd); _close_all_clients(); std::cerr << "Server destroyed\n"; diff --git a/srcs/webserv/init.cpp b/srcs/webserv/init.cpp index 97a3b90..b773964 100644 --- a/srcs/webserv/init.cpp +++ b/srcs/webserv/init.cpp @@ -1,23 +1,43 @@ #include "Webserv.hpp" -void Webserv::init_virtual_servers() // ADD config param +void Webserv::init_virtual_servers(std::vector* servers) { - _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ? - if (_socket_fd == -1) + int ret; + std::vector _open_ports; + + _servers = *servers; + _listen_sockets.clear(); + std::vector::iterator it = _servers.begin(); + + while (it != _servers.end()) { - std::perror("err socket()"); - throw std::runtime_error("Socket init"); + if ( std::find(_open_ports.begin(), _open_ports.end(), std::atoi(it->port.data()) ) != _open_ports.end() ) + { + ++it; + continue; + } + + ret = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); // (SOCK_CLOEXEC) for CGI fork ? + if (ret == -1) + { + std::perror("err socket()"); + throw std::runtime_error("Socket init"); + } + _listen_sockets.push_back(ret); + + _bind(_listen_sockets.back(), std::atoi(it->port.data()), it->host); + _listen(_listen_sockets.back(), 512); // 512 arbitrary + + if (_epoll_update(_listen_sockets.back(), EPOLLIN, EPOLL_CTL_ADD) == -1) + throw std::runtime_error("Socket init"); + + _open_ports.push_back(std::atoi(it->port.data())); + ++it; } - - _bind(_socket_fd, 4040); - _listen(_socket_fd, 512); // 512 arbitrary - - if (_epoll_update(_socket_fd, EPOLLIN, EPOLL_CTL_ADD) == -1) - throw std::runtime_error("Socket init"); } -void Webserv::_bind(int socket_fd, in_port_t port) +void Webserv::_bind(int socket_fd, in_port_t port, std::string host) { // cast invalid ? how to ? // const struct sockaddr* cast_test = static_cast(addr); @@ -26,7 +46,8 @@ void Webserv::_bind(int socket_fd, in_port_t port) std::memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = ::htons(port); - addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ? + addr.sin_addr.s_addr = ::htonl(::inet_addr(host.c_str())); + // addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ? if (::bind(socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) { diff --git a/srcs/webserv/run_loop.cpp b/srcs/webserv/run_loop.cpp index 9b466b5..f8ce7cb 100644 --- a/srcs/webserv/run_loop.cpp +++ b/srcs/webserv/run_loop.cpp @@ -34,7 +34,9 @@ void Webserv::run() while (i < nfds) { // if ((events[i].data.u32 == SERVER_FD) && (events[i].events & EPOLLIN)) // Dont work, see "SERVER_FD" define - if ((events[i].data.fd == _socket_fd) && (events[i].events & EPOLLIN)) + // if ((events[i].data.fd == _socket_fd) && (events[i].events & EPOLLIN)) + if ((std::find(_listen_sockets.begin(), _listen_sockets.end(), events[i].data.fd) != _listen_sockets.end()) + && (events[i].events & EPOLLIN)) _accept_connection(events[i].data.fd); else if (events[i].events & EPOLLIN) _request(static_cast(events[i].data.ptr)); diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..7de0cd1 --- /dev/null +++ b/www/index.html @@ -0,0 +1,16 @@ + + + + + + + + + +

My First Heading

+

My first paragraph.

+ + + + +