Repository navigation
Conversation
### Motivation Resolves swift-server#137. When cancelling the task running `NIOHTTPServer.serve`, child channels that have already been accepted by the listening socket but not yet yielded by the inbound connection stream's `for try await` iteration are dropped when `NIOThrowingAsyncSequenceProducer` transitions directly to `.cancelled`. Because child channels wrap their underlying NIO channel synchronously with `closeOnDeinit: false`, deallocating them without finishing their outbound `NIOAsyncWriter` triggers a fatal error: NIOCore/NIOAsyncWriter.swift:177: Fatal error: Deinited NIOAsyncWriter without calling finish() ### Modifications - Added `serveInboundConnections` and `AcceptLoopCoordinator` in `NIOHTTPServer.swift`. On outer task cancellation, the accept loop continues to drain remaining buffered connections yielded by `connectionStream` as the listening socket closes via `.sourceFinished`. - Each drained connection is dispatched to `withDiscardingTaskGroup` where its `NIOAsyncWriter` is safely finished via `executeThenClose`. - Once the stream finishes draining, `group.cancelAll()` cancels active child tasks so in-flight requests terminate promptly. - Refactored `serveInsecureHTTP1_1` and `serveSecureUpgrade` to delegate connection serving to `serveInboundConnections`. - Added reproduction test case `testCancellationWithBufferedAcceptedConnections` in `ConnectionLifecycleTests.swift` covering both plaintext HTTP/1.1 and TLS HTTP/1.1. *Note: Antigravity was used to assist with development, root cause analysis, and test verification.* ### Result Cancelling `NIOHTTPServer.serve` while connections are buffered drains and terminates them cleanly without trapping.
|
Test Status:
(Note: Google Antigravity pair programming assistant was used to assist with root cause analysis, test verification, and development.) |
|
I am relatively new to Swift and SwiftNIO, so please bear with me if repetitive feedback or extra iterations are needed during code review. Happy to make any adjustments required! |
FranzBusch
left a comment
There was a problem hiding this comment.
I think we might need to fix this at the NIO level instead of here so it is fixed for all users. @aryan-25 @gjcairo this is the existing problem of NIOAsyncChannel where if we stop the iteration of it while there are still elements in there we crash. Lately, I was thinking that the right fix might be to close the underlying channel pipeline on cancel but still return all the elements to the user. To ensure that no element is dropped. This would be a localized fix in the NIOAsyncSequenceProducer + the channel handler.
|
Thank you for clarification! fixing this directly in swift-nio (NIOThrowingAsyncSequenceProducer / NIOAsyncChannelHandler) would make complete sense and solve the root problem rather than working around it at the server level can i try to implement the upstream fix in |
Motivation
Resolves #137.
When cancelling the task running
NIOHTTPServer.serve, child channels that have already been accepted by the listening socket but not yet yielded by the inbound connection stream'sfor try awaititeration are dropped whenNIOThrowingAsyncSequenceProducertransitions directly to.cancelled. Because child channels wrap their underlying NIO channel synchronously withcloseOnDeinit: false, deallocating them without finishing their outboundNIOAsyncWritertriggers a fatal error:Modifications
serveInboundConnectionsandAcceptLoopCoordinatorinNIOHTTPServer.swift:connectionStreamas the listening socket closes cleanly via.sourceFinished.executeThenCloseinwithDiscardingTaskGroup, finishing theirNIOAsyncWritercleanly on cancellation.group.cancelAll()cancels active child tasks so in-flight requests abort promptly.serveInsecureHTTP1_1andserveSecureUpgradeto delegate connection serving toserveInboundConnections.testCancellationWithBufferedAcceptedConnectionsinConnectionLifecycleTests.swiftcovering bursts of 32 connections across 5 iterations on both.plaintextHTTP1_1and.http1_1.Result
Cancelling
NIOHTTPServer.servewhile connections are buffered drains and terminates them cleanly without trapping.