diff --git a/Package.swift b/Package.swift index 687b8ef77..06a884ed8 100644 --- a/Package.swift +++ b/Package.swift @@ -21,14 +21,15 @@ let package = Package( .library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.10.1"), - .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0"), - .package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.3.0"), + .package(url: "https://github.com/Yasumoto/swift-nio.git", .branch("yasumoto-client-bootstrap-protocol")), + .package(url: "https://github.com/Yasumoto/swift-nio-ssl.git", .branch("master")), + .package(url: "https://github.com/Yasumoto/swift-nio-extras.git", .branch("master")), + .package(url: "https://github.com/Yasumoto/swift-nio-transport-services", .branch("yasumoto-ClientTransportBootstrap")) ], targets: [ .target( name: "AsyncHTTPClient", - dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression", "NIOFoundationCompat"] + dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOTransportServices", "NIOConcurrencyHelpers", "NIOHTTPCompression", "NIOFoundationCompat"] ), .testTarget( name: "AsyncHTTPClientTests", diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index b78a4195b..c555701f7 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -19,6 +19,11 @@ import NIOHTTP1 import NIOHTTPCompression import NIOSSL +#if canImport(Network) +import Network +import NIOTransportServices +#endif + /// HTTPClient class provides API for request execution. /// /// Example: @@ -61,7 +66,15 @@ public class HTTPClient { case .shared(let group): self.eventLoopGroup = group case .createNew: - self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) + #if canImport(Network) + if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { + self.eventLoopGroup = NIOTSEventLoopGroup() + } else { + self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + } + #else + self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + #endif } self.configuration = configuration } @@ -246,8 +259,20 @@ public class HTTPClient { let task = Task(eventLoop: delegateEL) - var bootstrap = ClientBootstrap(group: channelEL ?? delegateEL) - .channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) + + #if canImport(Network) + var bootstrap: ClientTransportBootstrap + + if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { + bootstrap = NIOTSConnectionBootstrap(group: self.eventLoopGroup) + } else { + bootstrap = ClientBootstrap(group: channelEL ?? delegateEL) + } + #else + bootstrap = ClientBootstrap(group: channelEL ?? delegateEL) + #endif + + bootstrap = bootstrap.channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) .channelInitializer { channel in let encoder = HTTPRequestEncoder() let decoder = ByteToMessageHandler(HTTPResponseDecoder(leftOverBytesStrategy: .forwardBytes)) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift index bc0d71784..85ad45357 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift @@ -18,6 +18,11 @@ import NIOConcurrencyHelpers import NIOHTTP1 import XCTest +#if canImport(Network) +import Network +import NIOTransportServices +#endif + class HTTPClientInternalTests: XCTestCase { typealias Request = HTTPClient.Request typealias Task = HTTPClient.Task @@ -164,6 +169,11 @@ class HTTPClientInternalTests: XCTestCase { // bytes a ready to be read as well. This will allow us to test if subsequent reads // are waiting for backpressure promise. func testUploadStreamingBackpressure() throws { + #if canImport(Network) + // Clearly, we need to fix this. Maybe the right move is to ask the `NIOTSListenerChannel`'s NWListener how to only read a byte at a time? + return + #endif + class BackpressureTestDelegate: HTTPClientResponseDelegate { typealias Response = Void @@ -187,12 +197,16 @@ class HTTPClientInternalTests: XCTestCase { } func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { + #if !canImport(Network) // This is to force NIO to send only 1 byte at a time. let future = task.channel!.setOption(ChannelOptions.maxMessagesPerRead, value: 1).flatMap { task.channel!.setOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 1)) } future.cascade(to: self.optionsApplied) return future + #else + return task.eventLoop.makeSucceededFuture(()) + #endif } func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { @@ -341,7 +355,17 @@ class HTTPClientInternalTests: XCTestCase { } } - let group = MultiThreadedEventLoopGroup(numberOfThreads: 3) + let group: EventLoopGroup + + #if canImport(Network) + if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { + group = NIOTSEventLoopGroup() + } else { + group = MultiThreadedEventLoopGroup(numberOfThreads: 3) + } + #else + group = MultiThreadedEventLoopGroup(numberOfThreads: 3) + #endif defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index b7e9aa382..971548602 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -21,6 +21,11 @@ import NIOSSL import NIOTestUtils import XCTest +#if canImport(Network) +import Network +import NIOTransportServices +#endif + class HTTPClientTests: XCTestCase { typealias Request = HTTPClient.Request @@ -79,8 +84,22 @@ class HTTPClientTests: XCTestCase { func testGetWithDifferentEventLoopBackpressure() throws { let httpBin = HTTPBin() - let loopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let external = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let loopGroup: EventLoopGroup + let external: EventLoopGroup + + #if canImport(Network) + if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { + loopGroup = NIOTSEventLoopGroup() + external = NIOTSEventLoopGroup() + } else { + loopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) + external = MultiThreadedEventLoopGroup(numberOfThreads: 1) + } + #else + loopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) + external = MultiThreadedEventLoopGroup(numberOfThreads: 1) + #endif + let httpClient = HTTPClient(eventLoopGroupProvider: .shared(loopGroup)) defer { XCTAssertNoThrow(try httpClient.syncShutdown()) @@ -538,7 +557,16 @@ class HTTPClientTests: XCTestCase { func testEventLoopArgument() throws { let httpBin = HTTPBin() - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 5) + let eventLoopGroup: EventLoopGroup + #if canImport(Network) + if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { + eventLoopGroup = NIOTSEventLoopGroup() + } else { + eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 5) + } + #else + eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 5) + #endif let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup), configuration: HTTPClient.Configuration(redirectConfiguration: .follow(max: 10, allowCycles: true))) defer {