High performance HTTP 2.0 server with Boost.ASIO and OpenSSL
This server started as a learning project in order to satisfy my curiosity about network programming. It was complex enough and cool enough for me to try it, and oh boy... it was something. The server is built entirely from scratch using raw Boost.ASIO and OpenSSL, no high level abstraction libraries like Boost.Beast or nghttp2 were used.
-
Async I/O: Proactor pattern using boost::asio::io_context and std::enable_shared_from_this for concurrent connection handling without memory leaks.
-
TLS 1.3: Full OpenSSL integration, including manual ALPN negotiation via a native_handle() to force the h2 protocol. And yes, it has a C-like API.
-
HTTP/2 Preface: Validates the 24-bytes of the preface
-
Binary framing: Frame header serialization/deserialization, handling Big-Endian and Little-Endian byte ordering via bitwise operations
-
Connection Lifecycle: Parses client SETTINGS, responds with server SETTINGS, correctly handles ACKs and identifies WINDOW_UPDATE and GOAWAY frames
-
Hardcoded Response: Successfully sends a spec-compliant 200 OK HEADERS frame (tested with curl --http2 -k)
-
Test-Driven Parsing: Frame header parser and preface validator built via Catch2 before integration into the Asio loop. Yeah, test-driven development is a blast.
-
HPACK Decoding: Integrating a decoder (or implementing one from scratch) to decompress incoming HEADERS payload into :method - :path key-value pairs
-
DATA Frames: Actual response bodies after the header frame
-
Flow Control: Tracking connection and stream window sizes and sending WINDOW_UPDATE to avoid stalling
First of all, this is my first serious project on github, so i learnt a lot of things along the way. Asides from the difficulty of the project itself, there were some very specific highly painful points that took me a bit to figure out:
-
The Asio Mental Model: Shifting from synchronous loops to the weird asio model with chained callbacks, shared pointers, weird async functions look like recursive functions (but actually aren't) was one of the worst parts. After a while everything clicked in place and things started going kind of smooth. The pattern becomes clear once you struggle with it.
-
HTTP/2 Strictness: HTTP/2 is VERY strict, sending an HPACK encoded without emitting first a table size update and boom, you get a FRAME_SIZE_ERROR from nghttp2 clients. You either comply or crash, beautiful in a way.
-
ALPN Abstraction Leaks: Boost.Asio lacks a C++ wrapper for ALPN, requiring a drop down to OpenSSL C APIs that honestly look eldritch compared to your usual nice C++ API, which itself sometimes ain't nice at all.
Overall, a fun (and painful) experience. It really makes you think about how hard is to do this type of thing, and to be honest, it would've been painful to do this in a non-agile way for me. Decomposing the big problem of making a server like this in small actionable steps and always writing tests or taking time to learn how the API works first into an isolated environment before writing directly into my project really eased some of the process. Right now the project is paused, but i might come back some day and finish it... or remake it into something new. Thank you for reading.