Containers
The library provides modifiable containers for HTTP messages and standalone field collections. Unlike some HTTP libraries, the message body is kept separate—these containers hold only the start line and headers, avoiding the template complexity that comes from parameterizing on body type.
Container Types
| Type | Description |
|---|---|
|
A modifiable container of header fields (name-value pairs). |
|
A modifiable container holding a request line and header fields. |
|
A modifiable container holding a status line and header fields. |
|
A request container using externally-provided storage. |
|
A response container using externally-provided storage. |
All containers maintain this invariant: contents are always valid HTTP. Operations that would produce malformed output throw an exception. This means you can safely serialize any container at any time.
Working with Fields
The fields class stores a collection of header fields. Use it when you
need headers independent of a full request or response—for example, when
building trailer fields for chunked encoding.
Creating Fields
// Empty fields container
fields f;
// With initial capacity (bytes)
fields f(1024);
// Parse from a string
fields f(
"Content-Type: text/html\r\n"
"Cache-Control: no-cache\r\n"
"\r\n");
Adding Fields
fields f;
// Append with field constant (preferred - faster lookup)
f.append(field::content_type, "text/html");
f.append(field::cache_control, "no-cache");
// Append with string name
f.append("X-Custom-Header", "custom-value");
Multiple fields with the same name are allowed:
f.append(field::set_cookie, "session=abc123");
f.append(field::set_cookie, "theme=dark");
// Both Set-Cookie headers are preserved
Querying Fields
// Check if a field exists
if (f.exists(field::content_type)) { /* ... */ }
// Get the value (throws if not found)
auto ct = f.at(field::content_type);
// Get the value or a default
auto ct = f.value_or(field::content_type, "application/octet-stream");
// Count occurrences
std::size_t n = f.count(field::set_cookie);
// Iterate all fields
for (auto const& fld : f)
{
std::cout << fld.name << ": " << fld.value << "\n";
}
Working with Requests
The request class represents a complete HTTP request (minus the body).
Creating Requests
// Default: GET / HTTP/1.1
request req;
// With method and target
request req(method::post, "/api/users");
// With method, target, and version
request req(method::get, "/", version::http_1_0);
// Parse from a string
request req(
"POST /api/data HTTP/1.1\r\n"
"Host: example.com\r\n"
"Content-Length: 13\r\n"
"\r\n");
Request Properties
request req(method::get, "/api/users");
// Access start line components
method m = req.method(); // method::get
auto target = req.target(); // "/api/users"
version v = req.version(); // version::http_1_1
// For unknown methods
auto text = req.method_text(); // "GET"
// Modify start line
req.set_method(method::post);
req.set_target("/api/users/123");
req.set_version(version::http_1_0);
// Or set all at once (more efficient)
req.set_start_line(method::put, "/api/users/123", version::http_1_1);
Request Headers
Requests inherit all field operations from fields_base:
request req(method::get, "/");
req.set(field::host, "example.com");
req.set(field::accept, "application/json");
req.append(field::accept_encoding, "gzip, deflate");
// Serialized form
std::cout << req.buffer();
Output:
GET / HTTP/1.1
Host: example.com
Accept: application/json
Accept-Encoding: gzip, deflate
Working with Responses
The response class represents a complete HTTP response (minus the body).
Creating Responses
// Default: HTTP/1.1 200 OK
response res;
// With status code
response res(status::not_found);
// With status and version
response res(status::created, version::http_1_0);
// Parse from a string
response res(
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\n"
"\r\n");
Response Properties
response res(status::ok);
// Access start line components
status s = res.status(); // status::ok
unsigned code = res.status_int(); // 200
auto reason = res.reason(); // "OK"
version v = res.version(); // version::http_1_1
// Modify status
res.set_status(status::created);
// Set status with custom reason (rarely needed)
res.set_status(200, "All Good");
// Set version
res.set_version(version::http_1_0);
// Set all at once
res.set_start_line(status::accepted, version::http_1_1);
Common Response Patterns
// JSON API response
response res(status::ok);
res.set(field::content_type, "application/json");
res.set_content_length(json_body.size());
// Redirect
response res(status::moved_permanently);
res.set(field::location, "https://example.com/new-path");
// Error with body
response res(status::bad_request);
res.set(field::content_type, "text/plain");
res.set_content_length(error_message.size());
Methods and Status Codes
HTTP Methods
The method enumeration provides constants for standard HTTP methods:
method::get // GET
method::head // HEAD
method::post // POST
method::put // PUT
method::delete_ // DELETE (underscore due to C++ keyword)
method::connect // CONNECT
method::options // OPTIONS
method::trace // TRACE
method::patch // PATCH
// ... plus WebDAV methods, etc.
Convert between strings and enum values:
// String to method
method m = string_to_method("POST"); // method::post
// Method to string
auto s = to_string(method::get); // "GET"
For non-standard methods, use method::unknown and work with strings:
request req;
req.set_method("CUSTOM"); // method() returns method::unknown
auto text = req.method_text(); // "CUSTOM"
Status Codes
The status enumeration covers standard HTTP status codes:
// Informational (1xx)
status::continue_ // 100
status::switching_protocols // 101
// Successful (2xx)
status::ok // 200
status::created // 201
status::accepted // 202
status::no_content // 204
// Redirection (3xx)
status::moved_permanently // 301
status::found // 302
status::not_modified // 304
// Client Error (4xx)
status::bad_request // 400
status::unauthorized // 401
status::forbidden // 403
status::not_found // 404
// Server Error (5xx)
status::internal_server_error // 500
status::not_implemented // 501
status::bad_gateway // 502
status::service_unavailable // 503
Status codes have associated reason phrases:
auto reason = to_string(status::not_found); // "Not Found"
Classify status codes by category:
auto sc = status_class::successful; // 2xx
auto sc = to_status_class(status::ok); // status_class::successful
auto sc = to_status_class(404); // status_class::client_error
The Field Enumeration
The field enumeration defines constants for well-known HTTP headers.
Using these constants instead of strings enables:
-
Faster comparisons (integer vs string)
-
Case-insensitive matching without overhead
-
Compile-time checking of field names
// Standard fields (partial list)
field::accept
field::accept_encoding
field::authorization
field::cache_control
field::connection
field::content_length
field::content_type
field::cookie
field::host
field::location
field::server
field::set_cookie
field::transfer_encoding
field::user_agent
// ... many more
Convert between strings and enum:
// String to field (case-insensitive)
auto f = string_to_field("Content-Type"); // field::content_type
// Field to string (canonical casing)
auto s = to_string(field::content_type); // "Content-Type"
Memory Management
Capacity Control
Containers allocate memory dynamically as needed. You can control this:
// Pre-allocate capacity
request req(1024); // Reserve 1024 bytes
// Reserve more capacity
req.reserve_bytes(4096);
// Get current capacity
std::size_t cap = req.capacity_in_bytes();
// Set maximum allowed capacity
req.set_max_capacity_in_bytes(8192); // Throws if exceeded
Static Containers
For performance-critical paths, use static containers that never allocate:
// Fixed buffer of 2048 bytes
std::array<char, 2048> storage;
static_request req(storage.data(), storage.size());
// If capacity is exceeded, operations throw
Static containers are useful when:
-
You need guaranteed no-allocation paths
-
You have a fixed upper bound on message size
-
You’re working in embedded or real-time contexts
Serialization
Every container can produce its serialized form instantly:
request req(method::get, "/");
req.set(field::host, "example.com");
// Get the complete serialized message (minus body)
core::string_view sv = req.buffer();
// Stream output
std::cout << req;
The buffer() function returns a view to internal storage. It runs in
constant time and never allocates. The view remains valid until the
container is modified.
Next Steps
Now that you can build and inspect HTTP messages, learn how to parse incoming messages from the network:
-
Parsing — parse request and response messages