@@ -18,12 +18,42 @@ import NIOConcurrencyHelpers
18
18
import NIOHTTP1
19
19
import NIOSSL
20
20
21
+ /// HTTPClient class provides API for request execution.
22
+ ///
23
+ /// Example:
24
+ ///
25
+ /// ```swift
26
+ /// let client = HTTPClient(eventLoopGroupProvider = .createNew)
27
+ /// client.get(url: "https://swift.org", deadline: .now() + .seconds(1)).whenComplete { result in
28
+ /// switch result {
29
+ /// case .failure(let error):
30
+ /// // process error
31
+ /// case .success(let response):
32
+ /// if let response.status == .ok {
33
+ /// // handle response
34
+ /// } else {
35
+ /// // handle remote error
36
+ /// }
37
+ /// }
38
+ /// }
39
+ /// ```
40
+ ///
41
+ /// It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO `EventLoopGroup`:
42
+ ///
43
+ /// ```swift
44
+ /// try client.syncShutdown()
45
+ /// ```
21
46
public class HTTPClient {
22
47
public let eventLoopGroup : EventLoopGroup
23
48
let eventLoopGroupProvider : EventLoopGroupProvider
24
49
let configuration : Configuration
25
50
let isShutdown = Atomic < Bool > ( value: false )
26
51
52
+ /// Create an `HTTPClient` with specified `EventLoopGroup` provider and configuration.
53
+ ///
54
+ /// - parameters:
55
+ /// - eventLoopGroupProvider: Specify how `EventLoopGroup` will be created.
56
+ /// - configuration: Client configuration.
27
57
public init ( eventLoopGroupProvider: EventLoopGroupProvider , configuration: Configuration = Configuration ( ) ) {
28
58
self . eventLoopGroupProvider = eventLoopGroupProvider
29
59
switch self . eventLoopGroupProvider {
@@ -44,6 +74,7 @@ public class HTTPClient {
44
74
}
45
75
}
46
76
77
+ /// Shuts down the client and `EventLoopGroup` if it was created by the client.
47
78
public func syncShutdown( ) throws {
48
79
switch self . eventLoopGroupProvider {
49
80
case . shared:
@@ -58,6 +89,11 @@ public class HTTPClient {
58
89
}
59
90
}
60
91
92
+ /// Execute `GET` request using specified URL.
93
+ ///
94
+ /// - parameters:
95
+ /// - url: Remote URL.
96
+ /// - deadline: Point in time by which the request must complete.
61
97
public func get( url: String , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
62
98
do {
63
99
let request = try Request ( url: url, method: . GET)
@@ -67,6 +103,12 @@ public class HTTPClient {
67
103
}
68
104
}
69
105
106
+ /// Execute `POST` request using specified URL.
107
+ ///
108
+ /// - parameters:
109
+ /// - url: Remote URL.
110
+ /// - body: Request body.
111
+ /// - deadline: Point in time by which the request must complete.
70
112
public func post( url: String , body: Body ? = nil , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
71
113
do {
72
114
let request = try HTTPClient . Request ( url: url, method: . POST, body: body)
@@ -76,6 +118,12 @@ public class HTTPClient {
76
118
}
77
119
}
78
120
121
+ /// Execute `PATCH` request using specified URL.
122
+ ///
123
+ /// - parameters:
124
+ /// - url: Remote URL.
125
+ /// - body: Request body.
126
+ /// - deadline: Point in time by which the request must complete.
79
127
public func patch( url: String , body: Body ? = nil , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
80
128
do {
81
129
let request = try HTTPClient . Request ( url: url, method: . PATCH, body: body)
@@ -85,6 +133,12 @@ public class HTTPClient {
85
133
}
86
134
}
87
135
136
+ /// Execute `PUT` request using specified URL.
137
+ ///
138
+ /// - parameters:
139
+ /// - url: Remote URL.
140
+ /// - body: Request body.
141
+ /// - deadline: Point in time by which the request must complete.
88
142
public func put( url: String , body: Body ? = nil , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
89
143
do {
90
144
let request = try HTTPClient . Request ( url: url, method: . PUT, body: body)
@@ -94,6 +148,11 @@ public class HTTPClient {
94
148
}
95
149
}
96
150
151
+ /// Execute `DELETE` request using specified URL.
152
+ ///
153
+ /// - parameters:
154
+ /// - url: Remote URL.
155
+ /// - deadline: The time when the request must have been completed by.
97
156
public func delete( url: String , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
98
157
do {
99
158
let request = try Request ( url: url, method: . DELETE)
@@ -103,11 +162,22 @@ public class HTTPClient {
103
162
}
104
163
}
105
164
165
+ /// Execute arbitrary HTTP request using specified URL.
166
+ ///
167
+ /// - parameters:
168
+ /// - request: HTTP request to execute.
169
+ /// - deadline: Point in time by which the request must complete.
106
170
public func execute( request: Request , deadline: NIODeadline ? = nil ) -> EventLoopFuture < Response > {
107
171
let accumulator = ResponseAccumulator ( request: request)
108
172
return self . execute ( request: request, delegate: accumulator, deadline: deadline) . futureResult
109
173
}
110
174
175
+ /// Execute arbitrary HTTP request and handle response processing using provided delegate.
176
+ ///
177
+ /// - parameters:
178
+ /// - request: HTTP request to execute.
179
+ /// - delegate: Delegate to process response parts.
180
+ /// - deadline: Point in time by which the request must complete.
111
181
public func execute< T: HTTPClientResponseDelegate > ( request: Request , delegate: T , deadline: NIODeadline ? = nil ) -> Task < T . Response > {
112
182
let eventLoop = self . eventLoopGroup. next ( )
113
183
@@ -187,10 +257,24 @@ public class HTTPClient {
187
257
}
188
258
}
189
259
260
+ /// `HTTPClient` configuration.
190
261
public struct Configuration {
262
+ /// TLS configuration, defaults to `TLSConfiguration.forClient()`.
191
263
public var tlsConfiguration : TLSConfiguration ?
264
+ /// Enables following 3xx redirects automatically, defaults to `false`.
265
+ ///
266
+ /// Following redirects are supported:
267
+ /// - `301: Moved Permanently`
268
+ /// - `302: Found`
269
+ /// - `303: See Other`
270
+ /// - `304: Not Modified`
271
+ /// - `305: Use Proxy`
272
+ /// - `307: Temporary Redirect`
273
+ /// - `308: Permanent Redirect`
192
274
public var followRedirects : Bool
275
+ /// Default client timeout, defaults to no timeouts.
193
276
public var timeout : Timeout
277
+ /// Upstream proxy, defaults to no proxy.
194
278
public var proxy : Proxy ?
195
279
196
280
public init ( tlsConfiguration: TLSConfiguration ? = nil , followRedirects: Bool = false , timeout: Timeout = Timeout ( ) , proxy: Proxy ? = nil ) {
@@ -208,15 +292,26 @@ public class HTTPClient {
208
292
}
209
293
}
210
294
295
+ /// Specifies how `EventLoopGroup` will be created and establishes lifecycle ownership.
211
296
public enum EventLoopGroupProvider {
297
+ /// `EventLoopGroup` will be provided by the user. Owner of this group is responsible for its lifecycle.
212
298
case shared( EventLoopGroup )
299
+ /// `EventLoopGroup` will be created by the client. When `syncShutdown` is called, created `EventLoopGroup` will be shut down as well.
213
300
case createNew
214
301
}
215
302
303
+ /// Timeout configuration
216
304
public struct Timeout {
305
+ /// Specifies connect timeout.
217
306
public var connect : TimeAmount ?
307
+ /// Specifies read timeout.
218
308
public var read : TimeAmount ?
219
309
310
+ /// Create timeout.
311
+ ///
312
+ /// - parameters:
313
+ /// - connect: `connect` timeout.
314
+ /// - read: `read` timeout.
220
315
public init ( connect: TimeAmount ? = nil , read: TimeAmount ? = nil ) {
221
316
self . connect = connect
222
317
self . read = read
@@ -255,6 +350,7 @@ private extension ChannelPipeline {
255
350
}
256
351
}
257
352
353
+ /// Possible client errors.
258
354
public struct HTTPClientError : Error , Equatable , CustomStringConvertible {
259
355
private enum Code : Equatable {
260
356
case invalidURL
@@ -281,16 +377,28 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
281
377
return " HTTPClientError. \( String ( describing: self . code) ) "
282
378
}
283
379
380
+ /// URL provided is invalid.
284
381
public static let invalidURL = HTTPClientError ( code: . invalidURL)
382
+ /// URL does not contain host.
285
383
public static let emptyHost = HTTPClientError ( code: . emptyHost)
384
+ /// Client is shutdown and cannot be used for new requests.
286
385
public static let alreadyShutdown = HTTPClientError ( code: . alreadyShutdown)
386
+ /// URL does not contain scheme.
287
387
public static let emptyScheme = HTTPClientError ( code: . emptyScheme)
388
+ /// Provided URL scheme is not supported, supported schemes are: `http` and `https`
288
389
public static func unsupportedScheme( _ scheme: String ) -> HTTPClientError { return HTTPClientError ( code: . unsupportedScheme( scheme) ) }
390
+ /// Request timed out.
289
391
public static let readTimeout = HTTPClientError ( code: . readTimeout)
392
+ /// Remote connection was closed unexpectedly.
290
393
public static let remoteConnectionClosed = HTTPClientError ( code: . remoteConnectionClosed)
394
+ /// Request was cancelled.
291
395
public static let cancelled = HTTPClientError ( code: . cancelled)
396
+ /// Request contains invalid identity encoding.
292
397
public static let identityCodingIncorrectlyPresent = HTTPClientError ( code: . identityCodingIncorrectlyPresent)
398
+ /// Request contains multiple chunks definitions.
293
399
public static let chunkedSpecifiedMultipleTimes = HTTPClientError ( code: . chunkedSpecifiedMultipleTimes)
400
+ /// Proxy response was invalid.
294
401
public static let invalidProxyResponse = HTTPClientError ( code: . invalidProxyResponse)
402
+ /// Request does not contain `Content-Length` header.
295
403
public static let contentLengthMissing = HTTPClientError ( code: . contentLengthMissing)
296
404
}
0 commit comments