Request.swift 24 KB

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