Multithreaded HTTP Web Server

A high-performance HTTP/1.1 web server built from scratch in C++ using raw POSIX sockets, a custom thread pool, and mutex synchronization.

Why I Built This

In my operating systems course I spent a lot of time studying how the CPU schedules processes and handles concurrency at the kernel level. At some point I wanted to stop reading about it and actually build something where multithreading was not optional, where the whole point of the system falls apart without it.

A web server felt like the perfect target. Every serious server in the real world handles thousands of simultaneous clients, and the only way to do that efficiently is with a well designed threading model. I wanted to build one from scratch, understand every layer, and benchmark it against real load.

I chose C++ over C because RAII makes resource management safe. Sockets and file descriptors get cleaned up automatically, which matters a lot when you have N threads all handling connections concurrently. C would have worked but the risk of leaking file descriptors under load was not worth it.

ℹ︎ Disclaimer: I used Claude Code to generate this interactive 2D flowchart — my CSS design skills are limited, do not expect much from a systems engineer. The flowchart may not render correctly on all browsers, and honestly I am surprised it renders at all. Feel free to view my original static flowchart version here → here →

Overview

A fully functional HTTP/1.1 web server built from scratch in C++ with no frameworks or networking libraries. The server handles multiple concurrent clients using a custom thread pool built with POSIX threads, a mutex-protected shared queue, and a condition variable for efficient thread synchronization.

Benchmarked at 5,500+ requests per second under 100 concurrent connections with an average latency of 1.77ms, measured using wrk on a MacBook Pro.

Architecture

The server is built around three components that each own a single responsibility and hand off work to the next. The Listener sits at the front, creating a raw TCP socket bound to port 8080 and blocking on accept() consuming zero CPU while idle. The moment a client connects it grabs the file descriptor and immediately pushes it to the thread pool, then goes straight back to waiting for the next connection.

The Thread Pool is where the concurrency lives. A fixed number of worker threads are created at startup and put to sleep on a condition variable. When the listener pushes a new connection, one thread wakes up, acquires the mutex lock, pulls the file descriptor from the shared queue, and handles that client completely independently from the rest.

Once a thread has a connection, the Request Handler takes over. It reads raw bytes off the socket, parses the HTTP request line to extract the method, path, and version, then maps the path to a file in the static/ folder on disk and writes a proper HTTP response back with headers and file contents. If the file does not exist it returns a clean 404.

Challenges

The first issue hit me during early testing. On startup all worker threads were being created and immediately spinning in a loop checking whether the queue had any pending requests. Even with zero client connections my MacBook fan was going full speed. The threads were burning CPU doing nothing useful. The fix was to put all threads to sleep on startup using a condition variable and only wake one up when the listener actually enqueues a new connection. Idle threads now consume zero CPU.

The second problem was trickier. Once I fixed the busy waiting, I ran into a race condition. When a new connection came in, multiple threads would wake up at the same time, all trying to grab the same request from the queue. This caused corrupted state and unpredictable behavior. The fix was two things together: a mutex lock around every queue access so only one thread can touch it at a time, and using notify_one() instead of notify_all() so only a single thread wakes up per new connection rather than all of them competing.

Source Code

The complete server implementation including TCP socket setup, thread pool, HTTP parser, and static file serving is available on GitHub.