Repository navigation
Buffer HTTP response bytes in HTTPClient - #245
Merged
Merged
Conversation
HTTPConnection wraps socket.bytes in AsyncBufferingSequence so that the decoder does not pull one byte per syscall while it parses the request line and headers (6238522). HTTPClient was left reading the response unbuffered: `AsyncSocket.readResponse()` hands the raw bytes to `decodeResponse`, so the status line and every header are read through `iterator.next()`, one read(2) each. HTTPClient opens a socket for each request and reads one response from it, so it can read through a buffer without anything being lost to a later reader. `AsyncSocket.readResponse()` itself stays unbuffered: its callers go on to read WebSocket frames or further responses from the same socket, and bytes pulled past the response would not reach them. Measurements: release build on x86-64 Linux, 300 requests to a loopback HTTPServer whose response has six headers and a 600 byte body. Process CPU per request, client and server together, 0.43-0.60 ms -> 0.35 ms; wall time per request 0.29-0.38 ms -> 0.22 ms. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8cahEd4GfsHQ89sNrmP4p
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #245 +/- ##
==========================================
- Coverage 93.72% 93.70% -0.02%
==========================================
Files 72 72
Lines 3777 3783 +6
==========================================
+ Hits 3540 3545 +5
- Misses 237 238 +1 ☔ View full report in Codecov by Harness. |
swhitty
approved these changes
Oct 3, 2026
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.
HTTPConnectionwrapssocket.bytesinAsyncBufferingSequenceso that the decoder does not pull one byte per syscall while it parses the request line and headers (6238522).HTTPClientwas left reading the response unbuffered:AsyncSocket.readResponse()hands the raw bytes todecodeResponse, so the status line and every header are read throughiterator.next(), oneread(2)each.This shows up for a client that makes many small requests. In a
perfprofile of a daemon registering about 260 resources with an HTTP registry at start-up, the client's response reading was around 0.5 s of CPU on a Cortex-A72, most of it in the single-byteAsyncSocket.read()andCollectUntil.Change
HTTPClient.sendHTTPRequestopens a socket for each request and reads one response from it, so it now reads that response through anAsyncBufferingSequence. Nothing reads from the socket afterwards, so nothing is lost to the buffer.AsyncSocket.readResponse()itself stays unbuffered. Its other callers (the tests) go on to read WebSocket frames or further responses from the same socket, and bytes pulled past the response would not reach them. It gains areadResponse(from:)taking the bytes to read.Measurements
Release build on x86-64 Linux, 300 requests to a loopback
HTTPServerwhose response has six headers and a 600 byte body:Testing
swift teston Linux (Swift 6.4): 300 tests, the same 3 issues as onmainwithout this change (HTTPDecoderTests.standardizedPathandnaughtyPath_IsParsed, both about/../path standardisation).🤖 Generated with Claude Code
https://claude.ai/code/session_01T8cahEd4GfsHQ89sNrmP4p