diff --git a/config/default.conf b/config/default.conf index 9e76bbc..ee5c443 100644 --- a/config/default.conf +++ b/config/default.conf @@ -11,35 +11,82 @@ # Server Configuration server { - listen localhost:8080; # [M - O] choose a Host:Port combination ; IPv4:{1 - 16bit} ; default: INADDR_ANY:8080 - server_name localhost; # [M - O] Setup the server_names ; [A-Za-z_]* ; default: [EMPTY] - error_dir /error; # [1 - O] Set a directory that has error page ; [/]{1}[A-Za-z_/:w]* ; Default: [EMPTY] + listen 127.0.0.1:8080; + server_name lucy_saus; # [M - O] retup the server_names ; [A-Za-z_]* ; default: [EMPTY] + error_dir /data/errors/; # [1 - O] Set a directory that has error page ; [/]{1}[A-Za-z_/:w]* ; Default: [EMPTY] client_max_body_size 3M; # [1 - O] Limit client body size ; [\d]{1,3}[KkMm]? ; Default: 1 mB location / { # [1 - O] Request Target is the Target of the HTTP request; starts with a / - alias /data/www; # [1 - O] links the stated Direcotry as the RequestTarget Location ; [/]{1}[A-Za-z_/:w]* ; Default: /data/www + alias /data/www/; # [1 - O] links the stated Direcotry as the RequestTarget Location ; [/]{1}[A-Za-z_/:w]* ; Default: /data/www + index index.html; # [1 - O] Set a default file to answer if the request is a directory ; [A-Za-z_/.]* ; Default: index.html + allowed_methods GET; # [M - O] Define a list of accepted HTTP methods for the route; GET/POST/DELETE ; Default: GET + autoindex on; # [B - O] Turn on or off directory listing; on; Default: off + } + +} + +server { + listen 127.0.0.1:8080; # [M - O] choose a Host:Port combination ; IPv4:{1 - 16bit} ; default: INADDR_ANY:8080 + server_name localhost; # [M - O] retup the server_names ; [A-Za-z_]* ; default: [EMPTY] + error_dir /data/errors/; # [1 - O] Set a directory that has error page ; [/]{1}[A-Za-z_/:w]* ; Default: [EMPTY] + client_max_body_size 3M; # [1 - O] Limit client body size ; [\d]{1,3}[KkMm]? ; Default: 1 mB + + location / { # [1 - O] Request Target is the Target of the HTTP request; starts with a / + alias /data/www/; # [1 - O] links the stated Direcotry as the RequestTarget Location ; [/]{1}[A-Za-z_/:w]* ; Default: /data/www index index.html; # [1 - O] Set a default file to answer if the request is a directory ; [A-Za-z_/.]* ; Default: index.html allowed_methods GET; # [M - O] Define a list of accepted HTTP methods for the route; GET/POST/DELETE ; Default: GET autoindex on; # [B - O] Turn on or off directory listing; on; Default: off } location /python/ { - alias /cgi/python; # already defined; - cgi .py /bin/python3; # [1 - O] Set a Path to where the CGI can find the Binary; [A-Za-z_/.]* ; Default: [EMPTY] + alias /cgi/python/; # already defined; + cgi on; # [1 - O] Set a Path to where the CGI can find the Binary; [A-Za-z_/.]* ; Default: [EMPTY] } location /images/ { - root /data/images; # already defined; - allow_methods GET POST; # already defined; - autoindex off; # already defined; + alias /data/images/; + allowed_methods GET POST; + autoindex on; + } + + location /png/ { + alias /data/images/png/; + allowed_methods GET POST; + autoindex off; + } + + location /images/jpg/ { + alias /data/upload/images/jpg/; + allowed_methods GET POST; + autoindex off; } location /upload/ { - root /data/upload; - allowed_moteds POST DELETE; + alias /data/upload/; + allowed_methods POST DELETE; } location /removed_folder/ { - return localhost; # [1 - O] Reroute a directory to another URL; URL ; Default: [] + alias /data/www/; + allowed_methods GET; + return /images/coffee-resized.jpeg ; # [1 - O] Reroute a directory to another URL; URL ; Default: [] } } + +#TODO: all Settings still need implementation into the Webserver +# [ ] SetDefaultValues for configfile. +# [ ] ServerBlock: +# [ ] listen; // +# [ ] server_name; // should go over the Clients/HTTPServers after +# // the request is recieved and see which serverSettings block is relevant to the request; +# [x] error_dir; // should search this direcotry to find the relevant error file [e.g. 404.html]; +# [x] client_max_body_size; // limits the size of the client Request, returning 413 when it exceeds this value; +# [ ] Location Block: +# [x] alias; // should do a fitted preppend on the path in URI; +# [x] index; // file return of the request just specifies this directory ; +# [x] autoindex; // Automaticly should create a Index file that shows an overview of the direcotry; +# [x] allowed_methods; // should check if the HTTP Method is allowed in this location; +# [ ] cgi; // is a boolean that shows if the directory is a CGI and the value is the path to the executable; +# [x] return; // +# [x] HTTPerrorpagegenerator // method that can generate a simple ERROR page to the _response. +# diff --git a/config/simple.conf b/config/simple.conf index d282b77..6641fc6 100644 --- a/config/simple.conf +++ b/config/simple.conf @@ -2,9 +2,8 @@ # Server Configuration server { - port 9696; - host localhost; # Choose the port and host; default for this host:port - server_name lucy_saus; # Setup the server_names or not + listen 127.0.0.1:8080; + server_name eerste_saus; # Setup the server_names or not client_max_body_size 3M; # Limit client body size error_pages 404; @@ -14,3 +13,56 @@ server { allow_methods GET POST; # Define a list of accepted HTTP methods for the route } } + +server { + listen 127.0.1.1:8080; + server_name tweede_saus; # Setup the server_names or not + client_max_body_size 3M; # Limit client body size + error_pages 404; + + location / { + root /data/www; # Define a directory where the file should be searched + index index.html; # Set a default file to answer if the request is a directory + allow_methods GET POST; # Define a list of accepted HTTP methods for the route + } +} + +server { + listen localhost:8080; + server_name sala_SAUS; # Setup the server_names or not + client_max_body_size 3M; # Limit client body size + error_pages 404; + + location / { + root /data/www; # Define a directory where the file should be searched + index index.html; # Set a default file to answer if the request is a directory + allow_methods GET POST; # Define a list of accepted HTTP methods for the route + } +} + +server { + listen localhost:8080; + server_name Lucy_saus; # Setup the server_names or not + client_max_body_size 3M; # Limit client body size + error_pages 404; + + location / { + root /data/www; # Define a directory where the file should be searched + index index.html; # Set a default file to answer if the request is a directory + allow_methods GET POST; # Define a list of accepted HTTP methods for the route + } +} + +server { + listen dump-linux:8080; + server_name maarty-saus; # Setup the server_names or not + client_max_body_size 3M; # Limit client body size + error_pages 404; + + location / { + root /data/www; # Define a directory where the file should be searched + index index.html; # Set a default file to answer if the request is a directory + allow_methods GET POST; # Define a list of accepted HTTP methods for the route + } +} + diff --git a/config/test.conf b/config/test.conf new file mode 100644 index 0000000..416c7a8 --- /dev/null +++ b/config/test.conf @@ -0,0 +1,10 @@ +# Server Configuration + +server { + location / { + alias /data/www; + } +} + + + diff --git a/data/errors/404.html b/data/errors/404.html index 2056402..9a2acb3 100644 --- a/data/errors/404.html +++ b/data/errors/404.html @@ -5,6 +5,9 @@

404 Not Found

-

The requested page could not be found.

+
+

The requested page could not be found. +
+
This was loaded using our error_dir

diff --git a/data/errors/500.html b/data/errors/500.html new file mode 100644 index 0000000..a60a156 --- /dev/null +++ b/data/errors/500.html @@ -0,0 +1,12 @@ + + + + 500 Not Found + + +

500 Not Found

+

The server has encountered an internal error. +
+
File: /data/error/500.html

+ + diff --git a/data/images/coffee-resized.jpeg b/data/images/coffee-resized.jpeg new file mode 100644 index 0000000..799bdb5 Binary files /dev/null and b/data/images/coffee-resized.jpeg differ diff --git a/data/nose_monkey.webp b/data/images/nose_monkey.webp similarity index 100% rename from data/nose_monkey.webp rename to data/images/nose_monkey.webp diff --git a/cgi-bin/print.py b/data/python/print.py similarity index 100% rename from cgi-bin/print.py rename to data/python/print.py diff --git a/data/www/autoindexresult.html b/data/www/autoindexresult.html new file mode 100644 index 0000000..affc527 --- /dev/null +++ b/data/www/autoindexresult.html @@ -0,0 +1,29 @@ + + + + Index of / + + +

Index of /

+ + + + + + + + + + + + + + + + + + + + +
File Name File Size Last Modification
index.html389903 01 24
test.file010 01 24
test.txt010 01 24
+ diff --git a/data/www/index.html b/data/www/index.html index 4b3e145..8e3d599 100644 --- a/data/www/index.html +++ b/data/www/index.html @@ -22,7 +22,10 @@

Do not go gentle into that good night

- +

hallo

diff --git a/data/www/test.file b/data/www/test.file new file mode 100644 index 0000000..e69de29 diff --git a/in.txt b/in.txt deleted file mode 100644 index d151800..0000000 --- a/in.txt +++ /dev/null @@ -1,2 +0,0 @@ -hello world! -goodbye world! diff --git a/include/AutoIndexGenerator.hpp b/include/AutoIndexGenerator.hpp new file mode 100644 index 0000000..cd401cc --- /dev/null +++ b/include/AutoIndexGenerator.hpp @@ -0,0 +1,17 @@ +#ifndef AUTOINDEXGENERATOR_HPP +#define AUTOINDEXGENERATOR_HPP + +#include "Logger.hpp" + +#include +#include +#include + +namespace AutoIndexGenerator +{ +std::fstream OpenAutoIndex(std::string directory, const std::string uri); +std::string AutoIndexGenerator(const std::string dir, const std::string uri); + +} // namespace AutoIndexGenerator + +#endif // !AUTOINDEXGENERATOR_HPP diff --git a/include/Client.hpp b/include/Client.hpp index fb0e010..d019cac 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -1,22 +1,24 @@ #ifndef CLIENT_HPP #define CLIENT_HPP -#include -#include -#include -#include -#include +#include "CGI.hpp" +#include "FileManager.hpp" +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "ServerSettings.hpp" +#include "Socket.hpp" class Client { public: - Client(const int &server_fd); + Client(const int &server_fd, std::vector &serversettings); Client() = delete; Client(const Client &other) = delete; const Client &operator=(const Client &other) = delete; ~Client(); ClientState handleConnection(short events); + void resolveServerSetting(); int getFD(void) const; private: @@ -25,6 +27,8 @@ class Client FileManager _file_manager; CGI _cgi; Socket _socket; + const std::vector &_server_list; + ServerSettings &_serversetting; ClientState _state; }; diff --git a/include/ConfigParser.hpp b/include/ConfigParser.hpp index d242cde..683e78e 100644 --- a/include/ConfigParser.hpp +++ b/include/ConfigParser.hpp @@ -29,6 +29,7 @@ class ConfigParser void ParseConfig(); const std::vector &getServerSettings(); + std::vector> sortServerSettings(); }; void tokenizeStream(std::stringstream sstream, diff --git a/include/FileManager.hpp b/include/FileManager.hpp index 8f08a4c..6070fab 100644 --- a/include/FileManager.hpp +++ b/include/FileManager.hpp @@ -1,8 +1,11 @@ #ifndef FILE_MANAGER_HPP #define FILE_MANAGER_HPP -#include -#include +#include "HTTPRequest.hpp" +#include "HTTPStatus.hpp" +#include "LocationSettings.hpp" +#include "ServerSettings.hpp" + #include #include @@ -11,6 +14,11 @@ class FileManager private: std::string _response; std::fstream _request_target; + ServerSettings _serversetting; + bool _autoindex; + + std::string applyLocationSettings(const std::string &request_target, + HTTPMethod method); public: FileManager(); @@ -22,7 +30,6 @@ class FileManager void openPostFile(const std::string &request_target_path); ClientState openErrorPage(const std::string &error_pages_path, const StatusCode &status_code); - ClientState setErrorResponse(const StatusCode &status_code); ClientState loadErrorPage(void); ClientState manage(HTTPMethod method, const std::string &filename, const std::string &body); @@ -31,6 +38,10 @@ class FileManager ClientState manageDelete(const std::string &reqest_target_path); const std::string &getResponse(void) const; + void addToResponse(const std::string str); + void setResponse(const std::string str); + + void setServerSetting(const ServerSettings &serversetting); }; #endif diff --git a/include/HTTPRequest.hpp b/include/HTTPRequest.hpp index ca893a7..6a2aac5 100644 --- a/include/HTTPRequest.hpp +++ b/include/HTTPRequest.hpp @@ -2,6 +2,7 @@ #define HTTP_REQUEST_HPP #include +#include #include #include @@ -15,6 +16,8 @@ #endif // ENUM +#ifndef HTTP_METHOD_ENUM +#define HTTP_METHOD_ENUM enum class HTTPMethod { GET, @@ -22,6 +25,7 @@ enum class HTTPMethod DELETE, UNKNOWN, }; +#endif // CLASS class HTTPRequest @@ -29,7 +33,7 @@ class HTTPRequest public: HTTPRequest(); HTTPRequest(const HTTPRequest &rhs) = delete; - HTTPRequest &operator=(const HTTPRequest &other) = delete; + HTTPRequest &operator=(const HTTPRequest &rhs) = delete; ~HTTPRequest(); void setMethodType(const std::string &method_type); @@ -38,6 +42,9 @@ class HTTPRequest void setRequestTarget(const std::string &request_target); const std::string &getRequestTarget(void) const; + void setMaxBodySize(std::string inp); + ssize_t getMaxBodySize(void) const; + void setHTTPVersion(const std::string &http_version); const std::string &getHTTPVersion(void) const; @@ -45,11 +52,17 @@ class HTTPRequest const std::string &getHeader(const std::string &key) const; const std::string &getBody(void) const; + ClientState setRequestVariables(size_t pos); ClientState receive(int fd); + void setHeaderEnd(bool b); + bool getHeaderEnd() const; + private: + bool _header_end; ssize_t _bytes_read; size_t _content_length; + size_t _max_body_size; HTTPMethod _methodType; std::string _http_request; std::string _request_target; @@ -59,6 +72,7 @@ class HTTPRequest size_t parseStartLine(size_t &i); size_t parseHeaders(size_t &i); + void setLocationdependancies(std::string request_target); }; #endif diff --git a/include/HTTPServer.hpp b/include/HTTPServer.hpp index e1d7e18..28464f2 100644 --- a/include/HTTPServer.hpp +++ b/include/HTTPServer.hpp @@ -28,7 +28,7 @@ class HTTPServer void setupServers(void); void handleActivePollFDs(); - void handleNewConnection(int fd); + void handleNewConnection(int fd, std::vector &ServerBlock); void handleExistingConnection(const pollfd &poll_fd); }; diff --git a/include/HTTPStatus.hpp b/include/HTTPStatus.hpp index 135d784..6897c4d 100644 --- a/include/HTTPStatus.hpp +++ b/include/HTTPStatus.hpp @@ -20,6 +20,7 @@ class HTTPStatus ~HTTPStatus(); std::string getStatusLine(const std::string &version) const; + std::string getStatusLineCRLF(const std::string &version) const; std::string getHTMLStatus(void) const; StatusCode getStatusCode() const; }; diff --git a/include/LocationSettings.hpp b/include/LocationSettings.hpp index 6f07610..a4162a6 100644 --- a/include/LocationSettings.hpp +++ b/include/LocationSettings.hpp @@ -5,6 +5,18 @@ #include +// ENUM +#ifndef HTTP_METHOD_ENUM +#define HTTP_METHOD_ENUM +enum class HTTPMethod +{ + GET, + POST, + DELETE, + UNKNOWN, +}; +#endif + class LocationSettings { public: @@ -12,31 +24,34 @@ class LocationSettings ~LocationSettings(); LocationSettings(std::vector::iterator &token); LocationSettings(const LocationSettings &rhs); - LocationSettings &operator=(const LocationSettings &rhs) = delete; + LocationSettings &operator=(const LocationSettings &rhs); // Functionality: // getters: - const std::string &getRequestTarget() const; + + const std::string &getPath() const; const std::string &getAlias() const; const std::string &getIndex() const; const std::string &getAllowedMethods() const; - const std::string &getReturn() const; + const std::string &getRedirect() const; bool getAutoIndex() const; - // setters: - void setDir(const std::string &path); + // resolves: + + const std::string resolveAlias(const std::string request_target) const; + bool resolveMethod(const HTTPMethod method) const; // Printing: void printLocationSettings() const; private: - std::string _requesttarget; + std::string _path; std::string _alias; std::string _index; std::string _allowed_methods; std::string _cgi_path; - std::string _return; + std::string _redirect; bool _auto_index; void parseAlias(const Token token); diff --git a/include/ReturnException.hpp b/include/ReturnException.hpp new file mode 100644 index 0000000..45fe6d4 --- /dev/null +++ b/include/ReturnException.hpp @@ -0,0 +1,24 @@ +#ifndef RETURN_EXCEPTION_HPP +#define RETURN_EXCEPTION_HPP + +#include "LocationSettings.hpp" +#include +#include + +class ReturnException : public HTTPStatus, public std::runtime_error +{ + private: + const std::string _redireciton; + + public: + ReturnException(StatusCode status, const LocationSettings &locationBlock) + : HTTPStatus(status), std::runtime_error(getStatusLineCRLF("HTTP/1.1")), + _redireciton(locationBlock.getRedirect()){}; + + const std::string &getRedirection(void) + { + return (_redireciton); + } +}; + +#endif diff --git a/include/Server.hpp b/include/Server.hpp index 853bfd4..c59db2d 100644 --- a/include/Server.hpp +++ b/include/Server.hpp @@ -8,15 +8,17 @@ class Server { public: - Server(const ServerSettings &settings); + Server(const std::vector &server_settings); Server() = delete; Server(const Server &rhs) = delete; Server &operator=(const Server &rhs) = delete; ~Server(); + int getFD(void) const; + std::vector &getServerSettings(void); private: - const ServerSettings &_server_settings; + std::vector _server_settings; Socket _socket; }; diff --git a/include/ServerSettings.hpp b/include/ServerSettings.hpp index 31889a0..3ed6117 100644 --- a/include/ServerSettings.hpp +++ b/include/ServerSettings.hpp @@ -1,7 +1,6 @@ #ifndef SERVERSETTING_HPP #define SERVERSETTING_HPP -#include #include #include @@ -14,10 +13,12 @@ class ServerSettings ServerSettings(std::vector::iterator &token); ~ServerSettings(); ServerSettings(const ServerSettings &rhs); - ServerSettings &operator=(const ServerSettings &rhs) = delete; + ServerSettings &operator=(const ServerSettings &rhs); // Functionality: - const LocationSettings &resolveLocation(const std::string &request_target); + const LocationSettings & + resolveLocation(const std::string &request_target) const; + const std::string &getListen() const; const std::string &getServerName() const; const std::string &getErrorDir() const; @@ -33,6 +34,8 @@ class ServerSettings std::string _client_max_body_size; std::vector _location_settings; + const LocationSettings &getRootLocationBlock() const; + // Parsing: void addValueToServerSettings(const Token &key, std::vector::iterator &value); @@ -40,9 +43,6 @@ class ServerSettings void parseServerName(const Token value); void parseErrorDir(const Token value); void parseClientMaxBodySize(const Token value); - - // TODO: methods fucntion that can resolve if a read/write/delete can be - // done on a certain location in the LocationSettings }; #endif diff --git a/include/StatusCode.hpp b/include/StatusCode.hpp index 17e63b8..72614db 100644 --- a/include/StatusCode.hpp +++ b/include/StatusCode.hpp @@ -7,6 +7,7 @@ enum class StatusCode Created = 201, Accepted = 202, NoContent = 204, + MovedPermanently = 301, Found = 302, NotModified = 304, BadRequest = 400, @@ -16,7 +17,7 @@ enum class StatusCode MethodNotAllowed = 405, RequestTimeout = 408, LenghtRequired = 411, - PayloadToLarge = 413, + RequestBodyTooLarge = 413, URIToLong = 414, UnsupportedMediaType = 415, InternalServerError = 500, diff --git a/include/Token.hpp b/include/Token.hpp index 4be8dec..4d24beb 100644 --- a/include/Token.hpp +++ b/include/Token.hpp @@ -13,7 +13,6 @@ enum class TokenType SEMICOLON, OPEN_BRACKET, CLOSE_BRACKET, - PATH, WORD, }; diff --git a/makerc/options.mk b/makerc/options.mk index 80f38ba..2a8ac9e 100644 --- a/makerc/options.mk +++ b/makerc/options.mk @@ -1,6 +1,6 @@ # Flags ifdef DEBUG - CFLAGS +=-g -fstandalone-debug + CFLAGS +=-g #-fstandalone-debug endif diff --git a/out.txt b/out.txt deleted file mode 100644 index 40f3220..0000000 --- a/out.txt +++ /dev/null @@ -1,2 +0,0 @@ -['hello world!\n', 'goodbye world!\n'] -Content-type: text/html diff --git a/saveData.cpp b/saveData.cpp deleted file mode 100644 index 02c8e47..0000000 --- a/saveData.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include -#include - -int post_image(const std::string filename, const std::string post_data) -{ - std::ifstream inputFile(filename); - std::ofstream outputFile("temp.txt"); - std::ifstream image("data/images/" + post_data); - const std::string check_point = " "; - std::string converted_post_data = " "; - std::string line; - std::vector lines; - int currentLine = 0; - while (std::getline(inputFile, line)) { - currentLine++; - if (line == check_point && done) { - lines.push_back(line); - lines.push_back(converted_post_data); - done = false; - continue ; - } - lines.push_back(line); - } - for (const std::string& storedLine : lines) { - outputFile << storedLine << '\n'; - } - inputFile.close(); - outputFile.close(); - if (std::rename("temp.txt", filename.c_str()) != 0) { - std::cerr << "Error renaming the file." << std::endl; - return 1; - } - std::cout << "Image inserted successfully." << std::endl; - return 0; -} - - -int post_text(const std::string filename, const std::string content) -{ - std::ifstream inputFile(filename); - std::ofstream outputFile("temp.txt"); - const std::string check_point = " "; - std::string converted_post_data = "

"; - bool done = true; - - if (!inputFile || !outputFile) { - std::cerr << "Error opening files." << std::endl; - return 1; - } - if (content == "") - return 204; - converted_post_data += content + "

"; - std::string line; - std::vector lines; - int currentLine = 0; - while (std::getline(inputFile, line)) { - currentLine++; - if (line == check_point && done) { - lines.push_back(line); - lines.push_back(converted_post_data); - done = false; - continue ; - } - lines.push_back(line); - } - - for (const std::string& storedLine : lines) { - outputFile << storedLine << '\n'; - } - - inputFile.close(); - outputFile.close(); - - if (std::rename("temp.txt", filename.c_str()) != 0) { - std::cerr << "Error renaming the file." << std::endl; - return 1; - } - - std::cout << "Text inserted successfully." << std::endl; - return 0; -} - -// when implementing save_image it can be simplified by replacing -// function body with: - // std::ofstream new_image("data/" + post_message); - // new_image << conent; -bool save_data(const std::string post_message, const std::string content, - const std::string content_type) -{ - std::ifstream image_content(content); - std::string line; - - if (!image_content) - { - std::cerr << "Error opening files." << std::endl; - return (false); - } - // std::cout << "data/" + content_type + "/" + post_message + "\n"; - std::ofstream new_image("data/" + content_type + "/" + post_message); - while (std::getline(image_content, line)) { - new_image << line << '\n'; - } - return (true); -} - -int main(int argc, char **argv) -{ - if (argc != 4) - { - std::cout << "Invalid number of parameters.\n"; - return 0; - } - // const std::string filename = "data/www/index.html"; - const std::string post_message = argv[2]; - const std::string content_type = argv[1]; - if (save_data(post_message, argv[3], content_type) == false) { - std::cout << "400 BAD REQUEST\n"; - return (400); - } - - // post functionality is optional I guess - // if (content_type == "images") { - // if (post_image(filename, post_message) == 204) { - // std::cout << "204 NO CONTENT\n"; - // return (204); - // } - // } else if (content_type == "text") { - // if (post_text(filename, post_message) == 204) { - // std::cout << "204 NO CONTENT\n"; - // return (204); - // } - // } - return 0; -} \ No newline at end of file diff --git a/src/AutoIndexGenerator.cpp b/src/AutoIndexGenerator.cpp new file mode 100644 index 0000000..a148c13 --- /dev/null +++ b/src/AutoIndexGenerator.cpp @@ -0,0 +1,130 @@ + +#include "AutoIndexGenerator.hpp" +#include "Logger.hpp" + +#include +#include +#include + +#include +#include +#include + +std::string excludeSpecialCharacters(std::string str) +{ + std::string result; + + for (std::string::iterator it = str.begin(); it != str.end() - 1; ++it) + { + if (*it == '/') + result += "_"; + else + result += *it; + } + return (result); +} + +const std::string getStatSize(struct stat &filestat) +{ + std::string str; + char buf[100]; + time_t date = filestat.st_mtime; + struct tm *time = localtime(&date); + + strftime(buf, sizeof(buf), "%d %m %y", time); + str = std::string(buf); + + return (str); +} + +const std::string getStatDateLastModified(struct stat &filestat) +{ + std::string str = std::to_string((intmax_t)filestat.st_size); + return (str); +} + +void getTableLines(std::string &response, const std::string dir) +{ + // clang-format off + DIR *dirptr = opendir(dir.c_str()); + struct dirent *direnty; + + std::filesystem::path pwd = std::filesystem::current_path(); + short statreturn; + std::string filepath; + struct stat filestat; + + for (size_t i = 0 ; i < 2 ; i++) + direnty = readdir(dirptr); + while ((direnty = readdir(dirptr)) != NULL) + { + filepath = pwd.string() + "/" + dir + direnty->d_name; + statreturn = stat(filepath.c_str(), &filestat); + response += "\t\t\n\ + " + std::string(direnty->d_name) + "\n\ + " + ((statreturn != -1) ? getStatDateLastModified(filestat) : "UNKOWN") + "\n\ + " + ((statreturn != -1) ? getStatSize(filestat) : "UNKNOWN") + "\n\ + \n"; + } + // clang-format on +} + +std::string AutoIndexGenerator::AutoIndexGenerator(const std::string dir, + const std::string uri) +{ + std::string response; + + // clang-format off + response = "\ +\n\ +\n\ + \n\ + \t Index of "; + response += uri; + response += "\n\ + \n\ + \n\ +

Index of "; + response += uri; + response += "

\n\ +
\n\ + \n\ + \n\ + \n\ + \t\n\ + \t\n\ + \t\n\ + \n"; + + getTableLines(response, dir); + + response += "\ + \n\ +
File Name File Size Last Modification
\n\ + \n"; + // clang-format on + return (response); +} + +std::fstream AutoIndexGenerator::OpenAutoIndex(std::string directory, + const std::string uri) +{ + Logger &logger = Logger::getInstance(); + + logger.log(DEBUG, "OpenAutoIndex method is called:"); + const std::string escapeddir = excludeSpecialCharacters(directory); + + const std::string filename = "/tmp/" + escapeddir + ".autoindex"; + + std::fstream autoindex_fstream(filename.c_str(), std::fstream::out); + if (!autoindex_fstream.is_open()) + logger.log(DEBUG, filename + " isn't opened."); + + autoindex_fstream << AutoIndexGenerator(directory, uri); + autoindex_fstream.close(); + + std::fstream return_fstream(filename, std::ios::in); + std::remove(filename.c_str()); + + return (return_fstream); +} diff --git a/src/Client.cpp b/src/Client.cpp index 1911c29..be81d57 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,10 +1,16 @@ +#include "ClientState.hpp" +#include "ReturnException.hpp" #include #include #include +#include +#include #include -Client::Client(const int &server_fd) : _socket(server_fd) +Client::Client(const int &server_fd, std::vector &serversetting) + : _request(), _file_manager(), _socket(server_fd), + _server_list(serversetting), _serversetting(serversetting.at(0)) { _socket.setupClient(); } @@ -13,6 +19,33 @@ Client::~Client() { } +void Client::resolveServerSetting() +{ + Logger &logger = Logger::getInstance(); + const std::string &hp = _request.getHeader("Host"); + std::string host = hp.substr(0, hp.find_first_of(":")); + for (const ServerSettings &block : _server_list) + { + std::stringstream ss(block.getServerName()); + std::string name; + + for (; std::getline(ss, name, ' ');) + { + if (host == name) + { + _serversetting = block; + _request.setHeaderEnd(false); + break; + } + } + if (_request.getHeaderEnd() == false) + break; + } + logger.log(INFO, "Found ServerSetting: " + _serversetting.getServerName()); + _request.setMaxBodySize(_serversetting.getClientMaxBodySize()); + _file_manager.setServerSetting(_serversetting); +} + int Client::getFD(void) const { return (_socket.getFD()); @@ -28,14 +61,15 @@ ClientState Client::handleConnection(short events) if (events & POLLIN) { _state = _request.receive(_socket.getFD()); + if (_request.getHeaderEnd()) + resolveServerSetting(); return (_state); } else if (events & POLLOUT && _state == ClientState::Loading) { - _state = _file_manager.manage( - _request.getMethodType(), - "./data/www" + _request.getRequestTarget(), - _request.getBody()); // TODO: resolve location + _state = _file_manager.manage(_request.getMethodType(), + _request.getRequestTarget(), + _request.getBody()); return (_state); } else if (events & POLLOUT && _state == ClientState::Error) @@ -54,9 +88,19 @@ ClientState Client::handleConnection(short events) { logger.log(ERROR, "Client exception: " + std::string(e.what())); _response.clear(); - _response.append(e.what()); + _file_manager.setResponse(e.what()); _state = _file_manager.openErrorPage( - "./data/errors", e.getStatusCode()); // TODO: resolve location + _serversetting.getErrorDir().substr(1), e.getStatusCode()); + return (_state); + } + catch (ReturnException &e) + { + logger.log(ERROR, "Return exception: " + std::string(e.what())); + _response.clear(); + _file_manager.setResponse(e.what()); + _file_manager.addToResponse("Location: " + e.getRedirection() + + "\r\n\r\n"); + _state = ClientState::Sending; return (_state); } return (ClientState::Unkown); diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index fe4d1a3..66cc7bf 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -6,7 +6,9 @@ #include #include -#include +#include +#include +#include #include #include #include @@ -20,6 +22,31 @@ ConfigParser::~ConfigParser() { } +std::vector> ConfigParser::sortServerSettings() +{ + Logger &logger = Logger::getInstance(); + logger.log(INFO, "sortServerSettings"); + + std::set hpset; + std::vector> vec_servers; + + for (ServerSettings block : _server_settings) + { + std::set::iterator it = hpset.find(block.getListen()); + if (it == hpset.end()) + { + hpset.emplace(block.getListen()); + std::vector vec; + vec.push_back(block); + vec_servers.emplace_back(vec); + } + else + vec_servers.at(std::distance(hpset.begin(), it)) + .emplace_back(block); + } + return (vec_servers); +} + const std::vector &ConfigParser::getServerSettings() { return (_server_settings); @@ -50,20 +77,11 @@ void ConfigParser::ParseConfig() tokenizeStream(OpenFile(), tokenlist); syntax(tokenlist); - logger.log(INFO, "Syntax of " + _config_file_path + " finished"); + logger.log(INFO, "Passed Syntax " + _config_file_path); for (std::vector::iterator it = tokenlist.begin(); it != tokenlist.end(); it++) _server_settings.emplace_back(ServerSettings(it)); - // { - // const LocationSettings &loc = it.resolveLocation("/t/"); - // - // logger.log(DEBUG, "LocationSettings.getRequestTarget: " + - // loc.getRequestTarget() + - // "\tcheck: " + loc.getIndex()); - // } - // - // logger.log(INFO, "Parsed configfile: " + _config_file_path); } diff --git a/src/ConfigSyntax.cpp b/src/ConfigSyntax.cpp index 2ddc11e..a00b51c 100644 --- a/src/ConfigSyntax.cpp +++ b/src/ConfigSyntax.cpp @@ -99,7 +99,6 @@ void syntaxLocationBlock(std::vector &tokens, while (it != closing_token) { - syntaxLine(it); it++; } diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 03328bf..4b6375f 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -1,11 +1,15 @@ -#include -#include -#include +#include "FileManager.hpp" +#include "AutoIndexGenerator.hpp" +#include "ClientException.hpp" +#include "Logger.hpp" +#include "ReturnException.hpp" +#include "StatusCode.hpp" -#include #include +#include -FileManager::FileManager() : _response(), _request_target() +FileManager::FileManager() + : _response(), _request_target(), _serversetting(), _autoindex(false) { } @@ -13,11 +17,53 @@ FileManager::~FileManager() { } +std::string +FileManager::applyLocationSettings(const std::string &request_target, + HTTPMethod method) +{ + + // substr is required to remove starting '/' + const LocationSettings &loc = + _serversetting.resolveLocation(request_target); + + if (loc.resolveMethod(method) == false) + throw ClientException(StatusCode::MethodNotAllowed); + if (!loc.getRedirect().empty()) + throw ReturnException(StatusCode::Found, loc); + + if (request_target.find_last_of('/') == request_target.length() - 1) + { + if (!loc.getIndex().empty()) + return (loc.resolveAlias(request_target).substr(1) + + loc.getIndex()); + else if (loc.getAutoIndex() == false) + return (loc.resolveAlias(request_target).substr(1) + + "index.html"); /* default value, potential + TODO: set Default values in default + assigner; */ + _request_target = AutoIndexGenerator::OpenAutoIndex( + loc.resolveAlias(request_target).substr(1), request_target); + _autoindex = true; + } + + return (loc.resolveAlias(request_target).substr(1)); +} + void FileManager::openGetFile(const std::string &request_target_path) { - if (!std::filesystem::exists(request_target_path)) + const std::string resolved_target = + applyLocationSettings(request_target_path, HTTPMethod::GET); + + if (_autoindex == true) + { + HTTPStatus status(StatusCode::OK); + _response += status.getStatusLine("HTTP/1.1"); + return; + } + + if (!std::filesystem::exists(resolved_target)) throw ClientException(StatusCode::NotFound); - _request_target.open(request_target_path, std::ios::in); + _request_target.open(resolved_target, std::ios::in | std::ios::binary); if (!_request_target.is_open()) throw ClientException(StatusCode::NotFound); HTTPStatus status(StatusCode::OK); @@ -26,9 +72,12 @@ void FileManager::openGetFile(const std::string &request_target_path) void FileManager::openPostFile(const std::string &request_target_path) { - if (!std::filesystem::exists(request_target_path)) + const std::string resolved_target = + applyLocationSettings(request_target_path, HTTPMethod::GET); + + if (!std::filesystem::exists(resolved_target)) { - _request_target.open(request_target_path, std::ios::out); + _request_target.open(resolved_target, std::ios::out); if (!_request_target.is_open()) throw ClientException(StatusCode::InternalServerError); HTTPStatus status(StatusCode::Created); @@ -48,9 +97,15 @@ void FileManager::openPostFile(const std::string &request_target_path) ClientState FileManager::openErrorPage(const std::string &error_pages_path, const StatusCode &status_code) { + Logger &logger = Logger::getInstance(); + + logger.log(DEBUG, "openErrorPage method is called. (" + error_pages_path + + std::to_string(static_cast(status_code)) + + ".html)"); _request_target.open(error_pages_path + - std::to_string(static_cast(status_code)) + - ".html"); + std::to_string(static_cast(status_code)) + + ".html", + std::ios::in); if (!_request_target.is_open()) { HTTPStatus status(status_code); @@ -69,10 +124,13 @@ ClientState FileManager::loadErrorPage(void) HTTPStatus status(StatusCode::InternalServerError); _response = status.getStatusLine("HTTP/1.1") + status.getHTMLStatus(); } - _response += std::string(buffer); + buffer[_request_target.gcount()] = '\0'; + _response += std::string(buffer, _request_target.gcount()); if (_request_target.eof()) + { return (ClientState::Sending); - return (ClientState::Loading); + } + return (ClientState::Error); } ClientState FileManager::manageGet(void) @@ -80,13 +138,12 @@ ClientState FileManager::manageGet(void) Logger &logger = Logger::getInstance(); char buffer[BUFFER_SIZE + 1]; - logger.log(DEBUG, "manageGet method is called"); + logger.log(DEBUG, "manageGet method is called:"); _request_target.read(buffer, BUFFER_SIZE); if (_request_target.bad()) throw ClientException(StatusCode::InternalServerError); - logger.log(DEBUG, "get buffer: " + std::string(buffer)); buffer[_request_target.gcount()] = '\0'; - _response += std::string(buffer); + _response += std::string(buffer, _request_target.gcount()); if (_request_target.eof()) return (ClientState::Sending); return (ClientState::Loading); @@ -123,6 +180,9 @@ ClientState FileManager::manage(HTTPMethod method, const std::string &request_target_path, const std::string &body) { + Logger &logger = Logger::getInstance(); + + logger.log(DEBUG, "FileManager::manage: "); if (method == HTTPMethod::DELETE) return (manageDelete(request_target_path)); if (method == HTTPMethod::GET) @@ -144,3 +204,18 @@ const std::string &FileManager::getResponse(void) const { return (_response); } + +void FileManager::addToResponse(const std::string str) +{ + _response += str; +} + +void FileManager::setResponse(const std::string str) +{ + _response = str; +} + +void FileManager::setServerSetting(const ServerSettings &serversetting) +{ + _serversetting = serversetting; +} diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 25c80e4..7030399 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -1,5 +1,8 @@ + +#include #include #include +#include #include #include @@ -7,8 +10,9 @@ #include HTTPRequest::HTTPRequest() - : _bytes_read(0), _content_length(0), _methodType(HTTPMethod::UNKNOWN), - _http_request(), _request_target(), _http_version(), _body(), _headers() + : _bytes_read(0), _content_length(0), _max_body_size(), + _methodType(HTTPMethod::UNKNOWN), _http_request(), _request_target(), + _http_version(), _body(), _headers() { } @@ -33,6 +37,28 @@ HTTPMethod HTTPRequest::getMethodType(void) const return (_methodType); } +void HTTPRequest::setMaxBodySize(std::string inp) +{ + size_t pos = inp.find_first_of("KM"); + std::string nbr = inp.substr(0, pos); + std::string mag; + if (pos != std::string::npos) + { + mag = inp.substr(pos); + if (mag == "K") + nbr += "000"; + else // (mag == "M") + nbr += "000000"; + } + + _max_body_size = std::stoull(nbr); +} + +ssize_t HTTPRequest::getMaxBodySize(void) const +{ + return (_max_body_size); +} + void HTTPRequest::setHeader(const std::string &key, const std::string &header) { _headers.emplace(key, header); @@ -68,6 +94,16 @@ const std::string &HTTPRequest::getBody(void) const return (_body); } +void HTTPRequest::setHeaderEnd(bool b) +{ + _header_end = b; +} + +bool HTTPRequest::getHeaderEnd() const +{ + return (_header_end); +} + size_t HTTPRequest::parseStartLine(size_t &i) { Logger &logger = Logger::getInstance(); @@ -75,9 +111,11 @@ size_t HTTPRequest::parseStartLine(size_t &i) pos = _http_request.find(' ', i); setMethodType(_http_request.substr(i, pos - i)); + i = pos + 1; pos = _http_request.find(' ', i); setRequestTarget(_http_request.substr(i, pos - i)); + i = pos + 1; pos = _http_request.find("\r\n", i); setHTTPVersion(_http_request.substr(i, pos - i)); @@ -107,6 +145,18 @@ size_t HTTPRequest::parseHeaders(size_t &i) return (i); } +ClientState HTTPRequest::setRequestVariables(size_t pos) +{ + _header_end = true; + if (_headers.find("Content-length") != _headers.end()) + _content_length = std::stoi(getHeader("Content-length")); + + if (_content_length == 0) + return (ClientState::Loading); + _body += _http_request.substr(pos + 2); + return (ClientState::Receiving); +} + ClientState HTTPRequest::receive(int client_fd) { Logger &logger = Logger::getInstance(); @@ -121,6 +171,8 @@ ClientState HTTPRequest::receive(int client_fd) if (_content_length != 0) { _body += std::string(buffer, _bytes_read); + if (_body.size() >= _max_body_size) + throw ClientException(StatusCode::RequestBodyTooLarge); if (_body.size() >= _content_length) { logger.log(DEBUG, "Body: " + _body); @@ -136,18 +188,7 @@ ClientState HTTPRequest::receive(int client_fd) { header_end = _http_request.substr(pos - 2, 4); if (header_end == "\r\n\r\n") - { - if (_headers.find("Content-length") != _headers.end()) - { - _content_length = std::stoi(getHeader("Content-length")); - if (_content_length == 0) - return (ClientState::Loading); - _body += _http_request.substr(pos + 2); - return (ClientState::Receiving); - } - else - return (ClientState::Loading); - } + return (setRequestVariables(pos)); pos = parseHeaders(i); } return (ClientState::Receiving); diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index 4770c28..3e8c801 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -1,8 +1,9 @@ -#include "HTTPRequest.hpp" +#include #include #include #include +#include #include HTTPResponse::HTTPResponse() : _bytes_sent(0), _response("") @@ -31,10 +32,11 @@ ClientState HTTPResponse::send(int client_fd, const std::string &response) ssize_t w_size; if (_response.empty()) + { _response = response; + } logger.log(INFO, "Sending response to client on fd: " + std::to_string(client_fd)); - logger.log(DEBUG, "response: " + _response); if (_response.length() - _bytes_sent < BUFFER_SIZE) bytes_to_send = _response.length() - _bytes_sent; else @@ -43,7 +45,7 @@ ClientState HTTPResponse::send(int client_fd, const std::string &response) write(client_fd, _response.substr(_bytes_sent).c_str(), bytes_to_send); if (w_size == -1) throw SystemException("Error: write failed on: " + - std::to_string(client_fd)); // TODO handle error + std::to_string(client_fd)); // TODO: handle error _bytes_sent += w_size; if (_bytes_sent == _response.length()) { diff --git a/src/HTTPServer.cpp b/src/HTTPServer.cpp index 5aec298..d6d0516 100644 --- a/src/HTTPServer.cpp +++ b/src/HTTPServer.cpp @@ -26,6 +26,8 @@ int HTTPServer::run() _parser.ParseConfig(); setupServers(); logger.log(INFO, "Server started"); + while (true) + handleActivePollFDs(); } catch (const std::runtime_error &e) { @@ -33,19 +35,6 @@ int HTTPServer::run() return (EXIT_FAILURE); } return (EXIT_SUCCESS); - - while (true) - { - try - { - handleActivePollFDs(); - } - catch (const std::runtime_error &e) - { - logger.log(FATAL, e.what()); - return (EXIT_FAILURE); - } - } } void HTTPServer::setupServers(void) @@ -53,13 +42,12 @@ void HTTPServer::setupServers(void) Logger &logger = Logger::getInstance(); logger.log(INFO, "Setting up server sockets"); - const std::vector &server_settings = - _parser.getServerSettings(); + std::vector> server_list = + _parser.sortServerSettings(); - for (const auto &server_setting : server_settings) + for (const std::vector &list : server_list) { - std::shared_ptr server = - std::make_shared(server_setting); + std::shared_ptr server = std::make_shared(list); _active_servers.emplace(server->getFD(), server); _poll.addPollFD(server->getFD(), POLLIN); } @@ -88,7 +76,9 @@ void HTTPServer::handleActivePollFDs() " revents: " + _poll.pollEventsToString(poll_fd.revents)); if (_active_servers.find(poll_fd.fd) != _active_servers.end()) - handleNewConnection(poll_fd.fd); + handleNewConnection( + poll_fd.fd, + _active_servers.find(poll_fd.fd)->second->getServerSettings()); else if (_active_clients.find(poll_fd.fd) != _active_clients.end()) handleExistingConnection(poll_fd); else @@ -96,9 +86,11 @@ void HTTPServer::handleActivePollFDs() } } -void HTTPServer::handleNewConnection(int fd) +void HTTPServer::handleNewConnection( + int fd, std::vector &ServerSettings) { - std::shared_ptr client = std::make_shared(fd); + std::shared_ptr client = + std::make_shared(fd, ServerSettings); _active_clients.emplace(client->getFD(), client); _poll.addPollFD(client->getFD(), POLLIN); } diff --git a/src/HTTPStatus.cpp b/src/HTTPStatus.cpp index 52d7ad0..a5be2a4 100644 --- a/src/HTTPStatus.cpp +++ b/src/HTTPStatus.cpp @@ -6,6 +6,7 @@ std::unordered_map HTTPStatus::_message = { {StatusCode::Created, "Created"}, {StatusCode::Accepted, "Accepted"}, {StatusCode::NoContent, "No Content"}, + {StatusCode::MovedPermanently, "Moved Permanently"}, {StatusCode::Found, "Found"}, {StatusCode::NotModified, "Not Modified"}, {StatusCode::BadRequest, "Bad Request"}, @@ -15,7 +16,7 @@ std::unordered_map HTTPStatus::_message = { {StatusCode::MethodNotAllowed, "Method Not Allowed"}, {StatusCode::RequestTimeout, "Request Timeout"}, {StatusCode::LenghtRequired, "Length Required"}, - {StatusCode::PayloadToLarge, "Payload Too Large"}, + {StatusCode::RequestBodyTooLarge, "Request Body Too Large"}, {StatusCode::URIToLong, "URI Too Long"}, {StatusCode::UnsupportedMediaType, "Unsupported Media Type"}, {StatusCode::InternalServerError, "Internal Server Error"}, @@ -32,6 +33,13 @@ HTTPStatus::~HTTPStatus() { } +std::string HTTPStatus::getStatusLineCRLF(const std::string &version) const +{ + return (version + " " + std::to_string(static_cast(_status_code)) + + " " + _message.at(_status_code)) + + "\r\n"; +} + std::string HTTPStatus::getStatusLine(const std::string &version) const { return (version + " " + std::to_string(static_cast(_status_code)) + @@ -41,10 +49,10 @@ std::string HTTPStatus::getStatusLine(const std::string &version) const std::string HTTPStatus::getHTMLStatus(void) const { - return ("

" + - std::to_string(static_cast(_status_code)) + " " + + return ("

ERROR: " + + std::to_string(static_cast(_status_code)) + "

(" + _message.at(_status_code)) + - "

"; + ")

"; } StatusCode HTTPStatus::getStatusCode() const diff --git a/src/LocationSettings.cpp b/src/LocationSettings.cpp index 887fa68..6bf2625 100644 --- a/src/LocationSettings.cpp +++ b/src/LocationSettings.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -7,27 +8,42 @@ #include LocationSettings::LocationSettings() - : _requesttarget(), _alias(), _index(), _allowed_methods(), _cgi_path(), - _auto_index() + : _path(), _alias(), _index(), _allowed_methods(), _cgi_path(), _redirect(), + _auto_index(false) { } LocationSettings::LocationSettings(const LocationSettings &rhs) - : _requesttarget(rhs._requesttarget), _alias(rhs._alias), - _index(rhs._index), _allowed_methods(rhs._allowed_methods), - _cgi_path(rhs._cgi_path), _auto_index(rhs._auto_index) + : _path(rhs._path), _alias(rhs._alias), _index(rhs._index), + _allowed_methods(rhs._allowed_methods), _cgi_path(rhs._cgi_path), + _redirect(rhs._redirect), _auto_index(rhs._auto_index) { } +LocationSettings &LocationSettings::operator=(const LocationSettings &rhs) +{ + if (this == &rhs) + return (*this); + + _path = rhs._path; + + _alias = rhs._alias; + _index = rhs._index; + _allowed_methods = rhs._allowed_methods; + _cgi_path = rhs._cgi_path; + _redirect = rhs._redirect; + _auto_index = rhs._auto_index; + + return (*this); +} + LocationSettings::~LocationSettings() { } LocationSettings::LocationSettings(std::vector::iterator &token) - : _requesttarget(), _alias(), _index(), _allowed_methods(), _cgi_path(), - _auto_index() { - _requesttarget = token->getString(); + _path = token->getString(); token += 2; while (token->getType() != TokenType::CLOSE_BRACKET) @@ -47,14 +63,16 @@ LocationSettings::LocationSettings(std::vector::iterator &token) parseAutoIndex(*token); else if (key.getString() == "allowed_methods") parseAllowedMethods(*token); - else if (key.getString() == "cgi_path") + else if (key.getString() == "cgi") parseCgiPath(*token); else if (key.getString() == "return") parseReturn(*token); else + { logger.log(WARNING, "LocationSettings: unknown KEY token: " + key.getString()); - + break; + } token++; } token++; @@ -67,8 +85,7 @@ void LocationSettings::parseAlias(const Token token) if (!_alias.empty()) logger.log(WARNING, - "ConfigParser: redefining alias in locationblock: " + - _requesttarget); + "ConfigParser: redefining alias in locationblock: " + _path); _alias = token.getString(); } @@ -78,8 +95,7 @@ void LocationSettings::parseIndex(const Token token) if (!_index.empty()) logger.log(WARNING, - "ConfigParser: redefining index in locationblock: " + - _requesttarget); + "ConfigParser: redefining index in locationblock: " + _path); _index = token.getString(); } @@ -106,26 +122,31 @@ void LocationSettings::parseAllowedMethods(const Token token) void LocationSettings::parseCgiPath(const Token token) { + Logger &logger = Logger::getInstance(); - _cgi_path.append(" " + token.getString()); + if (!_index.empty()) + logger.log(WARNING, + "ConfigParser: redefining cgi_path in locationblock: " + + _path); + _cgi_path = token.getString(); } void LocationSettings::parseReturn(const Token token) { Logger &logger = Logger::getInstance(); - if (!_return.empty()) + if (!_redirect.empty()) logger.log(WARNING, "ConfigParser: redefining return in locationblock: " + - _requesttarget); - _return.append(" " + token.getString()); + _path); + _redirect = token.getString(); } // Functionality: // getters: -const std::string &LocationSettings::getRequestTarget() const +const std::string &LocationSettings::getPath() const { - return (_requesttarget); + return (_path); } const std::string &LocationSettings::getAlias() const @@ -148,15 +169,75 @@ bool LocationSettings::getAutoIndex() const return (_auto_index); } -const std::string &LocationSettings::getReturn() const +const std::string &LocationSettings::getRedirect() const { - return (_return); + return (_redirect); } -// setters: -void LocationSettings::setDir(const std::string &requesttarget) +const std::string MethodToString(HTTPMethod num) { - _requesttarget = requesttarget; + switch (num) + { + case (HTTPMethod::GET): + return ("GET"); + case (HTTPMethod::POST): + return ("POST"); + case (HTTPMethod::DELETE): + return ("DELETE"); + default: + { + Logger &logger = Logger::getInstance(); + logger.log(WARNING, "LocationSettings MethodToString: unknown Method"); + return ("UNKOWNMETHOD"); + } + } +} + +// resolveMethod +bool LocationSettings::resolveMethod(const HTTPMethod method) const +{ + Logger &logger = Logger::getInstance(); + + if (getAllowedMethods().empty()) + logger.log(WARNING, + "ResolveMethod: No HTTPMethod specified in Locationblock: " + + getPath()); // TODO: Should throw exception? + std::stringstream ss(getAllowedMethods()); + std::string option; + for (; std::getline(ss, option, ' ');) + { + if (option == MethodToString(method)) + return (true); + } + return (false); +} + +// resolveAlias + +const std::string LocationSettings::resolveAlias(const std::string inp) const +{ + Logger &logger = Logger::getInstance(); + + std::string alias = getAlias(); + logger.log(DEBUG, "resolveAlias: received: " + inp + "\tAlias: " + alias); + if (alias.empty()) + return (inp); + if (_path.length() == 1) + return (alias + inp.substr(1, inp.length() - 1)); + + size_t pos_begin = alias.length() - 1; + size_t pos_end = pos_begin; + while (pos_end != 0) + { + pos_end = pos_begin; + pos_begin = alias.find_last_of("/", pos_end - 1); + std::string hit = alias.substr(pos_begin, pos_end - pos_begin + 1); + if (inp.find(hit) == std::string::npos) // aka hit not found in inp. + break; + } + std::string request_target = alias.substr(0, pos_end) + inp; + + return (request_target); } // THIS IS PRINTING FUNCTION @@ -165,12 +246,12 @@ void LocationSettings::printLocationSettings() const { Logger &logger = Logger::getInstance(); - logger.log(DEBUG, "\tLocation Prefix:\t" + _requesttarget); + logger.log(DEBUG, "\tLocation Prefix:\t" + _path); logger.log(DEBUG, "\t\tAlias:\t\t\t" + _alias); logger.log(DEBUG, "\t\tIndex:\t\t\t" + _index); logger.log(DEBUG, "\t\tAllowed_methods:\t" + _allowed_methods); logger.log(DEBUG, "\t\tCGI Path:\t\t" + _cgi_path); - logger.log(DEBUG, "\t\tReturn:\t\t" + _return); + logger.log(DEBUG, "\t\tRedirect:\t\t" + _redirect); logger.log(DEBUG, "\t\tAutoIndex:\t\t" + (_auto_index ? std::string(" ON") : std::string(" OFF"))); diff --git a/src/Poll.cpp b/src/Poll.cpp index 2262a66..67c4fae 100644 --- a/src/Poll.cpp +++ b/src/Poll.cpp @@ -44,7 +44,7 @@ void Poll::pollFDs(void) " file descriptors"); int poll_count = poll(_poll_fds.data(), _poll_fds.size(), NO_TIMEOUT); if (poll_count == SYSTEM_ERROR || poll_count == 0) - throw SystemException("poll"); // TODO change poll_count 0 handler + throw SystemException("poll"); // TODO: change poll_count 0 handler } std::string Poll::pollEventsToString(short events) const diff --git a/src/Server.cpp b/src/Server.cpp index a856e04..2b33923 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -1,17 +1,16 @@ #include #include #include +#include -Server::Server(const ServerSettings &server_settings) +Server::Server(const std::vector &server_settings) : _server_settings(server_settings), _socket() { Logger &logger = Logger::getInstance(); - _socket.setupServer( - _server_settings.getListen()); // TODO: make sure setupServer is capable - // of having getListen as an input. + _socket.setupServer(_server_settings.at(0).getListen()); logger.log(DEBUG, "Created Server on host:port " + - _server_settings.getListen() + + _server_settings.at(0).getListen() + " on fd: " + std::to_string(_socket.getFD())); } @@ -19,6 +18,11 @@ Server::~Server() { } +std::vector &Server::getServerSettings(void) +{ + return (_server_settings); +} + int Server::getFD(void) const { return (_socket.getFD()); diff --git a/src/ServerSettings.cpp b/src/ServerSettings.cpp index 7eab019..d17467a 100644 --- a/src/ServerSettings.cpp +++ b/src/ServerSettings.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include +#include #include #include @@ -23,6 +26,18 @@ ServerSettings::ServerSettings(const ServerSettings &rhs) { } +ServerSettings &ServerSettings::operator=(const ServerSettings &rhs) +{ + if (this == &rhs) + return (*this); + _listen = rhs._listen; + _server_name = rhs._server_name; + _error_dir = rhs._error_dir; + _client_max_body_size = rhs._client_max_body_size; + _location_settings = rhs._location_settings; + return (*this); +} + ServerSettings::~ServerSettings() { } @@ -50,7 +65,31 @@ ServerSettings::ServerSettings(std::vector::iterator &token) } } -void validateListen(const std::string &str) +const std::string convertHost(const std::string &str) +{ + Logger &logger = Logger::getInstance(); + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_STREAM; + + struct addrinfo *res = NULL; + int ret; + + if ((ret = getaddrinfo(str.c_str(), "http", &hints, &res)) != 0) + { + logger.log(WARNING, + "Unable to get IPv4: " + std::string(gai_strerror(ret))); + // TODO: trhow? + } + void *addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + char ipstr[INET_ADDRSTRLEN]; + inet_ntop(res->ai_family, addr, ipstr, sizeof(ipstr)); + return (std::string(ipstr)); +} + +const std::string validateListen(const std::string &str) { size_t pos = str.find_first_of(":"); if (pos == std::string::npos || pos != str.find_last_of(":")) @@ -62,19 +101,23 @@ void validateListen(const std::string &str) try { int port_ = std::stoi(port); - if (port_ < 1 || port_ > 65535) // + if (port_ < 1 || port_ > 65535) throw std::exception(); } catch (std::exception &e) { - throw std::runtime_error("Parsing Error: invalid port found in listen"); + throw std::runtime_error("Parsing Error: found invalid PORT in listen"); } + return (convertHost(ip) + ":" + port); } void ServerSettings::parseListen(const Token value) { - validateListen(value.getString()); - _listen.append(" " + value.getString()); + Logger &logger = Logger::getInstance(); + + if (!_listen.empty()) + logger.log(WARNING, "ConfigParser: redefining listen"); + _listen = validateListen(value.getString()); } void ServerSettings::parseServerName(const Token value) @@ -95,9 +138,24 @@ void ServerSettings::parseClientMaxBodySize(const Token value) { Logger &logger = Logger::getInstance(); + const std::regex rgx_pat = std::regex("^\\d{1,3}[KM]?$"); + + std::sregex_iterator it(value.getString().begin(), value.getString().end(), + rgx_pat); + std::sregex_iterator end; + + if (std::distance(it, end) == 0) + { + logger.log(FATAL, "ConfigParser: clientmaxbodysize inpropperly " + "formated: \"d{1,3}[MK]?\""); + throw std::runtime_error( + "ConfigParser: invalid value for clientmaxbodysize"); + } + if (!_client_max_body_size.empty()) logger.log(WARNING, "ConfigParser: redefining clientmaxbodysize"); - _client_max_body_size = value.getString(); + + _client_max_body_size = it->str(); } void ServerSettings::addValueToServerSettings( @@ -123,22 +181,6 @@ void ServerSettings::addValueToServerSettings( } // Functionality: -const std::string -methodToString(HTTPMethod method) // TODO: change data_types in function -{ - switch (method) - { - case (HTTPMethod::GET): - return ("GET"); - case (HTTPMethod::POST): - return ("POST"); - case (HTTPMethod::DELETE): - return ("DELETE"); - default: - throw std::runtime_error("Unknown HTTPMethod"); - } -} - // getters: const std::string &ServerSettings::getListen() const { @@ -160,9 +202,12 @@ const std::string &ServerSettings::getClientMaxBodySize() const return (_client_max_body_size); } -// Funcion: find the longest possible locationblock form the URI. -// URI will be stripped from it's trailing file. (line 3) -// and expects LocationBlock requesttarget to always start with a '/' +// Funcion: find the longest possible locationblock that fits the +// request_target. request_target will be stripped from it's trailing input. +// (line 3) and expects LocationBlock requesttarget to always start and end with +// a '/' +// +// EXAMPLES: // // server { // location / {} @@ -179,24 +224,24 @@ const std::string &ServerSettings::getClientMaxBodySize() const // /png/images/ => / // -const LocationSettings &ServerSettings::resolveLocation(const std::string &URI) +const LocationSettings & +ServerSettings::resolveLocation(const std::string &request_target) const { - LocationSettings *ret = nullptr; - const std::string requesttarget = URI.substr(0, URI.find_last_of("/") + 1); + const LocationSettings *ret = nullptr; + std::string searched = request_target.substr(0, request_target.find("?")); - for (auto &instance : _location_settings) + for (const auto &instance : _location_settings) { - const size_t pos = requesttarget.find(instance.getRequestTarget()); + const size_t pos = request_target.find(instance.getPath()); if (pos != 0) continue; - if (ret != nullptr) + if (ret == nullptr) { - if (instance.getRequestTarget().length() > - ret->getRequestTarget().length()) - ret = &instance; + ret = &instance; + continue; } - else + if (instance.getPath().length() > ret->getPath().length()) ret = &instance; } if (ret == nullptr) @@ -222,9 +267,9 @@ void ServerSettings::printServerSettings() const for (auto &location_instance : _location_settings) { - logger.log(DEBUG, "\n"); location_instance.printLocationSettings(); } + logger.log(DEBUG, "\n"); // We can go over the different strings by using Getline // diff --git a/src/Socket.cpp b/src/Socket.cpp index 630f26b..c85178d 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -46,16 +47,20 @@ Socket::~Socket() assert(close(getFD()) != SYSTEM_ERROR); } -void Socket::initSockaddrIn(t_sockaddr_in &addr, const std::string &_port) +void Socket::initSockaddrIn(t_sockaddr_in &addr, const std::string &_listen) { + size_t pos = _listen.find(":"); + std::string host = _listen.substr(0, pos); + std::string port = _listen.substr(pos + 1); + bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(std::stoi(_port)); + addr.sin_addr.s_addr = htonl(INADDR_ANY); // TODO: implement getaddrinfo + addr.sin_port = htons(std::stoi(port)); std::fill_n(addr.sin_zero, sizeof(addr.sin_zero), '\0'); } -void Socket::setupServer(const std::string &port) +void Socket::setupServer(const std::string &Listen) { int option = 1; if (fcntl(getFD(), F_SETFL, O_NONBLOCK) == SYSTEM_ERROR) @@ -63,7 +68,7 @@ void Socket::setupServer(const std::string &port) if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) == SYSTEM_ERROR) throw SystemException("setsockopt failed"); - initSockaddrIn(_addr, port); + initSockaddrIn(_addr, Listen); if (bind(getFD(), (t_sockaddr *)&_addr, sizeof(t_sockaddr_in)) == SYSTEM_ERROR) throw SystemException("Bind"); diff --git a/src/Token.cpp b/src/Token.cpp index 4a3419f..67dbc4d 100644 --- a/src/Token.cpp +++ b/src/Token.cpp @@ -110,8 +110,6 @@ std::string Token::typeToString() const return ("OPEN_BRACKET"); case TokenType::CLOSE_BRACKET: return ("CLOSE_BRACKET"); - case TokenType::PATH: - return ("PATH"); case TokenType::WORD: return ("WORD"); } diff --git a/tests/GET_local.sh b/tests/GET_local.sh index ba99459..1a2a114 100755 --- a/tests/GET_local.sh +++ b/tests/GET_local.sh @@ -1,16 +1,18 @@ #!/bin/bash if [[ -z $1 ]]; then - SERVER_PORT="9696" + SERVER_PORT="8080" else SERVER_PORT=$1 fi -HTTP_VERSION="http1.1" +HTTP_VERSION="HTTP/1.1" SERVER_HOST="localhost" SERVER_LOG_DIR=../build/log/ +HTTP_REQUEST="./request/get.txt" + echo "GET REQUEST on localhost at port $SERVER_PORT" echo "" diff --git a/tests/get_request.sh b/tests/get_request.sh index 8923074..30d03d3 100755 --- a/tests/get_request.sh +++ b/tests/get_request.sh @@ -2,10 +2,10 @@ # Server configuration SERVER_HOST="localhost" -SERVER_PORT="9696" +SERVER_PORT="8080" # Request file -REQUEST_FILE="tests/request/delete.txt" +REQUEST_FILE="tests/request/get.txt" # Use netcat to send the contents of the request file to the server -nc "${SERVER_HOST}" "${SERVER_PORT}" < "${REQUEST_FILE}" +nc "${SERVER_HOST}" "${SERVER_PORT}" <"${REQUEST_FILE}" diff --git a/tests/request/get.txt b/tests/request/get.txt index b5de54c..6543118 100755 --- a/tests/request/get.txt +++ b/tests/request/get.txt @@ -1,4 +1,4 @@ -GET /index.html HTTP/1.1 +GET /this/wont/work HTTP/1.1 User-Agent: custom-client Host: weebserv Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5