Repository navigation
Conversation
Motivation: HTTPKeepAliveHandler never inspects the inbound Connection header, so an HTTP/1.1 client that sends Connection: close (RFC 9112 §9.6) is left on an open connection until the idle or read-header timeout. Modifications: Set closeAfterResponseEnd when the request head carries Connection: close, and add Connection: close to the response head when streaming a fully received request. Add HTTPKeepAliveHandlerTests that cover the end-to-end HTTP/1.1 path and an EmbeddedChannel unit test. Result: A request with Connection: close receives Connection: close on the response and the connection closes after the response ends.
Motivation: Writing response .end can synchronously deliver the next pipelined request into HTTPKeepAliveHandler.channelRead, which overwrites closeAfterResponseEnd before the current response decides whether to close. That drops later pipelined responses when only the last request carries Connection: close. The header check also compared the whole field to "close", so list forms such as "keep-alive, close" were ignored. Modifications: Capture shouldClose before context.write in the streaming path and before the writes in flushBuffer, and use that value for the Connection header and the close decision. Parse every Connection field by splitting on commas, trimming whitespace, and matching tokens case-insensitively. Add pipelined POST and token-form tests. Result: Only the request that asked to close receives Connection: close, later pipelined responses are still written, and list or multi-field Connection: close tokens are honored.
hippi345
marked this pull request as ready for review
October 17, 2026 09:12
hippi345
requested review from
FranzBusch,
aryan-25 and
gjcairo
as code owners
October 17, 2026 09:12
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.
Motivation
HTTPKeepAliveHandlernever read the inboundConnectionheader. A client that sendsConnection: close(RFC 9112 §9.6) is stating that this is the last request on the connection, but the server kept the channel open until the idle or read-header timeout. The response also omittedConnection: close.The first fix set
closeAfterResponseEndfrom the request head. That introduced a pipelining bug:HTTPServerPipelineHandler.write(.end)can synchronously deliver the next pending request, whose.headre-enterschannelReadand overwritescloseAfterResponseEndbefore the current response decides whether to close. Three pipelined POSTs withConnection: closeonly on the last request then produced r1 and r2 and EOF — r3 never went out, and r2 had noConnection: close. The header check also used== "close", so list forms such askeep-alive, closewere ignored.Fixes #119
HTTP/1.0 default close / keep-alive handling is intentionally out of scope to keep the change small and reduce conflicts with #94.
Modifications
HTTPKeepAliveHandler.channelRead, when anyConnectionfield contains aclosetoken, setcloseAfterResponseEnd.Connectionfield: split on,, trim whitespace, compare tokens case-insensitively.Connection: closeonto the response head.let shouldClose = self.closeAfterResponseEndbeforecontext.writein the.streamingpath, and capture the close decision before the writes influshBuffer. Use that captured value for theConnectionheader and the close-after-.enddecision so a pipelined next request cannot close the current response early.HTTPKeepAliveHandlerTests: single-request close (plaintext and TLS),EmbeddedChannelclose, pipelined POSTs where only the last request hasConnection: close(plaintext and TLS), and token forms (keep-alive, close,close, TE,Close, two separateConnectionfields).Result
A request with a
closetoken receivesConnection: closeon the response, and the connection closes after that response ends. Earlier pipelined responses on the same connection stay keep-alive and are fully written.Testing
Toolchain: Swift 6.4.1 (swift-6.4.1-RELEASE),
x86_64-unknown-linux-gnuCommands (CI-equivalent):
swift-format lint --stricton the two changed files passed.Before (unmodified main, new tests only)
All new tests failed at runtime:
Before (35416cd, new tests only)
The first fix honored a lone
close/Closetoken, but missed list forms and broke pipelining:After (this change)
New tests:
Full suite:
This change was prepared with AI assistance and reviewed by the author.