TaskDelegate.swift 16 KB

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