A lightweight, dependency-free HTTP/1.1 server built in modern C++17 β from raw POSIX sockets up.
___
___ _ __ _ __ | |__ | |_ |_|_|_ |_)
/ __| '_ \| '_ \ | '_ \ | __| | | | |
| (__| |_) | |_) || | | || |_ | | | |
\___| .__/| .__/ |_| |_| \__| |_| |_|
|_| |_|
cpphttp is a learning project that demonstrates how a real HTTP server works β no Boost, no libuv, no framework magic. Just sockets, threads, and clean modern C++.
| Feature | Details |
|---|---|
| HTTP/1.1 parser | Method, path, query string, headers, body |
| Express-style routing | GET /users/:id with named parameters |
| Middleware chain | app.use(handler) β runs before every route |
| Static file serving | Serve a directory with MIME detection |
| Thread pool | One thread per connection (configurable) |
| Colourised logger | Access log + structured log levels |
| Zero dependencies | Only the C++17 standard library + POSIX |
Server running β 12 threads, live access log:

Browser output at localhost:8080:

- CMake β₯ 3.16
- GCC β₯ 9 or Clang β₯ 10 (with C++17 support)
- Linux or macOS
git clone https://github.com/codcreater1/cpphttp.git
cd cpphttp
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel./build/hello_world
# cpphttp listening on http://0.0.0.0:8080 [8 threads]Open http://localhost:8080 in your browser or:
curl http://localhost:8080/api/hello?name=Ada
# {"message":"Hello, Ada!","server":"cpphttp"}
curl http://localhost:8080/api/users/42
# {"user_id":"42","name":"Alice","role":"admin"}#include <cpphttp.hpp>
using namespace cpphttp;
Server app; // uses hardware_concurrency() threads
Server app(4); // explicit thread countapp.get("/", handler);
app.post("/items", handler);
app.put("/items/:id", handler);
app.del("/items/:id", handler);[](const Request& req, Response& res) {
// Access path params
std::string id = req.param("id").value_or("0");
// Access query params β /search?q=hello
std::string q = req.query_param("q").value_or("");
// Access headers
std::string ct = req.header("content-type").value_or("");
// Send responses
res.json(R"({"ok":true})");
res.html("<h1>Hello</h1>");
res.status(201).send("Created");
res.status(404).json(R"({"error":"not found"})");
}// Simple request logger
app.use([](const Request& req, Response& /*res*/) {
std::cout << req.method << " " << req.path << "\n";
});
// Auth guard (short-circuit by calling res.send())
app.use([](const Request& req, Response& res) {
if (req.header("x-api-key") != "secret") {
res.status(401).json(R"({"error":"Unauthorized"})");
}
});app.serve_static("/public", "./www");
// GET /public/index.html β serves ./www/index.htmlapp.listen(8080); // blocks; default host 0.0.0.0
app.listen(3000, "127.0.0.1"); // custom host
// Graceful shutdown (e.g. from signal handler)
app.stop();cpphttp/
βββ include/
β βββ cpphttp.hpp β Single include
β βββ cpphttp/
β βββ server.hpp β Core server
β βββ request.hpp β HTTP request model
β βββ response.hpp β HTTP response builder
β βββ router.hpp β Path-param router
β βββ logger.hpp β Colourised logger
βββ src/
β βββ server.cpp
β βββ request.cpp
β βββ response.cpp
β βββ router.cpp
β βββ logger.cpp
βββ examples/
β βββ hello_world.cpp β Getting started
β βββ todo_api.cpp β Full CRUD REST API
βββ .github/
β βββ workflows/ci.yml β GitHub Actions CI
βββ CMakeLists.txt
This is the interesting bit β here's the lifecycle of a request:
Client
β
β TCP connect
βΌ
accept() β main thread blocks here
β
β spawn std::thread
βΌ
recv() β read raw bytes from socket
β
Request::parse() β tokenise HTTP/1.1 request line + headers
β
Middleware chain β app.use() handlers run in order
β
Router::match() β regex match path, extract :params
β
Route handler β your lambda runs here
β
Response::to_raw() β serialise status + headers + body
β
send() β write to socket
β
close()
./build/todo_api
# Create
curl -X POST http://localhost:8080/todos \
-H 'Content-Type: application/json' \
-d '{"title":"Learn C++ sockets"}'
# List
curl http://localhost:8080/todos
# Update
curl -X PUT http://localhost:8080/todos/1 \
-H 'Content-Type: application/json' \
-d '{"done":true}'
# Delete
curl -X DELETE http://localhost:8080/todos/1-
keep-aliveconnection reuse - Chunked transfer encoding
- HTTP/2 (h2c) upgrade
- TLS via OpenSSL (opt-in)
-
multipart/form-dataparser - WebSocket upgrade
- Route groups / sub-routers
Pull requests are welcome! Please:
- Fork the repo and create a feature branch
- Keep changes focused and well-commented
- Ensure the CI build passes
- Open a PR with a clear description
MIT Β© 2024 β see LICENSE for details.