TaskDelegate.swift 15 KB

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