Welcome! This is my personal educational project to build a real, functioning HTTP web server from scratch in C++ to deeply understand how networking, HTTP parsing, concurrency, and clean system design work.
It's a tiny but complete web server that:
- Accepts HTTP requests with full GET, POST, PUT, DELETE method support.
- Parses the request to extract method, path, headers, body.
- Uses a Router with method-specific routing and convenient helper methods.
- Builds and sends back clean HTTP responses with proper status codes and headers.
- Uses a ThreadPool to handle multiple clients concurrently.
- Implements graceful shutdown with signal handling (SIGINT, SIGTERM).
- Uses smart pointers for memory safety and RAII principles.
All using pure C++ and raw sockets (no external HTTP libraries), so you learn exactly what's happening under the hood.
Here's the life cycle of a client request:
- Socket Creation:
- We create a TCP socket using
socket(). - We bind it to a port (
8080) andINADDR_ANYso it listens on all interfaces. - We start
listen()to wait for clients.
- Signal Handling:
- The server registers handlers for
SIGINT(Ctrl+C) andSIGTERMfor graceful shutdown. - When received, the server stops accepting new connections and cleans up resources.
- Accepting Connections:
- When a client connects, the server calls
accept()to get a new socket representing that client.
- ThreadPool Handling:
- Instead of blocking the server for one client, we push the connection to a ThreadPool.
- The pool has a fixed number of worker threads, each picking up client connections and handling them in parallel.
- Connection Handling:
- Each connection reads the raw HTTP request into a buffer.
- We parse it into an
HTTPRequestobject withmethod,path,headers,body.
- Method-Aware Routing:
- The
Routerlooks at both the HTTP method and path to decide which handler function to call. - Support for
GET /users,POST /users,PUT /users/1,DELETE /users/1with different handlers. - The handler generates a
RouteResultwith:- Status code (
200,404,201, etc.) - Status text (
OK,Created,Not Found) - Body content
- Content-Type
- Additional headers (if needed)
- Status code (
- Responding:
- We convert the
RouteResultinto a full HTTP response string (HTTP/1.1 200 OK\r\n...). - We send it back to the client with
write(). - We close the client socket, freeing resources.
web-server/
├── include/ # All .hpp headers, organized clearly
│ ├── networking/
│ │ ├── server/ # SimpleServer, TestServer
│ │ ├── sockets/ # Socket hierarchy
│ │ ├── connection/ # Connection handling
│ │ ├── router/ # HTTP routing with method support
│ │ └── threadpool/ # Thread pool for concurrency
│ ├── http/ # HTTP request/response parsing
│ └── utils/ # Logger, Config, and utilities
├── src/ # All .cpp implementation files
│ ├── Sockets/
│ ├── Server/
│ ├── Connection/
│ ├── Router/
│ ├── ThreadPool/
│ └── utils/ # Configuration system
├── build/ # Where build outputs go
├── server.conf # Configuration file
├── CMakeLists.txt # For modern build system
└── README.md
- Smart Pointers:
SimpleServernow usesstd::unique_ptrinstead of raw pointers - RAII: Automatic cleanup of sockets and resources
- Exception Safety: Proper error handling without memory leaks
- Signal Handling: Responds to
SIGINT(Ctrl+C) andSIGTERM - Clean Termination: Stops accepting new connections and finishes processing existing ones
- Resource Cleanup: Properly closes sockets and joins threads
- GET: For retrieving resources
- POST: For creating new resources
- PUT: For updating existing resources
- DELETE: For removing resources
- Method-Specific Routing: Different handlers for same path but different methods
- Convenience Methods:
router.get(),router.post(),router.put(),router.delete_() - Backward Compatibility: Still supports old string-based handlers
- File-Based Configuration: Uses
server.conffile for easy customization - Runtime Defaults: Sensible defaults when config file is missing
- Flexible Settings: Configure port, thread pool size, timeouts, and more
- Comment Support: Configuration file supports comments and various formats
- Type Safety: Automatic type conversion for strings, integers, and booleans
The server can be configured using a server.conf file. If no configuration file is provided, the server will use sensible defaults.
# HDE Web Server Configuration File
# Lines starting with # are comments
# Format: key = value
# Server Settings
server.port = 8080
server.host = 0.0.0.0
server.backlog = 10
# Thread Pool Settings
threadpool.size = 4
# Logging Settings
logging.level = INFO
logging.enabled = true
# Performance Settings
server.timeout = 30
server.max_connections = 100
# Security Settings
server.enable_cors = true
server.cors_origin = *
# Static File Settings
static.enabled = false
static.root_dir = ./public
static.index_file = index.html
# Development Settings
dev.hot_reload = false
dev.debug = false| Setting | Default | Description |
|---|---|---|
server.port |
8080 | Port number for the server to listen on |
server.backlog |
10 | Maximum number of pending connections |
threadpool.size |
4 | Number of worker threads in the thread pool |
server.timeout |
30 | Connection timeout in seconds |
server.max_connections |
100 | Maximum concurrent connections |
logging.level |
INFO | Log level (DEBUG, INFO, WARNING, ERROR) |
logging.enabled |
true | Enable/disable logging |
# Use default configuration
./web_server
# Use custom configuration file
./web_server custom.confgit clone https://github.com/Dhia0Eddine/web-server.git
cd web-servermkdir build
cd build
cmake ..
make# Copy the sample configuration file
cp ../server.conf .
# Edit the configuration file
nano server.conf./web_server# Press Ctrl+C or send SIGTERM
kill -TERM <process_id># GET requests
curl -X GET http://localhost:8080/hello
curl -X GET http://localhost:8080/users
# POST requests
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
curl -X POST http://localhost:8080/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "secret"}'
# PUT requests
curl -X PUT http://localhost:8080/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "Bob Updated", "email": "bob@updated.com"}'
# DELETE requests
curl -X DELETE http://localhost:8080/users/1// GET request
fetch('http://localhost:8080/hello')
.then(response => response.text())
.then(data => console.log(data));
// POST request
fetch('http://localhost:8080/users', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Alice', email: 'alice@example.com'})
})
.then(response => response.json())
.then(data => console.log(data));- How TCP sockets work practically in C++.
- How to parse and structure HTTP requests manually.
- Building a clean router system with full HTTP method support.
- Managing concurrency using a ThreadPool.
- Memory management with smart pointers and RAII.
- Signal handling for graceful application shutdown.
- Configuration management with file-based settings and type-safe parsing.
- Organizing a real project structure with
include/,src/, and CMake. - Building reusable components like
Router,Connection,HTTPResponse, andConfig. - Modern C++ practices for safer, more maintainable code.
- Add comprehensive unit tests for all components.
- Implement static file serving (images, CSS, JS).
- Add middleware system for authentication, CORS, logging.
- Support HTTP/1.1 keep-alive connections.
- Add configuration file support.
- Implement request/response compression.
- Add performance metrics and monitoring.
- Optionally add TLS (HTTPS) support.
Because building your own HTTP server demystifies how the internet actually works, teaches modern C++ best practices, and makes you deeply comfortable with low-level system programming while maintaining clean, maintainable code.
Happy hacking, learning, and exploring!
– Dhia Eddine