TaskDelegate.swift 13 KB

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