Skip to content

Commit 33a2784

Browse files
authored
HTTPClient Enhancements (#3099)
* Add HTTPClientProtocol protocol: This provides a lightweight form of dependency injection for HTTP clients * Conform HTTPClientHeaders to ExpressibleByDictionaryLiteral * Reorganize HTTPClient declarations * Apply automatic formatting rules
1 parent 4a88ae1 commit 33a2784

File tree

2 files changed

+47
-25
lines changed

2 files changed

+47
-25
lines changed

Sources/Basics/HTPClient+URLSession.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import struct TSCUtility.Versioning
88
import FoundationNetworking
99
#endif
1010

11-
public struct URLSessionHTTPClient {
11+
public struct URLSessionHTTPClient: HTTPClientProtocol {
1212
private let configuration: URLSessionConfiguration
1313

1414
public init(configuration: URLSessionConfiguration = .default) {
1515
self.configuration = configuration
1616
}
1717

18-
public func execute(request: HTTPClient.Request, callback: @escaping (Result<HTTPClient.Response, Error>) -> Void) {
18+
public func execute(_ request: HTTPClient.Request, callback: @escaping (Result<HTTPClient.Response, Error>) -> Void) {
1919
let session = URLSession(configuration: self.configuration)
2020
let task = session.dataTask(with: request.urlRequest()) { data, response, error in
2121
if let error = error {

Sources/Basics/HTTPClient.swift

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,19 @@ import Glibc
2424
import CRT
2525
#endif
2626

27+
public protocol HTTPClientProtocol {
28+
func execute(_ request: HTTPClientRequest, callback: @escaping (Result<HTTPClientResponse, Error>) -> Void)
29+
}
30+
31+
public enum HTTPClientError: Error, Equatable {
32+
case invalidResponse
33+
case badResponseStatusCode(Int)
34+
case circuitBreakerTriggered
35+
}
36+
2737
// MARK: - HTTPClient
2838

29-
public struct HTTPClient {
39+
public struct HTTPClient: HTTPClientProtocol {
3040
public typealias Configuration = HTTPClientConfiguration
3141
public typealias Request = HTTPClientRequest
3242
public typealias Response = HTTPClientResponse
@@ -168,33 +178,29 @@ public struct HTTPClient {
168178
}
169179
}
170180

171-
extension HTTPClient {
172-
public func head(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
181+
public extension HTTPClient {
182+
func head(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
173183
self.execute(Request(method: .head, url: url, headers: headers, body: nil, options: options), callback: callback)
174184
}
175185

176-
public func get(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
186+
func get(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
177187
self.execute(Request(method: .get, url: url, headers: headers, body: nil, options: options), callback: callback)
178188
}
179189

180-
public func put(_ url: URL, body: Data?, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
190+
func put(_ url: URL, body: Data?, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
181191
self.execute(Request(method: .put, url: url, headers: headers, body: body, options: options), callback: callback)
182192
}
183193

184-
public func post(_ url: URL, body: Data?, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
194+
func post(_ url: URL, body: Data?, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
185195
self.execute(Request(method: .post, url: url, headers: headers, body: body, options: options), callback: callback)
186196
}
187197

188-
public func delete(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
198+
func delete(_ url: URL, headers: HTTPClientHeaders = .init(), options: Request.Options = .init(), callback: @escaping (Result<Response, Error>) -> Void) {
189199
self.execute(Request(method: .delete, url: url, headers: headers, body: nil, options: options), callback: callback)
190200
}
191201
}
192202

193-
extension HTTPClientResponse {
194-
public func decodeBody<T: Decodable>(_ type: T.Type, using decoder: JSONDecoder = .init()) throws -> T? {
195-
try self.body.flatMap { try decoder.decode(type, from: $0) }
196-
}
197-
}
203+
// MARK: - HTTPClientConfiguration
198204

199205
public struct HTTPClientConfiguration {
200206
public var requestHeaders: HTTPClientHeaders?
@@ -220,6 +226,8 @@ public enum HTTPClientCircuitBreakerStrategy {
220226
case hostErrors(maxErrors: Int, age: DispatchTimeInterval)
221227
}
222228

229+
// MARK: - HTTPClientRequest
230+
223231
public struct HTTPClientRequest {
224232
public let method: Method
225233
public let url: URL
@@ -231,7 +239,8 @@ public struct HTTPClientRequest {
231239
url: URL,
232240
headers: HTTPClientHeaders = .init(),
233241
body: Data? = nil,
234-
options: Options = .init()) {
242+
options: Options = .init())
243+
{
235244
self.method = method
236245
self.url = url
237246
self.headers = headers
@@ -266,6 +275,8 @@ public struct HTTPClientRequest {
266275
}
267276
}
268277

278+
// MARK: - HTTPClientResponse
279+
269280
public struct HTTPClientResponse {
270281
public let statusCode: Int
271282
public let statusText: String?
@@ -275,15 +286,22 @@ public struct HTTPClientResponse {
275286
public init(statusCode: Int,
276287
statusText: String? = nil,
277288
headers: HTTPClientHeaders = .init(),
278-
body: Data? = nil) {
289+
body: Data? = nil)
290+
{
279291
self.statusCode = statusCode
280292
self.statusText = statusText
281293
self.headers = headers
282294
self.body = body
283295
}
296+
297+
public func decodeBody<T: Decodable>(_ type: T.Type, using decoder: JSONDecoder = .init()) throws -> T? {
298+
try self.body.flatMap { try decoder.decode(type, from: $0) }
299+
}
284300
}
285301

286-
public struct HTTPClientHeaders: Sequence, Equatable {
302+
// MARK: - HTTPClientHeaders
303+
304+
public struct HTTPClientHeaders {
287305
private var items: [Item]
288306
private var headers: [String: [String]]
289307

@@ -336,10 +354,6 @@ public struct HTTPClientHeaders: Sequence, Equatable {
336354
self.headers[name.lowercased()] ?? []
337355
}
338356

339-
public func makeIterator() -> IndexingIterator<[Item]> {
340-
return self.items.makeIterator()
341-
}
342-
343357
public struct Item: Equatable {
344358
let name: String
345359
let value: String
@@ -349,14 +363,22 @@ public struct HTTPClientHeaders: Sequence, Equatable {
349363
self.value = value
350364
}
351365
}
366+
}
352367

368+
extension HTTPClientHeaders: Sequence {
369+
public func makeIterator() -> IndexingIterator<[Item]> {
370+
return self.items.makeIterator()
371+
}
372+
}
373+
374+
extension HTTPClientHeaders: Equatable {
353375
public static func == (lhs: HTTPClientHeaders, rhs: HTTPClientHeaders) -> Bool {
354376
return lhs.headers == rhs.headers
355377
}
356378
}
357379

358-
public enum HTTPClientError: Error, Equatable {
359-
case invalidResponse
360-
case badResponseStatusCode(Int)
361-
case circuitBreakerTriggered
380+
extension HTTPClientHeaders: ExpressibleByDictionaryLiteral {
381+
public init(dictionaryLiteral elements: (String, String)...) {
382+
self.init(elements.map(Item.init))
383+
}
362384
}

0 commit comments

Comments
 (0)