7
7
import Foundation
8
8
import UIKit
9
9
10
+ public enum URLAuthenticationChallengeResponse {
11
+ /// Use the specified credential.
12
+ case useCredential( URLCredential )
13
+ /// Use the default handling for the challenge as though this delegate method were not implemented.
14
+ case performDefaultHandling
15
+ /// Cancel the entire request.
16
+ case cancelAuthenticationChallenge
17
+ /// Reject this challenge, and call the authentication delegate method again with the next
18
+ /// authentication protection space.
19
+ case rejectProtectionSpace
20
+ }
21
+
10
22
/// Delegate protocol for `DefaultHTTPClient`.
11
23
public protocol DefaultHTTPClientDelegate : AnyObject {
12
24
/// Tells the delegate that the HTTP client will start a new `request`.
@@ -42,6 +54,14 @@ public protocol DefaultHTTPClientDelegate: AnyObject {
42
54
/// This will be called only if `httpClient(_:recoverRequest:fromError:completion:)` is not implemented, or returns
43
55
/// an error.
44
56
func httpClient( _ httpClient: DefaultHTTPClient , request: HTTPRequest , didFailWithError error: HTTPError )
57
+
58
+ /// Requests credentials from the delegate in response to an authentication request from the remote server.
59
+ func httpClient(
60
+ _ httpClient: DefaultHTTPClient ,
61
+ request: HTTPRequest ,
62
+ didReceive challenge: URLAuthenticationChallenge ,
63
+ completion: @escaping ( URLAuthenticationChallengeResponse ) -> Void
64
+ )
45
65
}
46
66
47
67
public extension DefaultHTTPClientDelegate {
@@ -55,6 +75,15 @@ public extension DefaultHTTPClientDelegate {
55
75
56
76
func httpClient( _ httpClient: DefaultHTTPClient , request: HTTPRequest , didReceiveResponse response: HTTPResponse ) { }
57
77
func httpClient( _ httpClient: DefaultHTTPClient , request: HTTPRequest , didFailWithError error: HTTPError ) { }
78
+
79
+ func httpClient(
80
+ _ httpClient: DefaultHTTPClient ,
81
+ request: HTTPRequest ,
82
+ didReceive challenge: URLAuthenticationChallenge ,
83
+ completion: @escaping ( URLAuthenticationChallengeResponse ) -> Void
84
+ ) {
85
+ completion ( . performDefaultHandling)
86
+ }
58
87
}
59
88
60
89
/// An implementation of `HTTPClient` using native APIs.
@@ -200,6 +229,13 @@ public final class DefaultHTTPClient: HTTPClient, Loggable {
200
229
}
201
230
receiveResponse ? ( response)
202
231
} ,
232
+ receiveChallenge: { [ weak self] challenge, completion in
233
+ if let self = self , let delegate = self . delegate {
234
+ delegate. httpClient ( self , request: request, didReceive: challenge, completion: completion)
235
+ } else {
236
+ completion ( . performDefaultHandling)
237
+ }
238
+ } ,
203
239
consume: consume,
204
240
completion: { [ weak self] result in
205
241
if let self = self , case let . failure( error) = result {
@@ -283,6 +319,15 @@ public final class DefaultHTTPClient: HTTPClient, Loggable {
283
319
public func urlSession( _ session: URLSession , task: URLSessionTask , didCompleteWithError error: Error ? ) {
284
320
findTask ( for: task) ? . urlSession ( session, didCompleteWithError: error)
285
321
}
322
+
323
+ func urlSession( _ session: URLSession , task: URLSessionTask , didReceive challenge: URLAuthenticationChallenge , completionHandler: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) {
324
+ guard let task = findTask ( for: task) else {
325
+ completionHandler ( . performDefaultHandling, nil )
326
+ return
327
+ }
328
+
329
+ task. urlSession ( session, didReceive: challenge, completion: completionHandler)
330
+ }
286
331
}
287
332
288
333
/// Represents an on-going HTTP task.
@@ -294,6 +339,7 @@ public final class DefaultHTTPClient: HTTPClient, Loggable {
294
339
private let request : HTTPRequest
295
340
fileprivate let task : URLSessionTask
296
341
private let receiveResponse : ( HTTPResponse ) -> Void
342
+ private let receiveChallenge : ( URLAuthenticationChallenge , @escaping ( URLAuthenticationChallengeResponse ) -> Void ) -> Void
297
343
private let consume : ( Data , Double ? ) -> Void
298
344
private let completion : ( HTTPResult < HTTPResponse > ) -> Void
299
345
@@ -312,11 +358,19 @@ public final class DefaultHTTPClient: HTTPClient, Loggable {
312
358
case finished
313
359
}
314
360
315
- init ( request: HTTPRequest , task: URLSessionDataTask , receiveResponse: @escaping ( ( HTTPResponse ) -> Void ) , consume: @escaping ( Data , Double ? ) -> Void , completion: @escaping ( HTTPResult < HTTPResponse > ) -> Void ) {
361
+ init (
362
+ request: HTTPRequest ,
363
+ task: URLSessionDataTask ,
364
+ receiveResponse: @escaping ( HTTPResponse ) -> Void ,
365
+ receiveChallenge: @escaping ( URLAuthenticationChallenge , @escaping ( URLAuthenticationChallengeResponse ) -> Void ) -> Void ,
366
+ consume: @escaping ( Data , Double ? ) -> Void ,
367
+ completion: @escaping ( HTTPResult < HTTPResponse > ) -> Void
368
+ ) {
316
369
self . request = request
317
370
self . task = task
318
371
self . completion = completion
319
372
self . receiveResponse = receiveResponse
373
+ self . receiveChallenge = receiveChallenge
320
374
self . consume = consume
321
375
}
322
376
@@ -427,6 +481,21 @@ public final class DefaultHTTPClient: HTTPClient, Loggable {
427
481
}
428
482
finish ( )
429
483
}
484
+
485
+ func urlSession( _ session: URLSession , didReceive challenge: URLAuthenticationChallenge , completion: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) {
486
+ receiveChallenge ( challenge) { response in
487
+ switch response {
488
+ case let . useCredential( credential) :
489
+ completion ( . useCredential, credential)
490
+ case . performDefaultHandling:
491
+ completion ( . performDefaultHandling, nil )
492
+ case . cancelAuthenticationChallenge:
493
+ completion ( . cancelAuthenticationChallenge, nil )
494
+ case . rejectProtectionSpace:
495
+ completion ( . rejectProtectionSpace, nil )
496
+ }
497
+ }
498
+ }
430
499
}
431
500
}
432
501
0 commit comments