HTTP/2 stream stays half-closed after an early response with status >= 300
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 55/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- swift
- 领域
- backend, networking
调研方向
Start with HTTP2ClientRequestHandler.runSuccessfulFinalAction and the state machine path described in the issue; compare how .close is handled for an early response while the request body is incomplete. Run swift test --filter 'HTTP2ClientRequestHandlerTests/testStreamIs' using the added tests in Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift. Done means the client sends RST_STREAM(CANCEL), the stream is released, and shutdown() completes.
由索引模型根据 Issue 内容生成。
描述
AsyncHTTPClient commit hash: 017115279d09dbfd262a6c678e96d31c90b3b2aa (main).
Context
After an early complete response with status >= 300 (RFC 9113, section 8.1), the client sends neither END_STREAM nor RST_STREAM. The stream stays half-closed (remote), and the connection counts it as open. HTTPClient.shutdown() does not complete until the connection closes. Status < 300 is a different case: #922.
Example: apple/containerization sends each blob PUT without credentials first, and the registry answers 401 during the upload.
Cause:
- The request body is not complete.
HTTPRequestStateMachinereturns.forwardResponseEnd(.close, ...). - Since #657,
HTTP2ClientRequestHandler.runSuccessfulFinalActionignores.close, because "an h2 stream is closed after every request anyway". This is not true here. The request body stream is paused, and the.finishedstate machine rejects more body parts and the request end. HTTP2IdleHandlergets noStreamClosedEventfor this stream.- At
shutdown(), the stream getsHTTPConnectionEvent.shutdownRequested. The request is finished, sorequestCancelled()returns.wait.
Steps to reproduce
The repro sends one 64 MiB PUT without credentials to registry-1.docker.io over HTTP/2. The registry answers 401 during the upload.
git clone https://github.com/beshkenadze/async-http-client-early-response-leak
cd async-http-client-early-response-leak
AHC_REVISION=0171152 ./run.sh # AHC main
The README has the commands for 1.36.2, a lazy body and --wait 660, the output fields and the requirements.
The tests use only in-memory channels. git and SwiftPM need network access. The commands add only the fix branch's new tests to main:
git clone https://github.com/swift-server/async-http-client.git
cd async-http-client
git checkout 017115279d09dbfd262a6c678e96d31c90b3b2aa
git fetch https://github.com/beshkenadze/async-http-client.git fix/h2-reset-stream-after-early-response
git checkout FETCH_HEAD -- Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift
swift test --filter 'HTTP2ClientRequestHandlerTests/testStreamIs'
Expected result
The client sends RST_STREAM(CANCEL). The connection releases the stream, and shutdown() completes.
Actual result
| Run | openH2Streams |
shutdown() |
|---|---|---|
main and 1.36.2, eager and lazy body, 2 runs each |
1, in 8 of 8 runs | Does not complete in 10 s, in 8 of 8 runs. |
1.36.2, eager body, --wait 660, 1 run |
The stream and the connection closed together 61 s after the start of the run. | Completed at once after the 660 s wait. |
The fix for #939 does not change these results in 4 runs. The raw data names that build local@afcf468. Its Sources/ tree (6b69592) is the same as in beshkenadze:fix/early-response-request-body-stream.
| Test | Result |
|---|---|
testStreamIsClosedIfErrorResponseEndsBeforeRequestBodyIsSent |
Fails. After an early 401, the stream channel is still active (XCTAssertFalse failed). |
testStreamIsResetIfErrorResponseEndsBeforeRequestBodyIsSent |
Fails. The client sends no RST_STREAM: XCTAssertEqual failed: ("[]") is not equal to ("[HTTP2ErrorCode<0x8 Cancel>]") |
| The other 2 of the 4 tests | Pass. |
Raw data:
Environment
swift-nio-http2 1.46.0, macOS 27.0.1 (26A434).
$ swift --version
swift-driver version: 1.168.6 Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
Target: arm64-apple-macosx27.0.0
$ uname -srvm
Darwin 27.0.0 Darwin Kernel Version 27.0.0: Tue Aug 11 21:22:49 PDT 2026; root:xnu-13432.1.9~1/RELEASE_ARM64_T6000 arm64
- 主要语言
- Swift
- 星标
- 1.1k
- 派生
- 159
- 平均合并
- 2 小时 56 分钟
- 30 天内合并 PR
- 1
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
swift-server/async-http-client 的其他 Issue
-
难度 5/5 一周以上 新手友好度 25/100
swift-server/async-http-client#946 · 2 条评论 ·
-
RequestBag keeps the request body after an early response with status >= 300可能已有人在做 @beshkenadze 于 19 天前认领。 未关闭
难度 3/5 1-2 天 新手友好度 68/100
-
Transaction keeps the request body after an early response with status >= 300可能已有人在做 @beshkenadze 于 19 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 35/100
-
难度 4/5 3-5 天 新手友好度 22/100
-
难度 4/5 3-5 天 新手友好度 35/100
swift-server/async-http-client#927 · 2 条评论 ·
查看 swift-server/async-http-client 的全部 Issue
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
ls1intum/artemis-ios#571 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
ml-explore/mlx-swift-lm#791 ·
维护者通常 2 天内回复
-
Tell Siri what's on screen (iOS 27 App Entity annotations)可能已有人在做 @alexandrereol 今天认领。 未关闭enhancement
难度 2/5 1-3 小时 新手友好度 78/100
alexandrereol/ethmensa#32 ·
维护者通常 1 天内回复
-
a: plugins d: docs/ fyi-macos P2 platform-ios platform-macos team-ios
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复