You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Remove the `DecoderTestExtensions` shim and `ServerRequestDecoder.TryDecode`'s synchronous `ByteArrayContent` accumulation. Move the `PipedStreamContent`/pipe lifecycle into the decoders themselves so no caller ever constructs a `Pipe` or `PipedStreamContent` externally. Tests and StateMachines use a single `TryDecode` entry point.
10
+
11
+
## Background
12
+
13
+
### Current state (legacy)
14
+
15
+
-`DecoderTestExtensions.TryDecode` — extension method on `Decoder` that wraps `TryDecodeHeaders` + `FeedBody` into a synchronous "collect all responses" API. Uses a static `Dictionary<int, PendingResponse>` keyed by `RuntimeHelpers.GetHashCode` (memory leak risk). ~50 test files depend on it.
16
+
-`ServerRequestDecoder.TryDecode` — real method on the decoder that synchronously accumulates body bytes into `ByteArrayContent` before returning. Does not match production `StateMachine` behavior.
17
+
- Both `ServerStateMachine` and `StateMachine` (H/1.1) create `RequestBodyHandle`/`ResponseBodyHandle` externally, then attach `new PipedStreamContent(handle.Reader)` to the request/response.
18
+
19
+
### Target state
20
+
21
+
All pipe lifecycle is owned by the decoder. StateMachines orchestrate only; decoders produce ready-to-consume messages with `PipedStreamContent` already attached.
Wraps the single-request overload in a loop, collecting all complete requests from one data chunk. For pipelined data, after each request is returned, the loop continues with `ReadOnlyMemory<byte>.Empty` so the decoder drains its remainder buffer. Body data for each request is fed and the pipe completed before the decoder advances to the next request's headers.
47
+
48
+
**Abort surface:**
49
+
50
+
```csharp
51
+
publicvoidAbortBody(Exceptionreason)
52
+
```
53
+
54
+
Called by `StateMachine` on timeout or connection close. Completes the internal pipe writer with the exception so any awaiting consumer receives a clean error.
55
+
56
+
**Removed from public surface:**`TryDecodeHeaders`, `FeedBody`, `Phase` property.
Timeout and connection-close paths call `_decoder.AbortBody(reason)` where they previously called `_activeBody.Abort(...)`.
113
+
114
+
### `StateMachine` (client-side)
115
+
116
+
Same simplification: `ResponseBodyHandle? _responseBody` removed; loop calls `_decoder.TryDecode(data, out var response)`.
117
+
118
+
### `RequestBodyHandle` / `ResponseBodyHandle`
119
+
120
+
Moved to `internal` — they become implementation details of the respective decoder. No external type references them after the refactor.
121
+
122
+
## Test Migration
123
+
124
+
### Tests using `ServerRequestDecoder.TryDecode(data, out IReadOnlyList<HttpRequestMessage>)`
125
+
126
+
The convenience overload preserves this signature. **No test changes required** for simple single-request tests.
127
+
128
+
The only observable difference: `request.Content` is now `PipedStreamContent` instead of `ByteArrayContent`. All existing tests already use `await request.Content.ReadAsStringAsync(...)` — this continues to work because the pipe is completed synchronously when all data arrives in one chunk.
129
+
130
+
### Tests using `DecoderTestExtensions.TryDecode`
131
+
132
+
Same: `Decoder` gains a `TryDecode(ReadOnlyMemory<byte>, out IReadOnlyList<HttpResponseMessage>?)` convenience overload. The ~50 test files remain **unchanged**.
|`RequestBodyHandle` as external type |`Protocol/Http11/`| internal in decoder |
155
+
|`_activeBody` field in `ServerStateMachine`|`Protocol/Http11/ServerStateMachine.cs`| removed |
156
+
157
+
## Risks
158
+
159
+
**Pipelining sequencing:** The new `TryDecode` loop must feed all of request N's body before decoding request N+1's headers from the remainder. This sequencing currently lives in `StateMachine`; it must be replicated correctly inside the decoder. Covered by existing `Http11RoundTripPipeliningSpec` tests.
160
+
161
+
**`TryDecodeEof`:** Remains as a separate method. Close-delimited response handling is distinct — no data parameter, reads from internal remainder. Not affected by this refactor.
162
+
163
+
**`AbortBody` coverage:** Every timeout/disconnect path in `ServerStateMachine` that previously called `_activeBody.Abort(...)` must be updated to call `_decoder.AbortBody(reason)`. Requires careful audit of the `StateMachine` timeout paths.
164
+
165
+
**Benchmark breakage:**`Http11DecoderBenchmark` and `Http11ChunkedDecoderBenchmark` call `TryDecodeHeaders`/`FeedBody` directly. These must be updated as part of the implementation.
166
+
167
+
## Out of Scope (future work)
168
+
169
+
H/1.0, H/2, and H/3 `StateMachine` classes also build pipes externally and should receive the same treatment. Deferred until H/1.1 refactor is complete and patterns are proven.
0 commit comments