Skip to content

Commit 973064c

Browse files
etrclaude
andcommitted
docs(architecture): add class-map and request-flow diagrams
Store the two v2.0 architecture visualizations in-repo so collaborators can find them without the external artifact links: - docs/architecture/class-map.html — webserver façade → webserver_impl composition root (5 state collaborators + 7 behavior services + MHD adapter facet), domain layer, response_body hierarchy, route internals, hook system; per-class header/cpp locations. - docs/architecture/request-flow.html — the MHD callback spine, body loop, WebSocket branch, four-tier route_table::lookup_v2, and the 11 hook phases in firing order. - docs/architecture/README.md — GitHub-native Mermaid renderings of both plus the hook-phase table, indexing the HTML pages. Both HTML pages are self-contained (no external assets, light/dark aware). Reflects the DR-014 decomposition and the current naming (connection_context, *_response_body). Linked from README.md and specs/architecture/03-system-overview.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015tAodxYJMEY4VxCX4dk62e
1 parent ea3e67c commit 973064c

5 files changed

Lines changed: 1139 additions & 0 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ section. It is comprehensive but not exhaustive — the headers under
5151
[`examples/`](examples/) tree contains a runnable demonstration of every
5252
feature.
5353

54+
For a visual mental model, see
55+
[`docs/architecture/`](docs/architecture/): a **class, relationship &
56+
filesystem map** and a **request lifecycle & routing flow** diagram
57+
(Mermaid inline on GitHub, plus richer self-contained HTML pages).
58+
5459
The shortest possible server looks like this:
5560

5661
```cpp

docs/architecture/README.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# libhttpserver v2.0 — architecture diagrams
2+
3+
Two visual references for the v2.0 (`feature/v2.0`) codebase. Each exists in two forms:
4+
5+
| Diagram | GitHub-native (below) | Rich standalone page |
6+
|---|---|---|
7+
| **Class, relationship & filesystem map** | [Mermaid ↓](#1-class-relationship--filesystem-map) | [`class-map.html`](class-map.html) |
8+
| **Request lifecycle & routing flow** | [Mermaid ↓](#2-request-lifecycle--routing-flow) | [`request-flow.html`](request-flow.html) |
9+
10+
The `.html` files are self-contained (no external assets, light/dark aware) — open them directly in a browser for the full, colour-coded detail: every class card, filesystem location, ownership edge, all 28 flow steps, the four-tier route cascade, and the hook rail. The Mermaid versions below render inline on GitHub for a quick orientation.
11+
12+
> **Colour language** (shared by both diagrams): composition-root = blue · state collaborator = amber · behavior service = teal · MHD C-ABI adapter = purple · domain / value type = slate.
13+
14+
---
15+
16+
## 1. Class, relationship & filesystem map
17+
18+
Post-**DR-014**, `webserver` is a thin façade over `webserver_impl`, which is a **pure composition root** holding **5 state collaborators** (own their mutexes + data) and **7 behavior services** (stateless; hold `const&` into state and each other), plus a **static MHD adapter facet** (the C-ABI trampolines). Ownership is strictly linear and top-down; services form an acyclic DAG with no back-pointer (the sole exception: `daemon_lifecycle` needs `webserver_impl*` to read broad config while building the MHD option array).
19+
20+
Stereotypes below encode the role: `<<state>>` = state collaborator (owns mutex + data), `<<behavior>>` = behavior service (stateless), `<<adapter>>` = MHD C-ABI facet. Solid diamond (`*--`) = owns by value; dashed arrow (`..>`) = holds `const&` reference.
21+
22+
```mermaid
23+
classDiagram
24+
direction LR
25+
class create_webserver
26+
class webserver
27+
class webserver_impl {
28+
<<adapter>>
29+
}
30+
create_webserver --> webserver : builds
31+
webserver *-- webserver_impl : owns
32+
33+
class daemon_lifecycle {
34+
<<state>>
35+
}
36+
class route_table {
37+
<<state>>
38+
}
39+
class hook_bus {
40+
<<state>>
41+
}
42+
class ip_access_control {
43+
<<state>>
44+
}
45+
class ws_registry {
46+
<<state>>
47+
}
48+
webserver_impl *-- daemon_lifecycle
49+
webserver_impl *-- route_table
50+
webserver_impl *-- hook_bus
51+
webserver_impl *-- ip_access_control
52+
webserver_impl *-- ws_registry
53+
54+
class error_pages {
55+
<<behavior>>
56+
}
57+
class hook_dispatcher {
58+
<<behavior>>
59+
}
60+
class response_materializer {
61+
<<behavior>>
62+
}
63+
class upload_pipeline {
64+
<<behavior>>
65+
}
66+
class websocket_upgrader {
67+
<<behavior>>
68+
}
69+
class request_dispatcher {
70+
<<behavior>>
71+
}
72+
class request_pipeline {
73+
<<behavior>>
74+
}
75+
webserver_impl *-- error_pages
76+
webserver_impl *-- hook_dispatcher
77+
webserver_impl *-- response_materializer
78+
webserver_impl *-- upload_pipeline
79+
webserver_impl *-- websocket_upgrader
80+
webserver_impl *-- request_dispatcher
81+
webserver_impl *-- request_pipeline
82+
83+
hook_dispatcher ..> hook_bus
84+
response_materializer ..> error_pages
85+
response_materializer ..> hook_dispatcher
86+
websocket_upgrader ..> ws_registry
87+
request_dispatcher ..> route_table
88+
request_dispatcher ..> hook_dispatcher
89+
request_dispatcher ..> error_pages
90+
request_dispatcher ..> response_materializer
91+
request_dispatcher ..> websocket_upgrader
92+
request_pipeline ..> hook_dispatcher
93+
request_pipeline ..> request_dispatcher
94+
```
95+
96+
`daemon_lifecycle` (HAVE_WEBSOCKET builds also wire `ws_registry` + `websocket_upgrader`) — `route_table` owns `route_entry` / `segment_trie` / `route_cache`; `hook_bus` holds the 11 server-wide phase vectors; `response_materializer` turns `http_response` into an `MHD_Response`; `request_pipeline` is the re-entrant body-accumulation state machine. Full descriptions and per-class file locations: [`class-map.html`](class-map.html).
97+
98+
**Per-request / per-connection state** (threaded through the services):
99+
100+
```mermaid
101+
classDiagram
102+
direction LR
103+
class connection_context
104+
class connection_state
105+
class http_request
106+
class http_response
107+
class http_resource
108+
class response_body {
109+
<<abstract>>
110+
}
111+
112+
connection_context *-- http_request : owns
113+
connection_context *-- http_response : owns optional
114+
connection_context ..> http_resource : weak_ptr
115+
http_response *-- response_body : owns 64B SBO
116+
response_body <|-- empty_response_body
117+
response_body <|-- string_response_body
118+
response_body <|-- file_response_body
119+
response_body <|-- iovec_response_body
120+
response_body <|-- pipe_response_body
121+
response_body <|-- deferred_response_body
122+
response_body <|-- digest_challenge_response_body
123+
```
124+
125+
**Filesystem convention.** Public surface in `src/httpserver/` (installed); internal detail headers in `src/httpserver/detail/` (never installed); implementations in `src/` and `src/detail/`. `webserver` = one façade TU (`src/webserver.cpp`); `webserver_impl` = two TUs (`src/detail/webserver_impl.cpp` composition root + `src/detail/webserver_callbacks.cpp` MHD adapter). The full per-class header/cpp locations are in [`class-map.html`](class-map.html).
126+
127+
---
128+
129+
## 2. Request lifecycle & routing flow
130+
131+
One HTTP request is not one function call. libmicrohttpd drives the exchange through a fixed sequence of C-ABI callbacks (static `webserver_impl` trampolines in `webserver_callbacks.cpp`), each forwarding into a behavior service. `answer_to_connection` is called **1..N times** — once for a bodyless `GET`, many times while a `POST` body streams in — but resolves to exactly one `finalize_answer`.
132+
133+
```mermaid
134+
sequenceDiagram
135+
autonumber
136+
participant MHD as libmicrohttpd
137+
participant AD as webserver_impl trampolines
138+
participant PL as request_pipeline
139+
participant DP as request_dispatcher
140+
participant RT as route_table
141+
participant RM as response_materializer
142+
143+
MHD->>AD: policy_callback · IP ACL
144+
Note over AD: ip_access_control.classify → accept? · ◈ accept_decision
145+
MHD->>AD: connection_notify STARTED
146+
Note over AD: new connection_state (arena) · ◈ connection_opened
147+
MHD->>AD: uri_log
148+
Note over AD: new connection_context → *con_cls
149+
150+
MHD->>AD: answer_to_connection · first · request==nullptr
151+
Note over AD: canonicalize URL · resolve_method_callback
152+
AD->>PL: requests_answer_first_step
153+
Note over PL: build http_request · ◈ request_received short-circuit
154+
alt has body · POST or PUT
155+
loop until zero-size chunk
156+
MHD->>AD: answer_to_connection · body chunk
157+
AD->>PL: requests_answer_second_step
158+
Note over PL: ◈ body_chunk · grow_content / post_iterator → upload_pipeline
159+
end
160+
end
161+
162+
MHD->>AD: answer_to_connection · terminal · size==0
163+
AD->>PL: requests_answer_second_step → complete_request
164+
PL->>DP: finalize_answer
165+
Note over DP: try_ws_upgrade → 101 branch bypasses the rest
166+
DP->>RT: lookup_v2 · exact→cache→radix→regex
167+
RT-->>DP: entry + captured_params · else 404
168+
Note over DP: ◈ route_resolved · ◈ before_handler auth · is_allowed → 405
169+
Note over DP: pointer-to-member → render_get · catch → ◈ handler_exception → 500
170+
Note over DP: ◈ after_handler may replace response
171+
DP->>RM: materialize_and_queue_response
172+
Note over RM: response_body.materialize → decorate → MHD_queue_response ◀ SEND · ◈ response_sent
173+
MHD->>AD: request_completed
174+
Note over AD: ◈ request_completed · delete connection_context · reset_arena
175+
MHD->>AD: connection_notify CLOSED
176+
Note over AD: ◈ connection_closed · delete connection_state
177+
```
178+
179+
**Four-tier route resolution** (`route_table::lookup_v2`, cheapest first, first hit wins):
180+
181+
```mermaid
182+
flowchart LR
183+
C[canonicalize path] --> T1{Tier 1<br/>exact_routes_ map}
184+
T1 -- hit --> H[entry + captured_params]
185+
T1 -- miss --> T2{Tier 2<br/>LRU cache}
186+
T2 -- hit --> H
187+
T2 -- miss --> T3{Tier 3<br/>segment_trie radix}
188+
T3 -- hit --> INS[cache install] --> H
189+
T3 -- miss --> T4{Tier 4<br/>regex scan}
190+
T4 -- hit --> INS
191+
T4 -- miss --> NF[404 not found]
192+
```
193+
194+
The entry is returned **regardless of method** so the 405 path still sees it. Method → handler is chosen separately: the verb became a pointer-to-member (`render_get`/`render_post`/…) in `resolve_method_callback`, and `dispatch_resource_handler` checks `http_resource::is_allowed(method_enum)` → mismatch yields **405** + the resource's `Allow:` header.
195+
196+
### The 11 hook phases (firing order)
197+
198+
Server-wide hooks live on `hook_bus`; the five post-route-resolution phases are also **per-resource** (`resource_hook_table`, via `http_resource::add_hook`). Every phase is guarded by a relaxed-atomic `has_hooks_for` check — zero cost when unused.
199+
200+
| # | Phase | Fires in | Scope | Kind |
201+
|---|---|---|---|---|
202+
| 1 | `connection_opened` | `connection_notify` STARTED | server | observe |
203+
| 2 | `accept_decision` | `policy_callback` | server | observe |
204+
| 3 | `request_received` | `requests_answer_first_step` | server | short-circuit |
205+
| 4 | `body_chunk` | `requests_answer_second_step` (per chunk) | server | short-circuit |
206+
| 5 | `route_resolved` | `finalize_answer` (after lookup) | server | observe |
207+
| 6 | `before_handler` | `finalize_answer` (pre-dispatch · **auth**) | server + route | short-circuit |
208+
| 7 | `handler_exception` | `dispatch_resource_handler` catch | server + route | short-circuit |
209+
| 8 | `after_handler` | `finalize_answer` (post-handler) | server + route | replace resp |
210+
| 9 | `response_sent` | `materialize_and_queue` (after queue) | server + route · log_access alias | observe |
211+
| 10 | `request_completed` | `request_completed` callback | server + route | observe |
212+
| 11 | `connection_closed` | `connection_notify` CLOSED | server | observe |
213+
214+
---
215+
216+
## Keeping these current
217+
218+
These diagrams describe `feature/v2.0` as of the DR-014 decomposition. When the composition root's collaborators, the callback spine, the route tiers, or the hook phases change, update **both** the Mermaid blocks above and the corresponding `.html` page (they are hand-authored, not generated). The rich pages are also published as live Claude artifacts — see the team's shared links.

0 commit comments

Comments
 (0)