Skip to content

Fix crash in debug when request body stream finishes after a >= 300 response head - #934

Merged
fabianfett merged 2 commits into
swift-server:mainfrom
fabianfett:ff-crash-fix
Oct 7, 2026
Merged

fabianfett merged 2 commits into
swift-server:mainfrom
fabianfett:ff-crash-fix

Conversation

@fabianfett

Copy link
Copy Markdown
Member

Motivation

If a server responds with a status >= 300 while we are still uploading the request body, we ask the producer to pause the request body stream. The producer may still finish the stream, because it raced with the pause request. In this case we hit assert(head.status.code < 300) in HTTPRequestStateMachine.requestStreamFinished, which crashes debug builds.

Changes

  • Remove the wrong assertion. Finishing the request body stream after a >= 300 response head is a valid state transition: we send the request .end and move into .endSent.
  • Add a reproducer in HTTPRequestStateMachineTests that streams a body part, receives a 413 Payload Too Large head and then finishes the request body stream.

Result

A request body stream that finishes after the server responded with a status >= 300 no longer crashes in debug. Since the request .end is on the wire, the connection can be reused after the response ends.

@fabianfett fabianfett added the 🔨 semver/patch No public API change. label Oct 7, 2026
@fabianfett
fabianfett merged commit 73d0974 into swift-server:main Oct 7, 2026
37 checks passed
@fabianfett
fabianfett deleted the ff-crash-fix branch October 7, 2026 10:30
o-nnerb added a commit to request-dl/async-http-client that referenced this pull request Oct 7, 2026
* Fix crash in debug when request body stream finishes after a >= 300 response head (swift-server#934)

### Motivation

If a server responds with a status >= 300 while we are still uploading
the request body, we ask the producer to pause the request body stream.
The producer may still finish the stream, because it raced with the
pause request. In this case we hit `assert(head.status.code < 300)` in
`HTTPRequestStateMachine.requestStreamFinished`, which crashes debug
builds.

### Changes

- Remove the wrong assertion. Finishing the request body stream after a
>= 300 response head is a valid state transition: we send the request
`.end` and move into `.endSent`.
- Add a reproducer in `HTTPRequestStateMachineTests` that streams a body
part, receives a 413 Payload Too Large head and then finishes the
request body stream.

### Result

A request body stream that finishes after the server responded with a
`status >= 300` no longer crashes in debug. Since the request `.end` is
on the wire, the connection can be reused after the response ends.

---------

Co-authored-by: Eric Rosenberg <eric_rosenberg@apple.com>

* Lift minimum Swift version to 6.2 (swift-server#935)

Co-authored-by: Si Beaumont <simonjbeaumont@gmail.com>

---------

Co-authored-by: Fabian Fett <fabianfett@apple.com>
Co-authored-by: Eric Rosenberg <eric_rosenberg@apple.com>
Co-authored-by: Si Beaumont <simonjbeaumont@gmail.com>
Co-authored-by: brennobemoura <37243584+brennobemoura@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 semver/patch No public API change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants