Skip to content

Commit bf13997

Browse files
committed
Update docs
1 parent 25d9c28 commit bf13997

7 files changed

Lines changed: 39 additions & 96 deletions

File tree

CLAUDE.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Starting with Feature 040, test files are organized by **component/protocol vers
166166
| Project | Structure |
167167
|---------|-----------|
168168
| `TurboHttp.Tests/` | `Http10/`, `Http11/`, `Http2/`, `Http3/`, `Semantics/`, `Caching/`, `Cookies/`, `Transport/`, `Security/`, `Diagnostics/`, `Hosting/` |
169-
| `TurboHttp.StreamTests/` | Same folder structure as above |
169+
| `TurboHttp.StreamTests/` | `Http10/`, `Http11/`, `Http2/`, `Http3/`, `Semantics/`, `Caching/`, `Cookies/`, `Transport/`, `Dispatchers/`, `Streams/` |
170170
| `TurboHttp.IntegrationTests/` | Unchanged: `H10/`, `H11/`, `H2/`, `H3/`, `TLS/` |
171171

172172
#### RFC → Component Mapping
@@ -246,29 +246,26 @@ The RFC-based folders are being replaced incrementally. Migration order:
246246

247247
**No big-bang sprint:** New tests land directly in the new structure; old tests migrate as they are touched.
248248

249-
#### Guard-Rail: displayname-validator
249+
#### Guard-Rail: spec-naming-validator
250250

251-
The `displayname-validator` agent warns when a **new test file** is created in a deprecated `RFC*/` folder:
252-
- Only flags files modified after the skeleton structure was created (not pre-existing tests)
253-
- Suggests the correct new folder destination based on the RFC→Component mapping table
251+
The `spec-naming-validator` agent validates naming conventions in new component-based test files:
252+
- Checks `Spec.cs` file names, `sealed` classes, BDD method names, `[Trait("RFC", ...)]` usage
254253
- Does NOT block build/tests — it is a quality gate for new code
255-
- Run after adding new test files: `displayname-validator` (agents/.claude/agents/displayname-validator)
254+
- Run after adding new test files: `spec-naming-validator` (`.claude/agents/spec-naming-validator`)
256255

257256
## Dependencies
258257

259-
- **Akka.Streams** 1.5.63, **Servus.Akka** 0.3.10, **.NET 10.0**, **xunit.v3.mtp-v2** 3.2.2, **Akka.TestKit.Xunit** 1.5.63, **BenchmarkDotNet** 0.15.8
258+
- **Akka.Streams** 1.5.64, **Servus.Akka** 0.3.10, **.NET 10.0**, **xunit.v3.mtp-v2** 3.2.2, **Akka.TestKit.Xunit** 1.5.64, **BenchmarkDotNet** 0.15.8
260259

261260
## Custom Agents (`.claude/agents/`)
262261

263262
| Agent | When to use |
264263
|-------|-------------|
265-
| `rfc-test-writer` | Generate a new RFC test file following exact project conventions |
266264
| `akka-stage-builder` | Implement a new Akka.Streams `GraphStage` |
267265
| `build-guardian` | Run full build + tests; produce RFC-breakdown coverage report |
268266
| `namespace-refactorer` | Execute namespace reorganisation tasks |
269-
| `stream-test-writer` | Generate `TurboHttp.StreamTests` files following `StreamTestBase` conventions |
270267
| `stage-port-validator` | Scan all stages for port naming convention violations |
271-
| `displayname-validator` | Validate `[Fact]`/`[Theory]` DisplayName attributes follow project naming convention |
268+
| `spec-naming-validator` | Validate Spec naming conventions (file, class, method, RFC traits) in new component-based test files |
272269

273270
## Agent Guidance: dotnet-skills
274271

docs/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ Every page targets **library users** — .NET developers who use TurboHttp in th
4242

4343
## Build Commands
4444

45-
See root `CLAUDE.md` for VitePress dev server, build, preview, and LikeC4 SVG export commands.
45+
See root `CLAUDE.md` for VitePress dev server, build, and preview commands.

docs/architecture/engines.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,16 @@ HTTP/2 provides **stream multiplexing** — many logical requests share a single
8383
**Stage sequence:**
8484

8585
```
86-
Http20StreamIdAllocatorStage → Http20Request2FrameStage → Http20ConnectionStage → Http20EncoderStage → Http20PrependPrefaceStage → ConnectionStage → TCP
87-
88-
TCP → ConnectionStage → Http20DecoderStage → Http20ConnectionStage → Http20StreamStage → (response downstream)
86+
Http20ConnectionStage → [frame batch] → Http20EncoderStage → ConnectionStage → TCP
87+
TCP → ConnectionStage → Http20DecoderStage → Http20ConnectionStage
8988
```
9089

9190
| Stage | Role |
9291
|-------|------|
93-
| `Http20StreamIdAllocatorStage` | Allocates client stream IDs (1, 3, 5, …) — odd integers, client-initiated |
94-
| `Http20Request2FrameStage` | HPACK-encodes request headers; emits `HEADERS` frame + `DATA` frame(s) |
95-
| `Http20ConnectionStage` | Bidirectional flow control; handles `SETTINGS`, `PING`, `WINDOW_UPDATE`, `GOAWAY` frames |
96-
| `Http20EncoderStage` | Serialises `Http2Frame` objects to wire bytes (9-byte frame header + payload) |
97-
| `Http20PrependPrefaceStage` | Injects the HTTP/2 connection preface (`PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n` + client `SETTINGS`) on the first connection |
98-
| `ConnectionStage` | TCP transport; shared with HTTP/1.x via the `Engine` demultiplexer |
92+
| `Http20ConnectionStage` | Central bidirectional stage: allocates client stream IDs (1, 3, 5, …), HPACK-encodes request headers and emits `HEADERS` + `DATA` frames, handles connection-level frames (`SETTINGS`, `PING`, `WINDOW_UPDATE`, `GOAWAY`), assembles per-stream `HEADERS` + `DATA` frames into `HttpResponseMessage`, and correlates responses back to pending requests by stream ID |
93+
| `Http20EncoderStage` | Serialises `Http2Frame` objects to wire bytes (9-byte frame header + payload); emits the HTTP/2 connection preface (`PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n` + client `SETTINGS`) on its first pull |
9994
| `Http20DecoderStage` | Stateful parser; reassembles frames from TCP byte stream, handles partial frame delivery |
100-
| `Http20StreamStage` | Assembles per-stream `HEADERS` + `DATA` frames into `HttpResponseMessage`; HPACK-decodes headers |
101-
| `Http20CorrelationStage` | Maps stream IDs back to pending requests; supports concurrent in-flight streams |
95+
| `ConnectionStage` | TCP transport; shared with HTTP/1.x via the `Engine` demultiplexer |
10296

10397
**HPACK header compression:**
10498

@@ -121,14 +115,14 @@ HTTP/3 runs over **QUIC** instead of TCP. Each request uses an independent QUIC
121115
**Stage sequence:**
122116

123117
```
124-
Http30Http20Request2FrameStage → Http30ConnectionStage → Http30EncoderStage → Http3ConnectionStage → QUIC
118+
Http30Request2FrameStage → Http30ConnectionStage → Http30EncoderStage → Http3ConnectionStage → QUIC
125119
126120
QUIC → Http3ConnectionStage → Http30DecoderStage → Http30ConnectionStage → Http30StreamStage → (response downstream)
127121
```
128122

129123
| Stage | Role |
130124
|-------|------|
131-
| `Http30Http20Request2FrameStage` | QPACK-encodes request headers; emits `HEADERS` frame + `DATA` frame(s) |
125+
| `Http30Request2FrameStage` | QPACK-encodes request headers; emits `HEADERS` frame + `DATA` frame(s) |
132126
| `Http30ConnectionStage` | Bidirectional connection manager; handles `SETTINGS`, `GOAWAY`, and stream lifecycle |
133127
| `Http30EncoderStage` | Serialises HTTP/3 frames to wire bytes using QUIC variable-length encoding |
134128
| `Http3ConnectionStage` | QUIC transport bridge; acquires a QUIC connection from the pool, writes/reads bytes |

docs/architecture/scenarios.md

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Here's what happens when you send a request with different HTTP versions. The de
2626
9. The server's response bytes arrive via TCP and flow through `ClientByteMover` into the inbound channel.
2727
10. `Http10DecoderStage` parses the HTTP/1.0 response; body length is determined by `Content-Length` or EOF.
2828
11. `Http1XCorrelationStage` matches the response to the pending request.
29-
12. `DecompressionBidiStage` decompresses the body if needed.
29+
12. `ContentEncodingBidiStage` decompresses the body if needed.
3030
13. `CookieBidiStage` stores any `Set-Cookie` headers.
3131
14. `CacheBidiStage` caches the response if it is cacheable.
3232
15. `RetryBidiStage` passes the response through (no retry needed for a successful response).
@@ -79,12 +79,10 @@ HTTP/2 is fundamentally different from HTTP/1.x. A single TCP connection carries
7979

8080
### Request Framing
8181

82-
1. `Http20StreamIdAllocatorStage` assigns the next available stream ID (1, 3, 5, …).
83-
2. `Http20Request2FrameStage` HPACK-encodes the request headers into a `HEADERS` frame, and the body (if any) into `DATA` frame(s).
84-
3. `Http20ConnectionStage` applies connection-level and stream-level flow control — it will withhold frames if the server's receive window is exhausted.
85-
4. `Http20EncoderStage` serialises each `Http2Frame` to its 9-byte framed wire format.
86-
5. `Http20PrependPrefaceStage` injects the HTTP/2 connection preface (`PRI * HTTP/2.0…` + initial `SETTINGS`) on the first connection only.
87-
6. Frames travel to TCP via `ConnectionStage` and `ClientByteMover`.
82+
1. `Http20ConnectionStage` assigns the next available stream ID (1, 3, 5, …), HPACK-encodes the request headers into a `HEADERS` frame, and serialises the body (if any) into `DATA` frame(s).
83+
2. `Http20ConnectionStage` applies connection-level and stream-level flow control — it will withhold frames if the server's receive window is exhausted.
84+
3. `Http20EncoderStage` serialises each `Http2Frame` to its 9-byte framed wire format; on the first connection it also injects the HTTP/2 connection preface (`PRI * HTTP/2.0…` + initial `SETTINGS`).
85+
4. Frames travel to TCP via `ConnectionStage` and `ClientByteMover`.
8886

8987
### Connection-Level Frames
9088

@@ -98,10 +96,8 @@ While request/response streams are active, `Http20ConnectionStage` also handles:
9896
### Response Assembly
9997

10098
7. Raw bytes from TCP are parsed by `Http20DecoderStage` into `Http2Frame` objects (handles partial frames across TCP boundaries).
101-
8. `Http20ConnectionStage` routes connection-level frames (SETTINGS, PING, GOAWAY) to internal handlers and forwards per-stream frames downstream.
102-
9. `Http20StreamStage` groups `HEADERS` and `DATA` frames by stream ID and assembles them into an `HttpResponseMessage`; HPACK-decodes the response headers.
103-
10. `Http20CorrelationStage` matches each assembled response back to its pending request using the stream ID.
104-
11. The response continues through `DecompressionBidiStage`, `CookieBidiStage`, `CacheBidiStage`, `RetryBidiStage`, and `RedirectBidiStage` — the same response chain as HTTP/1.x.
99+
8. `Http20ConnectionStage` routes connection-level frames (`SETTINGS`, `PING`, `GOAWAY`) to internal handlers, assembles per-stream `HEADERS` + `DATA` frames into an `HttpResponseMessage`, HPACK-decodes response headers, and correlates each assembled response back to its pending request using the stream ID.
100+
9. The response continues through `ContentEncodingBidiStage`, `CookieBidiStage`, `CacheBidiStage`, `RetryBidiStage`, and `RedirectBidiStage` — the same response chain as HTTP/1.x.
105101

106102
### Stream ID Exhaustion
107103

@@ -119,7 +115,7 @@ HTTP/3 replaces TCP with **QUIC**, a UDP-based transport that provides built-in
119115

120116
### Request Framing
121117

122-
1. `Http30Http20Request2FrameStage` QPACK-encodes the request headers into a `HEADERS` frame, and the body (if any) into `DATA` frame(s).
118+
1. `Http30Request2FrameStage` QPACK-encodes the request headers into a `HEADERS` frame, and the body (if any) into `DATA` frame(s).
123119
2. `Http30ConnectionStage` manages connection-level concerns — `SETTINGS`, `GOAWAY`, and stream lifecycle.
124120
3. `Http30EncoderStage` serialises each HTTP/3 frame to wire bytes using QUIC variable-length integer encoding.
125121
4. `Http3ConnectionStage` acquires a QUIC connection from the pool and sends the bytes over the network.
@@ -136,7 +132,7 @@ While request/response streams are active, `Http30ConnectionStage` handles:
136132
5. Raw bytes from QUIC are parsed by `Http30DecoderStage` into HTTP/3 frame objects.
137133
6. `Http30ConnectionStage` routes connection-level frames to internal handlers and forwards per-stream frames downstream.
138134
7. `Http30StreamStage` groups `HEADERS` and `DATA` frames by stream and assembles them into an `HttpResponseMessage`; QPACK-decodes the response headers.
139-
8. The response continues through `DecompressionBidiStage`, `CookieBidiStage`, `CacheBidiStage`, `RetryBidiStage`, and `RedirectBidiStage` — the same response chain as HTTP/1.x and HTTP/2.
135+
8. The response continues through `ContentEncodingBidiStage`, `CookieBidiStage`, `CacheBidiStage`, `RetryBidiStage`, and `RedirectBidiStage` — the same response chain as HTTP/1.x and HTTP/2.
140136

141137
### Key Differences from HTTP/2
142138

docs/likec4/model-pipeline.c4

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// redirect ─── redirect request ──▶ cookie (re-enters with cookie injection for new URL)
1717
// retry ─── retry request ──▶ expect100 (re-enters below cookie, above engine)
1818
// cache hit ─── cached response ──▶ response chain at retry level (bypasses ContentEncoding + engine)
19-
// QPACK ─── table updates ──▶ Http30Request2FrameStage (encoder/decoder table sync)
19+
// QPACK ─── table updates ──▶ Http30Request2FrameStage (QPACK encoder/decoder table sync)
2020

2121
model {
2222
// ── Request chain — outermost to innermost ────────────────────────────────
@@ -54,19 +54,13 @@ model {
5454
turbohttp.streams.ConnectionReuseStage -[feedback]-> turbohttp.streams.ConnectionStage 'reuse or close decision'
5555
turbohttp.streams.Http1XCorrelationStage -[flows]-> turbohttp.streams.ContentEncodingBidiStage 'matched response'
5656

57-
// ── HTTP/2 engine: stream IDs → HPACK → frames → TCP ─────────────────────
58-
turbohttp.streams.Http20Engine -[flows]-> turbohttp.streams.Http20StreamIdAllocatorStage 'HttpRequestMessage'
59-
turbohttp.streams.Http20StreamIdAllocatorStage -[flows]-> turbohttp.streams.Http20Request2FrameStage 'request with stream ID (frame path)'
60-
turbohttp.streams.Http20StreamIdAllocatorStage -[flows]-> turbohttp.streams.Http20CorrelationStage 'request with stream ID (correlation path)'
61-
turbohttp.streams.Http20Request2FrameStage -[flows]-> turbohttp.streams.Http20ConnectionStage 'HEADERS + DATA frames'
62-
turbohttp.streams.Http20ConnectionStage -[flows]-> turbohttp.streams.Http20EncoderStage 'outbound frames'
63-
turbohttp.streams.Http20EncoderStage -[flows]-> turbohttp.streams.Http20PrependPrefaceStage 'outbound bytes'
64-
turbohttp.streams.Http20PrependPrefaceStage -[flows]-> turbohttp.streams.ConnectionStage 'bytes with connection preface'
57+
// ── HTTP/2 engine: connection stage → HPACK → frames → TCP ──────────────────
58+
turbohttp.streams.Http20Engine -[flows]-> turbohttp.streams.Http20ConnectionStage 'HttpRequestMessage'
59+
turbohttp.streams.Http20ConnectionStage -[flows]-> turbohttp.streams.Http20EncoderStage 'outbound frames (batched)'
60+
turbohttp.streams.Http20EncoderStage -[flows]-> turbohttp.streams.ConnectionStage 'bytes (with preface on first connect)'
6561
turbohttp.streams.ConnectionStage -[flows]-> turbohttp.streams.Http20DecoderStage 'inbound bytes'
6662
turbohttp.streams.Http20DecoderStage -[flows]-> turbohttp.streams.Http20ConnectionStage 'inbound frames'
67-
turbohttp.streams.Http20ConnectionStage -[flows]-> turbohttp.streams.Http20StreamStage 'response frames per stream'
68-
turbohttp.streams.Http20StreamStage -[flows]-> turbohttp.streams.Http20CorrelationStage 'assembled HttpResponseMessage'
69-
turbohttp.streams.Http20CorrelationStage -[flows]-> turbohttp.streams.ContentEncodingBidiStage 'matched response'
63+
turbohttp.streams.Http20ConnectionStage -[flows]-> turbohttp.streams.ContentEncodingBidiStage 'matched response'
7064

7165
// ── HTTP/3 engine: QPACK → frames → QUIC ─────────────────────────────────
7266
turbohttp.streams.Http30Engine -[flows]-> turbohttp.streams.Http30Request2FrameStage 'HttpRequestMessage'

docs/likec4/model.c4

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,16 @@ model {
128128
}
129129

130130
// HTTP/2 stages
131-
Http20StreamIdAllocatorStage = component 'Http20StreamIdAllocatorStage' {
132-
#graphstage #http2
133-
technology 'GraphStage'
134-
description 'Allocates client-initiated stream IDs: 1, 3, 5, ...'
135-
}
136-
137-
Http20Request2FrameStage = component 'Http20Request2FrameStage' {
131+
Http20ConnectionStage = component 'Http20ConnectionStage' {
138132
#graphstage #http2
139-
technology 'GraphStage'
140-
description 'Converts requests into HEADERS + DATA frames using HPACK compression'
133+
technology 'GraphStage (BidiFlow)'
134+
description 'Central HTTP/2 bidirectional stage: stream ID allocation (1, 3, 5, …), HPACK request encoding, HEADERS/DATA frame emission, SETTINGS/PING/WINDOW_UPDATE/GOAWAY handling, response assembly, and stream-ID-based correlation'
141135
}
142136

143137
Http20EncoderStage = component 'Http20EncoderStage' {
144138
#graphstage #http2
145139
technology 'GraphStage'
146-
description 'Serialises HTTP/2 frames to wire bytes (9-byte header + payload)'
140+
description 'Serialises HTTP/2 frames to wire bytes (9-byte header + payload); emits connection preface on first pull'
147141
}
148142

149143
Http20DecoderStage = component 'Http20DecoderStage' {
@@ -152,30 +146,6 @@ model {
152146
description 'Parses wire bytes into HTTP/2 frames (handles TCP fragmentation)'
153147
}
154148

155-
Http20ConnectionStage = component 'Http20ConnectionStage' {
156-
#graphstage #http2
157-
technology 'GraphStage (BidiFlow)'
158-
description 'Bidirectional HTTP/2 connection manager: flow control, SETTINGS/PING/GOAWAY handling, stream backpressure'
159-
}
160-
161-
Http20StreamStage = component 'Http20StreamStage' {
162-
#graphstage #http2
163-
technology 'GraphStage'
164-
description 'Assembles per-stream HEADERS + DATA frames into HttpResponseMessage'
165-
}
166-
167-
Http20PrependPrefaceStage = component 'Http20PrependPrefaceStage' {
168-
#graphstage #http2
169-
technology 'GraphStage'
170-
description 'Injects the HTTP/2 connection preface on first connect'
171-
}
172-
173-
Http20CorrelationStage = component 'Http20CorrelationStage' {
174-
#graphstage #http2
175-
technology 'GraphStage'
176-
description 'Stream-ID-based request-response correlation for multiplexed HTTP/2 streams'
177-
}
178-
179149
// HTTP/3 stages
180150
Http30Request2FrameStage = component 'Http30Request2FrameStage' {
181151
#graphstage #http3
@@ -560,12 +530,9 @@ model {
560530
turbohttp.streams.Http11Engine -> turbohttp.streams.ConnectionStage 'Reads/writes bytes via transport'
561531

562532
// HTTP/2 engine internals
563-
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20StreamIdAllocatorStage 'Allocates stream IDs'
564-
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20Request2FrameStage 'Converts requests to HEADERS + DATA frames'
565-
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20ConnectionStage 'Manages connection-level frames'
533+
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20ConnectionStage 'Manages HTTP/2 streams: ID allocation, HPACK encoding, flow control, response assembly, correlation'
566534
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20EncoderStage 'Serialises frames to bytes'
567535
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20DecoderStage 'Parses bytes to frames'
568-
turbohttp.streams.Http20Engine -> turbohttp.streams.Http20StreamStage 'Assembles response from frames'
569536
turbohttp.streams.Http20Engine -> turbohttp.streams.ConnectionStage 'Reads/writes bytes via transport'
570537

571538
// HTTP/3 engine internals
@@ -592,9 +559,9 @@ model {
592559
turbohttp.streams.Http10DecoderStage -> turbohttp.protocol.Http10Decoder 'Delegates decoding'
593560
turbohttp.streams.Http11DecoderStage -> turbohttp.protocol.Http11Decoder 'Delegates decoding'
594561
turbohttp.streams.Http20DecoderStage -> turbohttp.protocol.Http2FrameDecoder 'Delegates frame decoding'
595-
turbohttp.streams.Http20StreamStage -> turbohttp.protocol.HpackDecoder 'Decompresses response headers'
596-
turbohttp.streams.Http20Request2FrameStage -> turbohttp.protocol.Http2RequestEncoder 'Encodes requests to HEADERS + DATA frames'
597-
turbohttp.streams.Http20Request2FrameStage -> turbohttp.protocol.HpackEncoder 'Compresses request headers'
562+
turbohttp.streams.Http20ConnectionStage -> turbohttp.protocol.Http2RequestEncoder 'Encodes requests to HEADERS + DATA frames'
563+
turbohttp.streams.Http20ConnectionStage -> turbohttp.protocol.HpackEncoder 'Compresses request headers'
564+
turbohttp.streams.Http20ConnectionStage -> turbohttp.protocol.HpackDecoder 'Decompresses response headers'
598565

599566
// HPACK internals
600567
turbohttp.protocol.HpackEncoder -> turbohttp.protocol.HpackDynamicTable 'Maintains dynamic table'

0 commit comments

Comments
 (0)