Added Android code
[wl-app.git] / iOS / Pods / Alamofire / Source / SessionDelegate.swift
1 //
2 //  SessionDelegate.swift
3 //
4 //  Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
5 //
6 //  Permission is hereby granted, free of charge, to any person obtaining a copy
7 //  of this software and associated documentation files (the "Software"), to deal
8 //  in the Software without restriction, including without limitation the rights
9 //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 //  copies of the Software, and to permit persons to whom the Software is
11 //  furnished to do so, subject to the following conditions:
12 //
13 //  The above copyright notice and this permission notice shall be included in
14 //  all copies or substantial portions of the Software.
15 //
16 //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 //  THE SOFTWARE.
23 //
24
25 import Foundation
26
27 /// Responsible for handling all delegate callbacks for the underlying session.
28 open class SessionDelegate: NSObject {
29
30     // MARK: URLSessionDelegate Overrides
31
32     /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
33     open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
34
35     /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
36     open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
37
38     /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
39     open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
40
41     /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
42     open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
43
44     // MARK: URLSessionTaskDelegate Overrides
45
46     /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
47     open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
48
49     /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
50     /// requires the caller to call the `completionHandler`.
51     open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
52
53     /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
54     open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
55
56     /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
57     /// requires the caller to call the `completionHandler`.
58     open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
59
60     /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
61     open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
62
63     /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
64     /// requires the caller to call the `completionHandler`.
65     open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
66
67     /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
68     open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
69
70     /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
71     open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
72
73     // MARK: URLSessionDataDelegate Overrides
74
75     /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
76     open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
77
78     /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
79     /// requires caller to call the `completionHandler`.
80     open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
81
82     /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
83     open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
84
85     /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
86     open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
87
88     /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
89     open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
90
91     /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
92     /// requires caller to call the `completionHandler`.
93     open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
94
95     // MARK: URLSessionDownloadDelegate Overrides
96
97     /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
98     open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
99
100     /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
101     open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
102
103     /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
104     open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
105
106     // MARK: URLSessionStreamDelegate Overrides
107
108 #if !os(watchOS)
109
110     /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
111     @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
112     open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
113         get {
114             return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
115         }
116         set {
117             _streamTaskReadClosed = newValue
118         }
119     }
120
121     /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
122     @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
123     open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
124         get {
125             return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
126         }
127         set {
128             _streamTaskWriteClosed = newValue
129         }
130     }
131
132     /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
133     @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
134     open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
135         get {
136             return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
137         }
138         set {
139             _streamTaskBetterRouteDiscovered = newValue
140         }
141     }
142
143     /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
144     @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
145     open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
146         get {
147             return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
148         }
149         set {
150             _streamTaskDidBecomeInputStream = newValue
151         }
152     }
153
154     var _streamTaskReadClosed: Any?
155     var _streamTaskWriteClosed: Any?
156     var _streamTaskBetterRouteDiscovered: Any?
157     var _streamTaskDidBecomeInputStream: Any?
158
159 #endif
160
161     // MARK: Properties
162
163     var retrier: RequestRetrier?
164     weak var sessionManager: SessionManager?
165
166     private var requests: [Int: Request] = [:]
167     private let lock = NSLock()
168
169     /// Access the task delegate for the specified task in a thread-safe manner.
170     open subscript(task: URLSessionTask) -> Request? {
171         get {
172             lock.lock() ; defer { lock.unlock() }
173             return requests[task.taskIdentifier]
174         }
175         set {
176             lock.lock() ; defer { lock.unlock() }
177             requests[task.taskIdentifier] = newValue
178         }
179     }
180
181     // MARK: Lifecycle
182
183     /// Initializes the `SessionDelegate` instance.
184     ///
185     /// - returns: The new `SessionDelegate` instance.
186     public override init() {
187         super.init()
188     }
189
190     // MARK: NSObject Overrides
191
192     /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
193     /// to a specified message.
194     ///
195     /// - parameter selector: A selector that identifies a message.
196     ///
197     /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
198     open override func responds(to selector: Selector) -> Bool {
199         #if !os(macOS)
200             if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
201                 return sessionDidFinishEventsForBackgroundURLSession != nil
202             }
203         #endif
204
205         #if !os(watchOS)
206             if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
207                 switch selector {
208                 case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
209                     return streamTaskReadClosed != nil
210                 case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
211                     return streamTaskWriteClosed != nil
212                 case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
213                     return streamTaskBetterRouteDiscovered != nil
214                 case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
215                     return streamTaskDidBecomeInputAndOutputStreams != nil
216                 default:
217                     break
218                 }
219             }
220         #endif
221
222         switch selector {
223         case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
224             return sessionDidBecomeInvalidWithError != nil
225         case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
226             return (sessionDidReceiveChallenge != nil  || sessionDidReceiveChallengeWithCompletion != nil)
227         case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
228             return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
229         case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
230             return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
231         default:
232             return type(of: self).instancesRespond(to: selector)
233         }
234     }
235 }
236
237 // MARK: - URLSessionDelegate
238
239 extension SessionDelegate: URLSessionDelegate {
240     /// Tells the delegate that the session has been invalidated.
241     ///
242     /// - parameter session: The session object that was invalidated.
243     /// - parameter error:   The error that caused invalidation, or nil if the invalidation was explicit.
244     open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
245         sessionDidBecomeInvalidWithError?(session, error)
246     }
247
248     /// Requests credentials from the delegate in response to a session-level authentication request from the
249     /// remote server.
250     ///
251     /// - parameter session:           The session containing the task that requested authentication.
252     /// - parameter challenge:         An object that contains the request for authentication.
253     /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
254     ///                                and credential.
255     open func urlSession(
256         _ session: URLSession,
257         didReceive challenge: URLAuthenticationChallenge,
258         completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
259     {
260         guard sessionDidReceiveChallengeWithCompletion == nil else {
261             sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
262             return
263         }
264
265         var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
266         var credential: URLCredential?
267
268         if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
269             (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
270         } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
271             let host = challenge.protectionSpace.host
272
273             if
274                 let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
275                 let serverTrust = challenge.protectionSpace.serverTrust
276             {
277                 if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
278                     disposition = .useCredential
279                     credential = URLCredential(trust: serverTrust)
280                 } else {
281                     disposition = .cancelAuthenticationChallenge
282                 }
283             }
284         }
285
286         completionHandler(disposition, credential)
287     }
288
289 #if !os(macOS)
290
291     /// Tells the delegate that all messages enqueued for a session have been delivered.
292     ///
293     /// - parameter session: The session that no longer has any outstanding requests.
294     open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
295         sessionDidFinishEventsForBackgroundURLSession?(session)
296     }
297
298 #endif
299 }
300
301 // MARK: - URLSessionTaskDelegate
302
303 extension SessionDelegate: URLSessionTaskDelegate {
304     /// Tells the delegate that the remote server requested an HTTP redirect.
305     ///
306     /// - parameter session:           The session containing the task whose request resulted in a redirect.
307     /// - parameter task:              The task whose request resulted in a redirect.
308     /// - parameter response:          An object containing the server’s response to the original request.
309     /// - parameter request:           A URL request object filled out with the new location.
310     /// - parameter completionHandler: A closure that your handler should call with either the value of the request
311     ///                                parameter, a modified URL request object, or NULL to refuse the redirect and
312     ///                                return the body of the redirect response.
313     open func urlSession(
314         _ session: URLSession,
315         task: URLSessionTask,
316         willPerformHTTPRedirection response: HTTPURLResponse,
317         newRequest request: URLRequest,
318         completionHandler: @escaping (URLRequest?) -> Void)
319     {
320         guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
321             taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
322             return
323         }
324
325         var redirectRequest: URLRequest? = request
326
327         if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
328             redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
329         }
330
331         completionHandler(redirectRequest)
332     }
333
334     /// Requests credentials from the delegate in response to an authentication request from the remote server.
335     ///
336     /// - parameter session:           The session containing the task whose request requires authentication.
337     /// - parameter task:              The task whose request requires authentication.
338     /// - parameter challenge:         An object that contains the request for authentication.
339     /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
340     ///                                and credential.
341     open func urlSession(
342         _ session: URLSession,
343         task: URLSessionTask,
344         didReceive challenge: URLAuthenticationChallenge,
345         completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
346     {
347         guard taskDidReceiveChallengeWithCompletion == nil else {
348             taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
349             return
350         }
351
352         if let taskDidReceiveChallenge = taskDidReceiveChallenge {
353             let result = taskDidReceiveChallenge(session, task, challenge)
354             completionHandler(result.0, result.1)
355         } else if let delegate = self[task]?.delegate {
356             delegate.urlSession(
357                 session,
358                 task: task,
359                 didReceive: challenge,
360                 completionHandler: completionHandler
361             )
362         } else {
363             urlSession(session, didReceive: challenge, completionHandler: completionHandler)
364         }
365     }
366
367     /// Tells the delegate when a task requires a new request body stream to send to the remote server.
368     ///
369     /// - parameter session:           The session containing the task that needs a new body stream.
370     /// - parameter task:              The task that needs a new body stream.
371     /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
372     open func urlSession(
373         _ session: URLSession,
374         task: URLSessionTask,
375         needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
376     {
377         guard taskNeedNewBodyStreamWithCompletion == nil else {
378             taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
379             return
380         }
381
382         if let taskNeedNewBodyStream = taskNeedNewBodyStream {
383             completionHandler(taskNeedNewBodyStream(session, task))
384         } else if let delegate = self[task]?.delegate {
385             delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
386         }
387     }
388
389     /// Periodically informs the delegate of the progress of sending body content to the server.
390     ///
391     /// - parameter session:                  The session containing the data task.
392     /// - parameter task:                     The data task.
393     /// - parameter bytesSent:                The number of bytes sent since the last time this delegate method was called.
394     /// - parameter totalBytesSent:           The total number of bytes sent so far.
395     /// - parameter totalBytesExpectedToSend: The expected length of the body data.
396     open func urlSession(
397         _ session: URLSession,
398         task: URLSessionTask,
399         didSendBodyData bytesSent: Int64,
400         totalBytesSent: Int64,
401         totalBytesExpectedToSend: Int64)
402     {
403         if let taskDidSendBodyData = taskDidSendBodyData {
404             taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
405         } else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
406             delegate.URLSession(
407                 session,
408                 task: task,
409                 didSendBodyData: bytesSent,
410                 totalBytesSent: totalBytesSent,
411                 totalBytesExpectedToSend: totalBytesExpectedToSend
412             )
413         }
414     }
415
416 #if !os(watchOS)
417
418     /// Tells the delegate that the session finished collecting metrics for the task.
419     ///
420     /// - parameter session: The session collecting the metrics.
421     /// - parameter task:    The task whose metrics have been collected.
422     /// - parameter metrics: The collected metrics.
423     @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
424     @objc(URLSession:task:didFinishCollectingMetrics:)
425     open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
426         self[task]?.delegate.metrics = metrics
427     }
428
429 #endif
430
431     /// Tells the delegate that the task finished transferring data.
432     ///
433     /// - parameter session: The session containing the task whose request finished transferring data.
434     /// - parameter task:    The task whose request finished transferring data.
435     /// - parameter error:   If an error occurred, an error object indicating how the transfer failed, otherwise nil.
436     open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
437         /// Executed after it is determined that the request is not going to be retried
438         let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
439             guard let strongSelf = self else { return }
440
441             strongSelf.taskDidComplete?(session, task, error)
442
443             strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
444
445             NotificationCenter.default.post(
446                 name: Notification.Name.Task.DidComplete,
447                 object: strongSelf,
448                 userInfo: [Notification.Key.Task: task]
449             )
450
451             strongSelf[task] = nil
452         }
453
454         guard let request = self[task], let sessionManager = sessionManager else {
455             completeTask(session, task, error)
456             return
457         }
458
459         // Run all validations on the request before checking if an error occurred
460         request.validations.forEach { $0() }
461
462         // Determine whether an error has occurred
463         var error: Error? = error
464
465         if request.delegate.error != nil {
466             error = request.delegate.error
467         }
468
469         /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
470         /// should be retried. Otherwise, complete the task by notifying the task delegate.
471         if let retrier = retrier, let error = error {
472             retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
473                 guard shouldRetry else { completeTask(session, task, error) ; return }
474
475                 DispatchQueue.utility.after(timeDelay) { [weak self] in
476                     guard let strongSelf = self else { return }
477
478                     let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
479
480                     if retrySucceeded, let task = request.task {
481                         strongSelf[task] = request
482                         return
483                     } else {
484                         completeTask(session, task, error)
485                     }
486                 }
487             }
488         } else {
489             completeTask(session, task, error)
490         }
491     }
492 }
493
494 // MARK: - URLSessionDataDelegate
495
496 extension SessionDelegate: URLSessionDataDelegate {
497     /// Tells the delegate that the data task received the initial reply (headers) from the server.
498     ///
499     /// - parameter session:           The session containing the data task that received an initial reply.
500     /// - parameter dataTask:          The data task that received an initial reply.
501     /// - parameter response:          A URL response object populated with headers.
502     /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
503     ///                                constant to indicate whether the transfer should continue as a data task or
504     ///                                should become a download task.
505     open func urlSession(
506         _ session: URLSession,
507         dataTask: URLSessionDataTask,
508         didReceive response: URLResponse,
509         completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
510     {
511         guard dataTaskDidReceiveResponseWithCompletion == nil else {
512             dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
513             return
514         }
515
516         var disposition: URLSession.ResponseDisposition = .allow
517
518         if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
519             disposition = dataTaskDidReceiveResponse(session, dataTask, response)
520         }
521
522         completionHandler(disposition)
523     }
524
525     /// Tells the delegate that the data task was changed to a download task.
526     ///
527     /// - parameter session:      The session containing the task that was replaced by a download task.
528     /// - parameter dataTask:     The data task that was replaced by a download task.
529     /// - parameter downloadTask: The new download task that replaced the data task.
530     open func urlSession(
531         _ session: URLSession,
532         dataTask: URLSessionDataTask,
533         didBecome downloadTask: URLSessionDownloadTask)
534     {
535         if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
536             dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
537         } else {
538             self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
539         }
540     }
541
542     /// Tells the delegate that the data task has received some of the expected data.
543     ///
544     /// - parameter session:  The session containing the data task that provided data.
545     /// - parameter dataTask: The data task that provided data.
546     /// - parameter data:     A data object containing the transferred data.
547     open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
548         if let dataTaskDidReceiveData = dataTaskDidReceiveData {
549             dataTaskDidReceiveData(session, dataTask, data)
550         } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
551             delegate.urlSession(session, dataTask: dataTask, didReceive: data)
552         }
553     }
554
555     /// Asks the delegate whether the data (or upload) task should store the response in the cache.
556     ///
557     /// - parameter session:           The session containing the data (or upload) task.
558     /// - parameter dataTask:          The data (or upload) task.
559     /// - parameter proposedResponse:  The default caching behavior. This behavior is determined based on the current
560     ///                                caching policy and the values of certain received headers, such as the Pragma
561     ///                                and Cache-Control headers.
562     /// - parameter completionHandler: A block that your handler must call, providing either the original proposed
563     ///                                response, a modified version of that response, or NULL to prevent caching the
564     ///                                response. If your delegate implements this method, it must call this completion
565     ///                                handler; otherwise, your app leaks memory.
566     open func urlSession(
567         _ session: URLSession,
568         dataTask: URLSessionDataTask,
569         willCacheResponse proposedResponse: CachedURLResponse,
570         completionHandler: @escaping (CachedURLResponse?) -> Void)
571     {
572         guard dataTaskWillCacheResponseWithCompletion == nil else {
573             dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
574             return
575         }
576
577         if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
578             completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
579         } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
580             delegate.urlSession(
581                 session,
582                 dataTask: dataTask,
583                 willCacheResponse: proposedResponse,
584                 completionHandler: completionHandler
585             )
586         } else {
587             completionHandler(proposedResponse)
588         }
589     }
590 }
591
592 // MARK: - URLSessionDownloadDelegate
593
594 extension SessionDelegate: URLSessionDownloadDelegate {
595     /// Tells the delegate that a download task has finished downloading.
596     ///
597     /// - parameter session:      The session containing the download task that finished.
598     /// - parameter downloadTask: The download task that finished.
599     /// - parameter location:     A file URL for the temporary file. Because the file is temporary, you must either
600     ///                           open the file for reading or move it to a permanent location in your app’s sandbox
601     ///                           container directory before returning from this delegate method.
602     open func urlSession(
603         _ session: URLSession,
604         downloadTask: URLSessionDownloadTask,
605         didFinishDownloadingTo location: URL)
606     {
607         if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
608             downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
609         } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
610             delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
611         }
612     }
613
614     /// Periodically informs the delegate about the download’s progress.
615     ///
616     /// - parameter session:                   The session containing the download task.
617     /// - parameter downloadTask:              The download task.
618     /// - parameter bytesWritten:              The number of bytes transferred since the last time this delegate
619     ///                                        method was called.
620     /// - parameter totalBytesWritten:         The total number of bytes transferred so far.
621     /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
622     ///                                        header. If this header was not provided, the value is
623     ///                                        `NSURLSessionTransferSizeUnknown`.
624     open func urlSession(
625         _ session: URLSession,
626         downloadTask: URLSessionDownloadTask,
627         didWriteData bytesWritten: Int64,
628         totalBytesWritten: Int64,
629         totalBytesExpectedToWrite: Int64)
630     {
631         if let downloadTaskDidWriteData = downloadTaskDidWriteData {
632             downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
633         } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
634             delegate.urlSession(
635                 session,
636                 downloadTask: downloadTask,
637                 didWriteData: bytesWritten,
638                 totalBytesWritten: totalBytesWritten,
639                 totalBytesExpectedToWrite: totalBytesExpectedToWrite
640             )
641         }
642     }
643
644     /// Tells the delegate that the download task has resumed downloading.
645     ///
646     /// - parameter session:            The session containing the download task that finished.
647     /// - parameter downloadTask:       The download task that resumed. See explanation in the discussion.
648     /// - parameter fileOffset:         If the file's cache policy or last modified date prevents reuse of the
649     ///                                 existing content, then this value is zero. Otherwise, this value is an
650     ///                                 integer representing the number of bytes on disk that do not need to be
651     ///                                 retrieved again.
652     /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
653     ///                                 If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
654     open func urlSession(
655         _ session: URLSession,
656         downloadTask: URLSessionDownloadTask,
657         didResumeAtOffset fileOffset: Int64,
658         expectedTotalBytes: Int64)
659     {
660         if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
661             downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
662         } else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
663             delegate.urlSession(
664                 session,
665                 downloadTask: downloadTask,
666                 didResumeAtOffset: fileOffset,
667                 expectedTotalBytes: expectedTotalBytes
668             )
669         }
670     }
671 }
672
673 // MARK: - URLSessionStreamDelegate
674
675 #if !os(watchOS)
676
677 @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
678 extension SessionDelegate: URLSessionStreamDelegate {
679     /// Tells the delegate that the read side of the connection has been closed.
680     ///
681     /// - parameter session:    The session.
682     /// - parameter streamTask: The stream task.
683     open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
684         streamTaskReadClosed?(session, streamTask)
685     }
686
687     /// Tells the delegate that the write side of the connection has been closed.
688     ///
689     /// - parameter session:    The session.
690     /// - parameter streamTask: The stream task.
691     open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
692         streamTaskWriteClosed?(session, streamTask)
693     }
694
695     /// Tells the delegate that the system has determined that a better route to the host is available.
696     ///
697     /// - parameter session:    The session.
698     /// - parameter streamTask: The stream task.
699     open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
700         streamTaskBetterRouteDiscovered?(session, streamTask)
701     }
702
703     /// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
704     ///
705     /// - parameter session:      The session.
706     /// - parameter streamTask:   The stream task.
707     /// - parameter inputStream:  The new input stream.
708     /// - parameter outputStream: The new output stream.
709     open func urlSession(
710         _ session: URLSession,
711         streamTask: URLSessionStreamTask,
712         didBecome inputStream: InputStream,
713         outputStream: OutputStream)
714     {
715         streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
716     }
717 }
718
719 #endif