diff --git a/.iwyu.imp b/.iwyu.imp index 8949600..ec004d4 100644 --- a/.iwyu.imp +++ b/.iwyu.imp @@ -14,7 +14,7 @@ { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, - + # Standard library strict mappings (preference 5C - very strict) { include: ["", "public", "", "public"] }, { symbol: ["std::cout", "private", "", "public"] }, @@ -29,7 +29,7 @@ { symbol: ["std::shared_ptr", "private", "", "public"] }, { symbol: ["std::make_unique", "private", "", "public"] }, { symbol: ["std::make_shared", "private", "", "public"] }, - + # System headers for socket programming { include: ["", "public", "", "public"] }, { include: ["", "public", "", "public"] }, @@ -37,46 +37,46 @@ { include: ["", "public", "", "public"] }, { include: ["", "public", "", "public"] }, { include: ["", "public", "", "public"] }, - + # Error handling (preference 6A - stdexcept) { symbol: ["std::runtime_error", "private", "", "public"] }, { symbol: ["std::logic_error", "private", "", "public"] }, { symbol: ["std::invalid_argument", "private", "", "public"] }, { symbol: ["std::out_of_range", "private", "", "public"] }, - + # ============================================================================ # PROJECT HEADER MAPPINGS - Standardized on <> angle brackets # ============================================================================ - + # Convert quoted includes to angle brackets for consistency { include: ["\"webserv/log/Log.hpp\"", "public", "", "public"] }, { include: ["\"webserv/log/Channel.hpp\"", "public", "", "public"] }, { include: ["\"webserv/log/StdoutChannel.hpp\"", "public", "", "public"] }, { include: ["\"webserv/log/FileChannel.hpp\"", "public", "", "public"] }, - + { include: ["\"webserv/http/HttpRequest.hpp\"", "public", "", "public"] }, { include: ["\"webserv/http/HttpResponse.hpp\"", "public", "", "public"] }, { include: ["\"webserv/http/HttpHeaders.hpp\"", "public", "", "public"] }, { include: ["\"webserv/http/HttpConstants.hpp\"", "public", "", "public"] }, - + { include: ["\"webserv/config/ConfigManager.hpp\"", "public", "", "public"] }, { include: ["\"webserv/config/ServerConfig.hpp\"", "public", "", "public"] }, { include: ["\"webserv/config/LocationConfig.hpp\"", "public", "", "public"] }, - { include: ["\"webserv/config/utils.hpp\"", "public", "", "public"] }, - + { include: ["\i"webserv/utils/utls.hpp\"", "public", "", "public"] }, + { include: ["\"webserv/server/Server.hpp\"", "public", "", "public"] }, { include: ["\"webserv/client/Client.hpp\"", "public", "", "public"] }, { include: ["\"webserv/socket/Socket.hpp\"", "public", "", "public"] }, { include: ["\"webserv/router/Router.hpp\"", "public", "", "public"] }, - + { include: ["\"webserv/handler/CgiHandler.hpp\"", "public", "", "public"] }, { include: ["\"webserv/handler/FileHandler.hpp\"", "public", "", "public"] }, { include: ["\"webserv/handler/ErrorHandler.hpp\"", "public", "", "public"] }, - + # ============================================================================ - # FORWARD DECLARATION PREFERENCES + # FORWARD DECLARATION PREFERENCES # ============================================================================ - + # Suggest forward declarations instead of full includes in headers where possible { symbol: ["Client", "private", "", "public"] }, { symbol: ["Server", "private", "", "public"] }, diff --git a/config/default.conf b/config/default.conf index 16b5c86..30ca84e 100644 --- a/config/default.conf +++ b/config/default.conf @@ -26,7 +26,8 @@ server { location / { autoindex off; - index index.html; + root ./static_site/; + index index2.html index.html; allowed_methods GET POST DELETE; } @@ -37,7 +38,7 @@ server { } location /images { - root /var/www/images; + root ./static_site/img; autoindex off; index index.jpg; allowed_methods GET; diff --git a/static_site/img/nginy.jpg b/static_site/img/nginy.jpg new file mode 100644 index 0000000..43136e0 Binary files /dev/null and b/static_site/img/nginy.jpg differ diff --git a/static_site/index.html b/static_site/index.html new file mode 100644 index 0000000..a181e9f --- /dev/null +++ b/static_site/index.html @@ -0,0 +1,192 @@ + + + + + + + WebServ - HTTP Server + + + + +
+
+

WebServ

+

High-Performance HTTP Server

+ +
+ +
+
+

⚡ Fast & Efficient

+

Built with performance in mind, handling multiple concurrent connections with minimal resource usage. +

+
+ +
+

🛡️ Secure

+

Implements robust security measures and follows best practices for web server development.

+
+ +
+

🔧 Configurable

+

Highly customizable with flexible configuration options to suit your specific needs.

+
+ +
+

📊 HTTP/1.1 Compliant

+

Full support for HTTP/1.1 protocol with proper request/response handling.

+
+
+ +
+

Server Status

+

Server is running and ready to serve requests

+

Version: 1.0.0

+

Started:

+
+ Nginx +
+

© 2024 WebServ Project. Built with passion for web technologies.

+
+
+ + + + + + \ No newline at end of file diff --git a/webserv/client/Client.cpp b/webserv/client/Client.cpp index d56842d..15af01e 100644 --- a/webserv/client/Client.cpp +++ b/webserv/client/Client.cpp @@ -115,7 +115,7 @@ std::vector Client::getResponse() const const Router &router = server_.getRouter(); auto response = router.handleRequest(*httpRequest_); - return response.toBytes(); + return response->toBytes(); } // void Client::setError(int statusCode) diff --git a/webserv/config/AConfig.cpp b/webserv/config/AConfig.cpp index dbe4ae0..6336aca 100644 --- a/webserv/config/AConfig.cpp +++ b/webserv/config/AConfig.cpp @@ -1,9 +1,8 @@ #include // for AConfig - #include // for ADirective #include // for DirectiveFactory -#include // for trim #include // for Log, LOCATION +#include // for trim #include // for basic_stringstream, stringstream #include // for move, pair @@ -71,7 +70,7 @@ void AConfig::parseDirectives(const std::string &declarations) std::string AConfig::getErrorPage(int statusCode) const { - //TODO + // TODO const ADirective *directive = getDirective("error_page"); for (const auto &directive : directives_) { diff --git a/webserv/config/AConfig.hpp b/webserv/config/AConfig.hpp index 8e669f3..d4ac8d2 100644 --- a/webserv/config/AConfig.hpp +++ b/webserv/config/AConfig.hpp @@ -3,8 +3,8 @@ #include // for ADirective #include -#include // for map #include // for unique_ptr +#include #include // for string #include // for vector @@ -21,17 +21,17 @@ class AConfig virtual ~AConfig() = default; void addDirective(const std::string &line); - [[nodiscard]] const ADirective *getDirective(const std::string &name) const; [[nodiscard]] std::string getErrorPage(int statusCode) const; [[nodiscard]] bool hasDirective(const std::string &name) const; - template T getDirectiveValue(const std::string &name, const T &defaultValue = T{}) const + template + std::optional get(const std::string &name) const { const auto *directive = getDirective(name); if (!directive) { - return defaultValue; + return std::nullopt; } auto value = directive->getValue(); @@ -39,10 +39,11 @@ class AConfig { return value.get(); } - return defaultValue; + return std::nullopt; } - protected: + protected: + [[nodiscard]] const ADirective *getDirective(const std::string &name) const; virtual void parseBlock(const std::string &block) = 0; void parseDirectives(const std::string &declarations); std::vector> diff --git a/webserv/config/ConfigManager.cpp b/webserv/config/ConfigManager.cpp index 7b98c74..94cd091 100644 --- a/webserv/config/ConfigManager.cpp +++ b/webserv/config/ConfigManager.cpp @@ -1,8 +1,7 @@ #include - #include // for GlobalConfig -#include // for removeComments #include // for Log +#include // for removeComments #include // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream #include // for basic_stringstream @@ -86,8 +85,8 @@ ServerConfig *ConfigManager::getMatchingServerConfig(const std::string &host, in std::vector serverConfigs = globalConfig_->getServerConfigs(); for (ServerConfig *serverConfig : serverConfigs) { - auto serverName = serverConfig->getDirectiveValue("server_name", ""); - auto listenPorts = serverConfig->getDirectiveValue("listen", 80); + auto serverName = serverConfig->get("server_name").value_or(""); + auto listenPorts = serverConfig->get("listen").value_or(80); Log::debug("Checking server config: " + serverName + " on port " + std::to_string(listenPorts)); if ((serverName == host) && (listenPorts == port)) { diff --git a/webserv/config/GlobalConfig.cpp b/webserv/config/GlobalConfig.cpp index ec1a0bc..2fa4ae7 100644 --- a/webserv/config/GlobalConfig.cpp +++ b/webserv/config/GlobalConfig.cpp @@ -1,7 +1,6 @@ #include - -#include // for findCorrespondingClosingBrace -#include // for Log +#include // for Log +#include // for findCorrespondingClosingBrace #include // for runtime_error #include // for vector diff --git a/webserv/config/ServerConfig.cpp b/webserv/config/ServerConfig.cpp index 8eb9ad5..075b6be 100644 --- a/webserv/config/ServerConfig.cpp +++ b/webserv/config/ServerConfig.cpp @@ -1,8 +1,8 @@ #include // for AConfig #include // for LocationConfig #include -#include // for findCorrespondingClosingBrace, trim -#include // for Log, LOCATION +#include // for Log, LOCATION +#include // for findCorrespondingClosingBrace, trim #include // for runtime_error #include // for pair diff --git a/webserv/config/directive/BoolDirective.cpp b/webserv/config/directive/BoolDirective.cpp index d8d675a..b0f8276 100644 --- a/webserv/config/directive/BoolDirective.cpp +++ b/webserv/config/directive/BoolDirective.cpp @@ -1,8 +1,7 @@ -#include // for IntDirective - #include // for ADirective +#include // for IntDirective #include // for DirectiveValueType -#include // for trim +#include // for trim #include // for __transform_fn, transform #include // for tolower diff --git a/webserv/config/directive/DirectiveFactory.cpp b/webserv/config/directive/DirectiveFactory.cpp index d92a84c..23c0893 100644 --- a/webserv/config/directive/DirectiveFactory.cpp +++ b/webserv/config/directive/DirectiveFactory.cpp @@ -1,13 +1,12 @@ -#include // for DirectiveFactory - #include // for BoolDirective +#include // for DirectiveFactory #include // for IntDirective #include // for IntStringDirective #include // for SizeDirective #include // for StringDirective #include // for VectorDirective -#include // for trim, trimSemi #include // for LOCATION, Log +#include // for trim, trimSemi #include // for basic_stringstream, stringstream #include // for invalid_argument diff --git a/webserv/config/directive/IntStringDirective.cpp b/webserv/config/directive/IntStringDirective.cpp index 41c9dbc..c2f928f 100644 --- a/webserv/config/directive/IntStringDirective.cpp +++ b/webserv/config/directive/IntStringDirective.cpp @@ -1,8 +1,7 @@ -#include // for IntDirective - #include // for ADirective #include // for DirectiveValueType -#include // for trim +#include // for IntDirective +#include // for trim #include // for basic_istringstream, basic_istream::operator>>, istringstream diff --git a/webserv/config/directive/SizeDirective.cpp b/webserv/config/directive/SizeDirective.cpp index 3c7cefe..895ef97 100644 --- a/webserv/config/directive/SizeDirective.cpp +++ b/webserv/config/directive/SizeDirective.cpp @@ -1,8 +1,7 @@ -#include // for SizeDirective - #include // for ADirective #include // for DirectiveValueType -#include // for trim +#include // for SizeDirective +#include // for trim #include // for __transform_fn, transform #include // for tolower diff --git a/webserv/handler/ErrorHandler.hpp b/webserv/handler/ErrorHandler.hpp index 7c38474..dec6285 100644 --- a/webserv/handler/ErrorHandler.hpp +++ b/webserv/handler/ErrorHandler.hpp @@ -3,11 +3,12 @@ #include "webserv/http/HttpResponse.hpp" #include +#include class ErrorHandler { public: - static HttpResponse getErrorResponse(int statusCode, AConfig *config = nullptr); + static std::unique_ptr getErrorResponse(int statusCode, AConfig *config = nullptr); private: static std::string generateErrorPage(int statusCode, AConfig *config = nullptr); diff --git a/webserv/handler/Errorhandler.cpp b/webserv/handler/Errorhandler.cpp index 91ceb59..bb1ae17 100644 --- a/webserv/handler/Errorhandler.cpp +++ b/webserv/handler/Errorhandler.cpp @@ -8,18 +8,21 @@ #include // for Log #include // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream +#include #include // for basic_stringstream #include // for basic_string, operator+, allocator, char_traits, string, to_string -HttpResponse ErrorHandler::getErrorResponse(int statusCode, AConfig *config) +std::unique_ptr ErrorHandler::getErrorResponse(int statusCode, AConfig *config) { - HttpResponse response; + std::string statusMessage = Http::getStatusCodeReason(statusCode); + Log::warning("Generating error response: " + std::to_string(statusCode) + " " + statusMessage); + auto response = std::make_unique(); std::string body = generateErrorPage(statusCode, config); - response.appendBody(body); - response.setStatus(statusCode); - response.addHeader("Content-Type", "text/html"); - response.addHeader("Connection", "close"); + response->appendBody(body); + response->setStatus(statusCode); + response->addHeader("Content-Type", "text/html"); + response->addHeader("Connection", "close"); return response; } @@ -33,20 +36,20 @@ std::string ErrorHandler::generateErrorPage(int statusCode, AConfig *config) } if (config != nullptr) { - Log::info("Checking for custom error page for status code: " + std::to_string(statusCode)); + Log::info("Checking for custom error page"); std::string customPage = config->getErrorPage(statusCode); if (!customPage.empty()) { return getErrorPageFile(customPage); } - Log::warning("No custom error page found in config for status code: " + std::to_string(statusCode)); + Log::warning("No custom error page found"); } return generateDefaultErrorPage(statusCode); } std::string ErrorHandler::generateDefaultErrorPage(int statusCode) { - Log::info("Generating default error page for status code: " + std::to_string(statusCode)); + Log::info("Generating default error page"); std::string statusMessage = Http::getStatusCodeReason(statusCode); std::string html = "" + std::to_string(statusCode) + " " + statusMessage + "

" + std::to_string(statusCode) + " " + statusMessage + @@ -56,7 +59,7 @@ std::string ErrorHandler::generateDefaultErrorPage(int statusCode) std::string ErrorHandler::getErrorPageFile(const std::string &path) { - Log::info("Loading custom error page from: " + path); + Log::debug("Loading custom error page from: " + path); std::ifstream file(path.c_str()); if (!file.is_open()) { diff --git a/webserv/handler/FileHandler.cpp b/webserv/handler/FileHandler.cpp new file mode 100644 index 0000000..9794a35 --- /dev/null +++ b/webserv/handler/FileHandler.cpp @@ -0,0 +1,125 @@ +#include "webserv/handler/ErrorHandler.hpp" +#include "webserv/http/HttpResponse.hpp" + +#include +#include +#include +#include +#include +#include + +#include // for errno +#include // for strerror, strlen +#include +#include +#include // for views +#include +#include +// for FILE, fopen, fclose, fread, SEEK_END, SEEK +#include // for stat, S_ISREG, S_ISDIR + +FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriParser) + : location_(location), uriParser_(uriParser) +{ + Log::trace(LOCATION); +} + +std::vector FileHandler::readBinaryFile(const std::string &filepath) +{ + Log::trace(LOCATION); + + std::ifstream file(filepath, std::ios::binary | std::ios::ate); + if (!file.is_open()) + { + Log::error("Failed to open file: " + filepath); + return {}; + } + + // Get file size + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + // Read entire file + std::vector buffer(size); + if (!file.read(buffer.data(), size)) + { + Log::error("Failed to read file: " + filepath); + return {}; + } + + return buffer; +} + +std::string FileHandler::readFileAsString(const std::string &filepath) +{ + Log::trace(LOCATION); + + std::ifstream file(filepath, std::ios::binary); + if (!file.is_open()) + { + return ""; + } + + return std::string(std::istreambuf_iterator(file), std::istreambuf_iterator()); +} + +std::unique_ptr FileHandler::getResponse() const +{ + Log::trace(LOCATION); + auto response = std::make_unique(); + response->setStatus(200); + std::string filepath = uriParser_.getFilePath(); + if (uriParser_.isFile()) + { + std::string extension = uriParser_.getExtension(); + std::string mimeType = MIMETypes().getType(extension); + response->addHeader("Content-Type", mimeType); + + std::vector fileData = readBinaryFile(filepath); + Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType); + if (fileData.empty()) + { + return ErrorHandler::getErrorResponse(404); + } + response->setBody(std::string(fileData.begin(), fileData.end())); + } + else if (uriParser_.isDirectory() && location_->get>("index").has_value()) + { + auto possible_indexes = location_->get>("index").value(); + std::string indexPath; + for (auto &index : possible_indexes) + { + indexPath = FileUtils::joinPath(filepath, index); + Log::debug("Checking for index file: " + indexPath); + if (FileUtils::isFile(indexPath)) + { + Log::debug("Found index file: " + indexPath); + break; + } + indexPath.clear(); + } + + std::vector fileData = readBinaryFile(indexPath); + Log::debug("Serving index file: " + indexPath); + if (fileData.empty()) + { + return ErrorHandler::getErrorResponse(404); + } + + std::string extension = FileUtils::getExtension(indexPath); + std::string mimeType = MIMETypes().getType(extension); + response->addHeader("Content-Type", mimeType); + response->setBody(std::string(fileData.begin(), fileData.end())); + } + + else if (uriParser_.isDirectory() && location_->get("autoindex").value_or(false)) + { + Log::debug("Requested path is a directory: " + filepath); + return ErrorHandler::getErrorResponse(403); + } + else + { + return ErrorHandler::getErrorResponse(404); + } + return response; +} \ No newline at end of file diff --git a/webserv/handler/FileHandler.hpp b/webserv/handler/FileHandler.hpp index 7b9637e..f8aa1bb 100644 --- a/webserv/handler/FileHandler.hpp +++ b/webserv/handler/FileHandler.hpp @@ -1 +1,23 @@ -#pragma once \ No newline at end of file +#pragma once + +#include "webserv/config/LocationConfig.hpp" +#include "webserv/handler/URIParser.hpp" +#include "webserv/http/HttpResponse.hpp" + +#include +#include + +class FileHandler +{ + public: + FileHandler(const LocationConfig *location, const URIParser &uriParser); + + [[nodiscard]] std::unique_ptr getResponse() const; + static std::vector readBinaryFile(const std ::string &filepath); + static std::string readFileAsString(const std::string &filepath); + + private: + // std::unique_ptr response_; + const LocationConfig *location_; + const URIParser &uriParser_; +}; \ No newline at end of file diff --git a/webserv/handler/MIMETypes.cpp b/webserv/handler/MIMETypes.cpp new file mode 100644 index 0000000..7a55684 --- /dev/null +++ b/webserv/handler/MIMETypes.cpp @@ -0,0 +1,37 @@ + +#include + + + +MIMETypes::MIMETypes() { + initializeDefaults(); +} + +std::string MIMETypes::getType(const std::string& extension) const { + auto it = mimeMap.find(extension); + if (it != mimeMap.end()) { + return it->second; + } + return "application/octet-stream"; // Default MIME type +} + +void MIMETypes::setType(const std::string& extension, const std::string& mimeType) { + mimeMap[extension] = mimeType; +} + +void MIMETypes::initializeDefaults() { + mimeMap["html"] = "text/html"; + mimeMap["htm"] = "text/html"; + mimeMap["css"] = "text/css"; + mimeMap["js"] = "application/javascript"; + mimeMap["json"] = "application/json"; + mimeMap["png"] = "image/png"; + mimeMap["jpg"] = "image/jpeg"; + mimeMap["jpeg"] = "image/jpeg"; + mimeMap["gif"] = "image/gif"; + mimeMap["svg"] = "image/svg+xml"; + mimeMap["txt"] = "text/plain"; + mimeMap["xml"] = "application/xml"; + mimeMap["pdf"] = "application/pdf"; + // Add more default MIME types as needed +} \ No newline at end of file diff --git a/webserv/handler/MIMETypes.hpp b/webserv/handler/MIMETypes.hpp new file mode 100644 index 0000000..95f653c --- /dev/null +++ b/webserv/handler/MIMETypes.hpp @@ -0,0 +1,24 @@ +#ifndef MIMETYPES_HPP +#define MIMETYPES_HPP + +#include +#include + +class MIMETypes { +public: + MIMETypes(); + + + // Returns the MIME type for a given file extension (e.g., "html" -> "text/html") + [[nodiscard]] std::string getType(const std::string& extension) const; + + // Adds or updates a MIME type mapping + void setType(const std::string& extension, const std::string& mimeType); + +private: + std::map mimeMap; + + void initializeDefaults(); +}; + +#endif // MIMETYPES_HPP \ No newline at end of file diff --git a/webserv/handler/URIParser.cpp b/webserv/handler/URIParser.cpp new file mode 100644 index 0000000..19a8df2 --- /dev/null +++ b/webserv/handler/URIParser.cpp @@ -0,0 +1,94 @@ +#include +#include +#include + +#include // for stat, S_ISREG, S_ISDIR + +URIParser::URIParser(const std::string &uri, const ServerConfig &serverConfig) : _locationConfig(nullptr) +{ + const auto &locations = serverConfig.getLocationPaths(); + size_t maxMatchLength = 0; + + for (const auto &locationPath : locations) + { + if (uri.starts_with((locationPath == "/") ? locationPath : locationPath + "/")) + { // TODO HMHMMz why does it need to end on a /? + if (locationPath.length() > maxMatchLength) + { + maxMatchLength = locationPath.length(); + _locationConfig = serverConfig.getLocation(locationPath); + } + } + } + + root_ = _locationConfig != nullptr ? _locationConfig->get("root").value_or("") : ""; + if (!root_.empty() && root_.back() == '/' ) + { + root_.pop_back(); // Remove trailing slash to avoid double slashes in path + } + + relativePath_ = uri.substr(maxMatchLength); + if (relativePath_.empty() || relativePath_[0] != '/') + { + relativePath_ = "/" + relativePath_; + } +} + +std::string URIParser::getFilePath() const +{ + return root_ + relativePath_; +} + +std::string URIParser::getFilename() const +{ + size_t lastSlash = relativePath_.find_last_of('/'); + if (lastSlash == std::string::npos) + { + return relativePath_; // No slashes, return the whole path + } + return relativePath_.substr(lastSlash + 1); +} + +std::string URIParser::getExtension() const +{ + std::string filename = getFilename(); + size_t lastDot = filename.find_last_of('.'); + if (lastDot == std::string::npos || lastDot == 0 || lastDot == filename.length() - 1) + { + return ""; // No extension found or dot is at start/end + } + return filename.substr(lastDot + 1); +} + + +LocationConfig const *URIParser::getLocation() const +{ + return _locationConfig; +} + +bool URIParser::isFile() const +{ + struct stat pathStat{}; + if (stat(getFilePath().c_str(), &pathStat) != 0) + { + return false; + } + return S_ISREG(pathStat.st_mode); +} + +bool URIParser::isDirectory() const +{ + struct stat pathStat{}; + if (stat(getFilePath().c_str(), &pathStat) != 0) + { + return false; + } + return S_ISDIR(pathStat.st_mode); +} + +bool URIParser::isValid() const +{ + struct stat pathStat{}; + return stat(getFilePath().c_str(), &pathStat) == 0; +} + diff --git a/webserv/handler/URIParser.hpp b/webserv/handler/URIParser.hpp new file mode 100644 index 0000000..bf6aa1c --- /dev/null +++ b/webserv/handler/URIParser.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "webserv/config/LocationConfig.hpp" +#include "webserv/config/ServerConfig.hpp" +#include "webserv/server/Server.hpp" + +class URIParser +{ + public: + URIParser(const std::string &uri, const ServerConfig &serverConfig); + + + [[nodiscard]] std::string getFilePath() const; + [[nodiscard]] std::string getFilename() const; + [[nodiscard]] std::string getExtension() const; + + + [[nodiscard]] const LocationConfig *getLocation() const ; + + [[nodiscard]] bool isFile() const; + + [[nodiscard]] bool isDirectory() const; + [[nodiscard]] bool isValid() const; + + private: + const LocationConfig *_locationConfig; + std::string relativePath_; + std::string root_; +}; \ No newline at end of file diff --git a/webserv/http/HttpHeaders.cpp b/webserv/http/HttpHeaders.cpp index 02cdfbb..681dccc 100644 --- a/webserv/http/HttpHeaders.cpp +++ b/webserv/http/HttpHeaders.cpp @@ -1,8 +1,7 @@ -#include // for HttpHeaders - -#include // for trim #include // for CRLF +#include // for HttpHeaders #include +#include // for trim #include // for __transform_fn, transform #include // for tolower diff --git a/webserv/http/HttpRequest.cpp b/webserv/http/HttpRequest.cpp index b801f01..2a868d5 100644 --- a/webserv/http/HttpRequest.cpp +++ b/webserv/http/HttpRequest.cpp @@ -1,9 +1,8 @@ -#include - #include // for Client -#include // for stoul #include // for CRLF, DOUBLE_CRLF, BAD_REQUEST -#include // for Log, LOCATION +#include +#include // for Log, LOCATION +#include // for stoul #include // for map #include // for optional diff --git a/webserv/main.cpp b/webserv/main.cpp index b7dcd5c..5b674f4 100644 --- a/webserv/main.cpp +++ b/webserv/main.cpp @@ -15,8 +15,8 @@ int main(int argc, char **argv) std::cerr << "Usage: " << argv[0] << " \n"; // NOLINT return 1; } - Log::setFileChannel("webserv.log", std::ios_base::app, Log::Level::Info); - Log::setStdoutChannel(Log::Level::Info); + Log::setFileChannel("webserv.log", std::ios_base::trunc, Log::Level::Info); + Log::setStdoutChannel(Log::Level::Trace); Log::info("\n======================\nStarting webserv...\n======================\n"); ConfigManager::getInstance().init(argv[1]); // NOLINT ConfigManager &configManager = ConfigManager::getInstance(); diff --git a/webserv/router/Router.cpp b/webserv/router/Router.cpp index f8ab561..3d8055f 100644 --- a/webserv/router/Router.cpp +++ b/webserv/router/Router.cpp @@ -2,80 +2,46 @@ #include "webserv/config/ConfigManager.hpp" #include "webserv/config/ServerConfig.hpp" #include "webserv/handler/ErrorHandler.hpp" +#include "webserv/handler/FileHandler.hpp" +#include "webserv/handler/URIParser.hpp" #include "webserv/http/HttpResponse.hpp" #include "webserv/log/Log.hpp" #include +#include #include Router::Router() {} -HttpResponse Router::handleRequest(const HttpRequest &request) const +std::unique_ptr Router::handleRequest(const HttpRequest &request) const { Log::trace(LOCATION); ServerConfig *config = ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or("")); - HttpResponse response; if (config == nullptr) { - Log::warning("No matching server config found"); - handleError(400, response); - return response; + return ErrorHandler::getErrorResponse(400); } + URIParser uriParser{request.getTarget(), *config}; const std::string &target = request.getTarget(); const std::string &method = request.getMethod(); - const LocationConfig *location = getLocation(target, *config); + const LocationConfig *location = uriParser.getLocation(); if (location == nullptr) { - Log::warning("No matching location found for target: " + target); - handleError(404, response, config); - return response; + return ErrorHandler::getErrorResponse(404); } - response.setStatus(200); - response.addHeader("Content-Type", "text/plain"); - response.setBody("Hello, World! You requested " + target + " on " + request.getHeaders().getHost().value_or("") + - ". Current resource is " + location->getPath()); - response.setComplete(); - return response; + FileHandler fileHandler(location, uriParser); + return fileHandler.getResponse(); } -void Router::handleError(int statusCode, HttpResponse &response, AConfig *config) const -{ - response = ErrorHandler::getErrorResponse(statusCode, config); - response.setComplete(); -} - -const LocationConfig *Router::getLocation(const std::string &path, const ServerConfig &serverConfig) const -{ - const LocationConfig *bestMatch = nullptr; - size_t maxSize = 0; - - std::vector locations = serverConfig.getLocationPaths(); - - for (const auto &locPath : locations) - { - if (!path.starts_with(locPath)) - { - continue; - } - if (locPath.length() > maxSize && - (path.length() == locPath.length() || locPath.back() == '/' || path[locPath.length()] == '/')) - { - maxSize = locPath.length(); - bestMatch = serverConfig.getLocation(locPath); - Log::debug("Found new best match: " + locPath); - } - } - - if (bestMatch == nullptr) - { - Log::warning("No location matched for path: " + path); - } - return bestMatch; -} \ No newline at end of file +// void Router::handleError(int statusCode, HttpResponse &response, AConfig *config) const +// { +// response = ErrorHandler::getErrorResponse(statusCode, config); +// response.setComplete(); +// } diff --git a/webserv/router/Router.hpp b/webserv/router/Router.hpp index eb9ea3c..f9cb958 100644 --- a/webserv/router/Router.hpp +++ b/webserv/router/Router.hpp @@ -5,6 +5,7 @@ #include #include +#include #include class Router @@ -12,9 +13,9 @@ class Router public: Router(); - [[nodiscard]] HttpResponse handleRequest(const HttpRequest &request) const; + [[nodiscard]] std::unique_ptr handleRequest(const HttpRequest &request) const; private: - void handleError(int statusCode, HttpResponse &response, AConfig *config = nullptr) const; + // void handleError(int statusCode, HttpResponse &response, AConfig *config = nullptr) const; [[nodiscard]] const LocationConfig *getLocation(const std::string &path, const ServerConfig &serverConfig) const; }; \ No newline at end of file diff --git a/webserv/server/Server.cpp b/webserv/server/Server.cpp index 47e1b55..1323dcf 100644 --- a/webserv/server/Server.cpp +++ b/webserv/server/Server.cpp @@ -102,8 +102,9 @@ void Server::setupServerSocket(const ServerConfig &config) Log::trace(LOCATION); try { - auto host = config.getDirectiveValue("host"); - auto port = config.getDirectiveValue("listen"); + auto host = config.get("host").value_or(std::string()); // TODO should not be a default host + + auto port = config.get("listen").value_or(0); // TODO should not be a default port std::unique_ptr serverSocket = std::make_unique(); serverSocket->bind(host, port); serverSocket->listen(SOMAXCONN); diff --git a/webserv/utils/FileUtils.cpp b/webserv/utils/FileUtils.cpp new file mode 100644 index 0000000..0bc0f9c --- /dev/null +++ b/webserv/utils/FileUtils.cpp @@ -0,0 +1,61 @@ +#include + +#include // for strlen +#include // for string + +#include // for stat, S_ISREG, S_ISDIR + +namespace FileUtils +{ +bool isDirectory(const std::string &path) +{ + struct stat pathStat{}; + if (stat(path.c_str(), &pathStat) != 0) + { + return false; // Could not access path + } + return S_ISDIR(pathStat.st_mode); +} + +bool isFile(const std::string &path) +{ + struct stat pathStat{}; + if (stat(path.c_str(), &pathStat) != 0) + { + return false; // Could not access path + } + return S_ISREG(pathStat.st_mode); +} + +std::string getExtension(const std::string &filename) +{ + size_t dotPos = filename.find_last_of('.'); + if (dotPos == std::string::npos || dotPos == filename.length() - 1) + { + return ""; // No extension found + } + return filename.substr(dotPos + 1); +} + +std::string joinPath(const std::string &base, const std::string &addition) // NOLINT +{ + std::string result = base; + if (result.empty()) + { + result = addition; + } + else if (result.back() == '/' && addition.front() == '/') + { + result += addition.substr(1); + } + else if (result.back() != '/' && addition.front() != '/') + { + result += '/' + addition; + } + else + { + result += addition; + } + return result; +} +} // namespace FileUtils \ No newline at end of file diff --git a/webserv/utils/FileUtils.hpp b/webserv/utils/FileUtils.hpp new file mode 100644 index 0000000..9c94096 --- /dev/null +++ b/webserv/utils/FileUtils.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace FileUtils +{ +bool isDirectory(const std::string &path); +bool isFile(const std::string &path); +std::string getExtension(const std::string &filename); +std::string joinPath(const std::string &base, const std::string &addition); +} // namespace FileUtils \ No newline at end of file diff --git a/webserv/config/utils.cpp b/webserv/utils/utils.cpp similarity index 97% rename from webserv/config/utils.cpp rename to webserv/utils/utils.cpp index 94f927e..a902758 100644 --- a/webserv/config/utils.cpp +++ b/webserv/utils/utils.cpp @@ -1,5 +1,5 @@ -#include +#include #include #include @@ -104,4 +104,4 @@ void removeComments(std::string &str) } removeEmptyLines(str); } -} // namespace utils +} // namespace utils \ No newline at end of file diff --git a/webserv/config/utils.hpp b/webserv/utils/utils.hpp similarity index 94% rename from webserv/config/utils.hpp rename to webserv/utils/utils.hpp index 70cb6c0..002b5e3 100644 --- a/webserv/config/utils.hpp +++ b/webserv/utils/utils.hpp @@ -11,4 +11,4 @@ std::string trim(const std::string &str); size_t findCorrespondingClosingBrace(const std::string &str, size_t openPos); void removeEmptyLines(std::string &str); void removeComments(std::string &str); -} // namespace utils +} // namespace utils \ No newline at end of file