feat: serve static pages

This commit is contained in:
whaffman 2025-10-01 17:50:10 +02:00
parent c5f7b82997
commit bcd33dacd7
32 changed files with 683 additions and 123 deletions

View File

@ -14,7 +14,7 @@
{ include: ["<bits/exception.h>", "private", "<exception>", "public"] }, { include: ["<bits/exception.h>", "private", "<exception>", "public"] },
{ include: ["<bits/std_function.h>", "private", "<functional>", "public"] }, { include: ["<bits/std_function.h>", "private", "<functional>", "public"] },
{ include: ["<bits/stringfwd.h>", "private", "<string>", "public"] }, { include: ["<bits/stringfwd.h>", "private", "<string>", "public"] },
# Standard library strict mappings (preference 5C - very strict) # Standard library strict mappings (preference 5C - very strict)
{ include: ["<iostream>", "public", "<iostream>", "public"] }, { include: ["<iostream>", "public", "<iostream>", "public"] },
{ symbol: ["std::cout", "private", "<iostream>", "public"] }, { symbol: ["std::cout", "private", "<iostream>", "public"] },
@ -29,7 +29,7 @@
{ symbol: ["std::shared_ptr", "private", "<memory>", "public"] }, { symbol: ["std::shared_ptr", "private", "<memory>", "public"] },
{ symbol: ["std::make_unique", "private", "<memory>", "public"] }, { symbol: ["std::make_unique", "private", "<memory>", "public"] },
{ symbol: ["std::make_shared", "private", "<memory>", "public"] }, { symbol: ["std::make_shared", "private", "<memory>", "public"] },
# System headers for socket programming # System headers for socket programming
{ include: ["<sys/socket.h>", "public", "<sys/socket.h>", "public"] }, { include: ["<sys/socket.h>", "public", "<sys/socket.h>", "public"] },
{ include: ["<netinet/in.h>", "public", "<netinet/in.h>", "public"] }, { include: ["<netinet/in.h>", "public", "<netinet/in.h>", "public"] },
@ -37,46 +37,46 @@
{ include: ["<sys/epoll.h>", "public", "<sys/epoll.h>", "public"] }, { include: ["<sys/epoll.h>", "public", "<sys/epoll.h>", "public"] },
{ include: ["<fcntl.h>", "public", "<fcntl.h>", "public"] }, { include: ["<fcntl.h>", "public", "<fcntl.h>", "public"] },
{ include: ["<unistd.h>", "public", "<unistd.h>", "public"] }, { include: ["<unistd.h>", "public", "<unistd.h>", "public"] },
# Error handling (preference 6A - stdexcept) # Error handling (preference 6A - stdexcept)
{ symbol: ["std::runtime_error", "private", "<stdexcept>", "public"] }, { symbol: ["std::runtime_error", "private", "<stdexcept>", "public"] },
{ symbol: ["std::logic_error", "private", "<stdexcept>", "public"] }, { symbol: ["std::logic_error", "private", "<stdexcept>", "public"] },
{ symbol: ["std::invalid_argument", "private", "<stdexcept>", "public"] }, { symbol: ["std::invalid_argument", "private", "<stdexcept>", "public"] },
{ symbol: ["std::out_of_range", "private", "<stdexcept>", "public"] }, { symbol: ["std::out_of_range", "private", "<stdexcept>", "public"] },
# ============================================================================ # ============================================================================
# PROJECT HEADER MAPPINGS - Standardized on <> angle brackets # PROJECT HEADER MAPPINGS - Standardized on <> angle brackets
# ============================================================================ # ============================================================================
# Convert quoted includes to angle brackets for consistency # Convert quoted includes to angle brackets for consistency
{ include: ["\"webserv/log/Log.hpp\"", "public", "<webserv/log/Log.hpp>", "public"] }, { include: ["\"webserv/log/Log.hpp\"", "public", "<webserv/log/Log.hpp>", "public"] },
{ include: ["\"webserv/log/Channel.hpp\"", "public", "<webserv/log/Channel.hpp>", "public"] }, { include: ["\"webserv/log/Channel.hpp\"", "public", "<webserv/log/Channel.hpp>", "public"] },
{ include: ["\"webserv/log/StdoutChannel.hpp\"", "public", "<webserv/log/StdoutChannel.hpp>", "public"] }, { include: ["\"webserv/log/StdoutChannel.hpp\"", "public", "<webserv/log/StdoutChannel.hpp>", "public"] },
{ include: ["\"webserv/log/FileChannel.hpp\"", "public", "<webserv/log/FileChannel.hpp>", "public"] }, { include: ["\"webserv/log/FileChannel.hpp\"", "public", "<webserv/log/FileChannel.hpp>", "public"] },
{ include: ["\"webserv/http/HttpRequest.hpp\"", "public", "<webserv/http/HttpRequest.hpp>", "public"] }, { include: ["\"webserv/http/HttpRequest.hpp\"", "public", "<webserv/http/HttpRequest.hpp>", "public"] },
{ include: ["\"webserv/http/HttpResponse.hpp\"", "public", "<webserv/http/HttpResponse.hpp>", "public"] }, { include: ["\"webserv/http/HttpResponse.hpp\"", "public", "<webserv/http/HttpResponse.hpp>", "public"] },
{ include: ["\"webserv/http/HttpHeaders.hpp\"", "public", "<webserv/http/HttpHeaders.hpp>", "public"] }, { include: ["\"webserv/http/HttpHeaders.hpp\"", "public", "<webserv/http/HttpHeaders.hpp>", "public"] },
{ include: ["\"webserv/http/HttpConstants.hpp\"", "public", "<webserv/http/HttpConstants.hpp>", "public"] }, { include: ["\"webserv/http/HttpConstants.hpp\"", "public", "<webserv/http/HttpConstants.hpp>", "public"] },
{ include: ["\"webserv/config/ConfigManager.hpp\"", "public", "<webserv/config/ConfigManager.hpp>", "public"] }, { include: ["\"webserv/config/ConfigManager.hpp\"", "public", "<webserv/config/ConfigManager.hpp>", "public"] },
{ include: ["\"webserv/config/ServerConfig.hpp\"", "public", "<webserv/config/ServerConfig.hpp>", "public"] }, { include: ["\"webserv/config/ServerConfig.hpp\"", "public", "<webserv/config/ServerConfig.hpp>", "public"] },
{ include: ["\"webserv/config/LocationConfig.hpp\"", "public", "<webserv/config/LocationConfig.hpp>", "public"] }, { include: ["\"webserv/config/LocationConfig.hpp\"", "public", "<webserv/config/LocationConfig.hpp>", "public"] },
{ include: ["\"webserv/config/utils.hpp\"", "public", "<webserv/config/utils.hpp>", "public"] }, { include: ["\i"webserv/utils/utls.hpp\"", "public", "<webserv/utils/utils.hpp>", "public"] },
{ include: ["\"webserv/server/Server.hpp\"", "public", "<webserv/server/Server.hpp>", "public"] }, { include: ["\"webserv/server/Server.hpp\"", "public", "<webserv/server/Server.hpp>", "public"] },
{ include: ["\"webserv/client/Client.hpp\"", "public", "<webserv/client/Client.hpp>", "public"] }, { include: ["\"webserv/client/Client.hpp\"", "public", "<webserv/client/Client.hpp>", "public"] },
{ include: ["\"webserv/socket/Socket.hpp\"", "public", "<webserv/socket/Socket.hpp>", "public"] }, { include: ["\"webserv/socket/Socket.hpp\"", "public", "<webserv/socket/Socket.hpp>", "public"] },
{ include: ["\"webserv/router/Router.hpp\"", "public", "<webserv/router/Router.hpp>", "public"] }, { include: ["\"webserv/router/Router.hpp\"", "public", "<webserv/router/Router.hpp>", "public"] },
{ include: ["\"webserv/handler/CgiHandler.hpp\"", "public", "<webserv/handler/CgiHandler.hpp>", "public"] }, { include: ["\"webserv/handler/CgiHandler.hpp\"", "public", "<webserv/handler/CgiHandler.hpp>", "public"] },
{ include: ["\"webserv/handler/FileHandler.hpp\"", "public", "<webserv/handler/FileHandler.hpp>", "public"] }, { include: ["\"webserv/handler/FileHandler.hpp\"", "public", "<webserv/handler/FileHandler.hpp>", "public"] },
{ include: ["\"webserv/handler/ErrorHandler.hpp\"", "public", "<webserv/handler/ErrorHandler.hpp>", "public"] }, { include: ["\"webserv/handler/ErrorHandler.hpp\"", "public", "<webserv/handler/ErrorHandler.hpp>", "public"] },
# ============================================================================ # ============================================================================
# FORWARD DECLARATION PREFERENCES # FORWARD DECLARATION PREFERENCES
# ============================================================================ # ============================================================================
# Suggest forward declarations instead of full includes in headers where possible # Suggest forward declarations instead of full includes in headers where possible
{ symbol: ["Client", "private", "<webserv/client/Client.hpp>", "public"] }, { symbol: ["Client", "private", "<webserv/client/Client.hpp>", "public"] },
{ symbol: ["Server", "private", "<webserv/server/Server.hpp>", "public"] }, { symbol: ["Server", "private", "<webserv/server/Server.hpp>", "public"] },

View File

@ -26,7 +26,8 @@ server {
location / { location / {
autoindex off; autoindex off;
index index.html; root ./static_site/;
index index2.html index.html;
allowed_methods GET POST DELETE; allowed_methods GET POST DELETE;
} }
@ -37,7 +38,7 @@ server {
} }
location /images { location /images {
root /var/www/images; root ./static_site/img;
autoindex off; autoindex off;
index index.jpg; index index.jpg;
allowed_methods GET; allowed_methods GET;

BIN
static_site/img/nginy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

192
static_site/index.html Normal file
View File

@ -0,0 +1,192 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebServ - HTTP Server</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
margin-bottom: 3rem;
color: white;
}
header h1 {
font-size: 3.5rem;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
header p {
font-size: 1.2rem;
opacity: 0.9;
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.feature-card {
background: rgba(255, 255, 255, 0.95);
padding: 2rem;
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-card h3 {
color: #5a67d8;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.status {
background: rgba(255, 255, 255, 0.95);
padding: 2rem;
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
text-align: center;
}
.status-indicator {
display: inline-block;
width: 20px;
height: 20px;
background: #48bb78;
border-radius: 50%;
margin-right: 10px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.footer {
text-align: center;
margin-top: 3rem;
color: rgba(255, 255, 255, 0.8);
}
.nav-links {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 2rem;
}
.nav-links a {
color: white;
text-decoration: none;
padding: 0.8rem 1.5rem;
background: rgba(255, 255, 255, 0.2);
border-radius: 25px;
transition: background 0.3s ease;
}
.nav-links a:hover {
background: rgba(255, 255, 255, 0.3);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>WebServ</h1>
<p>High-Performance HTTP Server</p>
<div class="nav-links">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/docs">Documentation</a>
<a href="/test">Test Page</a>
</div>
</header>
<div class="features">
<div class="feature-card">
<h3>⚡ Fast & Efficient</h3>
<p>Built with performance in mind, handling multiple concurrent connections with minimal resource usage.
</p>
</div>
<div class="feature-card">
<h3>🛡️ Secure</h3>
<p>Implements robust security measures and follows best practices for web server development.</p>
</div>
<div class="feature-card">
<h3>🔧 Configurable</h3>
<p>Highly customizable with flexible configuration options to suit your specific needs.</p>
</div>
<div class="feature-card">
<h3>📊 HTTP/1.1 Compliant</h3>
<p>Full support for HTTP/1.1 protocol with proper request/response handling.</p>
</div>
</div>
<div class="status">
<h2>Server Status</h2>
<p><span class="status-indicator"></span>Server is running and ready to serve requests</p>
<p><strong>Version:</strong> 1.0.0</p>
<p><strong>Started:</strong> <span id="timestamp"></span></p>
</div>
<img src="/images/nginy.jpg" alt="Nginx"
style="display: block; margin: 2rem auto; max-width: 100%; border-radius: 15px; box-shadow: 0 8px 32px rgba(0,0,0,0.1);">
<footer class="footer">
<p>&copy; 2024 WebServ Project. Built with passion for web technologies.</p>
</footer>
</div>
<script>
// Display current timestamp
document.getElementById('timestamp').textContent = new Date().toLocaleString();
// Update timestamp every second
setInterval(() => {
document.getElementById('timestamp').textContent = new Date().toLocaleString();
}, 1000);
</script>
</body>
</html>
</footer>

View File

@ -115,7 +115,7 @@ std::vector<uint8_t> Client::getResponse() const
const Router &router = server_.getRouter(); const Router &router = server_.getRouter();
auto response = router.handleRequest(*httpRequest_); auto response = router.handleRequest(*httpRequest_);
return response.toBytes(); return response->toBytes();
} }
// void Client::setError(int statusCode) // void Client::setError(int statusCode)

View File

@ -1,9 +1,8 @@
#include <webserv/config/AConfig.hpp> // for AConfig #include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/directive/ADirective.hpp> // for ADirective #include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory #include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory
#include <webserv/config/utils.hpp> // for trim
#include <webserv/log/Log.hpp> // for Log, LOCATION #include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/utils/utils.hpp> // for trim
#include <sstream> // for basic_stringstream, stringstream #include <sstream> // for basic_stringstream, stringstream
#include <utility> // for move, pair #include <utility> // for move, pair
@ -71,7 +70,7 @@ void AConfig::parseDirectives(const std::string &declarations)
std::string AConfig::getErrorPage(int statusCode) const std::string AConfig::getErrorPage(int statusCode) const
{ {
//TODO // TODO
const ADirective *directive = getDirective("error_page"); const ADirective *directive = getDirective("error_page");
for (const auto &directive : directives_) for (const auto &directive : directives_)
{ {

View File

@ -3,8 +3,8 @@
#include <webserv/config/directive/ADirective.hpp> // for ADirective #include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/DirectiveValue.hpp> #include <webserv/config/directive/DirectiveValue.hpp>
#include <map> // for map
#include <memory> // for unique_ptr #include <memory> // for unique_ptr
#include <optional>
#include <string> // for string #include <string> // for string
#include <vector> // for vector #include <vector> // for vector
@ -21,17 +21,17 @@ class AConfig
virtual ~AConfig() = default; virtual ~AConfig() = default;
void addDirective(const std::string &line); void addDirective(const std::string &line);
[[nodiscard]] const ADirective *getDirective(const std::string &name) const;
[[nodiscard]] std::string getErrorPage(int statusCode) const; [[nodiscard]] std::string getErrorPage(int statusCode) const;
[[nodiscard]] bool hasDirective(const std::string &name) const; [[nodiscard]] bool hasDirective(const std::string &name) const;
template <typename T> T getDirectiveValue(const std::string &name, const T &defaultValue = T{}) const template <typename T>
std::optional<T> get(const std::string &name) const
{ {
const auto *directive = getDirective(name); const auto *directive = getDirective(name);
if (!directive) if (!directive)
{ {
return defaultValue; return std::nullopt;
} }
auto value = directive->getValue(); auto value = directive->getValue();
@ -39,10 +39,11 @@ class AConfig
{ {
return value.get<T>(); return value.get<T>();
} }
return defaultValue; return std::nullopt;
} }
protected: protected:
[[nodiscard]] const ADirective *getDirective(const std::string &name) const;
virtual void parseBlock(const std::string &block) = 0; virtual void parseBlock(const std::string &block) = 0;
void parseDirectives(const std::string &declarations); void parseDirectives(const std::string &declarations);
std::vector<std::unique_ptr<ADirective>> std::vector<std::unique_ptr<ADirective>>

View File

@ -1,8 +1,7 @@
#include <webserv/config/ConfigManager.hpp> #include <webserv/config/ConfigManager.hpp>
#include <webserv/config/GlobalConfig.hpp> // for GlobalConfig #include <webserv/config/GlobalConfig.hpp> // for GlobalConfig
#include <webserv/config/utils.hpp> // for removeComments
#include <webserv/log/Log.hpp> // for Log #include <webserv/log/Log.hpp> // for Log
#include <webserv/utils/utils.hpp> // for removeComments
#include <fstream> // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream #include <fstream> // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream
#include <sstream> // for basic_stringstream #include <sstream> // for basic_stringstream
@ -86,8 +85,8 @@ ServerConfig *ConfigManager::getMatchingServerConfig(const std::string &host, in
std::vector<ServerConfig *> serverConfigs = globalConfig_->getServerConfigs(); std::vector<ServerConfig *> serverConfigs = globalConfig_->getServerConfigs();
for (ServerConfig *serverConfig : serverConfigs) for (ServerConfig *serverConfig : serverConfigs)
{ {
auto serverName = serverConfig->getDirectiveValue<std::string>("server_name", ""); auto serverName = serverConfig->get<std::string>("server_name").value_or("");
auto listenPorts = serverConfig->getDirectiveValue<int>("listen", 80); auto listenPorts = serverConfig->get<int>("listen").value_or(80);
Log::debug("Checking server config: " + serverName + " on port " + std::to_string(listenPorts)); Log::debug("Checking server config: " + serverName + " on port " + std::to_string(listenPorts));
if ((serverName == host) && (listenPorts == port)) if ((serverName == host) && (listenPorts == port))
{ {

View File

@ -1,7 +1,6 @@
#include <webserv/config/GlobalConfig.hpp> #include <webserv/config/GlobalConfig.hpp>
#include <webserv/log/Log.hpp> // for Log
#include <webserv/config/utils.hpp> // for findCorrespondingClosingBrace #include <webserv/utils/utils.hpp> // for findCorrespondingClosingBrace
#include <webserv/log/Log.hpp> // for Log
#include <stdexcept> // for runtime_error #include <stdexcept> // for runtime_error
#include <vector> // for vector #include <vector> // for vector

View File

@ -1,8 +1,8 @@
#include <webserv/config/AConfig.hpp> // for AConfig #include <webserv/config/AConfig.hpp> // for AConfig
#include <webserv/config/LocationConfig.hpp> // for LocationConfig #include <webserv/config/LocationConfig.hpp> // for LocationConfig
#include <webserv/config/ServerConfig.hpp> #include <webserv/config/ServerConfig.hpp>
#include <webserv/config/utils.hpp> // for findCorrespondingClosingBrace, trim #include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/log/Log.hpp> // for Log, LOCATION #include <webserv/utils/utils.hpp> // for findCorrespondingClosingBrace, trim
#include <stdexcept> // for runtime_error #include <stdexcept> // for runtime_error
#include <utility> // for pair #include <utility> // for pair

View File

@ -1,8 +1,7 @@
#include <webserv/config/directive/BoolDirective.hpp> // for IntDirective
#include <webserv/config/directive/ADirective.hpp> // for ADirective #include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/BoolDirective.hpp> // for IntDirective
#include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType #include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType
#include <webserv/config/utils.hpp> // for trim #include <webserv/utils/utils.hpp> // for trim
#include <algorithm> // for __transform_fn, transform #include <algorithm> // for __transform_fn, transform
#include <cctype> // for tolower #include <cctype> // for tolower

View File

@ -1,13 +1,12 @@
#include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory
#include <webserv/config/directive/BoolDirective.hpp> // for BoolDirective #include <webserv/config/directive/BoolDirective.hpp> // for BoolDirective
#include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory
#include <webserv/config/directive/IntDirective.hpp> // for IntDirective #include <webserv/config/directive/IntDirective.hpp> // for IntDirective
#include <webserv/config/directive/IntStringDirective.hpp> // for IntStringDirective #include <webserv/config/directive/IntStringDirective.hpp> // for IntStringDirective
#include <webserv/config/directive/SizeDirective.hpp> // for SizeDirective #include <webserv/config/directive/SizeDirective.hpp> // for SizeDirective
#include <webserv/config/directive/StringDirective.hpp> // for StringDirective #include <webserv/config/directive/StringDirective.hpp> // for StringDirective
#include <webserv/config/directive/VectorDirective.hpp> // for VectorDirective #include <webserv/config/directive/VectorDirective.hpp> // for VectorDirective
#include <webserv/config/utils.hpp> // for trim, trimSemi
#include <webserv/log/Log.hpp> // for LOCATION, Log #include <webserv/log/Log.hpp> // for LOCATION, Log
#include <webserv/utils/utils.hpp> // for trim, trimSemi
#include <sstream> // for basic_stringstream, stringstream #include <sstream> // for basic_stringstream, stringstream
#include <stdexcept> // for invalid_argument #include <stdexcept> // for invalid_argument

View File

@ -1,8 +1,7 @@
#include <webserv/config/directive/IntStringDirective.hpp> // for IntDirective
#include <webserv/config/directive/ADirective.hpp> // for ADirective #include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType #include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType
#include <webserv/config/utils.hpp> // for trim #include <webserv/config/directive/IntStringDirective.hpp> // for IntDirective
#include <webserv/utils/utils.hpp> // for trim
#include <sstream> // for basic_istringstream, basic_istream::operator>>, istringstream #include <sstream> // for basic_istringstream, basic_istream::operator>>, istringstream

View File

@ -1,8 +1,7 @@
#include <webserv/config/directive/SizeDirective.hpp> // for SizeDirective
#include <webserv/config/directive/ADirective.hpp> // for ADirective #include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType #include <webserv/config/directive/DirectiveValue.hpp> // for DirectiveValueType
#include <webserv/config/utils.hpp> // for trim #include <webserv/config/directive/SizeDirective.hpp> // for SizeDirective
#include <webserv/utils/utils.hpp> // for trim
#include <algorithm> // for __transform_fn, transform #include <algorithm> // for __transform_fn, transform
#include <cctype> // for tolower #include <cctype> // for tolower

View File

@ -3,11 +3,12 @@
#include "webserv/http/HttpResponse.hpp" #include "webserv/http/HttpResponse.hpp"
#include <webserv/config/AConfig.hpp> #include <webserv/config/AConfig.hpp>
#include <memory>
class ErrorHandler class ErrorHandler
{ {
public: public:
static HttpResponse getErrorResponse(int statusCode, AConfig *config = nullptr); static std::unique_ptr<HttpResponse> getErrorResponse(int statusCode, AConfig *config = nullptr);
private: private:
static std::string generateErrorPage(int statusCode, AConfig *config = nullptr); static std::string generateErrorPage(int statusCode, AConfig *config = nullptr);

View File

@ -8,18 +8,21 @@
#include <webserv/log/Log.hpp> // for Log #include <webserv/log/Log.hpp> // for Log
#include <fstream> // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream #include <fstream> // for basic_ifstream, basic_filebuf, basic_ostream::operator<<, ifstream, stringstream
#include <memory>
#include <sstream> // for basic_stringstream #include <sstream> // for basic_stringstream
#include <string> // for basic_string, operator+, allocator, char_traits, string, to_string #include <string> // for basic_string, operator+, allocator, char_traits, string, to_string
HttpResponse ErrorHandler::getErrorResponse(int statusCode, AConfig *config) std::unique_ptr<HttpResponse> 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<HttpResponse>();
std::string body = generateErrorPage(statusCode, config); std::string body = generateErrorPage(statusCode, config);
response.appendBody(body); response->appendBody(body);
response.setStatus(statusCode); response->setStatus(statusCode);
response.addHeader("Content-Type", "text/html"); response->addHeader("Content-Type", "text/html");
response.addHeader("Connection", "close"); response->addHeader("Connection", "close");
return response; return response;
} }
@ -33,20 +36,20 @@ std::string ErrorHandler::generateErrorPage(int statusCode, AConfig *config)
} }
if (config != nullptr) 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); std::string customPage = config->getErrorPage(statusCode);
if (!customPage.empty()) if (!customPage.empty())
{ {
return getErrorPageFile(customPage); 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); return generateDefaultErrorPage(statusCode);
} }
std::string ErrorHandler::generateDefaultErrorPage(int 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 statusMessage = Http::getStatusCodeReason(statusCode);
std::string html = "<html><head><title>" + std::to_string(statusCode) + " " + statusMessage + std::string html = "<html><head><title>" + std::to_string(statusCode) + " " + statusMessage +
"</title></head><body><h1>" + std::to_string(statusCode) + " " + statusMessage + "</title></head><body><h1>" + std::to_string(statusCode) + " " + statusMessage +
@ -56,7 +59,7 @@ std::string ErrorHandler::generateDefaultErrorPage(int statusCode)
std::string ErrorHandler::getErrorPageFile(const std::string &path) 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()); std::ifstream file(path.c_str());
if (!file.is_open()) if (!file.is_open())
{ {

View File

@ -0,0 +1,125 @@
#include "webserv/handler/ErrorHandler.hpp"
#include "webserv/http/HttpResponse.hpp"
#include <webserv/config/LocationConfig.hpp>
#include <webserv/handler/FileHandler.hpp>
#include <webserv/handler/MIMETypes.hpp>
#include <webserv/handler/URIParser.hpp>
#include <webserv/log/Log.hpp>
#include <webserv/utils/FileUtils.hpp>
#include <cerrno> // for errno
#include <cstring> // for strerror, strlen
#include <fstream>
#include <memory>
#include <ranges> // for views
#include <string>
#include <vector>
// for FILE, fopen, fclose, fread, SEEK_END, SEEK
#include <sys/stat.h> // for stat, S_ISREG, S_ISDIR
FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriParser)
: location_(location), uriParser_(uriParser)
{
Log::trace(LOCATION);
}
std::vector<char> 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<char> 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<char>(file), std::istreambuf_iterator<char>());
}
std::unique_ptr<HttpResponse> FileHandler::getResponse() const
{
Log::trace(LOCATION);
auto response = std::make_unique<HttpResponse>();
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<char> 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<std::vector<std::string>>("index").has_value())
{
auto possible_indexes = location_->get<std::vector<std::string>>("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<char> 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<bool>("autoindex").value_or(false))
{
Log::debug("Requested path is a directory: " + filepath);
return ErrorHandler::getErrorResponse(403);
}
else
{
return ErrorHandler::getErrorResponse(404);
}
return response;
}

View File

@ -1 +1,23 @@
#pragma once #pragma once
#include "webserv/config/LocationConfig.hpp"
#include "webserv/handler/URIParser.hpp"
#include "webserv/http/HttpResponse.hpp"
#include <cstdio>
#include <memory>
class FileHandler
{
public:
FileHandler(const LocationConfig *location, const URIParser &uriParser);
[[nodiscard]] std::unique_ptr<HttpResponse> getResponse() const;
static std::vector<char> readBinaryFile(const std ::string &filepath);
static std::string readFileAsString(const std::string &filepath);
private:
// std::unique_ptr<HttpResponse> response_;
const LocationConfig *location_;
const URIParser &uriParser_;
};

View File

@ -0,0 +1,37 @@
#include <webserv/handler/MIMETypes.hpp>
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
}

View File

@ -0,0 +1,24 @@
#ifndef MIMETYPES_HPP
#define MIMETYPES_HPP
#include <string>
#include <map>
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<std::string, std::string> mimeMap;
void initializeDefaults();
};
#endif // MIMETYPES_HPP

View File

@ -0,0 +1,94 @@
#include <webserv/config/LocationConfig.hpp>
#include <webserv/config/ServerConfig.hpp>
#include <webserv/handler/URIParser.hpp>
#include <sys/stat.h> // 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<std::string>("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;
}

View File

@ -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_;
};

View File

@ -1,8 +1,7 @@
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/config/utils.hpp> // for trim
#include <webserv/http/HttpConstants.hpp> // for CRLF #include <webserv/http/HttpConstants.hpp> // for CRLF
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
#include <webserv/log/Log.hpp> #include <webserv/log/Log.hpp>
#include <webserv/utils/utils.hpp> // for trim
#include <algorithm> // for __transform_fn, transform #include <algorithm> // for __transform_fn, transform
#include <cctype> // for tolower #include <cctype> // for tolower

View File

@ -1,9 +1,8 @@
#include <webserv/http/HttpRequest.hpp>
#include <webserv/client/Client.hpp> // for Client #include <webserv/client/Client.hpp> // for Client
#include <webserv/config/utils.hpp> // for stoul
#include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF, BAD_REQUEST #include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF, BAD_REQUEST
#include <webserv/log/Log.hpp> // for Log, LOCATION #include <webserv/http/HttpRequest.hpp>
#include <webserv/log/Log.hpp> // for Log, LOCATION
#include <webserv/utils/utils.hpp> // for stoul
#include <map> // for map #include <map> // for map
#include <optional> // for optional #include <optional> // for optional

View File

@ -15,8 +15,8 @@ int main(int argc, char **argv)
std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; // NOLINT std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; // NOLINT
return 1; return 1;
} }
Log::setFileChannel("webserv.log", std::ios_base::app, Log::Level::Info); Log::setFileChannel("webserv.log", std::ios_base::trunc, Log::Level::Info);
Log::setStdoutChannel(Log::Level::Info); Log::setStdoutChannel(Log::Level::Trace);
Log::info("\n======================\nStarting webserv...\n======================\n"); Log::info("\n======================\nStarting webserv...\n======================\n");
ConfigManager::getInstance().init(argv[1]); // NOLINT ConfigManager::getInstance().init(argv[1]); // NOLINT
ConfigManager &configManager = ConfigManager::getInstance(); ConfigManager &configManager = ConfigManager::getInstance();

View File

@ -2,80 +2,46 @@
#include "webserv/config/ConfigManager.hpp" #include "webserv/config/ConfigManager.hpp"
#include "webserv/config/ServerConfig.hpp" #include "webserv/config/ServerConfig.hpp"
#include "webserv/handler/ErrorHandler.hpp" #include "webserv/handler/ErrorHandler.hpp"
#include "webserv/handler/FileHandler.hpp"
#include "webserv/handler/URIParser.hpp"
#include "webserv/http/HttpResponse.hpp" #include "webserv/http/HttpResponse.hpp"
#include "webserv/log/Log.hpp" #include "webserv/log/Log.hpp"
#include <webserv/router/Router.hpp> #include <webserv/router/Router.hpp>
#include <memory>
#include <string> #include <string>
Router::Router() {} Router::Router() {}
HttpResponse Router::handleRequest(const HttpRequest &request) const std::unique_ptr<HttpResponse> Router::handleRequest(const HttpRequest &request) const
{ {
Log::trace(LOCATION); Log::trace(LOCATION);
ServerConfig *config = ServerConfig *config =
ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or("")); ConfigManager::getInstance().getMatchingServerConfig(request.getHeaders().getHost().value_or(""));
HttpResponse response;
if (config == nullptr) if (config == nullptr)
{ {
Log::warning("No matching server config found"); return ErrorHandler::getErrorResponse(400);
handleError(400, response);
return response;
} }
URIParser uriParser{request.getTarget(), *config};
const std::string &target = request.getTarget(); const std::string &target = request.getTarget();
const std::string &method = request.getMethod(); const std::string &method = request.getMethod();
const LocationConfig *location = getLocation(target, *config); const LocationConfig *location = uriParser.getLocation();
if (location == nullptr) if (location == nullptr)
{ {
Log::warning("No matching location found for target: " + target); return ErrorHandler::getErrorResponse(404);
handleError(404, response, config);
return response;
} }
response.setStatus(200); FileHandler fileHandler(location, uriParser);
response.addHeader("Content-Type", "text/plain"); return fileHandler.getResponse();
response.setBody("Hello, World! You requested " + target + " on " + request.getHeaders().getHost().value_or("") +
". Current resource is " + location->getPath());
response.setComplete();
return response;
} }
void Router::handleError(int statusCode, HttpResponse &response, AConfig *config) const // void Router::handleError(int statusCode, HttpResponse &response, AConfig *config) const
{ // {
response = ErrorHandler::getErrorResponse(statusCode, config); // response = ErrorHandler::getErrorResponse(statusCode, config);
response.setComplete(); // response.setComplete();
} // }
const LocationConfig *Router::getLocation(const std::string &path, const ServerConfig &serverConfig) const
{
const LocationConfig *bestMatch = nullptr;
size_t maxSize = 0;
std::vector<std::string> 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;
}

View File

@ -5,6 +5,7 @@
#include <webserv/http/HttpRequest.hpp> #include <webserv/http/HttpRequest.hpp>
#include <webserv/http/HttpResponse.hpp> #include <webserv/http/HttpResponse.hpp>
#include <memory>
#include <string> #include <string>
class Router class Router
@ -12,9 +13,9 @@ class Router
public: public:
Router(); Router();
[[nodiscard]] HttpResponse handleRequest(const HttpRequest &request) const; [[nodiscard]] std::unique_ptr<HttpResponse> handleRequest(const HttpRequest &request) const;
private: 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; [[nodiscard]] const LocationConfig *getLocation(const std::string &path, const ServerConfig &serverConfig) const;
}; };

View File

@ -102,8 +102,9 @@ void Server::setupServerSocket(const ServerConfig &config)
Log::trace(LOCATION); Log::trace(LOCATION);
try try
{ {
auto host = config.getDirectiveValue<std::string>("host"); auto host = config.get<std::string>("host").value_or(std::string()); // TODO should not be a default host
auto port = config.getDirectiveValue<int>("listen");
auto port = config.get<int>("listen").value_or(0); // TODO should not be a default port
std::unique_ptr<Socket> serverSocket = std::make_unique<Socket>(); std::unique_ptr<Socket> serverSocket = std::make_unique<Socket>();
serverSocket->bind(host, port); serverSocket->bind(host, port);
serverSocket->listen(SOMAXCONN); serverSocket->listen(SOMAXCONN);

View File

@ -0,0 +1,61 @@
#include <webserv/utils/FileUtils.hpp>
#include <cstring> // for strlen
#include <string> // for string
#include <sys/stat.h> // 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

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
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

View File

@ -1,5 +1,5 @@
#include <webserv/config/utils.hpp> #include <webserv/utils/utils.hpp>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
@ -104,4 +104,4 @@ void removeComments(std::string &str)
} }
removeEmptyLines(str); removeEmptyLines(str);
} }
} // namespace utils } // namespace utils

View File

@ -11,4 +11,4 @@ std::string trim(const std::string &str);
size_t findCorrespondingClosingBrace(const std::string &str, size_t openPos); size_t findCorrespondingClosingBrace(const std::string &str, size_t openPos);
void removeEmptyLines(std::string &str); void removeEmptyLines(std::string &str);
void removeComments(std::string &str); void removeComments(std::string &str);
} // namespace utils } // namespace utils