Request.swift 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. //
  2. // Request.swift
  3. //
  4. // Copyright (c) 2014-2018 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. public protocol RequestDelegate: AnyObject {
  26. var sessionConfiguration: URLSessionConfiguration { get }
  27. func isRetryingRequest(_ request: Request, ifNecessaryWithError error: Error) -> Bool
  28. func cancelRequest(_ request: Request)
  29. func cancelDownloadRequest(_ request: DownloadRequest, byProducingResumeData: @escaping (Data?) -> Void)
  30. func suspendRequest(_ request: Request)
  31. func resumeRequest(_ request: Request)
  32. }
  33. open class Request {
  34. /// A closure executed when monitoring upload or download progress of a request.
  35. public typealias ProgressHandler = (Progress) -> Void
  36. // TODO: Make publicly readable properties protected?
  37. public enum State {
  38. case initialized, resumed, suspended, cancelled
  39. func canTransitionTo(_ state: State) -> Bool {
  40. switch (self, state) {
  41. case (.initialized, _): return true
  42. case (_, .initialized), (.cancelled, _): return false
  43. case (.resumed, .cancelled), (.suspended, .cancelled),
  44. (.resumed, .suspended), (.suspended, .resumed): return true
  45. case (.suspended, .suspended), (.resumed, .resumed): return false
  46. }
  47. }
  48. }
  49. // MARK: - Initial State
  50. public let id: UUID
  51. public let underlyingQueue: DispatchQueue
  52. public let serializationQueue: DispatchQueue
  53. public let eventMonitor: EventMonitor?
  54. public weak var delegate: RequestDelegate?
  55. let internalQueue: OperationQueue
  56. // MARK: - Updated State
  57. private var protectedState: Protector<State> = Protector(.initialized)
  58. public fileprivate(set) var state: State {
  59. get { return protectedState.directValue }
  60. set { protectedState.directValue = newValue }
  61. }
  62. public var isCancelled: Bool { return state == .cancelled }
  63. public var isResumed: Bool { return state == .resumed }
  64. public var isSuspended: Bool { return state == .suspended }
  65. public var isInitialized: Bool { return state == .initialized }
  66. // Progress
  67. public let uploadProgress = Progress()
  68. public let downloadProgress = Progress()
  69. fileprivate var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
  70. fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
  71. // Requests
  72. private var protectedRequests = Protector<[URLRequest]>([])
  73. public var allRequests: [URLRequest] { return protectedRequests.directValue }
  74. public var firstRequest: URLRequest? { return allRequests.first }
  75. public var lastRequest: URLRequest? { return allRequests.last }
  76. public var request: URLRequest? { return lastRequest }
  77. // Metrics
  78. private var protectedMetrics = Protector<[URLSessionTaskMetrics]>([])
  79. public var allMetrics: [URLSessionTaskMetrics] { return protectedMetrics.directValue }
  80. public var firstMetrics: URLSessionTaskMetrics? { return allMetrics.first }
  81. public var lastMetrics: URLSessionTaskMetrics? { return allMetrics.last }
  82. public var metrics: URLSessionTaskMetrics? { return lastMetrics }
  83. // Tasks
  84. private var protectedTasks = Protector<[URLSessionTask]>([])
  85. public var tasks: [URLSessionTask] { return protectedTasks.directValue }
  86. public var initialTask: URLSessionTask? { return tasks.first }
  87. public var finalTask: URLSessionTask? { return tasks.last }
  88. public var task: URLSessionTask? { return finalTask }
  89. public var performedRequests: [URLRequest] { return protectedTasks.read { $0.compactMap { $0.currentRequest } } }
  90. public var retryCount: Int { return protectedTasks.read { max($0.count - 1, 0) } }
  91. open var response: HTTPURLResponse? {
  92. return finalTask?.response as? HTTPURLResponse
  93. }
  94. fileprivate(set) public var error: Error?
  95. private(set) var credential: URLCredential?
  96. fileprivate(set) var validators: [() -> Void] = []
  97. public init(id: UUID = UUID(),
  98. underlyingQueue: DispatchQueue,
  99. serializationQueue: DispatchQueue? = nil,
  100. eventMonitor: EventMonitor?,
  101. delegate: RequestDelegate) {
  102. self.id = id
  103. self.underlyingQueue = underlyingQueue
  104. self.serializationQueue = serializationQueue ?? underlyingQueue
  105. internalQueue = OperationQueue(maxConcurrentOperationCount: 1,
  106. underlyingQueue: underlyingQueue,
  107. name: "org.alamofire.request-\(id)",
  108. startSuspended: true)
  109. self.eventMonitor = eventMonitor
  110. self.delegate = delegate
  111. }
  112. // MARK: - Internal API
  113. // Called from internal queue.
  114. func didCreateURLRequest(_ request: URLRequest) {
  115. protectedRequests.append(request)
  116. eventMonitor?.request(self, didCreateURLRequest: request)
  117. }
  118. func didFailToCreateURLRequest(with error: Error) {
  119. self.error = error
  120. eventMonitor?.request(self, didFailToCreateURLRequestWithError: error)
  121. retryOrFinish(error: error)
  122. }
  123. func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) {
  124. protectedRequests.append(adaptedRequest)
  125. eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest)
  126. }
  127. func didFailToAdaptURLRequest(_ request: URLRequest, withError error: Error) {
  128. self.error = error
  129. eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error)
  130. retryOrFinish(error: error)
  131. }
  132. func didCreateTask(_ task: URLSessionTask) {
  133. protectedTasks.append(task)
  134. reset()
  135. eventMonitor?.request(self, didCreateTask: task)
  136. }
  137. func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
  138. uploadProgress.totalUnitCount = totalBytesExpectedToSend
  139. uploadProgress.completedUnitCount = totalBytesSent
  140. uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) }
  141. }
  142. // Resets task related state
  143. func reset() {
  144. error = nil
  145. uploadProgress.totalUnitCount = 0
  146. uploadProgress.completedUnitCount = 0
  147. downloadProgress.totalUnitCount = 0
  148. downloadProgress.completedUnitCount = 0
  149. }
  150. func didResume() {
  151. eventMonitor?.requestDidResume(self)
  152. }
  153. func didSuspend() {
  154. eventMonitor?.requestDidSuspend(self)
  155. }
  156. func didCancel() {
  157. error = AFError.explicitlyCancelled
  158. eventMonitor?.requestDidCancel(self)
  159. }
  160. func didGatherMetrics(_ metrics: URLSessionTaskMetrics) {
  161. protectedMetrics.append(metrics)
  162. eventMonitor?.request(self, didGatherMetrics: metrics)
  163. }
  164. // Should only be triggered by internal AF code, never URLSession
  165. func didFailTask(_ task: URLSessionTask, earlyWithError error: Error) {
  166. self.error = error
  167. // Task will still complete, so didCompleteTask(_:with:) will handle retry.
  168. eventMonitor?.request(self, didFailTask: task, earlyWithError: error)
  169. }
  170. // Completion point for all tasks.
  171. func didCompleteTask(_ task: URLSessionTask, with error: Error?) {
  172. self.error = self.error ?? error
  173. validators.forEach { $0() }
  174. eventMonitor?.request(self, didCompleteTask: task, with: error)
  175. retryOrFinish(error: self.error)
  176. }
  177. func retryOrFinish(error: Error?) {
  178. if let error = error, delegate?.isRetryingRequest(self, ifNecessaryWithError: error) == true {
  179. return
  180. } else {
  181. finish()
  182. }
  183. }
  184. func finish() {
  185. // Start response handlers
  186. internalQueue.isSuspended = false
  187. eventMonitor?.requestDidFinish(self)
  188. }
  189. // MARK: Task Creation
  190. // Subclasses wanting something other than URLSessionDataTask should override.
  191. func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
  192. return session.dataTask(with: request)
  193. }
  194. // MARK: - Public API
  195. // Callable from any queue.
  196. @discardableResult
  197. public func cancel() -> Self {
  198. guard state.canTransitionTo(.cancelled) else { return self }
  199. state = .cancelled
  200. delegate?.cancelRequest(self)
  201. return self
  202. }
  203. @discardableResult
  204. public func suspend() -> Self {
  205. guard state.canTransitionTo(.suspended) else { return self }
  206. state = .suspended
  207. delegate?.suspendRequest(self)
  208. return self
  209. }
  210. @discardableResult
  211. public func resume() -> Self {
  212. guard state.canTransitionTo(.resumed) else { return self }
  213. state = .resumed
  214. delegate?.resumeRequest(self)
  215. return self
  216. }
  217. // MARK: - Closure API
  218. // Callable from any queue
  219. // TODO: Handle race from internal queue?
  220. @discardableResult
  221. open func authenticate(withUsername username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self {
  222. let credential = URLCredential(user: username, password: password, persistence: persistence)
  223. return authenticate(with: credential)
  224. }
  225. @discardableResult
  226. open func authenticate(with credential: URLCredential) -> Self {
  227. self.credential = credential
  228. return self
  229. }
  230. /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
  231. ///
  232. /// - parameter queue: The dispatch queue to execute the closure on.
  233. /// - parameter closure: The code to be executed periodically as data is read from the server.
  234. ///
  235. /// - returns: The request.
  236. @discardableResult
  237. open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
  238. underlyingQueue.async { self.downloadProgressHandler = (closure, queue) }
  239. return self
  240. }
  241. // MARK: Upload Progress
  242. /// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to
  243. /// the server.
  244. ///
  245. /// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress
  246. /// of data being read from the server.
  247. ///
  248. /// - parameter queue: The dispatch queue to execute the closure on.
  249. /// - parameter closure: The code to be executed periodically as data is sent to the server.
  250. ///
  251. /// - returns: The request.
  252. @discardableResult
  253. open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
  254. underlyingQueue.async { self.uploadProgressHandler = (closure, queue) }
  255. return self
  256. }
  257. }
  258. extension Request: Equatable {
  259. public static func == (lhs: Request, rhs: Request) -> Bool {
  260. return lhs.id == rhs.id
  261. }
  262. }
  263. extension Request: Hashable {
  264. public var hashValue: Int {
  265. return id.hashValue
  266. }
  267. }
  268. extension Request: CustomStringConvertible {
  269. public var description: String {
  270. guard let request = performedRequests.last ?? lastRequest,
  271. let url = request.url,
  272. let method = request.httpMethod else { return "No request created yet." }
  273. let requestDescription = "\(method) \(url.absoluteString)"
  274. return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription
  275. }
  276. }
  277. extension Request: CustomDebugStringConvertible {
  278. public var debugDescription: String {
  279. return cURLRepresentation()
  280. }
  281. func cURLRepresentation() -> String {
  282. guard
  283. let request = lastRequest,
  284. let url = request.url,
  285. let host = url.host,
  286. let method = request.httpMethod else { return "$ curl command could not be created" }
  287. var components = ["$ curl -v"]
  288. components.append("-X \(method)")
  289. if let credentialStorage = delegate?.sessionConfiguration.urlCredentialStorage {
  290. let protectionSpace = URLProtectionSpace(
  291. host: host,
  292. port: url.port ?? 0,
  293. protocol: url.scheme,
  294. realm: host,
  295. authenticationMethod: NSURLAuthenticationMethodHTTPBasic
  296. )
  297. if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
  298. for credential in credentials {
  299. guard let user = credential.user, let password = credential.password else { continue }
  300. components.append("-u \(user):\(password)")
  301. }
  302. } else {
  303. if let credential = credential, let user = credential.user, let password = credential.password {
  304. components.append("-u \(user):\(password)")
  305. }
  306. }
  307. }
  308. if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies {
  309. if
  310. let cookieStorage = configuration.httpCookieStorage,
  311. let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty
  312. {
  313. let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";")
  314. components.append("-b \"\(allCookies)\"")
  315. }
  316. }
  317. var headers: [String: String] = [:]
  318. if let additionalHeaders = delegate?.sessionConfiguration.httpAdditionalHeaders as? [String: String] {
  319. for (field, value) in additionalHeaders where field != "Cookie" {
  320. headers[field] = value
  321. }
  322. }
  323. if let headerFields = request.allHTTPHeaderFields {
  324. for (field, value) in headerFields where field != "Cookie" {
  325. headers[field] = value
  326. }
  327. }
  328. for (field, value) in headers {
  329. let escapedValue = value.replacingOccurrences(of: "\"", with: "\\\"")
  330. components.append("-H \"\(field): \(escapedValue)\"")
  331. }
  332. if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
  333. var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
  334. escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
  335. components.append("-d \"\(escapedBody)\"")
  336. }
  337. components.append("\"\(url.absoluteString)\"")
  338. return components.joined(separator: " \\\n\t")
  339. }
  340. }
  341. open class DataRequest: Request {
  342. let convertible: URLRequestConvertible
  343. private(set) var data: Data?
  344. init(id: UUID = UUID(),
  345. convertible: URLRequestConvertible,
  346. underlyingQueue: DispatchQueue,
  347. serializationQueue: DispatchQueue? = nil,
  348. eventMonitor: EventMonitor?,
  349. delegate: RequestDelegate) {
  350. self.convertible = convertible
  351. super.init(id: id,
  352. underlyingQueue: underlyingQueue,
  353. serializationQueue: serializationQueue,
  354. eventMonitor: eventMonitor,
  355. delegate: delegate)
  356. }
  357. override func reset() {
  358. super.reset()
  359. data = nil
  360. }
  361. func didRecieve(data: Data) {
  362. if self.data == nil {
  363. self.data = data
  364. } else {
  365. self.data?.append(data)
  366. }
  367. updateDownloadProgress()
  368. }
  369. func updateDownloadProgress() {
  370. let totalBytesRecieved = Int64(self.data?.count ?? 0)
  371. let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
  372. downloadProgress.totalUnitCount = totalBytesExpected
  373. downloadProgress.completedUnitCount = totalBytesRecieved
  374. downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
  375. }
  376. /// Validates the request, using the specified closure.
  377. ///
  378. /// If validation fails, subsequent calls to response handlers will have an associated error.
  379. ///
  380. /// - parameter validation: A closure to validate the request.
  381. ///
  382. /// - returns: The request.
  383. @discardableResult
  384. public func validate(_ validation: @escaping Validation) -> Self {
  385. underlyingQueue.async {
  386. let validationExecution: () -> Void = { [unowned self] in
  387. guard self.error == nil, let response = self.response else { return }
  388. let result = validation(self.request, response, self.data)
  389. result.withError { self.error = $0 }
  390. self.eventMonitor?.request(self,
  391. didValidateRequest: self.request,
  392. response: response,
  393. data: self.data,
  394. withResult: result)
  395. }
  396. self.validators.append(validationExecution)
  397. }
  398. return self
  399. }
  400. }
  401. open class DownloadRequest: Request {
  402. /// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the
  403. /// destination URL.
  404. public struct Options: OptionSet {
  405. /// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
  406. public static let createIntermediateDirectories = Options(rawValue: 1 << 0)
  407. /// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
  408. public static let removePreviousFile = Options(rawValue: 1 << 1)
  409. /// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
  410. public let rawValue: Int
  411. /// Creates a `DownloadRequest.Options` instance with the specified raw value.
  412. ///
  413. /// - parameter rawValue: The raw bitmask value for the option.
  414. ///
  415. /// - returns: A new `DownloadRequest.Options` instance.
  416. public init(rawValue: Int) {
  417. self.rawValue = rawValue
  418. }
  419. }
  420. /// A closure executed once a download request has successfully completed in order to determine where to move the
  421. /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
  422. /// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and
  423. /// the options defining how the file should be moved.
  424. public typealias Destination = (_ temporaryURL: URL,
  425. _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)
  426. // MARK: Destination
  427. /// Creates a download file destination closure which uses the default file manager to move the temporary file to a
  428. /// file URL in the first available directory with the specified search path directory and search path domain mask.
  429. ///
  430. /// - parameter directory: The search path directory. `.documentDirectory` by default.
  431. /// - parameter domain: The search path domain mask. `.userDomainMask` by default.
  432. ///
  433. /// - returns: A download file destination closure.
  434. open class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory,
  435. in domain: FileManager.SearchPathDomainMask = .userDomainMask,
  436. options: Options = []) -> Destination {
  437. return { (temporaryURL, response) in
  438. let directoryURLs = FileManager.default.urls(for: directory, in: domain)
  439. let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL
  440. return (url, options)
  441. }
  442. }
  443. public enum Downloadable {
  444. case request(URLRequestConvertible)
  445. case resumeData(Data)
  446. }
  447. // MARK: Initial State
  448. let downloadable: Downloadable
  449. private let destination: Destination?
  450. // MARK: Updated State
  451. open internal(set) var resumeData: Data?
  452. open internal(set) var temporaryURL: URL?
  453. open internal(set) var destinationURL: URL?
  454. open var fileURL: URL? {
  455. return destinationURL ?? temporaryURL
  456. }
  457. // MARK: Init
  458. init(id: UUID = UUID(),
  459. downloadable: Downloadable,
  460. underlyingQueue: DispatchQueue,
  461. serializationQueue: DispatchQueue? = nil,
  462. eventMonitor: EventMonitor?,
  463. delegate: RequestDelegate,
  464. destination: Destination? = nil) {
  465. self.downloadable = downloadable
  466. self.destination = destination
  467. super.init(id: id,
  468. underlyingQueue: underlyingQueue,
  469. serializationQueue: serializationQueue,
  470. eventMonitor: eventMonitor,
  471. delegate: delegate)
  472. }
  473. override func reset() {
  474. super.reset()
  475. temporaryURL = nil
  476. destinationURL = nil
  477. resumeData = nil
  478. }
  479. func didComplete(task: URLSessionTask, with url: URL) {
  480. temporaryURL = url
  481. eventMonitor?.request(self, didCompleteTask: task, with: url)
  482. guard let destination = destination, let response = response else { return }
  483. let (destinationURL, options) = destination(url, response)
  484. self.destinationURL = destinationURL
  485. // TODO: Inject FileManager?
  486. do {
  487. if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
  488. try FileManager.default.removeItem(at: destinationURL)
  489. }
  490. if options.contains(.createIntermediateDirectories) {
  491. let directory = destinationURL.deletingLastPathComponent()
  492. try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
  493. }
  494. try FileManager.default.moveItem(at: url, to: destinationURL)
  495. } catch {
  496. self.error = error
  497. }
  498. }
  499. func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
  500. downloadProgress.totalUnitCount = totalBytesExpectedToWrite
  501. downloadProgress.completedUnitCount += bytesWritten
  502. downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
  503. }
  504. override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
  505. return session.downloadTask(with: request)
  506. }
  507. open func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask {
  508. return session.downloadTask(withResumeData: data)
  509. }
  510. @discardableResult
  511. public override func cancel() -> Self {
  512. // TODO: EventMonitor?
  513. guard state.canTransitionTo(.cancelled) else { return self }
  514. state = .cancelled
  515. delegate?.cancelDownloadRequest(self) { self.resumeData = $0 }
  516. eventMonitor?.requestDidCancel(self)
  517. return self
  518. }
  519. /// Validates the request, using the specified closure.
  520. ///
  521. /// If validation fails, subsequent calls to response handlers will have an associated error.
  522. ///
  523. /// - parameter validation: A closure to validate the request.
  524. ///
  525. /// - returns: The request.
  526. @discardableResult
  527. public func validate(_ validation: @escaping Validation) -> Self {
  528. underlyingQueue.async {
  529. let validationExecution: () -> Void = { [unowned self] in
  530. guard self.error == nil, let response = self.response else { return }
  531. let result = validation(self.request, response, self.temporaryURL, self.destinationURL)
  532. result.withError { self.error = $0 }
  533. self.eventMonitor?.request(self,
  534. didValidateRequest: self.request,
  535. response: response,
  536. temporaryURL: self.temporaryURL,
  537. destinationURL: self.destinationURL,
  538. withResult: result)
  539. }
  540. self.validators.append(validationExecution)
  541. }
  542. return self
  543. }
  544. }
  545. open class UploadRequest: DataRequest {
  546. public enum Uploadable {
  547. case data(Data)
  548. case file(URL, shouldRemove: Bool)
  549. case stream(InputStream)
  550. }
  551. // MARK: - Initial State
  552. let upload: UploadableConvertible
  553. // MARK: - Updated State
  554. public var uploadable: Uploadable?
  555. init(id: UUID = UUID(),
  556. convertible: UploadConvertible,
  557. underlyingQueue: DispatchQueue,
  558. serializationQueue: DispatchQueue? = nil,
  559. eventMonitor: EventMonitor?,
  560. delegate: RequestDelegate) {
  561. self.upload = convertible
  562. super.init(id: id,
  563. convertible: convertible,
  564. underlyingQueue: underlyingQueue,
  565. serializationQueue: serializationQueue,
  566. eventMonitor: eventMonitor,
  567. delegate: delegate)
  568. // Automatically remove temporary upload files (e.g. multipart form data)
  569. internalQueue.addOperation {
  570. guard
  571. let uploadable = self.uploadable,
  572. case let .file(url, shouldRemove) = uploadable,
  573. shouldRemove else { return }
  574. // TODO: Abstract file manager
  575. try? FileManager.default.removeItem(at: url)
  576. }
  577. }
  578. func didCreateUploadable(_ uploadable: Uploadable) {
  579. self.uploadable = uploadable
  580. eventMonitor?.request(self, didCreateUploadable: uploadable)
  581. }
  582. func didFailToCreateUploadable(with error: Error) {
  583. self.error = error
  584. eventMonitor?.request(self, didFailToCreateUploadableWithError: error)
  585. retryOrFinish(error: error)
  586. }
  587. override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
  588. guard let uploadable = uploadable else {
  589. fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.")
  590. }
  591. switch uploadable {
  592. case let .data(data): return session.uploadTask(with: request, from: data)
  593. case let .file(url, _): return session.uploadTask(with: request, fromFile: url)
  594. case .stream: return session.uploadTask(withStreamedRequest: request)
  595. }
  596. }
  597. func inputStream() -> InputStream {
  598. guard let uploadable = uploadable else {
  599. fatalError("Attempting to access the input stream but the uploadable doesn't exist.")
  600. }
  601. guard case let .stream(stream) = uploadable else {
  602. fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.")
  603. }
  604. eventMonitor?.request(self, didProvideInputStream: stream)
  605. return stream
  606. }
  607. }
  608. protocol UploadableConvertible {
  609. func createUploadable() throws -> UploadRequest.Uploadable
  610. }
  611. extension UploadRequest.Uploadable: UploadableConvertible {
  612. func createUploadable() throws -> UploadRequest.Uploadable {
  613. return self
  614. }
  615. }
  616. protocol UploadConvertible: UploadableConvertible & URLRequestConvertible { }