From 4932cb55e1fb529db310c5610043022a7fd862d3 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Sun, 19 Jan 2020 01:14:22 +0100 Subject: [PATCH 1/9] Support UNIX Domain Socket url --- Sources/AsyncHTTPClient/HTTPClient.swift | 15 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 146 ++++++++++++++++-- Sources/AsyncHTTPClient/HTTPRequest.swift | 26 ++++ .../HTTPClientTests.swift | 12 +- 4 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 Sources/AsyncHTTPClient/HTTPRequest.swift diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 331b5bc98..d7c61e5bd 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -281,15 +281,22 @@ public class HTTPClient { bootstrap = bootstrap.connectTimeout(timeout) } - let address = self.resolveAddress(request: request, proxy: self.configuration.proxy) - bootstrap.connect(host: address.host, port: address.port) - .map { channel in - task.setChannel(channel) + let eventLoopChannel: EventLoopFuture + if request.scheme == "file" { + eventLoopChannel = bootstrap.connect(unixDomainSocketPath: request.url.path) + } else { + let address = self.resolveAddress(request: request, proxy: self.configuration.proxy) + eventLoopChannel = bootstrap.connect(host: address.host, port: address.port) + } + + eventLoopChannel.map { channel in + task.setChannel(channel) } .flatMap { channel in channel.writeAndFlush(request) } .cascadeFailure(to: task.promise) + return task } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 5747af0d3..b1fa5ec34 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -89,17 +89,39 @@ extension HTTPClient { /// Represent HTTP request. public struct Request { /// Request HTTP method, defaults to `GET`. - public let method: HTTPMethod + public var method: HTTPMethod { + return value.method + } /// Remote URL. - public let url: URL + public var url: URL { + return value.url + } /// Remote HTTP scheme, resolved from `URL`. - public let scheme: String + public var scheme: String { + return value.scheme + } /// Remote host, resolved from `URL`. - public let host: String + public var host: String { + return value.host + } /// Request custom HTTP Headers, defaults to no headers. - public var headers: HTTPHeaders + public var headers: HTTPHeaders { + get { + return value.headers + } + set { + value.headers = newValue + } + } /// Request body, defaults to no body. - public var body: Body? + public var body: Body? { + get { + return value.body + } + set { + value.body = newValue + } + } struct RedirectState { var count: Int @@ -107,6 +129,7 @@ extension HTTPClient { } var redirectState: RedirectState? + private var value: HTTPRequest /// Create HTTP request. /// @@ -129,6 +152,57 @@ extension HTTPClient { try self.init(url: url, method: method, headers: headers, body: body) } + /// Create an HTTP `Request`. + /// + /// - parameters: + /// - url: Remote `URL`. + /// - version: HTTP version. + /// - method: HTTP method. + /// - headers: Custom HTTP headers. + /// - body: Request body. + /// - throws: + /// - `emptyScheme` if URL does not contain HTTP scheme. + /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. + /// - `emptyHost` if URL does not contains a host. + public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { + if url.isFileURL { + self.value = try UnixDomainRequest(url: url, method: method, headers: headers, body: body) + } else { + self.value = try HostRequest(url: url, method: method, headers: headers, body: body) + } + self.redirectState = nil + } + + /// Whether request will be executed using secure socket. + public var useTLS: Bool { + return self.scheme == "https" + } + + /// Resolved port. + public var port: Int { + return self.url.port ?? (self.useTLS ? 443 : 80) + } + + func isSchemeSupported(scheme: String) -> Bool { + return type(of: self.value).isSchemeSupported(scheme: scheme) + } + } + + /// Represent HTTP request. + public struct HostRequest: HTTPRequest { + /// Request HTTP method, defaults to `GET`. + public let method: HTTPMethod + /// Remote URL. + public let url: URL + /// Remote HTTP scheme, resolved from `URL`. + public let scheme: String + /// Remote host, resolved from `URL`. + public let host: String + /// Request custom HTTP Headers, defaults to no headers. + public var headers: HTTPHeaders + /// Request body, defaults to no body. + public var body: Body? + /// Create an HTTP `Request`. /// /// - parameters: @@ -146,7 +220,7 @@ extension HTTPClient { throw HTTPClientError.emptyScheme } - guard Request.isSchemeSupported(scheme: scheme) else { + guard Self.isSchemeSupported(scheme: scheme) else { throw HTTPClientError.unsupportedScheme(scheme) } @@ -160,23 +234,61 @@ extension HTTPClient { self.host = host self.headers = headers self.body = body - - self.redirectState = nil } - /// Whether request will be executed using secure socket. - public var useTLS: Bool { - return self.scheme == "https" + static func isSchemeSupported(scheme: String) -> Bool { + return scheme == "http" || scheme == "https" } + } - /// Resolved port. - public var port: Int { - return self.url.port ?? (self.useTLS ? 443 : 80) + /// Represent UNIX Domain Socket HTTP request. + public struct UnixDomainRequest: HTTPRequest { + /// Request HTTP method, defaults to `GET`. + public let method: HTTPMethod + /// UNIX Domain Socket file URL. + public let url: URL + /// Remote HTTP scheme, resolved from `URL`. Unused. + public let scheme: String + /// Remote host, resolved from `URL`. Unused. + public let host: String + /// Request custom HTTP Headers, defaults to no headers. + public var headers: HTTPHeaders + /// Request body, defaults to no body. + public var body: Body? + + /// Create an HTTP `Request`. + /// + /// - parameters: + /// - url: UNIX Domain Socket `URL`. + /// - version: HTTP version. + /// - method: HTTP method. + /// - headers: Custom HTTP headers. + /// - body: Request body. + /// - throws: + /// - `emptyScheme` if URL does not contain HTTP scheme. + /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. + /// - `emptyHost` if URL does not contains a host. + public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { + guard url.isFileURL else { + throw HTTPClientError.invalidURL + } + + guard let scheme = url.scheme?.lowercased() else { + throw HTTPClientError.emptyScheme + } + + self.method = method + self.url = url + self.scheme = scheme + self.host = "" + self.headers = headers + self.body = body } static func isSchemeSupported(scheme: String) -> Bool { - return scheme == "http" || scheme == "https" + return scheme == "file" } + } /// Represent HTTP response. @@ -812,7 +924,7 @@ internal struct RedirectHandler { return nil } - guard HTTPClient.Request.isSchemeSupported(scheme: self.request.scheme) else { + guard request.isSchemeSupported(scheme: self.request.scheme) else { return nil } diff --git a/Sources/AsyncHTTPClient/HTTPRequest.swift b/Sources/AsyncHTTPClient/HTTPRequest.swift new file mode 100644 index 000000000..6db81f639 --- /dev/null +++ b/Sources/AsyncHTTPClient/HTTPRequest.swift @@ -0,0 +1,26 @@ +// +// File.swift +// +// +// Created by Marcin Krzyzanowski on 19/01/2020. +// + +import Foundation +import NIOHTTP1 + +protocol HTTPRequest { + /// Request HTTP method + var method: HTTPMethod { get } + /// Remote URL. + var url: URL { get } + /// Remote HTTP scheme, resolved from `URL`. + var scheme: String { get } + /// Remote host, resolved from `URL`. + var host: String { get } + /// Request custom HTTP Headers, defaults to no headers. + var headers: HTTPHeaders { get set } + /// Request body, defaults to no body. + var body: HTTPClient.Body? { get set } + + static func isSchemeSupported(scheme: String) -> Bool +} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index 33c529330..29c925dfb 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -47,14 +47,21 @@ class HTTPClientTests: XCTestCase { let request2 = try Request(url: "https://someserver.com") XCTAssertEqual(request2.url.path, "") + + let request3 = try Request(url: "file:///tmp/file") + XCTAssertNil(request3.url.host) + XCTAssertEqual(request3.host, "") + XCTAssertEqual(request3.url.path, "/tmp/file") + XCTAssertEqual(request3.port, 80) + XCTAssertFalse(request3.useTLS) } func testBadRequestURI() throws { XCTAssertThrowsError(try Request(url: "some/path"), "should throw") { error in XCTAssertEqual(error as! HTTPClientError, HTTPClientError.emptyScheme) } - XCTAssertThrowsError(try Request(url: "file://somewhere/some/path?foo=bar"), "should throw") { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.unsupportedScheme("file")) + XCTAssertThrowsError(try Request(url: "app://somewhere/some/path?foo=bar"), "should throw") { error in + XCTAssertEqual(error as! HTTPClientError, HTTPClientError.unsupportedScheme("app")) } XCTAssertThrowsError(try Request(url: "https:/foo"), "should throw") { error in XCTAssertEqual(error as! HTTPClientError, HTTPClientError.emptyHost) @@ -63,6 +70,7 @@ class HTTPClientTests: XCTestCase { func testSchemaCasing() throws { XCTAssertNoThrow(try Request(url: "hTTpS://someserver.com:8888/some/path?foo=bar")) + XCTAssertNoThrow(try Request(url: "File://someserver.com:8888/some/path?foo=bar")) } func testGet() throws { From 41ee04ed54ffa6e01fd7d8a3594b66df012f651a Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Sun, 19 Jan 2020 02:12:06 +0100 Subject: [PATCH 2/9] Use relative URL as socket url --- Sources/AsyncHTTPClient/HTTPClient.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index d7c61e5bd..4b72454e3 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -282,15 +282,15 @@ public class HTTPClient { } let eventLoopChannel: EventLoopFuture - if request.scheme == "file" { - eventLoopChannel = bootstrap.connect(unixDomainSocketPath: request.url.path) + if request.scheme == "file", let baseURL = request.url.baseURL { + eventLoopChannel = bootstrap.connect(unixDomainSocketPath: baseURL.path) } else { let address = self.resolveAddress(request: request, proxy: self.configuration.proxy) eventLoopChannel = bootstrap.connect(host: address.host, port: address.port) } eventLoopChannel.map { channel in - task.setChannel(channel) + task.setChannel(channel) } .flatMap { channel in channel.writeAndFlush(request) From ac69f698ae3b805e5304689f85b5a98096bc9d56 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 20 Jan 2020 11:25:37 +0100 Subject: [PATCH 3/9] Adapt unix scheme for sockets --- Sources/AsyncHTTPClient/HTTPClient.swift | 2 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 12 ++++++------ Tests/AsyncHTTPClientTests/HTTPClientTests.swift | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 4b72454e3..3d00c053f 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -282,7 +282,7 @@ public class HTTPClient { } let eventLoopChannel: EventLoopFuture - if request.scheme == "file", let baseURL = request.url.baseURL { + if request.scheme == "unix", let baseURL = request.url.baseURL { eventLoopChannel = bootstrap.connect(unixDomainSocketPath: baseURL.path) } else { let address = self.resolveAddress(request: request, proxy: self.configuration.proxy) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index b1fa5ec34..461bc22f8 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -165,7 +165,7 @@ extension HTTPClient { /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. /// - `emptyHost` if URL does not contains a host. public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { - if url.isFileURL { + if url.scheme?.lowercased() == "unix" { self.value = try UnixDomainRequest(url: url, method: method, headers: headers, body: body) } else { self.value = try HostRequest(url: url, method: method, headers: headers, body: body) @@ -269,12 +269,12 @@ extension HTTPClient { /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. /// - `emptyHost` if URL does not contains a host. public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { - guard url.isFileURL else { - throw HTTPClientError.invalidURL + guard let scheme = url.scheme?.lowercased() else { + throw HTTPClientError.emptyScheme } - guard let scheme = url.scheme?.lowercased() else { - throw HTTPClientError.emptyScheme + guard scheme == "unix" else { + throw HTTPClientError.invalidURL } self.method = method @@ -286,7 +286,7 @@ extension HTTPClient { } static func isSchemeSupported(scheme: String) -> Bool { - return scheme == "file" + return scheme == "unix" } } diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index 29c925dfb..0f91d3995 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -48,7 +48,7 @@ class HTTPClientTests: XCTestCase { let request2 = try Request(url: "https://someserver.com") XCTAssertEqual(request2.url.path, "") - let request3 = try Request(url: "file:///tmp/file") + let request3 = try Request(url: "unix:///tmp/file") XCTAssertNil(request3.url.host) XCTAssertEqual(request3.host, "") XCTAssertEqual(request3.url.path, "/tmp/file") @@ -70,7 +70,7 @@ class HTTPClientTests: XCTestCase { func testSchemaCasing() throws { XCTAssertNoThrow(try Request(url: "hTTpS://someserver.com:8888/some/path?foo=bar")) - XCTAssertNoThrow(try Request(url: "File://someserver.com:8888/some/path?foo=bar")) + XCTAssertNoThrow(try Request(url: "uNIx:///some/path")) } func testGet() throws { From d3bbf37241968d18ba75f1d49cd6d638361ddc28 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Sun, 26 Jan 2020 16:09:38 +0100 Subject: [PATCH 4/9] Use Request.Kind to distinguish between host and unix socket kind of http request. --- Sources/AsyncHTTPClient/HTTPClient.swift | 2 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 182 ++++++---------------- Sources/AsyncHTTPClient/HTTPRequest.swift | 26 ---- 3 files changed, 47 insertions(+), 163 deletions(-) delete mode 100644 Sources/AsyncHTTPClient/HTTPRequest.swift diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 3d00c053f..bb939084b 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -282,7 +282,7 @@ public class HTTPClient { } let eventLoopChannel: EventLoopFuture - if request.scheme == "unix", let baseURL = request.url.baseURL { + if request.kind == .unixSocket, let baseURL = request.url.baseURL { eventLoopChannel = bootstrap.connect(unixDomainSocketPath: baseURL.path) } else { let address = self.resolveAddress(request: request, proxy: self.configuration.proxy) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 461bc22f8..29a33f642 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -88,40 +88,36 @@ extension HTTPClient { /// Represent HTTP request. public struct Request { - /// Request HTTP method, defaults to `GET`. - public var method: HTTPMethod { - return value.method + + /// Represent kind of Request + enum Kind { + /// Remote host request. + case host + /// UNIX Domain Socket HTTP request. + case unixSocket + + func isSchemeSupported(scheme: String) -> Bool { + switch self { + case .host: + return scheme == "http" || scheme == "https" + case .unixSocket: + return scheme == "unix" + } + } } + + /// Request HTTP method, defaults to `GET`. + public var method: HTTPMethod /// Remote URL. - public var url: URL { - return value.url - } + public var url: URL /// Remote HTTP scheme, resolved from `URL`. - public var scheme: String { - return value.scheme - } + public var scheme: String /// Remote host, resolved from `URL`. - public var host: String { - return value.host - } + public var host: String /// Request custom HTTP Headers, defaults to no headers. - public var headers: HTTPHeaders { - get { - return value.headers - } - set { - value.headers = newValue - } - } + public var headers: HTTPHeaders /// Request body, defaults to no body. - public var body: Body? { - get { - return value.body - } - set { - value.body = newValue - } - } + public var body: Body? struct RedirectState { var count: Int @@ -129,7 +125,7 @@ extension HTTPClient { } var redirectState: RedirectState? - private var value: HTTPRequest + let kind: Kind /// Create HTTP request. /// @@ -156,58 +152,6 @@ extension HTTPClient { /// /// - parameters: /// - url: Remote `URL`. - /// - version: HTTP version. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - throws: - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { - if url.scheme?.lowercased() == "unix" { - self.value = try UnixDomainRequest(url: url, method: method, headers: headers, body: body) - } else { - self.value = try HostRequest(url: url, method: method, headers: headers, body: body) - } - self.redirectState = nil - } - - /// Whether request will be executed using secure socket. - public var useTLS: Bool { - return self.scheme == "https" - } - - /// Resolved port. - public var port: Int { - return self.url.port ?? (self.useTLS ? 443 : 80) - } - - func isSchemeSupported(scheme: String) -> Bool { - return type(of: self.value).isSchemeSupported(scheme: scheme) - } - } - - /// Represent HTTP request. - public struct HostRequest: HTTPRequest { - /// Request HTTP method, defaults to `GET`. - public let method: HTTPMethod - /// Remote URL. - public let url: URL - /// Remote HTTP scheme, resolved from `URL`. - public let scheme: String - /// Remote host, resolved from `URL`. - public let host: String - /// Request custom HTTP Headers, defaults to no headers. - public var headers: HTTPHeaders - /// Request body, defaults to no body. - public var body: Body? - - /// Create an HTTP `Request`. - /// - /// - parameters: - /// - url: Remote `URL`. - /// - version: HTTP version. /// - method: HTTP method. /// - headers: Custom HTTP headers. /// - body: Request body. @@ -220,75 +164,41 @@ extension HTTPClient { throw HTTPClientError.emptyScheme } - guard Self.isSchemeSupported(scheme: scheme) else { - throw HTTPClientError.unsupportedScheme(scheme) - } + if Kind.host.isSchemeSupported(scheme: scheme) { + self.kind = .host + guard let host = url.host else { + throw HTTPClientError.emptyHost + } - guard let host = url.host else { - throw HTTPClientError.emptyHost + self.host = host + } else if Kind.unixSocket.isSchemeSupported(scheme: scheme) { + self.kind = .unixSocket + self.host = "" + } else { + throw HTTPClientError.unsupportedScheme(scheme) } - self.method = method + self.redirectState = nil self.url = url + self.method = method self.scheme = scheme - self.host = host self.headers = headers self.body = body } - static func isSchemeSupported(scheme: String) -> Bool { - return scheme == "http" || scheme == "https" + /// Whether request will be executed using secure socket. + public var useTLS: Bool { + return self.scheme == "https" } - } - - /// Represent UNIX Domain Socket HTTP request. - public struct UnixDomainRequest: HTTPRequest { - /// Request HTTP method, defaults to `GET`. - public let method: HTTPMethod - /// UNIX Domain Socket file URL. - public let url: URL - /// Remote HTTP scheme, resolved from `URL`. Unused. - public let scheme: String - /// Remote host, resolved from `URL`. Unused. - public let host: String - /// Request custom HTTP Headers, defaults to no headers. - public var headers: HTTPHeaders - /// Request body, defaults to no body. - public var body: Body? - - /// Create an HTTP `Request`. - /// - /// - parameters: - /// - url: UNIX Domain Socket `URL`. - /// - version: HTTP version. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - throws: - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws { - guard let scheme = url.scheme?.lowercased() else { - throw HTTPClientError.emptyScheme - } - guard scheme == "unix" else { - throw HTTPClientError.invalidURL - } - - self.method = method - self.url = url - self.scheme = scheme - self.host = "" - self.headers = headers - self.body = body + /// Resolved port. + public var port: Int { + return self.url.port ?? (self.useTLS ? 443 : 80) } - static func isSchemeSupported(scheme: String) -> Bool { - return scheme == "unix" + func isSchemeSupported(scheme: String) -> Bool { + return kind.isSchemeSupported(scheme: scheme) } - } /// Represent HTTP response. diff --git a/Sources/AsyncHTTPClient/HTTPRequest.swift b/Sources/AsyncHTTPClient/HTTPRequest.swift deleted file mode 100644 index 6db81f639..000000000 --- a/Sources/AsyncHTTPClient/HTTPRequest.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// -// -// Created by Marcin Krzyzanowski on 19/01/2020. -// - -import Foundation -import NIOHTTP1 - -protocol HTTPRequest { - /// Request HTTP method - var method: HTTPMethod { get } - /// Remote URL. - var url: URL { get } - /// Remote HTTP scheme, resolved from `URL`. - var scheme: String { get } - /// Remote host, resolved from `URL`. - var host: String { get } - /// Request custom HTTP Headers, defaults to no headers. - var headers: HTTPHeaders { get set } - /// Request body, defaults to no body. - var body: HTTPClient.Body? { get set } - - static func isSchemeSupported(scheme: String) -> Bool -} From bbd47f78e6d4981c7dc795b85a3d1eeb10671b2f Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 27 Jan 2020 11:49:31 +0100 Subject: [PATCH 5/9] refactor: Move some request logic to enum helpers --- Sources/AsyncHTTPClient/HTTPHandler.swift | 52 ++++++++++++++--------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 29a33f642..85800556b 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -96,12 +96,37 @@ extension HTTPClient { /// UNIX Domain Socket HTTP request. case unixSocket - func isSchemeSupported(scheme: String) -> Bool { + private static var hostSchemes = ["http", "https"] + private static var unixSchemes = ["unix"] + + init(forScheme scheme: String) throws { + if Self.host.supports(scheme: scheme) { + self = .host + } else if Self.unixSocket.supports(scheme: scheme) { + self = .unixSocket + } else { + throw HTTPClientError.unsupportedScheme(scheme) + } + } + + func hostFromURL(_ url: URL) throws -> String { + switch self { + case .host: + guard let host = url.host else { + throw HTTPClientError.emptyHost + } + return host + case .unixSocket: + return "" + } + } + + func supports(scheme: String) -> Bool { switch self { case .host: - return scheme == "http" || scheme == "https" + return Self.hostSchemes.contains(scheme) case .unixSocket: - return scheme == "unix" + return Self.unixSchemes.contains(scheme) } } } @@ -164,19 +189,8 @@ extension HTTPClient { throw HTTPClientError.emptyScheme } - if Kind.host.isSchemeSupported(scheme: scheme) { - self.kind = .host - guard let host = url.host else { - throw HTTPClientError.emptyHost - } - - self.host = host - } else if Kind.unixSocket.isSchemeSupported(scheme: scheme) { - self.kind = .unixSocket - self.host = "" - } else { - throw HTTPClientError.unsupportedScheme(scheme) - } + self.kind = try Kind(forScheme: scheme) + self.host = try self.kind.hostFromURL(url) self.redirectState = nil self.url = url @@ -195,10 +209,6 @@ extension HTTPClient { public var port: Int { return self.url.port ?? (self.useTLS ? 443 : 80) } - - func isSchemeSupported(scheme: String) -> Bool { - return kind.isSchemeSupported(scheme: scheme) - } } /// Represent HTTP response. @@ -834,7 +844,7 @@ internal struct RedirectHandler { return nil } - guard request.isSchemeSupported(scheme: self.request.scheme) else { + guard request.kind.supports(scheme: self.request.scheme) else { return nil } From 45c443f9077fcc644df0af2db82d73e101e870b5 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 27 Jan 2020 14:47:31 +0100 Subject: [PATCH 6/9] Update formatting --- Sources/AsyncHTTPClient/HTTPClient.swift | 16 ++++++++-------- Sources/AsyncHTTPClient/HTTPHandler.swift | 17 ++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index bb939084b..6f15a2b52 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -290,12 +290,12 @@ public class HTTPClient { } eventLoopChannel.map { channel in - task.setChannel(channel) - } - .flatMap { channel in - channel.writeAndFlush(request) - } - .cascadeFailure(to: task.promise) + task.setChannel(channel) + } + .flatMap { channel in + channel.writeAndFlush(request) + } + .cascadeFailure(to: task.promise) return task } @@ -488,12 +488,12 @@ private extension ChannelPipeline { func addProxyHandler(for request: HTTPClient.Request, decoder: ByteToMessageHandler, encoder: HTTPRequestEncoder, tlsConfiguration: TLSConfiguration?, proxy: HTTPClient.Configuration.Proxy?) -> EventLoopFuture { let handler = HTTPClientProxyHandler(host: request.host, port: request.port, authorization: proxy?.authorization, onConnect: { channel in channel.pipeline.removeHandler(decoder).flatMap { - return channel.pipeline.addHandler( + channel.pipeline.addHandler( ByteToMessageHandler(HTTPResponseDecoder(leftOverBytesStrategy: .forwardBytes)), position: .after(encoder) ) }.flatMap { - return channel.pipeline.addSSLHandlerIfNeeded(for: request, tlsConfiguration: tlsConfiguration) + channel.pipeline.addSSLHandlerIfNeeded(for: request, tlsConfiguration: tlsConfiguration) } }) return self.addHandler(handler) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index eb86ee271..fc83eac5e 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -88,7 +88,6 @@ extension HTTPClient { /// Represent HTTP request. public struct Request { - /// Represent kind of Request enum Kind { /// Remote host request. @@ -111,13 +110,13 @@ extension HTTPClient { func hostFromURL(_ url: URL) throws -> String { switch self { - case .host: - guard let host = url.host else { - throw HTTPClientError.emptyHost - } - return host - case .unixSocket: - return "" + case .host: + guard let host = url.host else { + throw HTTPClientError.emptyHost + } + return host + case .unixSocket: + return "" } } @@ -844,7 +843,7 @@ internal struct RedirectHandler { return nil } - guard request.kind.supports(scheme: self.request.scheme) else { + guard self.request.kind.supports(scheme: self.request.scheme) else { return nil } From 535303b20c56be6335e8bb3758f7d093aa5e1dd8 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 27 Jan 2020 14:47:55 +0100 Subject: [PATCH 7/9] Revert host to let --- Sources/AsyncHTTPClient/HTTPHandler.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index fc83eac5e..e021c2b23 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -213,7 +213,7 @@ extension HTTPClient { /// Represent HTTP response. public struct Response { /// Remote host of the request. - public var host: String + public let host: String /// Response HTTP status. public var status: HTTPResponseStatus /// Reponse HTTP headers. From 5233d5c041177cf5916770f0bbd7808e710c38d2 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 27 Jan 2020 14:53:07 +0100 Subject: [PATCH 8/9] Don't use Self to please Swift 5.0 --- Sources/AsyncHTTPClient/HTTPHandler.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index e021c2b23..342c5a4d7 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -99,9 +99,9 @@ extension HTTPClient { private static var unixSchemes = ["unix"] init(forScheme scheme: String) throws { - if Self.host.supports(scheme: scheme) { + if Kind.host.supports(scheme: scheme) { self = .host - } else if Self.unixSocket.supports(scheme: scheme) { + } else if Kind.unixSocket.supports(scheme: scheme) { self = .unixSocket } else { throw HTTPClientError.unsupportedScheme(scheme) @@ -123,9 +123,9 @@ extension HTTPClient { func supports(scheme: String) -> Bool { switch self { case .host: - return Self.hostSchemes.contains(scheme) + return Kind.hostSchemes.contains(scheme) case .unixSocket: - return Self.unixSchemes.contains(scheme) + return Kind.unixSchemes.contains(scheme) } } } From b6cf0a4672a83cd14e0edbca30b99229f64e5651 Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Mon, 27 Jan 2020 15:04:43 +0100 Subject: [PATCH 9/9] Ups. Wrong property updated previously --- Sources/AsyncHTTPClient/HTTPHandler.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 342c5a4d7..88a790330 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -137,7 +137,7 @@ extension HTTPClient { /// Remote HTTP scheme, resolved from `URL`. public var scheme: String /// Remote host, resolved from `URL`. - public var host: String + public let host: String /// Request custom HTTP Headers, defaults to no headers. public var headers: HTTPHeaders /// Request body, defaults to no body. @@ -213,7 +213,7 @@ extension HTTPClient { /// Represent HTTP response. public struct Response { /// Remote host of the request. - public let host: String + public var host: String /// Response HTTP status. public var status: HTTPResponseStatus /// Reponse HTTP headers.