@@ -34,6 +34,27 @@ private func makeDefaultHTTPClient(
34
34
}
35
35
36
36
final class AsyncAwaitEndToEndTests: XCTestCase {
37
+ var clientGroup: EventLoopGroup!
38
+ var serverGroup: EventLoopGroup!
39
+
40
+ override func setUp() {
41
+ XCTAssertNil(self.clientGroup)
42
+ XCTAssertNil(self.serverGroup)
43
+
44
+ self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1)
45
+ self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
46
+ }
47
+
48
+ override func tearDown() {
49
+ XCTAssertNotNil(self.clientGroup)
50
+ XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully())
51
+ self.clientGroup = nil
52
+
53
+ XCTAssertNotNil(self.serverGroup)
54
+ XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully())
55
+ self.serverGroup = nil
56
+ }
57
+
37
58
func testSimpleGet() {
38
59
#if compiler(>=5.5.2) && canImport(_Concurrency)
39
60
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
@@ -394,6 +415,65 @@ final class AsyncAwaitEndToEndTests: XCTestCase {
394
415
#endif
395
416
}
396
417
418
+ func testConnectTimeout() {
419
+ #if compiler(>=5.5.2) && canImport(_Concurrency)
420
+ guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
421
+ XCTAsyncTest(timeout: 60) {
422
+ #if os(Linux)
423
+ // 198.51.100.254 is reserved for documentation only and therefore should not accept any TCP connection
424
+ let url = "http://198.51.100.254/get"
425
+ #else
426
+ // on macOS we can use the TCP backlog behaviour when the queue is full to simulate a non reachable server.
427
+ // this makes this test a bit more stable if `198.51.100.254` actually responds to connection attempt.
428
+ // The backlog behaviour on Linux can not be used to simulate a non-reachable server.
429
+ // Linux sends a `SYN/ACK` back even if the `backlog` queue is full as it has two queues.
430
+ // The second queue is not limit by `ChannelOptions.backlog` but by `/proc/sys/net/ipv4/tcp_max_syn_backlog`.
431
+
432
+ let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
433
+ defer {
434
+ XCTAssertNoThrow(try group.syncShutdownGracefully())
435
+ }
436
+
437
+ let serverChannel = try await ServerBootstrap(group: self.serverGroup)
438
+ .serverChannelOption(ChannelOptions.backlog, value: 1)
439
+ .serverChannelOption(ChannelOptions.autoRead, value: false)
440
+ .bind(host: "127.0.0.1", port: 0)
441
+ .get()
442
+ defer {
443
+ XCTAssertNoThrow(try serverChannel.close().wait())
444
+ }
445
+ let port = serverChannel.localAddress!.port!
446
+ let firstClientChannel = try ClientBootstrap(group: self.serverGroup)
447
+ .connect(host: "127.0.0.1", port: port)
448
+ .wait()
449
+ defer {
450
+ XCTAssertNoThrow(try firstClientChannel.close().wait())
451
+ }
452
+ let url = "http://localhost:\(port)/get"
453
+ #endif
454
+
455
+ let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
456
+ configuration: .init(timeout: .init(connect: .milliseconds(100), read: .milliseconds(150))))
457
+
458
+ defer {
459
+ XCTAssertNoThrow(try httpClient.syncShutdown())
460
+ }
461
+
462
+ let request = HTTPClientRequest(url: url)
463
+ let start = NIODeadline.now()
464
+ await XCTAssertThrowsError(try await httpClient.execute(request, deadline: .now() + .seconds(30))) {
465
+ XCTAssertEqualTypeAndValue($0, HTTPClientError.connectTimeout)
466
+ let end = NIODeadline.now()
467
+ let duration = end - start
468
+
469
+ // We give ourselves 10x slack in order to be confident that even on slow machines this assertion passes.
470
+ // It's 30x smaller than our other timeout though.
471
+ XCTAssertLessThan(duration, .seconds(1))
472
+ }
473
+ }
474
+ #endif
475
+ }
476
+
397
477
func testSelfSignedCertificateIsRejectedWithCorrectErrorIfRequestDeadlineIsExceeded() {
398
478
#if compiler(>=5.5.2) && canImport(_Concurrency)
399
479
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
0 commit comments