perf(client) + fix(h1): decode/body hot-path optimizations and a single-connection HTTP/1.1 pipelining deadlock#45
Merged
Conversation
…ping them (RFC 9113 6.8)
…re latch characterization
…of Task.Run loops
…ers" This reverts commit 687bfbb.
7448e0c to
2458cab
Compare
1d1928c to
4db1c82
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Five client hot-path performance optimizations on the HTTP/1.1 and HTTP/2 decode/body
paths, plus a fix for an intermittent HTTP/1.1 single-connection pipelining deadlock
that surfaced while stress-testing them. All behavior-preserving except one documented
internal decoder contract change.
Fix - HTTP/1.1 single-connection pipelining deadlock
Under heavy single-connection pipelining the server streams responses back to back, so a
response's status line or header block is frequently split across two TCP reads. The
H1.1 client kept no cross-read remainder (only the streaming back-pressure path retained
anything), so the unconsumed prefix of a split header was discarded and the next read's
continuation parsed as garbage →
HttpProtocolException: Malformed header field. Thatfaulted one request and stranded its in-flight pipelined siblings, deadlocking the
connection (
SingleConnectionConcurrencyRegressionSpecfailed ~50–80% of runs).Fix: retain the unconsumed prefix in
Http11ClientStateMachine(_partialResponse)and prepend it to the next inbound buffer - mirroring what the H2
FrameDecoderalreadydoes - cleared on disconnect, decode failure, and cleanup. Added a deterministic repro
(
Http11ClientFragmentedResponseSpec) covering status-line, header-line, andsecond-pipelined-response splits. The stress guard now drains 256 × 40 cleanly.
Performance optimizations
FrameDecoder.Decodereturns its reused_frameslist directly instead ofToArray()per inbound read — removes one array allocation per H2 network read on bothclient and server. Callers that hold a result across a later
Decodenow snapshotexplicitly;
CLAUDE.mdupdated to document the contract.of recomputing
GetByteCountwhen adding to the dynamic table (single 4-argAdd).BufferSearch.FindCrlfuses a single vectorized two-byteIndexOf("\r\n"u8)instead of find-CR-then-check-LF with scan restarts — runs once per response line.
QueuedBodyStream.CopyToAsyncoverride writes pooled body chunks straight to thedestination, removing a body-sized copy and the 81,920-byte framework rent per buffered
download (return-to-pool deferred until after the write completes).
Testing
All test projects green, no flakes:
unit 5754/0 · End2End 101/0 · Client 474/0 · Server 89/0 · AcceptanceTests 712/0 ·
API.Tests 1/0 · AspNetCore.Tests 36/0. Each change is TDD'd with a deterministic spec;
Roslyn diagnostics clean on every changed file.
Notes
lib/servus.akkais unchanged (gitlink not bumped).FrameDecoder.Decodereturning a reused buffer;all in-tree callers consume it synchronously and tests that held results were updated to
snapshot.