Skip to content

Fix the redirect-with-body test endpoint announcing a length it does not send - #16

Merged
o-nnerb merged 1 commit into
releasefrom
fix/async-redirect-closed-channel
Oct 2, 2026
Merged

o-nnerb merged 1 commit into
releasefrom
fix/async-redirect-closed-channel

Conversation

@o-nnerb

@o-nnerb o-nnerb commented Oct 2, 2026

Copy link
Copy Markdown
Member

What

Fixes the test endpoint /redirect/302-with-body, which announced a length it did not send, and adds a test that would have caught it.

This is test-only. No production code changes.

Why

The endpoint announces Content-Length: <size>, but HTTPBin wraps every response body into the JSON description of the request (RequestInfo), which is longer than size. The bytes after the announced length are taken by the client for the start of the next response. The parser fails (invalid constant string), the channel is closed, and the next request of a redirect chain gets that connection and fails with I/O on closed channel.

With the async API and .follow, 300 sequential requests to /redirect/302-with-body?size=4096 failed 41, 65, 44 and 42 times on release. With the endpoint fixed, 8 rounds of 300 requests had no failure.

I first took this for a race in the client, and I wrote that down in #15. That was wrong: it was the test server. The failures also showed up on upstream main and on feature/redirect-custom-handler because I had copied the same endpoint into the test server there to compare.

Changes

  • HTTPResponseBuilder.sendsBodyVerbatim makes the test server send the body as it is. The endpoint uses it.
  • testFollowingRedirectsWithBodiesThatAreNotReadDoesNotFailTheNextRequest follows 150 redirects whose body is not read. It passes 5 out of 5 runs with the fix and fails 5 out of 5 without it.

Testing

  • macOS: full suite, 614 tests. The only failures are testConnectTimeout in HTTPClientTests and AsyncAwaitEndToEndTests, which fail the same on a clean release here.
  • Linux (Apple container, Swift 6.2): build with -Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error, and the redirect tests.
  • swift format lint --strict is clean.

…not send

/redirect/302-with-body announces a Content-Length of `size`, but the test
server wrapped every body into the JSON description of the request, which is
longer. The bytes after the announced length were taken by the client for the
start of the next response, failed to parse, and closed the connection, so the
next request of a redirect chain failed with `I/O on closed channel` in 15 to
20 percent of the tries.

It was not a bug of the client: the same sequence of requests against the
fixed endpoint did not fail in 2400 tries. Add a way to send a body as it is,
use it for this endpoint, and add a test that follows redirects with bodies
that are not read, which fails when the endpoint announces a length that it
does not send.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@o-nnerb
o-nnerb merged commit 95daf12 into release Oct 2, 2026
35 of 36 checks passed
@o-nnerb
o-nnerb deleted the fix/async-redirect-closed-channel branch October 2, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant