diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 775254928..f5fe1693e 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -935,16 +935,9 @@ class TLSEventsHandler: ChannelInboundHandler, RemovableChannelHandler { } func errorCaught(context: ChannelHandlerContext, error: Error) { - if let sslError = error as? NIOSSLError { - switch sslError { - case .handshakeFailed: - self.completionPromise?.fail(error) - self.completionPromise = nil - context.pipeline.removeHandler(self, promise: nil) - default: - break - } - } + self.completionPromise?.fail(error) + self.completionPromise = nil + context.pipeline.removeHandler(self, promise: nil) context.fireErrorCaught(error) } diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift index 72df43dfa..655f31792 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift @@ -125,6 +125,7 @@ extension HTTPClientTests { ("testBodyUploadAfterEndFails", testBodyUploadAfterEndFails), ("testNoBytesSentOverBodyLimit", testNoBytesSentOverBodyLimit), ("testDoubleError", testDoubleError), + ("testSSLHandshakeErrorPropagation", testSSLHandshakeErrorPropagation), ] } } diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index df2348e03..94f649aab 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -2675,4 +2675,41 @@ class HTTPClientTests: XCTestCase { // we need to verify that second error on write after timeout does not lead to double-release. XCTAssertThrowsError(try self.defaultClient.execute(request: request, deadline: .now() + .milliseconds(2)).wait()) } + + func testSSLHandshakeErrorPropagation() throws { + class CloseHandler: ChannelInboundHandler { + typealias InboundIn = Any + + func channelActive(context: ChannelHandlerContext) { + context.close(promise: nil) + } + } + + let server = try ServerBootstrap(group: self.serverGroup) + .childChannelOption(ChannelOptions.autoRead, value: false) + .childChannelInitializer { channel in + channel.pipeline.addHandler(CloseHandler()) + } + .bind(host: "127.0.0.1", port: 0) + .wait() + + defer { + XCTAssertNoThrow(try server.close().wait()) + } + + let request = try Request(url: "https://localhost:\(server.localAddress!.port!)", method: .GET) + let task = self.defaultClient.execute(request: request, delegate: TestHTTPDelegate()) + + XCTAssertThrowsError(try task.wait()) { error in + #if os(Linux) + XCTAssertEqual(error as? NIOSSLError, NIOSSLError.uncleanShutdown) + #else + if isTestingNIOTS() { + XCTAssertEqual((error as? AsyncHTTPClient.HTTPClient.NWTLSError).map { $0.status }, errSSLClosedNoNotify) + } else { + XCTAssertEqual((error as? IOError).map { $0.errnoCode }, 54) + } + #endif + } + } }