TaskDelegate.swift 15 KB

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