Skip to content

Commit 29dc2c7

Browse files
committed
notify delegate about connect errors (swift-server#245)
1 parent 233f18b commit 29dc2c7

7 files changed

+94
-11
lines changed

Sources/AsyncHTTPClient/ConnectionPool.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -513,13 +513,13 @@ class HTTP1ConnectionProvider {
513513
}
514514

515515
private func makeChannel(preference: HTTPClient.EventLoopPreference) -> EventLoopFuture<Channel> {
516-
let eventLoop = preference.bestEventLoop ?? self.eventLoop
516+
let channelEventLoop = preference.bestEventLoop ?? self.eventLoop
517517
let requiresTLS = self.key.scheme.requiresTLS
518518
let bootstrap: NIOClientTCPBootstrap
519519
do {
520-
bootstrap = try NIOClientTCPBootstrap.makeHTTPClientBootstrapBase(on: eventLoop, host: self.key.host, port: self.key.port, requiresTLS: requiresTLS, configuration: self.configuration)
520+
bootstrap = try NIOClientTCPBootstrap.makeHTTPClientBootstrapBase(on: channelEventLoop, host: self.key.host, port: self.key.port, requiresTLS: requiresTLS, configuration: self.configuration)
521521
} catch {
522-
return eventLoop.makeFailedFuture(error)
522+
return channelEventLoop.makeFailedFuture(error)
523523
}
524524

525525
let channel: EventLoopFuture<Channel>
@@ -564,7 +564,7 @@ class HTTP1ConnectionProvider {
564564
error = HTTPClient.NWErrorHandler.translateError(error)
565565
}
566566
#endif
567-
return self.eventLoop.makeFailedFuture(error)
567+
return channelEventLoop.makeFailedFuture(error)
568568
}
569569
}
570570

Sources/AsyncHTTPClient/HTTPClient.swift

+14-7
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,14 @@ public class HTTPClient {
545545
deadline: deadline,
546546
setupComplete: setupComplete.futureResult,
547547
logger: logger)
548+
549+
let taskHandler = TaskHandler(task: task,
550+
kind: request.kind,
551+
delegate: delegate,
552+
redirectHandler: redirectHandler,
553+
ignoreUncleanSSLShutdown: self.configuration.ignoreUncleanSSLShutdown,
554+
logger: logger)
555+
548556
connection.flatMap { connection -> EventLoopFuture<Void> in
549557
logger.debug("got connection for request",
550558
metadata: ["ahc-connection": "\(connection)",
@@ -561,12 +569,6 @@ public class HTTPClient {
561569
}
562570

563571
return future.flatMap {
564-
let taskHandler = TaskHandler(task: task,
565-
kind: request.kind,
566-
delegate: delegate,
567-
redirectHandler: redirectHandler,
568-
ignoreUncleanSSLShutdown: self.configuration.ignoreUncleanSSLShutdown,
569-
logger: logger)
570572
return channel.pipeline.addHandler(taskHandler)
571573
}.flatMap {
572574
task.setConnection(connection)
@@ -590,7 +592,12 @@ public class HTTPClient {
590592
}
591593
}.always { _ in
592594
setupComplete.succeed(())
593-
}.cascadeFailure(to: task.promise)
595+
}.whenFailure { error in
596+
taskHandler.callOutToDelegateFireAndForget { task in
597+
delegate.didReceiveError(task: task, error)
598+
}
599+
task.promise.fail(error)
600+
}
594601

595602
return task
596603
}

Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ extension HTTPClientInternalTests {
4343
("testUploadStreamingIsCalledOnTaskEL", testUploadStreamingIsCalledOnTaskEL),
4444
("testWeCanActuallyExactlySetTheEventLoops", testWeCanActuallyExactlySetTheEventLoops),
4545
("testTaskPromiseBoundToEL", testTaskPromiseBoundToEL),
46+
("testConnectErrorCalloutOnCorrectEL", testConnectErrorCalloutOnCorrectEL),
4647
("testInternalRequestURI", testInternalRequestURI),
4748
]
4849
}

Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift

+39
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,45 @@ class HTTPClientInternalTests: XCTestCase {
960960
XCTAssertNoThrow(try task.wait())
961961
}
962962

963+
func testConnectErrorCalloutOnCorrectEL() throws {
964+
class TestDelegate: HTTPClientResponseDelegate {
965+
typealias Response = Void
966+
967+
let expectedEL: EventLoop
968+
var receivedError: Bool = false
969+
970+
init(expectedEL: EventLoop) {
971+
self.expectedEL = expectedEL
972+
}
973+
974+
func didFinishRequest(task: HTTPClient.Task<Void>) throws {}
975+
976+
func didReceiveError(task: HTTPClient.Task<Void>, _ error: Error) {
977+
self.receivedError = true
978+
XCTAssertTrue(self.expectedEL.inEventLoop)
979+
}
980+
}
981+
982+
let elg = getDefaultEventLoopGroup(numberOfThreads: 2)
983+
let el1 = elg.next()
984+
let el2 = elg.next()
985+
986+
let httpBin = HTTPBin(refusesConnections: true)
987+
let client = HTTPClient(eventLoopGroupProvider: .shared(elg))
988+
989+
defer {
990+
XCTAssertNoThrow(try client.syncShutdown())
991+
XCTAssertNoThrow(try elg.syncShutdownGracefully())
992+
}
993+
994+
let request = try HTTPClient.Request(url: "http://localhost:\(httpBin.port)/get")
995+
let delegate = TestDelegate(expectedEL: el1)
996+
XCTAssertNoThrow(try httpBin.shutdown())
997+
let task = client.execute(request: request, delegate: delegate, eventLoop: .init(.testOnly_exact(channelOn: el2, delegateOn: el1)))
998+
XCTAssertThrowsError(try task.wait())
999+
XCTAssertTrue(delegate.receivedError)
1000+
}
1001+
9631002
func testInternalRequestURI() throws {
9641003
let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar")
9651004
XCTAssertEqual(request1.kind, .host)

Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ extension HTTPClientTests {
115115
("testAllMethodsLog", testAllMethodsLog),
116116
("testClosingIdleConnectionsInPoolLogsInTheBackground", testClosingIdleConnectionsInPoolLogsInTheBackground),
117117
("testUploadStreamingNoLength", testUploadStreamingNoLength),
118+
("testConnectErrorPropagatedToDelegate", testConnectErrorPropagatedToDelegate),
118119
("testDelegateCallinsTolerateRandomEL", testDelegateCallinsTolerateRandomEL),
119120
("testContentLengthTooLongFails", testContentLengthTooLongFails),
120121
("testContentLengthTooShortFails", testContentLengthTooShortFails),

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+27
Original file line numberDiff line numberDiff line change
@@ -2405,6 +2405,33 @@ class HTTPClientTests: XCTestCase {
24052405
XCTAssertNoThrow(try future.wait())
24062406
}
24072407

2408+
func testConnectErrorPropagatedToDelegate() throws {
2409+
class TestDelegate: HTTPClientResponseDelegate {
2410+
typealias Response = Void
2411+
var error: Error?
2412+
func didFinishRequest(task: HTTPClient.Task<Void>) throws {}
2413+
func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error) {
2414+
self.error = error
2415+
}
2416+
}
2417+
2418+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
2419+
configuration: .init(timeout: .init(connect: .milliseconds(10))))
2420+
2421+
defer {
2422+
XCTAssertNoThrow(try httpClient.syncShutdown())
2423+
}
2424+
2425+
// This must throw as 198.51.100.254 is reserved for documentation only
2426+
let request = try HTTPClient.Request(url: "http://198.51.100.254:65535/get")
2427+
let delegate = TestDelegate()
2428+
2429+
XCTAssertThrowsError(try httpClient.execute(request: request, delegate: delegate).wait()) { error in
2430+
XCTAssertEqual(.connectTimeout(.milliseconds(10)), error as? ChannelError)
2431+
XCTAssertEqual(.connectTimeout(.milliseconds(10)), delegate.error as? ChannelError)
2432+
}
2433+
}
2434+
24082435
func testDelegateCallinsTolerateRandomEL() throws {
24092436
class TestDelegate: HTTPClientResponseDelegate {
24102437
typealias Response = Void

Tests/AsyncHTTPClientTests/RequestValidationTests+XCTest.swift

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ extension RequestValidationTests {
3232
("testGET_HEAD_DELETE_CONNECTRequestCanHaveBody", testGET_HEAD_DELETE_CONNECTRequestCanHaveBody),
3333
("testInvalidHeaderFieldNames", testInvalidHeaderFieldNames),
3434
("testValidHeaderFieldNames", testValidHeaderFieldNames),
35+
("testNoHeadersNoBody", testNoHeadersNoBody),
36+
("testNoHeadersHasBody", testNoHeadersHasBody),
37+
("testContentLengthHeaderNoBody", testContentLengthHeaderNoBody),
38+
("testContentLengthHeaderHasBody", testContentLengthHeaderHasBody),
39+
("testTransferEncodingHeaderNoBody", testTransferEncodingHeaderNoBody),
40+
("testTransferEncodingHeaderHasBody", testTransferEncodingHeaderHasBody),
41+
("testBothHeadersNoBody", testBothHeadersNoBody),
42+
("testBothHeadersHasBody", testBothHeadersHasBody),
3543
]
3644
}
3745
}

0 commit comments

Comments
 (0)