TaskDelegate.swift 13 KB

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