Skip to content

Commit f2c2f73

Browse files
karwafabianfettdnadoba
authored
Refactor URL component extraction (#485)
* Refactor URL component extraction * Remove superfluous test message Co-authored-by: Fabian Fett <[email protected]> Co-authored-by: David Nadoba <[email protected]>
1 parent 1119893 commit f2c2f73

File tree

3 files changed

+49
-63
lines changed

3 files changed

+49
-63
lines changed

Sources/AsyncHTTPClient/HTTPHandler.swift

Lines changed: 40 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -108,63 +108,52 @@ extension HTTPClient {
108108
private static var hostRestrictedSchemes: Set = ["http", "https"]
109109
private static var allSupportedSchemes: Set = ["http", "https", "unix", "http+unix", "https+unix"]
110110

111-
init(forScheme scheme: String) throws {
112-
switch scheme {
113-
case "http", "https": self = .host
114-
case "unix": self = .unixSocket(.baseURL)
115-
case "http+unix": self = .unixSocket(.http_unix)
116-
case "https+unix": self = .unixSocket(.https_unix)
117-
default:
118-
throw HTTPClientError.unsupportedScheme(scheme)
119-
}
120-
}
111+
func supportsRedirects(to scheme: String?) -> Bool {
112+
guard let scheme = scheme?.lowercased() else { return false }
121113

122-
func hostFromURL(_ url: URL) throws -> String {
123114
switch self {
124115
case .host:
125-
guard let host = url.host else {
126-
throw HTTPClientError.emptyHost
127-
}
128-
return host
116+
return Kind.hostRestrictedSchemes.contains(scheme)
129117
case .unixSocket:
130-
return ""
118+
return Kind.allSupportedSchemes.contains(scheme)
131119
}
132120
}
121+
}
133122

134-
func socketPathFromURL(_ url: URL) throws -> String {
135-
switch self {
136-
case .unixSocket(.baseURL):
137-
return url.baseURL?.path ?? url.path
138-
case .unixSocket:
139-
guard let socketPath = url.host else {
140-
throw HTTPClientError.missingSocketPath
141-
}
142-
return socketPath
143-
case .host:
144-
return ""
145-
}
146-
}
123+
static func useTLS(_ scheme: String) -> Bool {
124+
return scheme == "https" || scheme == "https+unix"
125+
}
147126

148-
func uriFromURL(_ url: URL) -> String {
149-
switch self {
150-
case .host:
151-
return url.uri
152-
case .unixSocket(.baseURL):
153-
return url.baseURL != nil ? url.uri : "/"
154-
case .unixSocket:
155-
return url.uri
156-
}
127+
static func deconstructURL(
128+
_ url: URL
129+
) throws -> (
130+
kind: Kind, scheme: String, hostname: String, port: Int, socketPath: String, uri: String
131+
) {
132+
guard let scheme = url.scheme?.lowercased() else {
133+
throw HTTPClientError.emptyScheme
157134
}
158-
159-
func supportsRedirects(to scheme: String?) -> Bool {
160-
guard let scheme = scheme?.lowercased() else { return false }
161-
162-
switch self {
163-
case .host:
164-
return Kind.hostRestrictedSchemes.contains(scheme)
165-
case .unixSocket:
166-
return Kind.allSupportedSchemes.contains(scheme)
135+
switch scheme {
136+
case "http", "https":
137+
guard let host = url.host, !host.isEmpty else {
138+
throw HTTPClientError.emptyHost
139+
}
140+
let defaultPort = self.useTLS(scheme) ? 443 : 80
141+
return (.host, scheme, host, url.port ?? defaultPort, "", url.uri)
142+
case "http+unix", "https+unix":
143+
guard let socketPath = url.host, !socketPath.isEmpty else {
144+
throw HTTPClientError.missingSocketPath
145+
}
146+
let (kind, defaultPort) = self.useTLS(scheme) ? (Kind.UnixScheme.https_unix, 443) : (.http_unix, 80)
147+
return (.unixSocket(kind), scheme, "", url.port ?? defaultPort, socketPath, url.uri)
148+
case "unix":
149+
let socketPath = url.baseURL?.path ?? url.path
150+
let uri = url.baseURL != nil ? url.uri : "/"
151+
guard !socketPath.isEmpty else {
152+
throw HTTPClientError.missingSocketPath
167153
}
154+
return (.unixSocket(.baseURL), scheme, "", url.port ?? 80, socketPath, uri)
155+
default:
156+
throw HTTPClientError.unsupportedScheme(url.scheme!)
168157
}
169158
}
170159

@@ -176,6 +165,8 @@ extension HTTPClient {
176165
public let scheme: String
177166
/// Remote host, resolved from `URL`.
178167
public let host: String
168+
/// Resolved port.
169+
public let port: Int
179170
/// Socket path, resolved from `URL`.
180171
let socketPath: String
181172
/// URI composed of the path and query, resolved from `URL`.
@@ -264,32 +255,18 @@ extension HTTPClient {
264255
/// - `emptyHost` if URL does not contains a host.
265256
/// - `missingSocketPath` if URL does not contains a socketPath as an encoded host.
266257
public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws {
267-
guard let scheme = url.scheme?.lowercased() else {
268-
throw HTTPClientError.emptyScheme
269-
}
270-
271-
self.kind = try Kind(forScheme: scheme)
272-
self.host = try self.kind.hostFromURL(url)
273-
self.socketPath = try self.kind.socketPathFromURL(url)
274-
self.uri = self.kind.uriFromURL(url)
275-
258+
(self.kind, self.scheme, self.host, self.port, self.socketPath, self.uri) = try Request.deconstructURL(url)
276259
self.redirectState = nil
277260
self.url = url
278261
self.method = method
279-
self.scheme = scheme
280262
self.headers = headers
281263
self.body = body
282264
self.tlsConfiguration = tlsConfiguration
283265
}
284266

285267
/// Whether request will be executed using secure socket.
286268
public var useTLS: Bool {
287-
return self.scheme == "https" || self.scheme == "https+unix"
288-
}
289-
290-
/// Resolved port.
291-
public var port: Int {
292-
return self.url.port ?? (self.useTLS ? 443 : 80)
269+
return Request.useTLS(self.scheme)
293270
}
294271

295272
func createRequestHead() throws -> (HTTPRequestHead, RequestFramingMetadata) {

Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extension HTTPClientTests {
2929
("testBadRequestURI", testBadRequestURI),
3030
("testSchemaCasing", testSchemaCasing),
3131
("testURLSocketPathInitializers", testURLSocketPathInitializers),
32+
("testBadUnixWithBaseURL", testBadUnixWithBaseURL),
3233
("testConvenienceExecuteMethods", testConvenienceExecuteMethods),
3334
("testConvenienceExecuteMethodsOverSocket", testConvenienceExecuteMethodsOverSocket),
3435
("testConvenienceExecuteMethodsOverSecureSocket", testConvenienceExecuteMethodsOverSecureSocket),

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ class HTTPClientTests: XCTestCase {
215215
XCTAssertNil(url10)
216216
}
217217

218+
func testBadUnixWithBaseURL() {
219+
let badUnixBaseURL = URL(string: "/foo", relativeTo: URL(string: "unix:")!)!
220+
XCTAssertEqual(badUnixBaseURL.baseURL?.path, "")
221+
XCTAssertThrowsError(try Request(url: badUnixBaseURL)) { error in
222+
XCTAssertEqual(error as! HTTPClientError, HTTPClientError.missingSocketPath)
223+
}
224+
}
225+
218226
func testConvenienceExecuteMethods() throws {
219227
XCTAssertNoThrow(XCTAssertEqual(["GET"[...]],
220228
try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[canonicalForm: "X-Method-Used"]))

0 commit comments

Comments
 (0)