Skip to content

Commit 54ada63

Browse files
committed
[FOLD]
1 parent 852b6ea commit 54ada63

2 files changed

Lines changed: 143 additions & 0 deletions

File tree

doc/modules/ROOT/pages/3.messages.adoc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,97 @@
88
//
99

1010
= HTTP Messages
11+
12+
Every HTTP interaction is a message exchange: a client sends a request,
13+
a server sends a response. This library gives you three complementary
14+
components for working with messages:
15+
16+
* **Containers** build and inspect the start line and headers
17+
* **Serializer** transforms a container and body data into bytes for the wire
18+
* **Parser** transforms bytes from the wire into a container and body data
19+
20+
These components are designed to work together but remain independently
21+
useful. You can build a message with a container, serialize it for
22+
transmission, and parse the reply — or use any one in isolation.
23+
24+
== Containers Hold Headers, Not Bodies
25+
26+
An HTTP message consists of a start line, header fields, and an optional
27+
body. This library keeps the body separate from the container. The
28+
`request` and `response` types hold only the start line and headers in
29+
their serialized wire format. This avoids parameterizing on body type
30+
and keeps the containers simple:
31+
32+
[source,cpp]
33+
----
34+
request req(method::get, "/api/users");
35+
req.set(field::host, "example.com");
36+
req.set(field::accept, "application/json");
37+
38+
// buffer() returns the serialized start line + headers
39+
std::cout << req.buffer();
40+
----
41+
42+
Body data flows through the serializer or parser instead.
43+
44+
== The Serializer and Parser Are Persistent Objects
45+
46+
Both the serializer and parser allocate a fixed block of memory at
47+
construction and reuse it across every message on a connection. This
48+
eliminates per-message allocation and makes resource usage predictable.
49+
Create one of each when a connection is established and keep them alive
50+
for its duration:
51+
52+
[source,cpp]
53+
----
54+
// One parser and one serializer per connection
55+
request_parser pr;
56+
serializer sr(cfg);
57+
58+
pr.reset();
59+
60+
while (connection_open)
61+
{
62+
pr.start();
63+
// ... parse a request ...
64+
// ... serialize a response ...
65+
}
66+
----
67+
68+
== Two-Sided Interfaces
69+
70+
The serializer and parser each expose two sides:
71+
72+
[cols="1,2,2"]
73+
|===
74+
| |Input Side |Output Side
75+
76+
|**Serializer**
77+
|Sink — accepts a container and body data from the caller
78+
|Stream — emits serialized HTTP bytes for writing to a socket
79+
80+
|**Parser**
81+
|Stream — accepts raw bytes read from a socket
82+
|Source — yields a parsed container and body data to the caller
83+
|===
84+
85+
This two-sided design follows naturally from their role as transformers:
86+
the serializer converts structured data into bytes, and the parser
87+
converts bytes into structured data. Neither side performs I/O directly.
88+
You feed data in on one side and pull results out the other.
89+
90+
== Sans-I/O at Every Layer
91+
92+
None of these components touch a socket. The parser consumes buffers
93+
you fill; the serializer produces buffers you drain. This means the
94+
same code works with any transport — Asio, io_uring, or a test harness
95+
that feeds canned data — without recompilation or abstraction layers.
96+
97+
== Pages in This Section
98+
99+
* xref:3a.containers.adoc[Containers] — build and inspect requests,
100+
responses, and header field collections
101+
* xref:3b.serializing.adoc[Serializing] — transform messages and body
102+
data into bytes for the wire
103+
* xref:3c.parsing.adoc[Parsing] — transform bytes from the wire into
104+
messages and body data

doc/modules/ROOT/pages/3a.containers.adoc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,59 @@ the template complexity that comes from parameterizing on body type.
3737

3838
|===
3939

40+
These types form an inheritance hierarchy rooted at `fields_base`:
41+
42+
[mermaid]
43+
----
44+
classDiagram
45+
fields_base <|-- fields
46+
fields_base <|-- message_base
47+
message_base <|-- request_base
48+
message_base <|-- response_base
49+
request_base <|-- request
50+
request_base <|-- static_request
51+
response_base <|-- response
52+
response_base <|-- static_response
53+
54+
class fields_base["fields_base"] {
55+
field storage and iteration
56+
}
57+
class message_base["message_base"] {
58+
version, payload, keep-alive
59+
}
60+
class fields["fields"] {
61+
standalone header collection
62+
}
63+
class request_base["request_base"] {
64+
method, target
65+
}
66+
class response_base["response_base"] {
67+
status code, reason
68+
}
69+
class request["request"] {
70+
dynamic storage
71+
}
72+
class static_request["static_request"] {
73+
externally-provided storage
74+
}
75+
class response["response"] {
76+
dynamic storage
77+
}
78+
class static_response["static_response"] {
79+
externally-provided storage
80+
}
81+
----
82+
4083
All containers maintain this invariant: **contents are always valid HTTP**.
4184
Operations that would produce malformed output throw an exception. This
4285
means you can safely serialize any container at any time.
4386

87+
WARNING: Syntactic validity does not imply semantic correctness. A
88+
container will happily hold contradictory headers, nonsensical status
89+
codes, or fields that create security vulnerabilities such as response
90+
splitting. The caller is responsible for ensuring the contents reflect
91+
intent.
92+
4493
== Working with Fields
4594

4695
The `fields` class stores a collection of header fields. Use it when you

0 commit comments

Comments
 (0)