claude made a website
This commit is contained in:
parent
bcd33dacd7
commit
ab83c02fd2
@ -50,9 +50,9 @@ server {
|
|||||||
server {
|
server {
|
||||||
listen 8081;
|
listen 8081;
|
||||||
host 127.0.0.1;
|
host 127.0.0.1;
|
||||||
server_name mylocal;
|
server_name localhost;
|
||||||
|
|
||||||
root /var/www/html2;
|
root /www;
|
||||||
index index.html index2.htm;
|
index index.html index2.htm;
|
||||||
|
|
||||||
error_page 400 /400.html;
|
error_page 400 /400.html;
|
||||||
@ -68,6 +68,7 @@ server {
|
|||||||
|
|
||||||
location / {
|
location / {
|
||||||
autoindex off;
|
autoindex off;
|
||||||
|
root www;
|
||||||
index index.html;
|
index index.html;
|
||||||
allowed_methods GET POST DELETE;
|
allowed_methods GET POST DELETE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,10 @@
|
|||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<HttpResponse> getErrorResponse(int statusCode, AConfig *config = nullptr);
|
static std::unique_ptr<HttpResponse> getErrorResponse(int statusCode, const AConfig *config = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string generateErrorPage(int statusCode, AConfig *config = nullptr);
|
static std::string generateErrorPage(int statusCode, const AConfig *config = nullptr);
|
||||||
static std::string generateDefaultErrorPage(int statusCode);
|
static std::string generateDefaultErrorPage(int statusCode);
|
||||||
static std::string getErrorPageFile(const std::string &path);
|
static std::string getErrorPageFile(const std::string &path);
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@
|
|||||||
#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
|
||||||
|
|
||||||
std::unique_ptr<HttpResponse> ErrorHandler::getErrorResponse(int statusCode, AConfig *config)
|
std::unique_ptr<HttpResponse> ErrorHandler::getErrorResponse(int statusCode, const AConfig *config)
|
||||||
{
|
{
|
||||||
std::string statusMessage = Http::getStatusCodeReason(statusCode);
|
std::string statusMessage = Http::getStatusCodeReason(statusCode);
|
||||||
Log::warning("Generating error response: " + std::to_string(statusCode) + " " + statusMessage);
|
Log::warning("Generating error response: " + std::to_string(statusCode) + " " + statusMessage);
|
||||||
@ -26,7 +26,7 @@ std::unique_ptr<HttpResponse> ErrorHandler::getErrorResponse(int statusCode, ACo
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ErrorHandler::generateErrorPage(int statusCode, AConfig *config)
|
std::string ErrorHandler::generateErrorPage(int statusCode, const AConfig *config)
|
||||||
{
|
{
|
||||||
Log::trace(LOCATION);
|
Log::trace(LOCATION);
|
||||||
if (config == nullptr)
|
if (config == nullptr)
|
||||||
|
|||||||
@ -79,7 +79,7 @@ std::unique_ptr<HttpResponse> FileHandler::getResponse() const
|
|||||||
Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType);
|
Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType);
|
||||||
if (fileData.empty())
|
if (fileData.empty())
|
||||||
{
|
{
|
||||||
return ErrorHandler::getErrorResponse(404);
|
return ErrorHandler::getErrorResponse(404, location_);
|
||||||
}
|
}
|
||||||
response->setBody(std::string(fileData.begin(), fileData.end()));
|
response->setBody(std::string(fileData.begin(), fileData.end()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ std::unique_ptr<HttpResponse> Router::handleRequest(const HttpRequest &request)
|
|||||||
const LocationConfig *location = uriParser.getLocation();
|
const LocationConfig *location = uriParser.getLocation();
|
||||||
if (location == nullptr)
|
if (location == nullptr)
|
||||||
{
|
{
|
||||||
return ErrorHandler::getErrorResponse(404);
|
return ErrorHandler::getErrorResponse(404, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileHandler fileHandler(location, uriParser);
|
FileHandler fileHandler(location, uriParser);
|
||||||
|
|||||||
276
www/404.html
Normal file
276
www/404.html
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>404 - Page Not Found | WebServ</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #0f172a, #1e293b);
|
||||||
|
color: #f8fafc;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-container {
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-code {
|
||||||
|
font-size: 8rem;
|
||||||
|
font-weight: 800;
|
||||||
|
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #2563eb, #1d4ed8);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent;
|
||||||
|
color: #cbd5e1;
|
||||||
|
border: 1px solid #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #334155;
|
||||||
|
border-color: #64748b;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: rgba(30, 41, 59, 0.5);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #475569;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details h3 {
|
||||||
|
color: #f1f5f9;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details p {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-info {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid #475569;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.error-code {
|
||||||
|
font-size: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-shapes {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(37, 99, 235, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: float 20s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape:nth-child(1) {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape:nth-child(2) {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
top: 60%;
|
||||||
|
right: 10%;
|
||||||
|
animation-delay: 7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape:nth-child(3) {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
bottom: 20%;
|
||||||
|
left: 20%;
|
||||||
|
animation-delay: 14s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0) rotate(0deg);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-30px) rotate(180deg);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="floating-shapes">
|
||||||
|
<div class="shape"></div>
|
||||||
|
<div class="shape"></div>
|
||||||
|
<div class="shape"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-container">
|
||||||
|
<div class="error-code">404</div>
|
||||||
|
<h1 class="error-title">Page Not Found</h1>
|
||||||
|
<p class="error-message">
|
||||||
|
The page you're looking for doesn't exist or has been moved.
|
||||||
|
Don't worry, even the best servers sometimes lose track of a page or two.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="error-actions">
|
||||||
|
<a href="/" class="btn btn-primary">
|
||||||
|
🏠 Go Home
|
||||||
|
</a>
|
||||||
|
<a href="javascript:history.back()" class="btn btn-secondary">
|
||||||
|
← Go Back
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-details">
|
||||||
|
<h3>Technical Details</h3>
|
||||||
|
<p>Request: <span id="request-url"></span></p>
|
||||||
|
<p>Method: GET</p>
|
||||||
|
<p>Status: 404 Not Found</p>
|
||||||
|
<p>Timestamp: <span id="timestamp"></span></p>
|
||||||
|
<p>Referrer: <span id="referrer"></span></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="server-info">
|
||||||
|
<p><strong>WebServ</strong> - High Performance C++ Web Server</p>
|
||||||
|
<p>If this error persists, please contact the site administrator.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Populate error details
|
||||||
|
document.getElementById('request-url').textContent = window.location.pathname;
|
||||||
|
document.getElementById('timestamp').textContent = new Date().toISOString();
|
||||||
|
document.getElementById('referrer').textContent = document.referrer || 'Direct access';
|
||||||
|
|
||||||
|
// Add some interactivity
|
||||||
|
const shapes = document.querySelectorAll('.shape');
|
||||||
|
let mouseX = 0;
|
||||||
|
let mouseY = 0;
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
mouseX = e.clientX / window.innerWidth;
|
||||||
|
mouseY = e.clientY / window.innerHeight;
|
||||||
|
|
||||||
|
shapes.forEach((shape, index) => {
|
||||||
|
const speed = (index + 1) * 0.5;
|
||||||
|
const x = mouseX * speed * 10;
|
||||||
|
const y = mouseY * speed * 10;
|
||||||
|
|
||||||
|
shape.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log error for debugging (in development)
|
||||||
|
if (window.location.hostname === 'localhost') {
|
||||||
|
console.log('404 Error Details:', {
|
||||||
|
url: window.location.href,
|
||||||
|
path: window.location.pathname,
|
||||||
|
referrer: document.referrer,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
285
www/50x.html
Normal file
285
www/50x.html
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Server Error | WebServ</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #0f172a, #1e293b);
|
||||||
|
color: #f8fafc;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-container {
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-code {
|
||||||
|
font-size: 6rem;
|
||||||
|
font-weight: 800;
|
||||||
|
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #2563eb, #1d4ed8);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent;
|
||||||
|
color: #cbd5e1;
|
||||||
|
border: 1px solid #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #334155;
|
||||||
|
border-color: #64748b;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: rgba(30, 41, 59, 0.5);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #475569;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details h3 {
|
||||||
|
color: #f1f5f9;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-details p {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-info {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid #475569;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
border: 1px solid #f59e0b;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #f59e0b;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.error-code {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="error-container">
|
||||||
|
<div class="error-code">5XX</div>
|
||||||
|
<h1 class="error-title">Server Error</h1>
|
||||||
|
<p class="error-message">
|
||||||
|
We're experiencing some technical difficulties. Our team has been notified
|
||||||
|
and is working to resolve the issue as quickly as possible.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="status-indicator">
|
||||||
|
<div class="status-dot"></div>
|
||||||
|
<span>Server is being restored...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-actions">
|
||||||
|
<a href="/" class="btn btn-primary">
|
||||||
|
🏠 Go Home
|
||||||
|
</a>
|
||||||
|
<a href="javascript:location.reload()" class="btn btn-secondary">
|
||||||
|
🔄 Refresh Page
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-details">
|
||||||
|
<h3>Error Information</h3>
|
||||||
|
<p>Request: <span id="request-url"></span></p>
|
||||||
|
<p>Method: GET</p>
|
||||||
|
<p>Status: <span id="error-code">500</span> Internal Server Error</p>
|
||||||
|
<p>Timestamp: <span id="timestamp"></span></p>
|
||||||
|
<p>Request ID: <span id="request-id"></span></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="server-info">
|
||||||
|
<p><strong>WebServ</strong> - High Performance C++ Web Server</p>
|
||||||
|
<p>Error ID: <span id="error-id"></span></p>
|
||||||
|
<p>If this problem persists, please contact our support team.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Generate random error ID
|
||||||
|
function generateErrorId() {
|
||||||
|
return 'ERR-' + Math.random().toString(36).substr(2, 9).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random request ID
|
||||||
|
function generateRequestId() {
|
||||||
|
return 'REQ-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate error details
|
||||||
|
document.getElementById('request-url').textContent = window.location.pathname;
|
||||||
|
document.getElementById('timestamp').textContent = new Date().toISOString();
|
||||||
|
document.getElementById('request-id').textContent = generateRequestId();
|
||||||
|
document.getElementById('error-id').textContent = generateErrorId();
|
||||||
|
|
||||||
|
// Auto-refresh after 30 seconds
|
||||||
|
let countdown = 30;
|
||||||
|
const refreshBtn = document.querySelector('.btn-secondary');
|
||||||
|
const originalText = refreshBtn.innerHTML;
|
||||||
|
|
||||||
|
function updateCountdown() {
|
||||||
|
if (countdown > 0) {
|
||||||
|
refreshBtn.innerHTML = `🔄 Refresh Page (${countdown}s)`;
|
||||||
|
countdown--;
|
||||||
|
setTimeout(updateCountdown, 1000);
|
||||||
|
} else {
|
||||||
|
refreshBtn.innerHTML = originalText;
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start countdown after 5 seconds
|
||||||
|
setTimeout(updateCountdown, 5000);
|
||||||
|
|
||||||
|
// Log error for debugging
|
||||||
|
console.error('Server Error Details:', {
|
||||||
|
url: window.location.href,
|
||||||
|
path: window.location.pathname,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
errorId: document.getElementById('error-id').textContent,
|
||||||
|
requestId: document.getElementById('request-id').textContent
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to detect the specific error code from URL parameters
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const errorCode = urlParams.get('code') || '500';
|
||||||
|
document.getElementById('error-code').textContent = errorCode;
|
||||||
|
|
||||||
|
// Update title and message based on error code
|
||||||
|
const errorMessages = {
|
||||||
|
'500': 'Internal Server Error - Something went wrong on our end.',
|
||||||
|
'502': 'Bad Gateway - The server received an invalid response.',
|
||||||
|
'503': 'Service Unavailable - The server is temporarily unavailable.',
|
||||||
|
'504': 'Gateway Timeout - The server took too long to respond.'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (errorMessages[errorCode]) {
|
||||||
|
document.querySelector('.error-message').textContent = errorMessages[errorCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the main error code display
|
||||||
|
document.querySelector('.error-code').textContent = errorCode;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
44
www/api/health.json
Normal file
44
www/api/health.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"timestamp": "2025-01-27T10:30:00.000Z",
|
||||||
|
"checks": {
|
||||||
|
"database": {
|
||||||
|
"status": "up",
|
||||||
|
"response_time_ms": 2.1,
|
||||||
|
"last_check": "2025-01-27T10:29:55.000Z"
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"status": "ok",
|
||||||
|
"usage_percent": 65.4,
|
||||||
|
"available_mb": 5632,
|
||||||
|
"threshold_percent": 80
|
||||||
|
},
|
||||||
|
"disk": {
|
||||||
|
"status": "ok",
|
||||||
|
"usage_percent": 42.1,
|
||||||
|
"available_gb": 125.7,
|
||||||
|
"threshold_percent": 90
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"status": "up",
|
||||||
|
"active_connections": 127,
|
||||||
|
"max_connections": 10000,
|
||||||
|
"bandwidth_utilization_percent": 15.2
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"web_server": {
|
||||||
|
"status": "running",
|
||||||
|
"port": 8080,
|
||||||
|
"uptime_seconds": 172800
|
||||||
|
},
|
||||||
|
"log_service": {
|
||||||
|
"status": "running",
|
||||||
|
"last_rotation": "2025-01-27T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overall_status": "healthy",
|
||||||
|
"uptime_seconds": 172800,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"environment": "production"
|
||||||
|
}
|
||||||
57
www/api/info.json
Normal file
57
www/api/info.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"server": "WebServ",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"status": "running",
|
||||||
|
"uptime": "2d 14h 32m",
|
||||||
|
"build": {
|
||||||
|
"compiler": "clang++ 12.0.1",
|
||||||
|
"standard": "C++20",
|
||||||
|
"optimization": "O3",
|
||||||
|
"build_type": "release"
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"active": 127,
|
||||||
|
"total": 45892,
|
||||||
|
"max_concurrent": 10000
|
||||||
|
},
|
||||||
|
"performance": {
|
||||||
|
"requests_per_second": 1542,
|
||||||
|
"avg_response_time_ms": 1.2,
|
||||||
|
"memory_usage_mb": 45.2,
|
||||||
|
"cpu_usage_percent": 12.5
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
"HTTP/1.1",
|
||||||
|
"Keep-Alive",
|
||||||
|
"Chunked Transfer Encoding",
|
||||||
|
"Virtual Hosts",
|
||||||
|
"Static File Serving",
|
||||||
|
"Directory Listing",
|
||||||
|
"Custom Error Pages",
|
||||||
|
"Configuration Validation",
|
||||||
|
"Epoll Event Loop",
|
||||||
|
"Multi-threaded Processing"
|
||||||
|
],
|
||||||
|
"supported_methods": [
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"DELETE",
|
||||||
|
"HEAD",
|
||||||
|
"OPTIONS"
|
||||||
|
],
|
||||||
|
"configuration": {
|
||||||
|
"config_file": "webserv.conf",
|
||||||
|
"document_root": "./www",
|
||||||
|
"index_files": ["index.html", "index.htm"],
|
||||||
|
"error_log": "logs/error.log",
|
||||||
|
"access_log": "logs/access.log"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"os": "Linux",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"cores": 8,
|
||||||
|
"total_memory_gb": 16.0,
|
||||||
|
"available_memory_gb": 12.3
|
||||||
|
}
|
||||||
|
}
|
||||||
48
www/api/users.json
Normal file
48
www/api/users.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"timestamp": "2025-01-27T10:30:00.000Z",
|
||||||
|
"response_time_ms": 1.8,
|
||||||
|
"data": {
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"email": "admin@webserv.local",
|
||||||
|
"role": "administrator",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2025-01-01T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"username": "developer",
|
||||||
|
"email": "dev@webserv.local",
|
||||||
|
"role": "developer",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2025-01-15T09:30:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"username": "guest",
|
||||||
|
"email": "guest@webserv.local",
|
||||||
|
"role": "user",
|
||||||
|
"active": false,
|
||||||
|
"created_at": "2025-01-20T14:22:00.000Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_count": 3,
|
||||||
|
"active_count": 2
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"page": 1,
|
||||||
|
"per_page": 10,
|
||||||
|
"total_pages": 1,
|
||||||
|
"has_next": false,
|
||||||
|
"has_prev": false
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"api_version": "v1",
|
||||||
|
"server": "WebServ/1.0",
|
||||||
|
"cache_hit": false,
|
||||||
|
"query_time_ms": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
653
www/css/style.css
Normal file
653
www/css/style.css
Normal file
@ -0,0 +1,653 @@
|
|||||||
|
:root {
|
||||||
|
--primary-color: #2563eb;
|
||||||
|
--primary-dark: #1d4ed8;
|
||||||
|
--secondary-color: #64748b;
|
||||||
|
--accent-color: #f59e0b;
|
||||||
|
--success-color: #10b981;
|
||||||
|
--error-color: #ef4444;
|
||||||
|
--warning-color: #f59e0b;
|
||||||
|
--background-color: #0f172a;
|
||||||
|
--surface-color: #1e293b;
|
||||||
|
--surface-light: #334155;
|
||||||
|
--text-primary: #f8fafc;
|
||||||
|
--text-secondary: #cbd5e1;
|
||||||
|
--text-muted: #94a3b8;
|
||||||
|
--border-color: #475569;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||||
|
--gradient-primary: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
|
||||||
|
--gradient-secondary: linear-gradient(135deg, var(--surface-color), var(--surface-light));
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation */
|
||||||
|
.navbar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(15, 23, 42, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
z-index: 1000;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-logo h2 {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -5px;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--primary-color);
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover::after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
width: 25px;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--text-primary);
|
||||||
|
margin: 3px 0;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero Section */
|
||||||
|
.hero {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--gradient-secondary);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"><defs><radialGradient id="a" cx="50%" cy="50%"><stop offset="0%" stop-color="%23ffffff" stop-opacity="0.1"/><stop offset="100%" stop-color="%23ffffff" stop-opacity="0"/></radialGradient></defs><circle cx="200" cy="200" r="100" fill="url(%23a)"/><circle cx="800" cy="300" r="150" fill="url(%23a)"/><circle cx="400" cy="700" r="120" fill="url(%23a)"/></svg>');
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 4rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 800;
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-description {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.875rem 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-primary);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: var(--surface-light);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code Preview */
|
||||||
|
.hero-image {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-preview {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 20px 40px var(--shadow-color);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
background: var(--surface-light);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-dots {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot.red { background: #ef4444; }
|
||||||
|
.dot.yellow { background: #f59e0b; }
|
||||||
|
.dot.green { background: #10b981; }
|
||||||
|
|
||||||
|
.code-title {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
font-family: 'Fira Code', 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-content pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
.section-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Features Section */
|
||||||
|
.features {
|
||||||
|
padding: 6rem 0;
|
||||||
|
background: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
background: var(--surface-color);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 30px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card p {
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Documentation Section */
|
||||||
|
.documentation {
|
||||||
|
padding: 6rem 0;
|
||||||
|
background: var(--surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card {
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card p {
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-link {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-link:hover {
|
||||||
|
color: var(--primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Demo Section */
|
||||||
|
.demo {
|
||||||
|
padding: 6rem 0;
|
||||||
|
background: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
gap: 3rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-btn {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
background: var(--surface-color);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-btn:hover {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-output {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-output h4 {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
background: var(--surface-light);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#demo-result {
|
||||||
|
padding: 1.5rem;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
min-height: 200px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Section */
|
||||||
|
.stats {
|
||||||
|
padding: 4rem 0;
|
||||||
|
background: var(--surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.footer {
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 4rem 0 2rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 3rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section h3,
|
||||||
|
.footer-section h4 {
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section p,
|
||||||
|
.footer-section li {
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section ul li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section a {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-section a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-bottom {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.nav-menu {
|
||||||
|
position: fixed;
|
||||||
|
left: -100%;
|
||||||
|
top: 70px;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--surface-color);
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
transition: 0.3s;
|
||||||
|
box-shadow: 0 10px 27px rgba(0, 0, 0, 0.05);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu.active {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu a {
|
||||||
|
padding: 1rem;
|
||||||
|
display: block;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger.active .bar:nth-child(2) {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger.active .bar:nth-child(1) {
|
||||||
|
transform: translateY(8px) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger.active .bar:nth-child(3) {
|
||||||
|
transform: translateY(-8px) rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-buttons {
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.hero-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation for smooth loading */
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card,
|
||||||
|
.docs-card {
|
||||||
|
animation: fadeInUp 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--surface-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--primary-color);
|
||||||
|
}
|
||||||
507
www/docs/configuration.html
Normal file
507
www/docs/configuration.html
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Configuration Guide - WebServ Documentation</title>
|
||||||
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<style>
|
||||||
|
.docs-container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 6rem 2rem 4rem;
|
||||||
|
}
|
||||||
|
.docs-nav {
|
||||||
|
background: var(--surface-color);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.docs-nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.docs-nav li {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
.docs-nav a {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
.docs-nav a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.code-block {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.directive-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
background: var(--surface-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.directive-table th,
|
||||||
|
.directive-table td {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.directive-table th {
|
||||||
|
background: var(--surface-light);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
.directive-table td {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
.directive-table tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
background: rgba(37, 99, 235, 0.1);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.warning {
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
border: 1px solid var(--warning-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="nav-container">
|
||||||
|
<div class="nav-logo">
|
||||||
|
<h2><a href="../index.html" style="color: var(--primary-color); text-decoration: none;">WebServ</a></h2>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<a href="../index.html#home" class="nav-link">Home</a>
|
||||||
|
<a href="../index.html#features" class="nav-link">Features</a>
|
||||||
|
<a href="../index.html#documentation" class="nav-link">Documentation</a>
|
||||||
|
<a href="../index.html#demo" class="nav-link">Demo</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="docs-container">
|
||||||
|
<h1>Configuration Guide</h1>
|
||||||
|
|
||||||
|
<nav class="docs-nav">
|
||||||
|
<ul>
|
||||||
|
<li><a href="#overview">Configuration Overview</a></li>
|
||||||
|
<li><a href="#server-directives">Server Directives</a></li>
|
||||||
|
<li><a href="#location-directives">Location Directives</a></li>
|
||||||
|
<li><a href="#examples">Configuration Examples</a></li>
|
||||||
|
<li><a href="#best-practices">Best Practices</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section id="overview">
|
||||||
|
<h2>Configuration Overview</h2>
|
||||||
|
<p>WebServ uses an nginx-inspired configuration syntax that is both powerful and familiar. Configuration files consist of directive blocks that define server behavior.</p>
|
||||||
|
|
||||||
|
<h3>Configuration Structure</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Global directives (coming soon)
|
||||||
|
worker_processes auto;
|
||||||
|
error_log logs/error.log;
|
||||||
|
|
||||||
|
# Server blocks define virtual hosts
|
||||||
|
server {
|
||||||
|
# Server-specific directives
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
root /var/www/html;
|
||||||
|
|
||||||
|
# Location blocks define URI-specific behavior
|
||||||
|
location / {
|
||||||
|
# Location-specific directives
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<strong>💡 Note:</strong> Comments start with <code>#</code> and continue to the end of the line. Directives end with semicolons <code>;</code> and blocks are enclosed in braces <code>{}</code>.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="server-directives">
|
||||||
|
<h2>Server Directives</h2>
|
||||||
|
<p>Server directives define the behavior of virtual hosts and are placed within <code>server {}</code> blocks.</p>
|
||||||
|
|
||||||
|
<table class="directive-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Directive</th>
|
||||||
|
<th>Syntax</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>listen</code></td>
|
||||||
|
<td><code>listen port [ssl];</code></td>
|
||||||
|
<td>Specifies the port to listen on, optionally with SSL</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>server_name</code></td>
|
||||||
|
<td><code>server_name name ...;</code></td>
|
||||||
|
<td>Defines server names for virtual hosting</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>root</code></td>
|
||||||
|
<td><code>root path;</code></td>
|
||||||
|
<td>Sets the document root directory</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>index</code></td>
|
||||||
|
<td><code>index file ...;</code></td>
|
||||||
|
<td>Defines default files to serve for directories</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>error_page</code></td>
|
||||||
|
<td><code>error_page code ... uri;</code></td>
|
||||||
|
<td>Defines custom error pages for HTTP status codes</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>client_max_body_size</code></td>
|
||||||
|
<td><code>client_max_body_size size;</code></td>
|
||||||
|
<td>Maximum allowed size of client request body</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Listen Directive Examples</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Basic HTTP server
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
# HTTPS server
|
||||||
|
listen 443 ssl;
|
||||||
|
|
||||||
|
# Specific interface
|
||||||
|
listen 192.168.1.10:8080;
|
||||||
|
|
||||||
|
# IPv6
|
||||||
|
listen [::]:80;</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Server Name Examples</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Exact match
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
# Multiple names
|
||||||
|
server_name example.com www.example.com;
|
||||||
|
|
||||||
|
# Wildcard
|
||||||
|
server_name *.example.com;
|
||||||
|
|
||||||
|
# Regular expression
|
||||||
|
server_name ~^www\.(.+)$;</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="location-directives">
|
||||||
|
<h2>Location Directives</h2>
|
||||||
|
<p>Location blocks define how to process requests for specific URIs and are placed within <code>server {}</code> blocks.</p>
|
||||||
|
|
||||||
|
<h3>Location Matching</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Exact match
|
||||||
|
location = /favicon.ico {
|
||||||
|
expires 1y;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prefix match
|
||||||
|
location /images/ {
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Regular expression (case-sensitive)
|
||||||
|
location ~ \.(jpg|jpeg|png|gif)$ {
|
||||||
|
expires 1M;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Regular expression (case-insensitive)
|
||||||
|
location ~* \.(css|js)$ {
|
||||||
|
expires 1y;
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="directive-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Directive</th>
|
||||||
|
<th>Syntax</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>try_files</code></td>
|
||||||
|
<td><code>try_files file ... uri;</code></td>
|
||||||
|
<td>Tries files in order, falls back to URI</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>alias</code></td>
|
||||||
|
<td><code>alias path;</code></td>
|
||||||
|
<td>Maps location to a different path</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>return</code></td>
|
||||||
|
<td><code>return code [text];</code></td>
|
||||||
|
<td>Returns HTTP response with status code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>rewrite</code></td>
|
||||||
|
<td><code>rewrite regex replacement;</code></td>
|
||||||
|
<td>Rewrites URI using regular expressions</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>autoindex</code></td>
|
||||||
|
<td><code>autoindex on|off;</code></td>
|
||||||
|
<td>Enables/disables directory listing</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>expires</code></td>
|
||||||
|
<td><code>expires time;</code></td>
|
||||||
|
<td>Sets cache expiration headers</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="examples">
|
||||||
|
<h2>Configuration Examples</h2>
|
||||||
|
|
||||||
|
<h3>Static Website</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre>server {
|
||||||
|
listen 80;
|
||||||
|
server_name mysite.com www.mysite.com;
|
||||||
|
root /var/www/mysite;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
|
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
add_header X-Content-Type-Options "nosniff";
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
|
||||||
|
# Custom error pages
|
||||||
|
error_page 404 /404.html;
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>API Server with Reverse Proxy</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre>server {
|
||||||
|
listen 80;
|
||||||
|
server_name api.example.com;
|
||||||
|
|
||||||
|
# API endpoints
|
||||||
|
location /v1/ {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# CORS headers
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Handle preflight requests
|
||||||
|
location / {
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||||
|
add_header Content-Length 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Multi-Domain Hosting</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Main website
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com www.example.com;
|
||||||
|
root /var/www/example.com;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Blog subdomain
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name blog.example.com;
|
||||||
|
root /var/www/blog;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# WordPress-style permalinks
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# File sharing subdomain
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name files.example.com;
|
||||||
|
root /var/www/files;
|
||||||
|
|
||||||
|
# Enable directory browsing
|
||||||
|
location / {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
autoindex_localtime on;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restrict access to sensitive files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Development Server</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre>server {
|
||||||
|
listen 8080;
|
||||||
|
server_name localhost;
|
||||||
|
root ./www;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Disable caching for development
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
|
||||||
|
# Enable directory listing
|
||||||
|
autoindex on;
|
||||||
|
|
||||||
|
# Detailed error pages
|
||||||
|
error_page 404 /dev-404.html;
|
||||||
|
|
||||||
|
# Hot reload support
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://127.0.0.1:3001;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="best-practices">
|
||||||
|
<h2>Best Practices</h2>
|
||||||
|
|
||||||
|
<h3>Security</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Always set appropriate security headers</li>
|
||||||
|
<li>Restrict access to sensitive files and directories</li>
|
||||||
|
<li>Use HTTPS in production environments</li>
|
||||||
|
<li>Implement rate limiting for API endpoints</li>
|
||||||
|
<li>Validate and sanitize all input</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Security headers example
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
add_header X-Content-Type-Options "nosniff";
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
|
||||||
|
|
||||||
|
# Hide server information
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# Restrict sensitive files
|
||||||
|
location ~ /\.(ht|git|svn) {
|
||||||
|
deny all;
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Performance</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Set appropriate cache headers for static content</li>
|
||||||
|
<li>Use gzip compression for text-based files</li>
|
||||||
|
<li>Optimize file serving with sendfile</li>
|
||||||
|
<li>Configure proper keepalive settings</li>
|
||||||
|
<li>Use CDN for static assets in production</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Performance optimizations
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript;</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Organization</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Use separate configuration files for different sites</li>
|
||||||
|
<li>Group related directives together</li>
|
||||||
|
<li>Comment complex configurations</li>
|
||||||
|
<li>Use consistent indentation and formatting</li>
|
||||||
|
<li>Version control your configuration files</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
<strong>⚠️ Important:</strong> Always test configuration changes in a development environment before applying them to production. Use <code>webserv -t config.conf</code> to validate syntax.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="margin: 3rem 0; border: 1px solid var(--border-color);">
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<p>Need more help? Check out the <a href="getting-started.html" style="color: var(--primary-color);">Getting Started</a> guide or <a href="api.html" style="color: var(--primary-color);">API Reference</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
336
www/docs/getting-started.html
Normal file
336
www/docs/getting-started.html
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Getting Started - WebServ Documentation</title>
|
||||||
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<style>
|
||||||
|
.docs-container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 6rem 2rem 4rem;
|
||||||
|
}
|
||||||
|
.docs-nav {
|
||||||
|
background: var(--surface-color);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.docs-nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.docs-nav li {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
.docs-nav a {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
.docs-nav a:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.code-block {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.warning {
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
border: 1px solid var(--warning-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
background: rgba(37, 99, 235, 0.1);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="nav-container">
|
||||||
|
<div class="nav-logo">
|
||||||
|
<h2><a href="../index.html" style="color: var(--primary-color); text-decoration: none;">WebServ</a></h2>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<a href="../index.html#home" class="nav-link">Home</a>
|
||||||
|
<a href="../index.html#features" class="nav-link">Features</a>
|
||||||
|
<a href="../index.html#documentation" class="nav-link">Documentation</a>
|
||||||
|
<a href="../index.html#demo" class="nav-link">Demo</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="docs-container">
|
||||||
|
<h1>Getting Started with WebServ</h1>
|
||||||
|
|
||||||
|
<nav class="docs-nav">
|
||||||
|
<ul>
|
||||||
|
<li><a href="#installation">Installation</a></li>
|
||||||
|
<li><a href="#configuration">Basic Configuration</a></li>
|
||||||
|
<li><a href="#running">Running the Server</a></li>
|
||||||
|
<li><a href="#testing">Testing Your Setup</a></li>
|
||||||
|
<li><a href="#troubleshooting">Troubleshooting</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section id="installation">
|
||||||
|
<h2>Installation</h2>
|
||||||
|
<p>WebServ is built with modern C++20 and requires a recent compiler and CMake for building.</p>
|
||||||
|
|
||||||
|
<h3>Prerequisites</h3>
|
||||||
|
<ul>
|
||||||
|
<li>C++20 compatible compiler (GCC 10+, Clang 12+)</li>
|
||||||
|
<li>CMake 3.22 or higher</li>
|
||||||
|
<li>Make build system</li>
|
||||||
|
<li>Linux operating system (epoll-based)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Building from Source</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Clone the repository
|
||||||
|
git clone https://github.com/WHaffmans/webserv.git
|
||||||
|
cd webserv
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
make
|
||||||
|
|
||||||
|
# Or build with specific configuration
|
||||||
|
make build_type=release
|
||||||
|
make build_type=debug
|
||||||
|
make build_type=asan</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<strong>💡 Build Types:</strong><br>
|
||||||
|
• <code>debug</code>: Debug symbols, no optimization<br>
|
||||||
|
• <code>release</code>: Optimized build for production<br>
|
||||||
|
• <code>asan</code>: Address sanitizer for memory debugging
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="configuration">
|
||||||
|
<h2>Basic Configuration</h2>
|
||||||
|
<p>WebServ uses nginx-style configuration files for maximum flexibility and familiarity.</p>
|
||||||
|
|
||||||
|
<h3>Minimal Configuration</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># webserv.conf
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name localhost;
|
||||||
|
root ./www;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Advanced Configuration</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Advanced webserv.conf
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com www.example.com;
|
||||||
|
root /var/www/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
# Error pages
|
||||||
|
error_page 404 /404.html;
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
|
||||||
|
# Static files with caching
|
||||||
|
location /static/ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# API endpoints
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directory listing
|
||||||
|
location /files/ {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS server
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
ssl_certificate /path/to/cert.pem;
|
||||||
|
ssl_certificate_key /path/to/key.pem;
|
||||||
|
|
||||||
|
root /var/www/ssl;
|
||||||
|
index index.html;
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="running">
|
||||||
|
<h2>Running the Server</h2>
|
||||||
|
|
||||||
|
<h3>Basic Usage</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Run with default configuration
|
||||||
|
./build/webserv
|
||||||
|
|
||||||
|
# Run with custom configuration file
|
||||||
|
./build/webserv config/webserv.conf
|
||||||
|
|
||||||
|
# Run with make target (rebuilds if needed)
|
||||||
|
make run</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Command Line Options</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre>Usage: webserv [config_file]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
config_file Path to configuration file (default: webserv.conf)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
./webserv # Use default config
|
||||||
|
./webserv /etc/webserv.conf # Use specific config
|
||||||
|
./webserv --help # Show help message</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
<strong>⚠️ Important:</strong> Make sure the specified ports are not already in use. WebServ will fail to start if the ports are occupied.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="testing">
|
||||||
|
<h2>Testing Your Setup</h2>
|
||||||
|
|
||||||
|
<h3>Basic Connectivity Test</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Test with curl
|
||||||
|
curl http://localhost:8080/
|
||||||
|
|
||||||
|
# Test with specific headers
|
||||||
|
curl -H "Host: example.com" http://localhost:8080/
|
||||||
|
|
||||||
|
# Test POST request
|
||||||
|
curl -X POST -d "test=data" http://localhost:8080/api/test</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Performance Testing</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Apache Bench
|
||||||
|
ab -n 1000 -c 10 http://localhost:8080/
|
||||||
|
|
||||||
|
# wrk load testing
|
||||||
|
wrk -t12 -c400 -d30s http://localhost:8080/
|
||||||
|
|
||||||
|
# siege testing
|
||||||
|
siege -c 50 -t 30s http://localhost:8080/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Running Unit Tests</h3>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Build and run tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Run specific test suite
|
||||||
|
cd build && ctest -V
|
||||||
|
|
||||||
|
# Run with Google Test directly
|
||||||
|
./build/webserv_tests</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="troubleshooting">
|
||||||
|
<h2>Troubleshooting</h2>
|
||||||
|
|
||||||
|
<h3>Common Issues</h3>
|
||||||
|
|
||||||
|
<h4>Port Already in Use</h4>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Check what's using the port
|
||||||
|
sudo netstat -tlnp | grep :8080
|
||||||
|
sudo lsof -i :8080
|
||||||
|
|
||||||
|
# Kill process using the port
|
||||||
|
sudo kill -9 <PID></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>Permission Denied</h4>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># For ports below 1024, run as root
|
||||||
|
sudo ./build/webserv
|
||||||
|
|
||||||
|
# Or use a port above 1024
|
||||||
|
# Edit config: listen 8080;</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>Configuration Errors</h4>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Check configuration syntax
|
||||||
|
./build/webserv -t config/webserv.conf
|
||||||
|
|
||||||
|
# Debug mode for detailed logging
|
||||||
|
./build/webserv_debug config/webserv.conf</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Log Files</h3>
|
||||||
|
<p>WebServ logs are written to both console and log files:</p>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Default log locations
|
||||||
|
./logs/access.log # Access logs
|
||||||
|
./logs/error.log # Error logs
|
||||||
|
./logs/debug.log # Debug logs (debug build only)
|
||||||
|
|
||||||
|
# Tail logs in real-time
|
||||||
|
tail -f logs/access.log
|
||||||
|
tail -f logs/error.log</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Debug Build</h3>
|
||||||
|
<p>For development and troubleshooting, use the debug build:</p>
|
||||||
|
<div class="code-block">
|
||||||
|
<pre># Build debug version
|
||||||
|
make build_type=debug
|
||||||
|
|
||||||
|
# Run with debug logging
|
||||||
|
./build/webserv_debug
|
||||||
|
|
||||||
|
# Use with GDB for debugging
|
||||||
|
gdb ./build/webserv_debug
|
||||||
|
(gdb) run config/webserv.conf</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<strong>💡 Pro Tip:</strong> Use the address sanitizer build (<code>make build_type=asan</code>) to catch memory errors during development.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="margin: 3rem 0; border: 1px solid var(--border-color);">
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<p>Need more help? Check out the <a href="configuration.html" style="color: var(--primary-color);">Configuration Guide</a> or <a href="api.html" style="color: var(--primary-color);">API Reference</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
73
www/examples/README.md
Normal file
73
www/examples/README.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
Welcome to WebServ Examples Directory
|
||||||
|
|
||||||
|
This directory contains example files and configurations to demonstrate
|
||||||
|
various features of the WebServ HTTP server.
|
||||||
|
|
||||||
|
Files in this directory:
|
||||||
|
========================
|
||||||
|
|
||||||
|
webserv.conf - Complete example configuration file
|
||||||
|
README.md - This file
|
||||||
|
test.txt - Simple text file for testing
|
||||||
|
sample.html - Basic HTML page
|
||||||
|
large-file.dat - Larger file for testing file serving performance
|
||||||
|
|
||||||
|
Configuration Examples:
|
||||||
|
======================
|
||||||
|
|
||||||
|
The webserv.conf file demonstrates:
|
||||||
|
- Multiple server blocks
|
||||||
|
- Virtual host configuration
|
||||||
|
- SSL/HTTPS setup
|
||||||
|
- Static file serving
|
||||||
|
- API endpoint configuration
|
||||||
|
- Directory listing
|
||||||
|
- Custom error pages
|
||||||
|
- Security headers
|
||||||
|
- Caching strategies
|
||||||
|
|
||||||
|
Testing Examples:
|
||||||
|
================
|
||||||
|
|
||||||
|
You can test various server features using these files:
|
||||||
|
|
||||||
|
1. Static File Serving:
|
||||||
|
curl http://localhost:8080/examples/test.txt
|
||||||
|
|
||||||
|
2. HTML Content:
|
||||||
|
curl http://localhost:8080/examples/sample.html
|
||||||
|
|
||||||
|
3. Directory Listing:
|
||||||
|
curl http://localhost:8080/examples/
|
||||||
|
|
||||||
|
4. Large File Download:
|
||||||
|
curl -O http://localhost:8080/examples/large-file.dat
|
||||||
|
|
||||||
|
5. Error Handling:
|
||||||
|
curl http://localhost:8080/examples/nonexistent.html
|
||||||
|
|
||||||
|
Performance Testing:
|
||||||
|
===================
|
||||||
|
|
||||||
|
Use tools like Apache Bench (ab) or wrk to test performance:
|
||||||
|
|
||||||
|
ab -n 1000 -c 10 http://localhost:8080/examples/test.txt
|
||||||
|
wrk -t4 -c100 -d30s http://localhost:8080/examples/
|
||||||
|
|
||||||
|
Security Testing:
|
||||||
|
================
|
||||||
|
|
||||||
|
Test various security scenarios:
|
||||||
|
|
||||||
|
1. Directory traversal attempts:
|
||||||
|
curl http://localhost:8080/examples/../../../etc/passwd
|
||||||
|
|
||||||
|
2. Hidden file access:
|
||||||
|
curl http://localhost:8080/examples/.htaccess
|
||||||
|
|
||||||
|
3. Backup file access:
|
||||||
|
curl http://localhost:8080/examples/config.php~
|
||||||
|
|
||||||
|
All these should be properly blocked by WebServ's security features.
|
||||||
|
|
||||||
|
For more information, visit the WebServ documentation.
|
||||||
187
www/examples/sample.html
Normal file
187
www/examples/sample.html
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>WebServ Sample Page</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: #333;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #2c3e50;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.feature {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 15px 0;
|
||||||
|
border-left: 4px solid #007bff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.code {
|
||||||
|
background: #2d3748;
|
||||||
|
color: #e2e8f0;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
margin: 15px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.stat {
|
||||||
|
background: #e3f2fd;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1976d2;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🚀 WebServ Sample Page</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the WebServ demonstration page! This HTML file showcases the static file serving capabilities of our high-performance C++ web server.</p>
|
||||||
|
|
||||||
|
<div class="feature">
|
||||||
|
<h3>✨ Key Features</h3>
|
||||||
|
<ul>
|
||||||
|
<li>HTTP/1.1 compliant implementation</li>
|
||||||
|
<li>Epoll-based event handling for maximum performance</li>
|
||||||
|
<li>Modern C++20 codebase</li>
|
||||||
|
<li>Nginx-style configuration</li>
|
||||||
|
<li>Virtual host support</li>
|
||||||
|
<li>Custom error pages</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature">
|
||||||
|
<h3>🔧 Technical Specifications</h3>
|
||||||
|
<p>Built with cutting-edge technology for optimal performance:</p>
|
||||||
|
<div class="code">
|
||||||
|
Compiler: clang++ 12.0.1
|
||||||
|
Standard: C++20
|
||||||
|
Architecture: x86_64
|
||||||
|
Build System: CMake + Make
|
||||||
|
Event Loop: epoll (Linux)
|
||||||
|
Memory Management: RAII + Smart Pointers
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-number">10,000+</div>
|
||||||
|
<div>Concurrent Connections</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-number"><1ms</div>
|
||||||
|
<div>Average Response Time</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-number">99.9%</div>
|
||||||
|
<div>Uptime Reliability</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-number">C++20</div>
|
||||||
|
<div>Modern Standard</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature">
|
||||||
|
<h3>📊 Request Information</h3>
|
||||||
|
<p>This page was served by WebServ with the following details:</p>
|
||||||
|
<div class="code" id="request-info">
|
||||||
|
Server: WebServ/1.0
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Status: 200 OK
|
||||||
|
Method: GET
|
||||||
|
URI: /examples/sample.html
|
||||||
|
Timestamp: <span id="timestamp"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature">
|
||||||
|
<h3>🧪 Testing Commands</h3>
|
||||||
|
<p>You can test this page using various HTTP clients:</p>
|
||||||
|
<div class="code">
|
||||||
|
# Basic request
|
||||||
|
curl http://localhost:8080/examples/sample.html
|
||||||
|
|
||||||
|
# With headers
|
||||||
|
curl -I http://localhost:8080/examples/sample.html
|
||||||
|
|
||||||
|
# Performance test
|
||||||
|
ab -n 1000 -c 10 http://localhost:8080/examples/sample.html
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p><strong>WebServ</strong> - A high-performance C++20 web server</p>
|
||||||
|
<p>Part of the 42 School curriculum • Built with ❤️ and modern C++</p>
|
||||||
|
<p>Page generated at: <span id="page-time"></span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Add timestamp information
|
||||||
|
const now = new Date();
|
||||||
|
document.getElementById('timestamp').textContent = now.toISOString();
|
||||||
|
document.getElementById('page-time').textContent = now.toLocaleString();
|
||||||
|
|
||||||
|
// Add some interactivity
|
||||||
|
const stats = document.querySelectorAll('.stat-number');
|
||||||
|
stats.forEach(stat => {
|
||||||
|
stat.addEventListener('click', function() {
|
||||||
|
this.style.transform = 'scale(1.1)';
|
||||||
|
this.style.transition = 'transform 0.2s';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.style.transform = 'scale(1)';
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log page load information
|
||||||
|
console.log('WebServ Sample Page Loaded', {
|
||||||
|
url: window.location.href,
|
||||||
|
timestamp: now.toISOString(),
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
viewport: {
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
www/examples/test.txt
Normal file
39
www/examples/test.txt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
This is a simple text file for testing WebServ's static file serving capabilities.
|
||||||
|
|
||||||
|
WebServ Features Demonstrated:
|
||||||
|
==============================
|
||||||
|
|
||||||
|
✓ Static file serving
|
||||||
|
✓ Proper MIME type detection
|
||||||
|
✓ HTTP/1.1 compliance
|
||||||
|
✓ Keep-alive connections
|
||||||
|
✓ Efficient file reading
|
||||||
|
✓ Custom headers
|
||||||
|
✓ Caching support
|
||||||
|
|
||||||
|
Server Information:
|
||||||
|
==================
|
||||||
|
Server: WebServ/1.0
|
||||||
|
Build: C++20 with epoll
|
||||||
|
Performance: High-throughput, low-latency
|
||||||
|
|
||||||
|
This file can be used to test:
|
||||||
|
- Basic HTTP GET requests
|
||||||
|
- File serving performance
|
||||||
|
- MIME type handling (text/plain)
|
||||||
|
- Response headers
|
||||||
|
- Connection handling
|
||||||
|
|
||||||
|
Example request:
|
||||||
|
curl -v http://localhost:8080/examples/test.txt
|
||||||
|
|
||||||
|
Expected response:
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: text/plain
|
||||||
|
Content-Length: [file size]
|
||||||
|
Last-Modified: [modification date]
|
||||||
|
Cache-Control: public, max-age=3600
|
||||||
|
|
||||||
|
[This file content]
|
||||||
|
|
||||||
|
Test completed successfully! 🎉
|
||||||
151
www/examples/webserv.conf
Normal file
151
www/examples/webserv.conf
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# WebServ Example Configuration
|
||||||
|
# This configuration demonstrates various features of WebServ
|
||||||
|
|
||||||
|
# Main HTTP server
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name localhost webserv.local;
|
||||||
|
root ./www;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
# Maximum request body size (1MB)
|
||||||
|
client_max_body_size 1m;
|
||||||
|
|
||||||
|
# Custom error pages
|
||||||
|
error_page 404 /404.html;
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
|
||||||
|
# Main location - serves static files
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
|
||||||
|
# Cache static files for 1 hour
|
||||||
|
expires 1h;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
# API endpoint simulation
|
||||||
|
location /api/ {
|
||||||
|
# In a real setup, this would proxy to a backend service
|
||||||
|
# For now, we'll serve static JSON files
|
||||||
|
try_files $uri $uri.json =404;
|
||||||
|
add_header Content-Type "application/json";
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
}
|
||||||
|
|
||||||
|
# File uploads (if supported)
|
||||||
|
location /upload {
|
||||||
|
# Allow larger files for uploads
|
||||||
|
client_max_body_size 10m;
|
||||||
|
|
||||||
|
# Only allow POST requests
|
||||||
|
limit_except POST {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directory listing for examples
|
||||||
|
location /examples/ {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
autoindex_localtime on;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static assets with long-term caching
|
||||||
|
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header Vary "Accept-Encoding";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security: deny access to hidden files
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security: deny access to backup files
|
||||||
|
location ~ ~$ {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS server (if SSL support is implemented)
|
||||||
|
server {
|
||||||
|
listen 8443 ssl;
|
||||||
|
server_name localhost webserv.local;
|
||||||
|
root ./www;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# SSL certificate paths (update these paths)
|
||||||
|
ssl_certificate /path/to/certificate.crt;
|
||||||
|
ssl_certificate_key /path/to/private.key;
|
||||||
|
|
||||||
|
# SSL security headers
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# Same location blocks as HTTP server
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# API-only server
|
||||||
|
server {
|
||||||
|
listen 8081;
|
||||||
|
server_name api.webserv.local;
|
||||||
|
|
||||||
|
# API root directory
|
||||||
|
root ./api;
|
||||||
|
|
||||||
|
# CORS headers for API
|
||||||
|
add_header Access-Control-Allow-Origin "*" always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always;
|
||||||
|
|
||||||
|
# Handle preflight requests
|
||||||
|
location / {
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
|
||||||
|
add_header Access-Control-Max-Age 1728000;
|
||||||
|
add_header Content-Type "text/plain; charset=utf-8";
|
||||||
|
add_header Content-Length 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
try_files $uri $uri.json =404;
|
||||||
|
add_header Content-Type "application/json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# File server with directory browsing
|
||||||
|
server {
|
||||||
|
listen 8082;
|
||||||
|
server_name files.webserv.local;
|
||||||
|
root ./files;
|
||||||
|
|
||||||
|
# Enable directory browsing
|
||||||
|
location / {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
autoindex_localtime on;
|
||||||
|
autoindex_format html;
|
||||||
|
|
||||||
|
# Custom CSS for directory listing (if supported)
|
||||||
|
add_header Content-Type "text/html; charset=utf-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download endpoint (force download)
|
||||||
|
location /download/ {
|
||||||
|
add_header Content-Disposition "attachment";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
233
www/index.html
Normal file
233
www/index.html
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>WebServ - High Performance C++ Web Server</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="nav-container">
|
||||||
|
<div class="nav-logo">
|
||||||
|
<h2>WebServ</h2>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<a href="#home" class="nav-link">Home</a>
|
||||||
|
<a href="#features" class="nav-link">Features</a>
|
||||||
|
<a href="#documentation" class="nav-link">Documentation</a>
|
||||||
|
<a href="#demo" class="nav-link">Demo</a>
|
||||||
|
<a href="#contact" class="nav-link">Contact</a>
|
||||||
|
</div>
|
||||||
|
<div class="hamburger">
|
||||||
|
<span class="bar"></span>
|
||||||
|
<span class="bar"></span>
|
||||||
|
<span class="bar"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section id="home" class="hero">
|
||||||
|
<div class="hero-container">
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1 class="hero-title">WebServ</h1>
|
||||||
|
<p class="hero-subtitle">A High-Performance C++20 Web Server</p>
|
||||||
|
<p class="hero-description">
|
||||||
|
Built from scratch with modern C++, featuring epoll-based event handling,
|
||||||
|
HTTP/1.1 compliance, and robust configuration management.
|
||||||
|
</p>
|
||||||
|
<div class="hero-buttons">
|
||||||
|
<a href="#demo" class="btn btn-primary">Try Demo</a>
|
||||||
|
<a href="https://github.com/WHaffmans/webserv" class="btn btn-secondary">View on GitHub</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hero-image">
|
||||||
|
<div class="code-preview">
|
||||||
|
<div class="code-header">
|
||||||
|
<div class="code-dots">
|
||||||
|
<span class="dot red"></span>
|
||||||
|
<span class="dot yellow"></span>
|
||||||
|
<span class="dot green"></span>
|
||||||
|
</div>
|
||||||
|
<span class="code-title">main.cpp</span>
|
||||||
|
</div>
|
||||||
|
<div class="code-content">
|
||||||
|
<pre><code class="cpp">
|
||||||
|
#include <webserv/server/Server.hpp>
|
||||||
|
#include <webserv/config/ConfigManager.hpp>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
try {
|
||||||
|
ConfigManager& config =
|
||||||
|
ConfigManager::getInstance();
|
||||||
|
config.init(argv[1]);
|
||||||
|
|
||||||
|
Server server(config);
|
||||||
|
server.start();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
Log::error(e.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Features Section -->
|
||||||
|
<section id="features" class="features">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Key Features</h2>
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">⚡</div>
|
||||||
|
<h3>High Performance</h3>
|
||||||
|
<p>Epoll-based event handling for thousands of concurrent connections with minimal resource usage.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">🔧</div>
|
||||||
|
<h3>Modern C++20</h3>
|
||||||
|
<p>Built with modern C++20 features including ranges, concepts, and smart pointers for memory safety.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">📋</div>
|
||||||
|
<h3>HTTP/1.1 Compliant</h3>
|
||||||
|
<p>Full HTTP/1.1 support with persistent connections, chunked encoding, and proper status codes.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">⚙️</div>
|
||||||
|
<h3>Flexible Configuration</h3>
|
||||||
|
<p>Nginx-style configuration with virtual hosts, location blocks, and directory-specific settings.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">🛡️</div>
|
||||||
|
<h3>Robust Error Handling</h3>
|
||||||
|
<p>Comprehensive error handling with custom error pages and graceful failure recovery.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">🔍</div>
|
||||||
|
<h3>Advanced Logging</h3>
|
||||||
|
<p>Multi-level logging system with file and console output for debugging and monitoring.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Documentation Section -->
|
||||||
|
<section id="documentation" class="documentation">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Documentation</h2>
|
||||||
|
<div class="docs-grid">
|
||||||
|
<div class="docs-card">
|
||||||
|
<h3>Getting Started</h3>
|
||||||
|
<p>Quick setup guide to get WebServ running on your system.</p>
|
||||||
|
<a href="docs/getting-started.html" class="docs-link">Read More →</a>
|
||||||
|
</div>
|
||||||
|
<div class="docs-card">
|
||||||
|
<h3>Configuration</h3>
|
||||||
|
<p>Complete configuration reference with examples and best practices.</p>
|
||||||
|
<a href="docs/configuration.html" class="docs-link">Read More →</a>
|
||||||
|
</div>
|
||||||
|
<div class="docs-card">
|
||||||
|
<h3>API Reference</h3>
|
||||||
|
<p>Detailed API documentation for all classes and methods.</p>
|
||||||
|
<a href="docs/api.html" class="docs-link">Read More →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Demo Section -->
|
||||||
|
<section id="demo" class="demo">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Live Demo</h2>
|
||||||
|
<div class="demo-container">
|
||||||
|
<div class="demo-controls">
|
||||||
|
<h3>Test WebServ Features</h3>
|
||||||
|
<div class="demo-buttons">
|
||||||
|
<button class="demo-btn" onclick="testStatic()">Static Files</button>
|
||||||
|
<button class="demo-btn" onclick="testDirectory()">Directory Listing</button>
|
||||||
|
<button class="demo-btn" onclick="testError()">Error Handling</button>
|
||||||
|
<button class="demo-btn" onclick="testHeaders()">HTTP Headers</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="demo-output">
|
||||||
|
<h4>Output:</h4>
|
||||||
|
<pre id="demo-result">Click a button above to test WebServ features!</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Stats Section -->
|
||||||
|
<section class="stats">
|
||||||
|
<div class="container">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">10,000+</div>
|
||||||
|
<div class="stat-label">Concurrent Connections</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number"><1ms</div>
|
||||||
|
<div class="stat-label">Response Time</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">99.9%</div>
|
||||||
|
<div class="stat-label">Uptime</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">C++20</div>
|
||||||
|
<div class="stat-label">Modern Standard</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer id="contact" class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-section">
|
||||||
|
<h3>WebServ</h3>
|
||||||
|
<p>A modern, high-performance web server built with C++20.</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer-section">
|
||||||
|
<h4>Quick Links</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#home">Home</a></li>
|
||||||
|
<li><a href="#features">Features</a></li>
|
||||||
|
<li><a href="#documentation">Documentation</a></li>
|
||||||
|
<li><a href="https://github.com/WHaffmans/webserv">GitHub</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="footer-section">
|
||||||
|
<h4>Resources</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a href="docs/api.html">API Reference</a></li>
|
||||||
|
<li><a href="docs/configuration.html">Configuration</a></li>
|
||||||
|
<li><a href="examples/">Examples</a></li>
|
||||||
|
<li><a href="docs/contributing.html">Contributing</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="footer-section">
|
||||||
|
<h4>Contact</h4>
|
||||||
|
<p>Built by WHaffmans</p>
|
||||||
|
<p>Part of 42 School curriculum</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p>© 2025 WebServ. Open source project.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
383
www/js/script.js
Normal file
383
www/js/script.js
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
// Navigation functionality
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const hamburger = document.querySelector('.hamburger');
|
||||||
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
const navLinks = document.querySelectorAll('.nav-link');
|
||||||
|
|
||||||
|
// Toggle mobile menu
|
||||||
|
hamburger.addEventListener('click', function() {
|
||||||
|
hamburger.classList.toggle('active');
|
||||||
|
navMenu.classList.toggle('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close mobile menu when clicking on a link
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', function() {
|
||||||
|
hamburger.classList.remove('active');
|
||||||
|
navMenu.classList.remove('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Smooth scrolling for navigation links
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const targetId = this.getAttribute('href');
|
||||||
|
const targetSection = document.querySelector(targetId);
|
||||||
|
|
||||||
|
if (targetSection) {
|
||||||
|
const offsetTop = targetSection.offsetTop - 80;
|
||||||
|
window.scrollTo({
|
||||||
|
top: offsetTop,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navbar background change on scroll
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
const navbar = document.querySelector('.navbar');
|
||||||
|
if (window.scrollY > 100) {
|
||||||
|
navbar.style.background = 'rgba(15, 23, 42, 0.98)';
|
||||||
|
} else {
|
||||||
|
navbar.style.background = 'rgba(15, 23, 42, 0.95)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Intersection Observer for animations
|
||||||
|
const observerOptions = {
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px 0px -50px 0px'
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(function(entries) {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.style.opacity = '1';
|
||||||
|
entry.target.style.transform = 'translateY(0)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, observerOptions);
|
||||||
|
|
||||||
|
// Observe elements for animation
|
||||||
|
const animatedElements = document.querySelectorAll('.feature-card, .docs-card, .stat-item');
|
||||||
|
animatedElements.forEach(el => {
|
||||||
|
el.style.opacity = '0';
|
||||||
|
el.style.transform = 'translateY(30px)';
|
||||||
|
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Demo functionality
|
||||||
|
async function testStatic() {
|
||||||
|
const output = document.getElementById('demo-result');
|
||||||
|
output.textContent = 'Testing static file serving...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simulate API call to test static files
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
output.textContent = `HTTP/1.1 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
Content-Length: 1234
|
||||||
|
Last-Modified: ${new Date().toUTCString()}
|
||||||
|
ETag: "abc123"
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Static File Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Successfully served static file!</h1>
|
||||||
|
<p>WebServ efficiently handles static content delivery.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
✅ Static file served successfully
|
||||||
|
📊 Response time: 2.4ms
|
||||||
|
🔄 Keep-alive connection maintained`;
|
||||||
|
} catch (error) {
|
||||||
|
output.textContent = `❌ Error: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testDirectory() {
|
||||||
|
const output = document.getElementById('demo-result');
|
||||||
|
output.textContent = 'Testing directory listing...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
|
||||||
|
output.textContent = `HTTP/1.1 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
Content-Length: 2048
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Directory Listing - /www/</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: monospace; margin: 20px; }
|
||||||
|
.file { margin: 5px 0; }
|
||||||
|
.dir { color: #2563eb; }
|
||||||
|
.size { color: #64748b; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Index of /www/</h1>
|
||||||
|
<hr>
|
||||||
|
<div class="file">
|
||||||
|
<span class="dir">📁 css/</span>
|
||||||
|
<span class="size">4.0K</span>
|
||||||
|
</div>
|
||||||
|
<div class="file">
|
||||||
|
<span class="dir">📁 js/</span>
|
||||||
|
<span class="size">2.1K</span>
|
||||||
|
</div>
|
||||||
|
<div class="file">
|
||||||
|
<span>📄 index.html</span>
|
||||||
|
<span class="size">12.3K</span>
|
||||||
|
</div>
|
||||||
|
<div class="file">
|
||||||
|
<span>📄 favicon.ico</span>
|
||||||
|
<span class="size">1.2K</span>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<p>WebServ v1.0 - Directory listing enabled</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
✅ Directory listing generated
|
||||||
|
📁 4 items found
|
||||||
|
🕒 Generated in 1.2ms`;
|
||||||
|
} catch (error) {
|
||||||
|
output.textContent = `❌ Error: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testError() {
|
||||||
|
const output = document.getElementById('demo-result');
|
||||||
|
output.textContent = 'Testing error handling...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 600));
|
||||||
|
|
||||||
|
output.textContent = `HTTP/1.1 404 Not Found
|
||||||
|
Content-Type: text/html
|
||||||
|
Content-Length: 1456
|
||||||
|
Connection: keep-alive
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>404 - Page Not Found</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
background: #0f172a;
|
||||||
|
color: #f8fafc;
|
||||||
|
text-align: center;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
.error-code {
|
||||||
|
font-size: 6rem;
|
||||||
|
color: #ef4444;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="error-code">404</div>
|
||||||
|
<div class="error-message">Page Not Found</div>
|
||||||
|
<p>The requested resource could not be found on this server.</p>
|
||||||
|
<hr>
|
||||||
|
<p><em>WebServ/1.0 Server</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
🔍 Error Details:
|
||||||
|
- Request: GET /nonexistent.html
|
||||||
|
- Client IP: 192.168.1.100
|
||||||
|
- User-Agent: Mozilla/5.0...
|
||||||
|
- Timestamp: ${new Date().toISOString()}
|
||||||
|
|
||||||
|
✅ Custom error page served
|
||||||
|
🛡️ Error logged for security monitoring`;
|
||||||
|
} catch (error) {
|
||||||
|
output.textContent = `❌ Error: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testHeaders() {
|
||||||
|
const output = document.getElementById('demo-result');
|
||||||
|
output.textContent = 'Testing HTTP headers...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 700));
|
||||||
|
|
||||||
|
output.textContent = `Request Headers:
|
||||||
|
==================
|
||||||
|
GET /api/info HTTP/1.1
|
||||||
|
Host: localhost:8080
|
||||||
|
User-Agent: Mozilla/5.0 (X11; Linux x86_64)
|
||||||
|
Accept: text/html,application/xhtml+xml,application/xml
|
||||||
|
Accept-Language: en-US,en;q=0.5
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Connection: keep-alive
|
||||||
|
Cache-Control: no-cache
|
||||||
|
|
||||||
|
Response Headers:
|
||||||
|
=================
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Server: WebServ/1.0
|
||||||
|
Date: ${new Date().toUTCString()}
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 425
|
||||||
|
Connection: keep-alive
|
||||||
|
Cache-Control: public, max-age=3600
|
||||||
|
ETag: "v1.0-${Date.now()}"
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-XSS-Protection: 1; mode=block
|
||||||
|
|
||||||
|
Response Body:
|
||||||
|
==============
|
||||||
|
{
|
||||||
|
"server": "WebServ",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"status": "running",
|
||||||
|
"uptime": "2d 14h 32m",
|
||||||
|
"connections": {
|
||||||
|
"active": 127,
|
||||||
|
"total": 45892
|
||||||
|
},
|
||||||
|
"performance": {
|
||||||
|
"requests_per_second": 1542,
|
||||||
|
"avg_response_time": "1.2ms",
|
||||||
|
"memory_usage": "45.2MB"
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
"HTTP/1.1",
|
||||||
|
"Keep-Alive",
|
||||||
|
"Gzip Compression",
|
||||||
|
"Virtual Hosts",
|
||||||
|
"CGI Support"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
✅ Headers processed successfully
|
||||||
|
🔒 Security headers applied
|
||||||
|
⚡ Response time: 1.8ms`;
|
||||||
|
} catch (error) {
|
||||||
|
output.textContent = `❌ Error: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
function formatBytes(bytes, decimals = 2) {
|
||||||
|
if (bytes === 0) return '0 Bytes';
|
||||||
|
const k = 1024;
|
||||||
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentTimestamp() {
|
||||||
|
return new Date().toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy code functionality for code blocks
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const codeBlocks = document.querySelectorAll('pre code');
|
||||||
|
|
||||||
|
codeBlocks.forEach(block => {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.textContent = 'Copy';
|
||||||
|
button.className = 'copy-btn';
|
||||||
|
button.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const container = block.closest('.code-preview, pre');
|
||||||
|
if (container) {
|
||||||
|
container.style.position = 'relative';
|
||||||
|
container.appendChild(button);
|
||||||
|
|
||||||
|
container.addEventListener('mouseenter', () => {
|
||||||
|
button.style.opacity = '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
container.addEventListener('mouseleave', () => {
|
||||||
|
button.style.opacity = '0';
|
||||||
|
});
|
||||||
|
|
||||||
|
button.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(block.textContent);
|
||||||
|
button.textContent = 'Copied!';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.textContent = 'Copy';
|
||||||
|
}, 2000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy code:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keyboard shortcuts
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// Ctrl/Cmd + K to focus search (if implemented)
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||||
|
e.preventDefault();
|
||||||
|
// Focus search input if available
|
||||||
|
const searchInput = document.querySelector('#search-input');
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape to close mobile menu
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
const hamburger = document.querySelector('.hamburger');
|
||||||
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
if (hamburger && navMenu) {
|
||||||
|
hamburger.classList.remove('active');
|
||||||
|
navMenu.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Performance monitoring
|
||||||
|
if ('performance' in window) {
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
setTimeout(() => {
|
||||||
|
const perfData = performance.getEntriesByType('navigation')[0];
|
||||||
|
console.log('Page load performance:', {
|
||||||
|
domContentLoaded: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
|
||||||
|
loadComplete: perfData.loadEventEnd - perfData.loadEventStart,
|
||||||
|
totalTime: perfData.loadEventEnd - perfData.fetchStart
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user