TaskDelegate.swift 15 KB

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