Repository navigation
Share one fake HTTP body that enforces the net/http lifecycle in every test - #166
Merged
Merged
Conversation
…y test The fix in the parent PR was caught by a one-off body fake that failed reads after the request context was cancelled. The two existing fakes (trackedBody in api and in snykbroker) were backed by a strings.Reader that ignores the context, so the drain-and-close tests passed against code that cancelled the deadline before reading the body. The httptest based tests passed too, because net/http serves a small body out of its buffer after cancel; only bodies past the buffered threshold fail. test/httpfake now owns a single Body and Transport. Every body served fails reads once the request context is done, fails reads after Close, and records EOF and Close, so each test that builds a client on it gets all three rules without opting in. With the production fix reverted the existing TestCall_ReadsDrainsAndClosesResponseBody and TestCall_RateLimitedBodyIsDrainedAndClosed both fail, so the dedicated regression test is folded into the first of them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
HttpBackend is the only other consumer of an http.Client in the agent, and it had no tests: every tunnel test stubs the Backend interface above the HTTP layer. It hands the raw response body to the call handler, which streams it as frames under a context that startCall owns and cancels in a defer. That is the shape of the bug #165 fixed in the Cortex API path, with nothing pinning it down here. The new test drives one call through startCall with HttpBackend over an httpfake client and a body several frames long, and checks the whole body is streamed, the call ends with End rather than Cancel, and the body reached EOF and was closed. Cancelling the context right after backend.Do returns makes it fail with "upstream read failed: context canceled". httpfake gains NewBody so fakes above the HTTP client can hand out a tracked body too; the tunnel stubs now use it instead of io.NopCloser. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
shawnburke
enabled auto-merge (squash)
October 14, 2026 21:18
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.
Follow-up to #165.
Why
#165 was caught by a one-off body fake that fails reads after the request context is cancelled. Neither existing fake could have caught it:
trackedBodycopies inserver/apiandserver/snykbrokerare backed by astrings.Reader, which has no idea a request context exists. They verify drain and close, and nothing about cancellation.httptest.NewServertests use a real transport, but every body in them is a few bytes.net/httpserves bytes it already buffered even after cancel, so small bodies pass by accident. Against a real server, cancelling betweenDoandReadAllsucceeds for an 11-byte body and fails withcontext canceledfor a 1 MiB body after somewhere between 8 and 20 KB, varying run to run.What
Commit 1. A new
test/httpfakepackage owns a singleBodyandTransport. Every body served:Readonce the request's context is done (strictly, since the real buffered threshold is not something a test can control),ReadafterClose, asnet/httpdoes,Both test files now build their client on it, so every test through that path gets all three rules without opting in. The package has its own tests, including one that caught a flaw in my first draft (reusing one canned response served an exhausted reader).
Commit 2.
HttpBackendingrpctunnelis the only otherhttp.Clientconsumer in the agent and had no tests; every tunnel test stubs theBackendinterface above the HTTP layer. It hands the raw body to the call handler, which streams it under a context the handler cancels in a defer, which is the same shape as the #165 bug. A new test drives a multi-frame body throughstartCallwith a realHttpBackendover anhttpfakeclient and checks the body is streamed in full, the call ends with End not Cancel, and the body hit EOF and was closed.httpfake.NewBodylets the existing tunnel stubs hand out tracked bodies instead ofio.NopCloser.Proof
cortex_api.goandhttp_helper.goreverted to the pre-Keep the default Cortex API deadline active until the body is read, and clear curl CVEs #165 versions, the two pre-existing testsTestCall_ReadsDrainsAndClosesResponseBodyandTestCall_RateLimitedBodyIsDrainedAndClosedboth fail withcontext canceled. The dedicated regression test from Keep the default Cortex API deadline active until the body is read, and clear curl CVEs #165 is therefore folded into the first of them.backend.Doreturns inrunCall, the newTestHttpBackend_StreamsWholeBodyAndClosesItBeforeCallContextEndsfails withupstream read failed: context canceled.All four packages pass
go vetandgo test -race.🤖 Generated with Claude Code