Fast client keeps a keep-alive socket after truncating an oversized response, so the next request reads leftover body bytes and fails
perfloop/fortio · EXTENT MISMATCH
https://perfloop.ai/t/oss/case_9gtx1hy3p2
Verdict
VERIFIED · settled 2026-09-25 · pull request opened as fortio/fortio#1167
What happened: The assertion is violated on the comparison and satisfied with this change.
Hypothesis
fhttp/http_client.go:1232-1240: when headerLen + Content-Length exceeds len(c.buffer), readResponse logs "Buffer is too small for headers + data" and caps maxV at len(c.buffer) (line 1240), but leaves keepAlive true. The loop then breaks at `c.size >= maxV` with the rest of the body unread, and at line 1301 `if keepAlive && codeIsOK(c.code) && !c.reachedReuseThreshold()` keeps the socket (c.socket = socket). The next StreamFetch reuses that socket, writes a new request, and its first read returns the previous response's remaining body bytes; line 1150 parses the status code from those bytes, the code is not OK, and the request is counted as an error. So with keep-alive (the default) and responses larger than -httpbufferkb (default 128, line 71), requests alternate between success and failure. Existing test TestSmallBufferAndNoKeepAlive (fhttp/http_test.go:697) only calls Fetch once on the keep-alive client, so it does not cover the reuse. Upstream issue fortio/fortio#617 shows the resulting "Non ok http code -1" warnings in a user run. A local probe calling Fetch four times against EchoHandler with ?size=16*1024+100 and BufferSizeKb=16 returned 200, -1, 200, -1 (local, unverified observation). Laurent's advice in #617 is to raise -httpbufferkb; this fix does not replace that, it only stops one oversized response from poisoning the next request on the same connection.
Change to test: In FastClient.readResponse, when Content-Length exceeds the buffer (fhttp/http_client.go:1232-1241), also set keepAlive = false so the socket is closed at fhttp/http_client.go:1301 instead of being reused with unread body bytes still in it. Keep the existing warning and truncation. Prove with a test in fhttp/http_test.go next to TestSmallBufferAndNoKeepAlive (line ~697) that calls Fetch twice on the same keep-alive client against EchoHandler with ?size=BufferSizeKb*1024+1 and asserts the second call returns 200; it fails before and passes after. Run the repo's tests (go test ./fhttp/... and the full suite).
Where it lives
perfloop/fortio · cli/fortio_main.go
Evidence
The assertion is violated on the comparison and satisfied with this change: `With keep-alive enabled, a FastClient that truncates an oversized Content-Length response returns 200 for the next Fetch on the same client without reusing the socket containing unread body bytes.`
Checks: 1 of 1 passed. Verification: no defect found.
Timeline
2026-09-24· Case opened2026-09-25· PR opened