HTTP Messages
Every HTTP interaction is a message exchange: a client sends a request, a server sends a response. This library gives you three complementary components for working with messages:
-
Containers build and inspect the start line and headers
-
Serializer transforms a container and body data into bytes for the wire
-
Parser transforms bytes from the wire into a container and body data
These components are designed to work together but remain independently useful. You can build a message with a container, serialize it for transmission, and parse the reply — or use any one in isolation.
Containers Hold Headers, Not Bodies
An HTTP message consists of a start line, header fields, and an optional
body. This library keeps the body separate from the container. The
request and response types hold only the start line and headers in
their serialized wire format. This avoids parameterizing on body type
and keeps the containers simple:
request req(method::get, "/api/users");
req.set(field::host, "example.com");
req.set(field::accept, "application/json");
// buffer() returns the serialized start line + headers
std::cout << req.buffer();
Body data flows through the serializer or parser instead.
The Serializer and Parser Are Persistent Objects
Both the serializer and parser allocate a fixed block of memory at construction and reuse it across every message on a connection. This eliminates per-message allocation and makes resource usage predictable. Create one of each when a connection is established and keep them alive for its duration:
// One parser and one serializer per connection
request_parser pr;
serializer sr(cfg);
pr.reset();
while (connection_open)
{
pr.start();
// ... parse a request ...
// ... serialize a response ...
}
Two-Sided Interfaces
The serializer and parser each expose two sides:
| Input Side | Output Side | |
|---|---|---|
Serializer |
Sink — accepts a container and body data from the caller |
Stream — emits serialized HTTP bytes for writing to a socket |
Parser |
Stream — accepts raw bytes read from a socket |
Source — yields a parsed container and body data to the caller |
This two-sided design follows naturally from their role as transformers: the serializer converts structured data into bytes, and the parser converts bytes into structured data. Neither side performs I/O directly. You feed data in on one side and pull results out the other.
Sans-I/O at Every Layer
None of these components touch a socket. The parser consumes buffers you fill; the serializer produces buffers you drain. This means the same code works with any transport — Asio, io_uring, or a test harness that feeds canned data — without recompilation or abstraction layers.
Pages in This Section
-
Containers — build and inspect requests, responses, and header field collections
-
Serializing — transform messages and body data into bytes for the wire
-
Parsing — transform bytes from the wire into messages and body data