added iOS source code
[wl-app.git] / iOS / Pods / Alamofire / Source / TaskDelegate.swift
1 //
2 //  TaskDelegate.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 /// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as
28 /// executing all operations attached to the serial operation queue upon task completion.
29 open class TaskDelegate: NSObject {
30
31     // MARK: Properties
32
33     /// The serial operation queue used to execute all operations after the task completes.
34     open let queue: OperationQueue
35
36     /// The data returned by the server.
37     public var data: Data? { return nil }
38
39     /// The error generated throughout the lifecyle of the task.
40     public var error: Error?
41
42     var task: URLSessionTask? {
43         set {
44             taskLock.lock(); defer { taskLock.unlock() }
45             _task = newValue
46         }
47         get {
48             taskLock.lock(); defer { taskLock.unlock() }
49             return _task
50         }
51     }
52
53     var initialResponseTime: CFAbsoluteTime?
54     var credential: URLCredential?
55     var metrics: AnyObject? // URLSessionTaskMetrics
56
57     private var _task: URLSessionTask? {
58         didSet { reset() }
59     }
60
61     private let taskLock = NSLock()
62
63     // MARK: Lifecycle
64
65     init(task: URLSessionTask?) {
66         _task = task
67
68         self.queue = {
69             let operationQueue = OperationQueue()
70
71             operationQueue.maxConcurrentOperationCount = 1
72             operationQueue.isSuspended = true
73             operationQueue.qualityOfService = .utility
74
75             return operationQueue
76         }()
77     }
78
79     func reset() {
80         error = nil
81         initialResponseTime = nil
82     }
83
84     // MARK: URLSessionTaskDelegate
85
86     var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
87     var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
88     var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
89     var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?
90
91     @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
92     func urlSession(
93         _ session: URLSession,
94         task: URLSessionTask,
95         willPerformHTTPRedirection response: HTTPURLResponse,
96         newRequest request: URLRequest,
97         completionHandler: @escaping (URLRequest?) -> Void)
98     {
99         var redirectRequest: URLRequest? = request
100
101         if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
102             redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
103         }
104
105         completionHandler(redirectRequest)
106     }
107
108     @objc(URLSession:task:didReceiveChallenge:completionHandler:)
109     func urlSession(
110         _ session: URLSession,
111         task: URLSessionTask,
112         didReceive challenge: URLAuthenticationChallenge,
113         completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
114     {
115         var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
116         var credential: URLCredential?
117
118         if let taskDidReceiveChallenge = taskDidReceiveChallenge {
119             (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
120         } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
121             let host = challenge.protectionSpace.host
122
123             if
124                 let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
125                 let serverTrust = challenge.protectionSpace.serverTrust
126             {
127                 if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
128                     disposition = .useCredential
129                     credential = URLCredential(trust: serverTrust)
130                 } else {
131                     disposition = .cancelAuthenticationChallenge
132                 }
133             }
134         } else {
135             if challenge.previousFailureCount > 0 {
136                 disposition = .rejectProtectionSpace
137             } else {
138                 credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
139
140                 if credential != nil {
141                     disposition = .useCredential
142                 }
143             }
144         }
145
146         completionHandler(disposition, credential)
147     }
148
149     @objc(URLSession:task:needNewBodyStream:)
150     func urlSession(
151         _ session: URLSession,
152         task: URLSessionTask,
153         needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
154     {
155         var bodyStream: InputStream?
156
157         if let taskNeedNewBodyStream = taskNeedNewBodyStream {
158             bodyStream = taskNeedNewBodyStream(session, task)
159         }
160
161         completionHandler(bodyStream)
162     }
163
164     @objc(URLSession:task:didCompleteWithError:)
165     func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
166         if let taskDidCompleteWithError = taskDidCompleteWithError {
167             taskDidCompleteWithError(session, task, error)
168         } else {
169             if let error = error {
170                 if self.error == nil { self.error = error }
171
172                 if
173                     let downloadDelegate = self as? DownloadTaskDelegate,
174                     let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
175                 {
176                     downloadDelegate.resumeData = resumeData
177                 }
178             }
179
180             queue.isSuspended = false
181         }
182     }
183 }
184
185 // MARK: -
186
187 class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate {
188
189     // MARK: Properties
190
191     var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
192
193     override var data: Data? {
194         if dataStream != nil {
195             return nil
196         } else {
197             return mutableData
198         }
199     }
200
201     var progress: Progress
202     var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
203
204     var dataStream: ((_ data: Data) -> Void)?
205
206     private var totalBytesReceived: Int64 = 0
207     private var mutableData: Data
208
209     private var expectedContentLength: Int64?
210
211     // MARK: Lifecycle
212
213     override init(task: URLSessionTask?) {
214         mutableData = Data()
215         progress = Progress(totalUnitCount: 0)
216
217         super.init(task: task)
218     }
219
220     override func reset() {
221         super.reset()
222
223         progress = Progress(totalUnitCount: 0)
224         totalBytesReceived = 0
225         mutableData = Data()
226         expectedContentLength = nil
227     }
228
229     // MARK: URLSessionDataDelegate
230
231     var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
232     var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
233     var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
234     var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
235
236     func urlSession(
237         _ session: URLSession,
238         dataTask: URLSessionDataTask,
239         didReceive response: URLResponse,
240         completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
241     {
242         var disposition: URLSession.ResponseDisposition = .allow
243
244         expectedContentLength = response.expectedContentLength
245
246         if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
247             disposition = dataTaskDidReceiveResponse(session, dataTask, response)
248         }
249
250         completionHandler(disposition)
251     }
252
253     func urlSession(
254         _ session: URLSession,
255         dataTask: URLSessionDataTask,
256         didBecome downloadTask: URLSessionDownloadTask)
257     {
258         dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
259     }
260
261     func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
262         if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
263
264         if let dataTaskDidReceiveData = dataTaskDidReceiveData {
265             dataTaskDidReceiveData(session, dataTask, data)
266         } else {
267             if let dataStream = dataStream {
268                 dataStream(data)
269             } else {
270                 mutableData.append(data)
271             }
272
273             let bytesReceived = Int64(data.count)
274             totalBytesReceived += bytesReceived
275             let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
276
277             progress.totalUnitCount = totalBytesExpected
278             progress.completedUnitCount = totalBytesReceived
279
280             if let progressHandler = progressHandler {
281                 progressHandler.queue.async { progressHandler.closure(self.progress) }
282             }
283         }
284     }
285
286     func urlSession(
287         _ session: URLSession,
288         dataTask: URLSessionDataTask,
289         willCacheResponse proposedResponse: CachedURLResponse,
290         completionHandler: @escaping (CachedURLResponse?) -> Void)
291     {
292         var cachedResponse: CachedURLResponse? = proposedResponse
293
294         if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
295             cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
296         }
297
298         completionHandler(cachedResponse)
299     }
300 }
301
302 // MARK: -
303
304 class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate {
305
306     // MARK: Properties
307
308     var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask }
309
310     var progress: Progress
311     var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
312
313     var resumeData: Data?
314     override var data: Data? { return resumeData }
315
316     var destination: DownloadRequest.DownloadFileDestination?
317
318     var temporaryURL: URL?
319     var destinationURL: URL?
320
321     var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL }
322
323     // MARK: Lifecycle
324
325     override init(task: URLSessionTask?) {
326         progress = Progress(totalUnitCount: 0)
327         super.init(task: task)
328     }
329
330     override func reset() {
331         super.reset()
332
333         progress = Progress(totalUnitCount: 0)
334         resumeData = nil
335     }
336
337     // MARK: URLSessionDownloadDelegate
338
339     var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
340     var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
341     var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
342
343     func urlSession(
344         _ session: URLSession,
345         downloadTask: URLSessionDownloadTask,
346         didFinishDownloadingTo location: URL)
347     {
348         temporaryURL = location
349
350         guard
351             let destination = destination,
352             let response = downloadTask.response as? HTTPURLResponse
353         else { return }
354
355         let result = destination(location, response)
356         let destinationURL = result.destinationURL
357         let options = result.options
358
359         self.destinationURL = destinationURL
360
361         do {
362             if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
363                 try FileManager.default.removeItem(at: destinationURL)
364             }
365
366             if options.contains(.createIntermediateDirectories) {
367                 let directory = destinationURL.deletingLastPathComponent()
368                 try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
369             }
370
371             try FileManager.default.moveItem(at: location, to: destinationURL)
372         } catch {
373             self.error = error
374         }
375     }
376
377     func urlSession(
378         _ session: URLSession,
379         downloadTask: URLSessionDownloadTask,
380         didWriteData bytesWritten: Int64,
381         totalBytesWritten: Int64,
382         totalBytesExpectedToWrite: Int64)
383     {
384         if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
385
386         if let downloadTaskDidWriteData = downloadTaskDidWriteData {
387             downloadTaskDidWriteData(
388                 session,
389                 downloadTask,
390                 bytesWritten,
391                 totalBytesWritten,
392                 totalBytesExpectedToWrite
393             )
394         } else {
395             progress.totalUnitCount = totalBytesExpectedToWrite
396             progress.completedUnitCount = totalBytesWritten
397
398             if let progressHandler = progressHandler {
399                 progressHandler.queue.async { progressHandler.closure(self.progress) }
400             }
401         }
402     }
403
404     func urlSession(
405         _ session: URLSession,
406         downloadTask: URLSessionDownloadTask,
407         didResumeAtOffset fileOffset: Int64,
408         expectedTotalBytes: Int64)
409     {
410         if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
411             downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
412         } else {
413             progress.totalUnitCount = expectedTotalBytes
414             progress.completedUnitCount = fileOffset
415         }
416     }
417 }
418
419 // MARK: -
420
421 class UploadTaskDelegate: DataTaskDelegate {
422
423     // MARK: Properties
424
425     var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask }
426
427     var uploadProgress: Progress
428     var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
429
430     // MARK: Lifecycle
431
432     override init(task: URLSessionTask?) {
433         uploadProgress = Progress(totalUnitCount: 0)
434         super.init(task: task)
435     }
436
437     override func reset() {
438         super.reset()
439         uploadProgress = Progress(totalUnitCount: 0)
440     }
441
442     // MARK: URLSessionTaskDelegate
443
444     var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
445
446     func URLSession(
447         _ session: URLSession,
448         task: URLSessionTask,
449         didSendBodyData bytesSent: Int64,
450         totalBytesSent: Int64,
451         totalBytesExpectedToSend: Int64)
452     {
453         if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
454
455         if let taskDidSendBodyData = taskDidSendBodyData {
456             taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
457         } else {
458             uploadProgress.totalUnitCount = totalBytesExpectedToSend
459             uploadProgress.completedUnitCount = totalBytesSent
460
461             if let uploadProgressHandler = uploadProgressHandler {
462                 uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
463             }
464         }
465     }
466 }