diff --git a/Sources/AsyncHTTPClient/ConnectionPool.swift b/Sources/AsyncHTTPClient/ConnectionPool.swift index 8e01efc52..89cbcfcf9 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool.swift @@ -414,7 +414,6 @@ final class ConnectionPool { assert(MultiThreadedEventLoopGroup.currentEventLoop == nil, "HTTPClient shutdown on EventLoop unsupported") // calls .wait() so it would crash later anyway let (waitersFutures, closeFutures) = self.stateLock.withLock { () -> ([EventLoopFuture], [EventLoopFuture]) in - assert(self.state.activity == .opened, "Invalid activity: \(self.state.activity)") // Fail waiters let waitersCopy = self.state.waiters self.state.waiters.removeAll() diff --git a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift index 06f9b4228..a66cfcbf2 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift @@ -35,6 +35,7 @@ extension HTTPClientInternalTests { ("testChannelAndDelegateOnDifferentEventLoops", testChannelAndDelegateOnDifferentEventLoops), ("testResponseConnectionCloseGet", testResponseConnectionCloseGet), ("testWeNoticeRemoteClosuresEvenWhenConnectionIsIdleInPool", testWeNoticeRemoteClosuresEvenWhenConnectionIsIdleInPool), + ("testWeTolerateConnectionsGoingAwayWhilstPoolIsShuttingDown", testWeTolerateConnectionsGoingAwayWhilstPoolIsShuttingDown), ] } } diff --git a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift index d2124171e..a016b2b31 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift @@ -581,4 +581,46 @@ class HTTPClientInternalTests: XCTestCase { XCTAssertEqual(2, sharedStateServerHandler.connectionNumber.load()) XCTAssertEqual(2, sharedStateServerHandler.requestNumber.load()) } + + func testWeTolerateConnectionsGoingAwayWhilstPoolIsShuttingDown() { + struct NoChannelError: Error {} + + let client = HTTPClient(eventLoopGroupProvider: .createNew) + var maybeServersAndChannels: [(HTTPBin, Channel)]? + XCTAssertNoThrow(maybeServersAndChannels = try (0..<10).map { _ in + let web = HTTPBin() + defer { + XCTAssertNoThrow(try web.shutdown()) + } + + let req = try! HTTPClient.Request(url: "http://localhost:\(web.serverChannel.localAddress!.port!)/get", + method: .GET, + body: nil) + var maybeConnection: ConnectionPool.Connection? + XCTAssertNoThrow(try maybeConnection = client.pool.getConnection(for: req, + preference: .indifferent, + on: client.eventLoopGroup.next(), + deadline: nil).wait()) + guard let connection = maybeConnection else { + XCTFail("couldn't make connection") + throw NoChannelError() + } + + let channel = connection.channel + client.pool.release(connection) + return (web, channel) + }) + + guard let serversAndChannels = maybeServersAndChannels else { + XCTFail("couldn't open servers") + return + } + + DispatchQueue.global().async { + serversAndChannels.forEach { serverAndChannel in + serverAndChannel.1.close(promise: nil) + } + } + XCTAssertNoThrow(try client.syncShutdown()) + } }