TaskDelegate.swift 17 KB

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