Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  Request.swift
3
//
4
//  Copyright (c) 2014-2020 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
 
25
import Foundation
26
 
27
/// `Request` is the common superclass of all Alamofire request types and provides common state, delegate, and callback
28
/// handling.
29
public class Request {
30
    /// State of the `Request`, with managed transitions between states set when calling `resume()`, `suspend()`, or
31
    /// `cancel()` on the `Request`.
32
    public enum State {
33
        /// Initial state of the `Request`.
34
        case initialized
35
        /// `State` set when `resume()` is called. Any tasks created for the `Request` will have `resume()` called on
36
        /// them in this state.
37
        case resumed
38
        /// `State` set when `suspend()` is called. Any tasks created for the `Request` will have `suspend()` called on
39
        /// them in this state.
40
        case suspended
41
        /// `State` set when `cancel()` is called. Any tasks created for the `Request` will have `cancel()` called on
42
        /// them. Unlike `resumed` or `suspended`, once in the `cancelled` state, the `Request` can no longer transition
43
        /// to any other state.
44
        case cancelled
45
        /// `State` set when all response serialization completion closures have been cleared on the `Request` and
46
        /// enqueued on their respective queues.
47
        case finished
48
 
49
        /// Determines whether `self` can be transitioned to the provided `State`.
50
        func canTransitionTo(_ state: State) -> Bool {
51
            switch (self, state) {
52
            case (.initialized, _):
53
                return true
54
            case (_, .initialized), (.cancelled, _), (.finished, _):
55
                return false
56
            case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed):
57
                return true
58
            case (.suspended, .suspended), (.resumed, .resumed):
59
                return false
60
            case (_, .finished):
61
                return true
62
            }
63
        }
64
    }
65
 
66
    // MARK: - Initial State
67
 
68
    /// `UUID` providing a unique identifier for the `Request`, used in the `Hashable` and `Equatable` conformances.
69
    public let id: UUID
70
    /// The serial queue for all internal async actions.
71
    public let underlyingQueue: DispatchQueue
72
    /// The queue used for all serialization actions. By default it's a serial queue that targets `underlyingQueue`.
73
    public let serializationQueue: DispatchQueue
74
    /// `EventMonitor` used for event callbacks.
75
    public let eventMonitor: EventMonitor?
76
    /// The `Request`'s interceptor.
77
    public let interceptor: RequestInterceptor?
78
    /// The `Request`'s delegate.
79
    public private(set) weak var delegate: RequestDelegate?
80
 
81
    // MARK: - Mutable State
82
 
83
    /// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`.
84
    struct MutableState {
85
        /// State of the `Request`.
86
        var state: State = .initialized
87
        /// `ProgressHandler` and `DispatchQueue` provided for upload progress callbacks.
88
        var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
89
        /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks.
90
        var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
91
        /// `RedirectHandler` provided for to handle request redirection.
92
        var redirectHandler: RedirectHandler?
93
        /// `CachedResponseHandler` provided to handle response caching.
94
        var cachedResponseHandler: CachedResponseHandler?
95
        /// Queue and closure called when the `Request` is able to create a cURL description of itself.
96
        var cURLHandler: (queue: DispatchQueue, handler: (String) -> Void)?
97
        /// Queue and closure called when the `Request` creates a `URLRequest`.
98
        var urlRequestHandler: (queue: DispatchQueue, handler: (URLRequest) -> Void)?
99
        /// Queue and closure called when the `Request` creates a `URLSessionTask`.
100
        var urlSessionTaskHandler: (queue: DispatchQueue, handler: (URLSessionTask) -> Void)?
101
        /// Response serialization closures that handle response parsing.
102
        var responseSerializers: [() -> Void] = []
103
        /// Response serialization completion closures executed once all response serializers are complete.
104
        var responseSerializerCompletions: [() -> Void] = []
105
        /// Whether response serializer processing is finished.
106
        var responseSerializerProcessingFinished = false
107
        /// `URLCredential` used for authentication challenges.
108
        var credential: URLCredential?
109
        /// All `URLRequest`s created by Alamofire on behalf of the `Request`.
110
        var requests: [URLRequest] = []
111
        /// All `URLSessionTask`s created by Alamofire on behalf of the `Request`.
112
        var tasks: [URLSessionTask] = []
113
        /// All `URLSessionTaskMetrics` values gathered by Alamofire on behalf of the `Request`. Should correspond
114
        /// exactly the the `tasks` created.
115
        var metrics: [URLSessionTaskMetrics] = []
116
        /// Number of times any retriers provided retried the `Request`.
117
        var retryCount = 0
118
        /// Final `AFError` for the `Request`, whether from various internal Alamofire calls or as a result of a `task`.
119
        var error: AFError?
120
        /// Whether the instance has had `finish()` called and is running the serializers. Should be replaced with a
121
        /// representation in the state machine in the future.
122
        var isFinishing = false
123
        /// Actions to run when requests are finished. Use for concurrency support.
124
        var finishHandlers: [() -> Void] = []
125
    }
126
 
127
    /// Protected `MutableState` value that provides thread-safe access to state values.
128
    @Protected
129
    fileprivate var mutableState = MutableState()
130
 
131
    /// `State` of the `Request`.
132
    public var state: State { $mutableState.state }
133
    /// Returns whether `state` is `.initialized`.
134
    public var isInitialized: Bool { state == .initialized }
135
    /// Returns whether `state is `.resumed`.
136
    public var isResumed: Bool { state == .resumed }
137
    /// Returns whether `state` is `.suspended`.
138
    public var isSuspended: Bool { state == .suspended }
139
    /// Returns whether `state` is `.cancelled`.
140
    public var isCancelled: Bool { state == .cancelled }
141
    /// Returns whether `state` is `.finished`.
142
    public var isFinished: Bool { state == .finished }
143
 
144
    // MARK: Progress
145
 
146
    /// Closure type executed when monitoring the upload or download progress of a request.
147
    public typealias ProgressHandler = (Progress) -> Void
148
 
149
    /// `Progress` of the upload of the body of the executed `URLRequest`. Reset to `0` if the `Request` is retried.
150
    public let uploadProgress = Progress(totalUnitCount: 0)
151
    /// `Progress` of the download of any response data. Reset to `0` if the `Request` is retried.
152
    public let downloadProgress = Progress(totalUnitCount: 0)
153
    /// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`.
154
    private var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
155
        get { $mutableState.uploadProgressHandler }
156
        set { $mutableState.uploadProgressHandler = newValue }
157
    }
158
 
159
    /// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`.
160
    fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
161
        get { $mutableState.downloadProgressHandler }
162
        set { $mutableState.downloadProgressHandler = newValue }
163
    }
164
 
165
    // MARK: Redirect Handling
166
 
167
    /// `RedirectHandler` set on the instance.
168
    public private(set) var redirectHandler: RedirectHandler? {
169
        get { $mutableState.redirectHandler }
170
        set { $mutableState.redirectHandler = newValue }
171
    }
172
 
173
    // MARK: Cached Response Handling
174
 
175
    /// `CachedResponseHandler` set on the instance.
176
    public private(set) var cachedResponseHandler: CachedResponseHandler? {
177
        get { $mutableState.cachedResponseHandler }
178
        set { $mutableState.cachedResponseHandler = newValue }
179
    }
180
 
181
    // MARK: URLCredential
182
 
183
    /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods.
184
    public private(set) var credential: URLCredential? {
185
        get { $mutableState.credential }
186
        set { $mutableState.credential = newValue }
187
    }
188
 
189
    // MARK: Validators
190
 
191
    /// `Validator` callback closures that store the validation calls enqueued.
192
    @Protected
193
    fileprivate var validators: [() -> Void] = []
194
 
195
    // MARK: URLRequests
196
 
197
    /// All `URLRequests` created on behalf of the `Request`, including original and adapted requests.
198
    public var requests: [URLRequest] { $mutableState.requests }
199
    /// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed.
200
    public var firstRequest: URLRequest? { requests.first }
201
    /// Last `URLRequest` created on behalf of the `Request`.
202
    public var lastRequest: URLRequest? { requests.last }
203
    /// Current `URLRequest` created on behalf of the `Request`.
204
    public var request: URLRequest? { lastRequest }
205
 
206
    /// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from
207
    /// `requests` due to `URLSession` manipulation.
208
    public var performedRequests: [URLRequest] { $mutableState.read { $0.tasks.compactMap(\.currentRequest) } }
209
 
210
    // MARK: HTTPURLResponse
211
 
212
    /// `HTTPURLResponse` received from the server, if any. If the `Request` was retried, this is the response of the
213
    /// last `URLSessionTask`.
214
    public var response: HTTPURLResponse? { lastTask?.response as? HTTPURLResponse }
215
 
216
    // MARK: Tasks
217
 
218
    /// All `URLSessionTask`s created on behalf of the `Request`.
219
    public var tasks: [URLSessionTask] { $mutableState.tasks }
220
    /// First `URLSessionTask` created on behalf of the `Request`.
221
    public var firstTask: URLSessionTask? { tasks.first }
222
    /// Last `URLSessionTask` crated on behalf of the `Request`.
223
    public var lastTask: URLSessionTask? { tasks.last }
224
    /// Current `URLSessionTask` created on behalf of the `Request`.
225
    public var task: URLSessionTask? { lastTask }
226
 
227
    // MARK: Metrics
228
 
229
    /// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created.
230
    public var allMetrics: [URLSessionTaskMetrics] { $mutableState.metrics }
231
    /// First `URLSessionTaskMetrics` gathered on behalf of the `Request`.
232
    public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first }
233
    /// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`.
234
    public var lastMetrics: URLSessionTaskMetrics? { allMetrics.last }
235
    /// Current `URLSessionTaskMetrics` gathered on behalf of the `Request`.
236
    public var metrics: URLSessionTaskMetrics? { lastMetrics }
237
 
238
    // MARK: Retry Count
239
 
240
    /// Number of times the `Request` has been retried.
241
    public var retryCount: Int { $mutableState.retryCount }
242
 
243
    // MARK: Error
244
 
245
    /// `Error` returned from Alamofire internally, from the network request directly, or any validators executed.
246
    public fileprivate(set) var error: AFError? {
247
        get { $mutableState.error }
248
        set { $mutableState.error = newValue }
249
    }
250
 
251
    /// Default initializer for the `Request` superclass.
252
    ///
253
    /// - Parameters:
254
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
255
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
256
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
257
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
258
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
259
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
260
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
261
    init(id: UUID = UUID(),
262
         underlyingQueue: DispatchQueue,
263
         serializationQueue: DispatchQueue,
264
         eventMonitor: EventMonitor?,
265
         interceptor: RequestInterceptor?,
266
         delegate: RequestDelegate) {
267
        self.id = id
268
        self.underlyingQueue = underlyingQueue
269
        self.serializationQueue = serializationQueue
270
        self.eventMonitor = eventMonitor
271
        self.interceptor = interceptor
272
        self.delegate = delegate
273
    }
274
 
275
    // MARK: - Internal Event API
276
 
277
    // All API must be called from underlyingQueue.
278
 
279
    /// Called when an initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active,
280
    /// the `URLRequest` will be adapted before being issued.
281
    ///
282
    /// - Parameter request: The `URLRequest` created.
283
    func didCreateInitialURLRequest(_ request: URLRequest) {
284
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
285
 
286
        $mutableState.write { $0.requests.append(request) }
287
 
288
        eventMonitor?.request(self, didCreateInitialURLRequest: request)
289
    }
290
 
291
    /// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`.
292
    ///
293
    /// - Note: Triggers retry.
294
    ///
295
    /// - Parameter error: `AFError` thrown from the failed creation.
296
    func didFailToCreateURLRequest(with error: AFError) {
297
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
298
 
299
        self.error = error
300
 
301
        eventMonitor?.request(self, didFailToCreateURLRequestWithError: error)
302
 
303
        callCURLHandlerIfNecessary()
304
 
305
        retryOrFinish(error: error)
306
    }
307
 
308
    /// Called when a `RequestAdapter` has successfully adapted a `URLRequest`.
309
    ///
310
    /// - Parameters:
311
    ///   - initialRequest: The `URLRequest` that was adapted.
312
    ///   - adaptedRequest: The `URLRequest` returned by the `RequestAdapter`.
313
    func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) {
314
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
315
 
316
        $mutableState.write { $0.requests.append(adaptedRequest) }
317
 
318
        eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest)
319
    }
320
 
321
    /// Called when a `RequestAdapter` fails to adapt a `URLRequest`.
322
    ///
323
    /// - Note: Triggers retry.
324
    ///
325
    /// - Parameters:
326
    ///   - request: The `URLRequest` the adapter was called with.
327
    ///   - error:   The `AFError` returned by the `RequestAdapter`.
328
    func didFailToAdaptURLRequest(_ request: URLRequest, withError error: AFError) {
329
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
330
 
331
        self.error = error
332
 
333
        eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error)
334
 
335
        callCURLHandlerIfNecessary()
336
 
337
        retryOrFinish(error: error)
338
    }
339
 
340
    /// Final `URLRequest` has been created for the instance.
341
    ///
342
    /// - Parameter request: The `URLRequest` created.
343
    func didCreateURLRequest(_ request: URLRequest) {
344
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
345
 
346
        $mutableState.read { state in
347
            state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) }
348
        }
349
 
350
        eventMonitor?.request(self, didCreateURLRequest: request)
351
 
352
        callCURLHandlerIfNecessary()
353
    }
354
 
355
    /// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`.
356
    private func callCURLHandlerIfNecessary() {
357
        $mutableState.write { mutableState in
358
            guard let cURLHandler = mutableState.cURLHandler else { return }
359
 
360
            cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) }
361
 
362
            mutableState.cURLHandler = nil
363
        }
364
    }
365
 
366
    /// Called when a `URLSessionTask` is created on behalf of the instance.
367
    ///
368
    /// - Parameter task: The `URLSessionTask` created.
369
    func didCreateTask(_ task: URLSessionTask) {
370
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
371
 
372
        $mutableState.write { state in
373
            state.tasks.append(task)
374
 
375
            guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return }
376
 
377
            urlSessionTaskHandler.queue.async { urlSessionTaskHandler.handler(task) }
378
        }
379
 
380
        eventMonitor?.request(self, didCreateTask: task)
381
    }
382
 
383
    /// Called when resumption is completed.
384
    func didResume() {
385
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
386
 
387
        eventMonitor?.requestDidResume(self)
388
    }
389
 
390
    /// Called when a `URLSessionTask` is resumed on behalf of the instance.
391
    ///
392
    /// - Parameter task: The `URLSessionTask` resumed.
393
    func didResumeTask(_ task: URLSessionTask) {
394
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
395
 
396
        eventMonitor?.request(self, didResumeTask: task)
397
    }
398
 
399
    /// Called when suspension is completed.
400
    func didSuspend() {
401
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
402
 
403
        eventMonitor?.requestDidSuspend(self)
404
    }
405
 
406
    /// Called when a `URLSessionTask` is suspended on behalf of the instance.
407
    ///
408
    /// - Parameter task: The `URLSessionTask` suspended.
409
    func didSuspendTask(_ task: URLSessionTask) {
410
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
411
 
412
        eventMonitor?.request(self, didSuspendTask: task)
413
    }
414
 
415
    /// Called when cancellation is completed, sets `error` to `AFError.explicitlyCancelled`.
416
    func didCancel() {
417
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
418
 
419
        error = error ?? AFError.explicitlyCancelled
420
 
421
        eventMonitor?.requestDidCancel(self)
422
    }
423
 
424
    /// Called when a `URLSessionTask` is cancelled on behalf of the instance.
425
    ///
426
    /// - Parameter task: The `URLSessionTask` cancelled.
427
    func didCancelTask(_ task: URLSessionTask) {
428
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
429
 
430
        eventMonitor?.request(self, didCancelTask: task)
431
    }
432
 
433
    /// Called when a `URLSessionTaskMetrics` value is gathered on behalf of the instance.
434
    ///
435
    /// - Parameter metrics: The `URLSessionTaskMetrics` gathered.
436
    func didGatherMetrics(_ metrics: URLSessionTaskMetrics) {
437
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
438
 
439
        $mutableState.write { $0.metrics.append(metrics) }
440
 
441
        eventMonitor?.request(self, didGatherMetrics: metrics)
442
    }
443
 
444
    /// Called when a `URLSessionTask` fails before it is finished, typically during certificate pinning.
445
    ///
446
    /// - Parameters:
447
    ///   - task:  The `URLSessionTask` which failed.
448
    ///   - error: The early failure `AFError`.
449
    func didFailTask(_ task: URLSessionTask, earlyWithError error: AFError) {
450
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
451
 
452
        self.error = error
453
 
454
        // Task will still complete, so didCompleteTask(_:with:) will handle retry.
455
        eventMonitor?.request(self, didFailTask: task, earlyWithError: error)
456
    }
457
 
458
    /// Called when a `URLSessionTask` completes. All tasks will eventually call this method.
459
    ///
460
    /// - Note: Response validation is synchronously triggered in this step.
461
    ///
462
    /// - Parameters:
463
    ///   - task:  The `URLSessionTask` which completed.
464
    ///   - error: The `AFError` `task` may have completed with. If `error` has already been set on the instance, this
465
    ///            value is ignored.
466
    func didCompleteTask(_ task: URLSessionTask, with error: AFError?) {
467
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
468
 
469
        self.error = self.error ?? error
470
 
471
        validators.forEach { $0() }
472
 
473
        eventMonitor?.request(self, didCompleteTask: task, with: error)
474
 
475
        retryOrFinish(error: self.error)
476
    }
477
 
478
    /// Called when the `RequestDelegate` is going to retry this `Request`. Calls `reset()`.
479
    func prepareForRetry() {
480
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
481
 
482
        $mutableState.write { $0.retryCount += 1 }
483
 
484
        reset()
485
 
486
        eventMonitor?.requestIsRetrying(self)
487
    }
488
 
489
    /// Called to determine whether retry will be triggered for the particular error, or whether the instance should
490
    /// call `finish()`.
491
    ///
492
    /// - Parameter error: The possible `AFError` which may trigger retry.
493
    func retryOrFinish(error: AFError?) {
494
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
495
 
496
        guard let error = error, let delegate = delegate else { finish(); return }
497
 
498
        delegate.retryResult(for: self, dueTo: error) { retryResult in
499
            switch retryResult {
500
            case .doNotRetry:
501
                self.finish()
502
            case let .doNotRetryWithError(retryError):
503
                self.finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
504
            case .retry, .retryWithDelay:
505
                delegate.retryRequest(self, withDelay: retryResult.delay)
506
            }
507
        }
508
    }
509
 
510
    /// Finishes this `Request` and starts the response serializers.
511
    ///
512
    /// - Parameter error: The possible `Error` with which the instance will finish.
513
    func finish(error: AFError? = nil) {
514
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
515
 
516
        guard !$mutableState.isFinishing else { return }
517
 
518
        $mutableState.isFinishing = true
519
 
520
        if let error = error { self.error = error }
521
 
522
        // Start response handlers
523
        processNextResponseSerializer()
524
 
525
        eventMonitor?.requestDidFinish(self)
526
    }
527
 
528
    /// Appends the response serialization closure to the instance.
529
    ///
530
    ///  - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`.
531
    ///
532
    /// - Parameter closure: The closure containing the response serialization call.
533
    func appendResponseSerializer(_ closure: @escaping () -> Void) {
534
        $mutableState.write { mutableState in
535
            mutableState.responseSerializers.append(closure)
536
 
537
            if mutableState.state == .finished {
538
                mutableState.state = .resumed
539
            }
540
 
541
            if mutableState.responseSerializerProcessingFinished {
542
                underlyingQueue.async { self.processNextResponseSerializer() }
543
            }
544
 
545
            if mutableState.state.canTransitionTo(.resumed) {
546
                underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } }
547
            }
548
        }
549
    }
550
 
551
    /// Returns the next response serializer closure to execute if there's one left.
552
    ///
553
    /// - Returns: The next response serialization closure, if there is one.
554
    func nextResponseSerializer() -> (() -> Void)? {
555
        var responseSerializer: (() -> Void)?
556
 
557
        $mutableState.write { mutableState in
558
            let responseSerializerIndex = mutableState.responseSerializerCompletions.count
559
 
560
            if responseSerializerIndex < mutableState.responseSerializers.count {
561
                responseSerializer = mutableState.responseSerializers[responseSerializerIndex]
562
            }
563
        }
564
 
565
        return responseSerializer
566
    }
567
 
568
    /// Processes the next response serializer and calls all completions if response serialization is complete.
569
    func processNextResponseSerializer() {
570
        guard let responseSerializer = nextResponseSerializer() else {
571
            // Execute all response serializer completions and clear them
572
            var completions: [() -> Void] = []
573
 
574
            $mutableState.write { mutableState in
575
                completions = mutableState.responseSerializerCompletions
576
 
577
                // Clear out all response serializers and response serializer completions in mutable state since the
578
                // request is complete. It's important to do this prior to calling the completion closures in case
579
                // the completions call back into the request triggering a re-processing of the response serializers.
580
                // An example of how this can happen is by calling cancel inside a response completion closure.
581
                mutableState.responseSerializers.removeAll()
582
                mutableState.responseSerializerCompletions.removeAll()
583
 
584
                if mutableState.state.canTransitionTo(.finished) {
585
                    mutableState.state = .finished
586
                }
587
 
588
                mutableState.responseSerializerProcessingFinished = true
589
                mutableState.isFinishing = false
590
            }
591
 
592
            completions.forEach { $0() }
593
 
594
            // Cleanup the request
595
            cleanup()
596
 
597
            return
598
        }
599
 
600
        serializationQueue.async { responseSerializer() }
601
    }
602
 
603
    /// Notifies the `Request` that the response serializer is complete.
604
    ///
605
    /// - Parameter completion: The completion handler provided with the response serializer, called when all serializers
606
    ///                         are complete.
607
    func responseSerializerDidComplete(completion: @escaping () -> Void) {
608
        $mutableState.write { $0.responseSerializerCompletions.append(completion) }
609
        processNextResponseSerializer()
610
    }
611
 
612
    /// Resets all task and response serializer related state for retry.
613
    func reset() {
614
        error = nil
615
 
616
        uploadProgress.totalUnitCount = 0
617
        uploadProgress.completedUnitCount = 0
618
        downloadProgress.totalUnitCount = 0
619
        downloadProgress.completedUnitCount = 0
620
 
621
        $mutableState.write { state in
622
            state.isFinishing = false
623
            state.responseSerializerCompletions = []
624
        }
625
    }
626
 
627
    /// Called when updating the upload progress.
628
    ///
629
    /// - Parameters:
630
    ///   - totalBytesSent: Total bytes sent so far.
631
    ///   - totalBytesExpectedToSend: Total bytes expected to send.
632
    func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
633
        uploadProgress.totalUnitCount = totalBytesExpectedToSend
634
        uploadProgress.completedUnitCount = totalBytesSent
635
 
636
        uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) }
637
    }
638
 
639
    /// Perform a closure on the current `state` while locked.
640
    ///
641
    /// - Parameter perform: The closure to perform.
642
    func withState(perform: (State) -> Void) {
643
        $mutableState.withState(perform: perform)
644
    }
645
 
646
    // MARK: Task Creation
647
 
648
    /// Called when creating a `URLSessionTask` for this `Request`. Subclasses must override.
649
    ///
650
    /// - Parameters:
651
    ///   - request: `URLRequest` to use to create the `URLSessionTask`.
652
    ///   - session: `URLSession` which creates the `URLSessionTask`.
653
    ///
654
    /// - Returns:   The `URLSessionTask` created.
655
    func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
656
        fatalError("Subclasses must override.")
657
    }
658
 
659
    // MARK: - Public API
660
 
661
    // These APIs are callable from any queue.
662
 
663
    // MARK: State
664
 
665
    /// Cancels the instance. Once cancelled, a `Request` can no longer be resumed or suspended.
666
    ///
667
    /// - Returns: The instance.
668
    @discardableResult
669
    public func cancel() -> Self {
670
        $mutableState.write { mutableState in
671
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
672
 
673
            mutableState.state = .cancelled
674
 
675
            underlyingQueue.async { self.didCancel() }
676
 
677
            guard let task = mutableState.tasks.last, task.state != .completed else {
678
                underlyingQueue.async { self.finish() }
679
                return
680
            }
681
 
682
            // Resume to ensure metrics are gathered.
683
            task.resume()
684
            task.cancel()
685
            underlyingQueue.async { self.didCancelTask(task) }
686
        }
687
 
688
        return self
689
    }
690
 
691
    /// Suspends the instance.
692
    ///
693
    /// - Returns: The instance.
694
    @discardableResult
695
    public func suspend() -> Self {
696
        $mutableState.write { mutableState in
697
            guard mutableState.state.canTransitionTo(.suspended) else { return }
698
 
699
            mutableState.state = .suspended
700
 
701
            underlyingQueue.async { self.didSuspend() }
702
 
703
            guard let task = mutableState.tasks.last, task.state != .completed else { return }
704
 
705
            task.suspend()
706
            underlyingQueue.async { self.didSuspendTask(task) }
707
        }
708
 
709
        return self
710
    }
711
 
712
    /// Resumes the instance.
713
    ///
714
    /// - Returns: The instance.
715
    @discardableResult
716
    public func resume() -> Self {
717
        $mutableState.write { mutableState in
718
            guard mutableState.state.canTransitionTo(.resumed) else { return }
719
 
720
            mutableState.state = .resumed
721
 
722
            underlyingQueue.async { self.didResume() }
723
 
724
            guard let task = mutableState.tasks.last, task.state != .completed else { return }
725
 
726
            task.resume()
727
            underlyingQueue.async { self.didResumeTask(task) }
728
        }
729
 
730
        return self
731
    }
732
 
733
    // MARK: - Closure API
734
 
735
    /// Associates a credential using the provided values with the instance.
736
    ///
737
    /// - Parameters:
738
    ///   - username:    The username.
739
    ///   - password:    The password.
740
    ///   - persistence: The `URLCredential.Persistence` for the created `URLCredential`. `.forSession` by default.
741
    ///
742
    /// - Returns:       The instance.
743
    @discardableResult
744
    public func authenticate(username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self {
745
        let credential = URLCredential(user: username, password: password, persistence: persistence)
746
 
747
        return authenticate(with: credential)
748
    }
749
 
750
    /// Associates the provided credential with the instance.
751
    ///
752
    /// - Parameter credential: The `URLCredential`.
753
    ///
754
    /// - Returns:              The instance.
755
    @discardableResult
756
    public func authenticate(with credential: URLCredential) -> Self {
757
        $mutableState.credential = credential
758
 
759
        return self
760
    }
761
 
762
    /// Sets a closure to be called periodically during the lifecycle of the instance as data is read from the server.
763
    ///
764
    /// - Note: Only the last closure provided is used.
765
    ///
766
    /// - Parameters:
767
    ///   - queue:   The `DispatchQueue` to execute the closure on. `.main` by default.
768
    ///   - closure: The closure to be executed periodically as data is read from the server.
769
    ///
770
    /// - Returns:   The instance.
771
    @discardableResult
772
    public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
773
        $mutableState.downloadProgressHandler = (handler: closure, queue: queue)
774
 
775
        return self
776
    }
777
 
778
    /// Sets a closure to be called periodically during the lifecycle of the instance as data is sent to the server.
779
    ///
780
    /// - Note: Only the last closure provided is used.
781
    ///
782
    /// - Parameters:
783
    ///   - queue:   The `DispatchQueue` to execute the closure on. `.main` by default.
784
    ///   - closure: The closure to be executed periodically as data is sent to the server.
785
    ///
786
    /// - Returns:   The instance.
787
    @discardableResult
788
    public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
789
        $mutableState.uploadProgressHandler = (handler: closure, queue: queue)
790
 
791
        return self
792
    }
793
 
794
    // MARK: Redirects
795
 
796
    /// Sets the redirect handler for the instance which will be used if a redirect response is encountered.
797
    ///
798
    /// - Note: Attempting to set the redirect handler more than once is a logic error and will crash.
799
    ///
800
    /// - Parameter handler: The `RedirectHandler`.
801
    ///
802
    /// - Returns:           The instance.
803
    @discardableResult
804
    public func redirect(using handler: RedirectHandler) -> Self {
805
        $mutableState.write { mutableState in
806
            precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.")
807
            mutableState.redirectHandler = handler
808
        }
809
 
810
        return self
811
    }
812
 
813
    // MARK: Cached Responses
814
 
815
    /// Sets the cached response handler for the `Request` which will be used when attempting to cache a response.
816
    ///
817
    /// - Note: Attempting to set the cache handler more than once is a logic error and will crash.
818
    ///
819
    /// - Parameter handler: The `CachedResponseHandler`.
820
    ///
821
    /// - Returns:           The instance.
822
    @discardableResult
823
    public func cacheResponse(using handler: CachedResponseHandler) -> Self {
824
        $mutableState.write { mutableState in
825
            precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.")
826
            mutableState.cachedResponseHandler = handler
827
        }
828
 
829
        return self
830
    }
831
 
832
    // MARK: - Lifetime APIs
833
 
834
    /// Sets a handler to be called when the cURL description of the request is available.
835
    ///
836
    /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called.
837
    ///
838
    /// - Parameters:
839
    ///   - queue:   `DispatchQueue` on which `handler` will be called.
840
    ///   - handler: Closure to be called when the cURL description is available.
841
    ///
842
    /// - Returns:           The instance.
843
    @discardableResult
844
    public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self {
845
        $mutableState.write { mutableState in
846
            if mutableState.requests.last != nil {
847
                queue.async { handler(self.cURLDescription()) }
848
            } else {
849
                mutableState.cURLHandler = (queue, handler)
850
            }
851
        }
852
 
853
        return self
854
    }
855
 
856
    /// Sets a handler to be called when the cURL description of the request is available.
857
    ///
858
    /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called.
859
    ///
860
    /// - Parameter handler: Closure to be called when the cURL description is available. Called on the instance's
861
    ///                      `underlyingQueue` by default.
862
    ///
863
    /// - Returns:           The instance.
864
    @discardableResult
865
    public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self {
866
        $mutableState.write { mutableState in
867
            if mutableState.requests.last != nil {
868
                underlyingQueue.async { handler(self.cURLDescription()) }
869
            } else {
870
                mutableState.cURLHandler = (underlyingQueue, handler)
871
            }
872
        }
873
 
874
        return self
875
    }
876
 
877
    /// Sets a closure to called whenever Alamofire creates a `URLRequest` for this instance.
878
    ///
879
    /// - Note: This closure will be called multiple times if the instance adapts incoming `URLRequest`s or is retried.
880
    ///
881
    /// - Parameters:
882
    ///   - queue:   `DispatchQueue` on which `handler` will be called. `.main` by default.
883
    ///   - handler: Closure to be called when a `URLRequest` is available.
884
    ///
885
    /// - Returns:   The instance.
886
    @discardableResult
887
    public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self {
888
        $mutableState.write { state in
889
            if let request = state.requests.last {
890
                queue.async { handler(request) }
891
            }
892
 
893
            state.urlRequestHandler = (queue, handler)
894
        }
895
 
896
        return self
897
    }
898
 
899
    /// Sets a closure to be called whenever the instance creates a `URLSessionTask`.
900
    ///
901
    /// - Note: This API should only be used to provide `URLSessionTask`s to existing API, like `NSFileProvider`. It
902
    ///         **SHOULD NOT** be used to interact with tasks directly, as that may be break Alamofire features.
903
    ///         Additionally, this closure may be called multiple times if the instance is retried.
904
    ///
905
    /// - Parameters:
906
    ///   - queue:   `DispatchQueue` on which `handler` will be called. `.main` by default.
907
    ///   - handler: Closure to be called when the `URLSessionTask` is available.
908
    ///
909
    /// - Returns:   The instance.
910
    @discardableResult
911
    public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self {
912
        $mutableState.write { state in
913
            if let task = state.tasks.last {
914
                queue.async { handler(task) }
915
            }
916
 
917
            state.urlSessionTaskHandler = (queue, handler)
918
        }
919
 
920
        return self
921
    }
922
 
923
    // MARK: Cleanup
924
 
925
    /// Adds a `finishHandler` closure to be called when the request completes.
926
    ///
927
    /// - Parameter closure: Closure to be called when the request finishes.
928
    func onFinish(perform finishHandler: @escaping () -> Void) {
929
        guard !isFinished else { finishHandler(); return }
930
 
931
        $mutableState.write { state in
932
            state.finishHandlers.append(finishHandler)
933
        }
934
    }
935
 
936
    /// Final cleanup step executed when the instance finishes response serialization.
937
    func cleanup() {
938
        delegate?.cleanup(after: self)
939
        let handlers = $mutableState.finishHandlers
940
        handlers.forEach { $0() }
941
        $mutableState.write { state in
942
            state.finishHandlers.removeAll()
943
        }
944
    }
945
}
946
 
947
// MARK: - Protocol Conformances
948
 
949
extension Request: Equatable {
950
    public static func ==(lhs: Request, rhs: Request) -> Bool {
951
        lhs.id == rhs.id
952
    }
953
}
954
 
955
extension Request: Hashable {
956
    public func hash(into hasher: inout Hasher) {
957
        hasher.combine(id)
958
    }
959
}
960
 
961
extension Request: CustomStringConvertible {
962
    /// A textual representation of this instance, including the `HTTPMethod` and `URL` if the `URLRequest` has been
963
    /// created, as well as the response status code, if a response has been received.
964
    public var description: String {
965
        guard let request = performedRequests.last ?? lastRequest,
966
              let url = request.url,
967
              let method = request.httpMethod else { return "No request created yet." }
968
 
969
        let requestDescription = "\(method) \(url.absoluteString)"
970
 
971
        return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription
972
    }
973
}
974
 
975
extension Request {
976
    /// cURL representation of the instance.
977
    ///
978
    /// - Returns: The cURL equivalent of the instance.
979
    public func cURLDescription() -> String {
980
        guard
981
            let request = lastRequest,
982
            let url = request.url,
983
            let host = url.host,
984
            let method = request.httpMethod else { return "$ curl command could not be created" }
985
 
986
        var components = ["$ curl -v"]
987
 
988
        components.append("-X \(method)")
989
 
990
        if let credentialStorage = delegate?.sessionConfiguration.urlCredentialStorage {
991
            let protectionSpace = URLProtectionSpace(host: host,
992
                                                     port: url.port ?? 0,
993
                                                     protocol: url.scheme,
994
                                                     realm: host,
995
                                                     authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
996
 
997
            if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
998
                for credential in credentials {
999
                    guard let user = credential.user, let password = credential.password else { continue }
1000
                    components.append("-u \(user):\(password)")
1001
                }
1002
            } else {
1003
                if let credential = credential, let user = credential.user, let password = credential.password {
1004
                    components.append("-u \(user):\(password)")
1005
                }
1006
            }
1007
        }
1008
 
1009
        if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies {
1010
            if
1011
                let cookieStorage = configuration.httpCookieStorage,
1012
                let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty {
1013
                let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";")
1014
 
1015
                components.append("-b \"\(allCookies)\"")
1016
            }
1017
        }
1018
 
1019
        var headers = HTTPHeaders()
1020
 
1021
        if let sessionHeaders = delegate?.sessionConfiguration.headers {
1022
            for header in sessionHeaders where header.name != "Cookie" {
1023
                headers[header.name] = header.value
1024
            }
1025
        }
1026
 
1027
        for header in request.headers where header.name != "Cookie" {
1028
            headers[header.name] = header.value
1029
        }
1030
 
1031
        for header in headers {
1032
            let escapedValue = header.value.replacingOccurrences(of: "\"", with: "\\\"")
1033
            components.append("-H \"\(header.name): \(escapedValue)\"")
1034
        }
1035
 
1036
        if let httpBodyData = request.httpBody {
1037
            let httpBody = String(decoding: httpBodyData, as: UTF8.self)
1038
            var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
1039
            escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
1040
 
1041
            components.append("-d \"\(escapedBody)\"")
1042
        }
1043
 
1044
        components.append("\"\(url.absoluteString)\"")
1045
 
1046
        return components.joined(separator: " \\\n\t")
1047
    }
1048
}
1049
 
1050
/// Protocol abstraction for `Request`'s communication back to the `SessionDelegate`.
1051
public protocol RequestDelegate: AnyObject {
1052
    /// `URLSessionConfiguration` used to create the underlying `URLSessionTask`s.
1053
    var sessionConfiguration: URLSessionConfiguration { get }
1054
 
1055
    /// Determines whether the `Request` should automatically call `resume()` when adding the first response handler.
1056
    var startImmediately: Bool { get }
1057
 
1058
    /// Notifies the delegate the `Request` has reached a point where it needs cleanup.
1059
    ///
1060
    /// - Parameter request: The `Request` to cleanup after.
1061
    func cleanup(after request: Request)
1062
 
1063
    /// Asynchronously ask the delegate whether a `Request` will be retried.
1064
    ///
1065
    /// - Parameters:
1066
    ///   - request:    `Request` which failed.
1067
    ///   - error:      `Error` which produced the failure.
1068
    ///   - completion: Closure taking the `RetryResult` for evaluation.
1069
    func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void)
1070
 
1071
    /// Asynchronously retry the `Request`.
1072
    ///
1073
    /// - Parameters:
1074
    ///   - request:   `Request` which will be retried.
1075
    ///   - timeDelay: `TimeInterval` after which the retry will be triggered.
1076
    func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?)
1077
}
1078
 
1079
// MARK: - Subclasses
1080
 
1081
// MARK: - DataRequest
1082
 
1083
/// `Request` subclass which handles in-memory `Data` download using `URLSessionDataTask`.
1084
public class DataRequest: Request {
1085
    /// `URLRequestConvertible` value used to create `URLRequest`s for this instance.
1086
    public let convertible: URLRequestConvertible
1087
    /// `Data` read from the server so far.
1088
    public var data: Data? { mutableData }
1089
 
1090
    /// Protected storage for the `Data` read by the instance.
1091
    @Protected
1092
    private var mutableData: Data? = nil
1093
 
1094
    /// Creates a `DataRequest` using the provided parameters.
1095
    ///
1096
    /// - Parameters:
1097
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
1098
    ///   - convertible:        `URLRequestConvertible` value used to create `URLRequest`s for this instance.
1099
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
1100
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
1101
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
1102
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
1103
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
1104
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
1105
    init(id: UUID = UUID(),
1106
         convertible: URLRequestConvertible,
1107
         underlyingQueue: DispatchQueue,
1108
         serializationQueue: DispatchQueue,
1109
         eventMonitor: EventMonitor?,
1110
         interceptor: RequestInterceptor?,
1111
         delegate: RequestDelegate) {
1112
        self.convertible = convertible
1113
 
1114
        super.init(id: id,
1115
                   underlyingQueue: underlyingQueue,
1116
                   serializationQueue: serializationQueue,
1117
                   eventMonitor: eventMonitor,
1118
                   interceptor: interceptor,
1119
                   delegate: delegate)
1120
    }
1121
 
1122
    override func reset() {
1123
        super.reset()
1124
 
1125
        mutableData = nil
1126
    }
1127
 
1128
    /// Called when `Data` is received by this instance.
1129
    ///
1130
    /// - Note: Also calls `updateDownloadProgress`.
1131
    ///
1132
    /// - Parameter data: The `Data` received.
1133
    func didReceive(data: Data) {
1134
        if self.data == nil {
1135
            mutableData = data
1136
        } else {
1137
            $mutableData.write { $0?.append(data) }
1138
        }
1139
 
1140
        updateDownloadProgress()
1141
    }
1142
 
1143
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
1144
        let copiedRequest = request
1145
        return session.dataTask(with: copiedRequest)
1146
    }
1147
 
1148
    /// Called to updated the `downloadProgress` of the instance.
1149
    func updateDownloadProgress() {
1150
        let totalBytesReceived = Int64(data?.count ?? 0)
1151
        let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
1152
 
1153
        downloadProgress.totalUnitCount = totalBytesExpected
1154
        downloadProgress.completedUnitCount = totalBytesReceived
1155
 
1156
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
1157
    }
1158
 
1159
    /// Validates the request, using the specified closure.
1160
    ///
1161
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
1162
    ///
1163
    /// - Parameter validation: `Validation` closure used to validate the response.
1164
    ///
1165
    /// - Returns:              The instance.
1166
    @discardableResult
1167
    public func validate(_ validation: @escaping Validation) -> Self {
1168
        let validator: () -> Void = { [unowned self] in
1169
            guard self.error == nil, let response = self.response else { return }
1170
 
1171
            let result = validation(self.request, response, self.data)
1172
 
1173
            if case let .failure(error) = result { self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) }
1174
 
1175
            self.eventMonitor?.request(self,
1176
                                       didValidateRequest: self.request,
1177
                                       response: response,
1178
                                       data: self.data,
1179
                                       withResult: result)
1180
        }
1181
 
1182
        $validators.write { $0.append(validator) }
1183
 
1184
        return self
1185
    }
1186
}
1187
 
1188
// MARK: - DataStreamRequest
1189
 
1190
/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure.
1191
public final class DataStreamRequest: Request {
1192
    /// Closure type handling `DataStreamRequest.Stream` values.
1193
    public typealias Handler<Success, Failure: Error> = (Stream<Success, Failure>) throws -> Void
1194
 
1195
    /// Type encapsulating an `Event` as it flows through the stream, as well as a `CancellationToken` which can be used
1196
    /// to stop the stream at any time.
1197
    public struct Stream<Success, Failure: Error> {
1198
        /// Latest `Event` from the stream.
1199
        public let event: Event<Success, Failure>
1200
        /// Token used to cancel the stream.
1201
        public let token: CancellationToken
1202
 
1203
        /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
1204
        public func cancel() {
1205
            token.cancel()
1206
        }
1207
    }
1208
 
1209
    /// Type representing an event flowing through the stream. Contains either the `Result` of processing streamed
1210
    /// `Data` or the completion of the stream.
1211
    public enum Event<Success, Failure: Error> {
1212
        /// Output produced every time the instance receives additional `Data`. The associated value contains the
1213
        /// `Result` of processing the incoming `Data`.
1214
        case stream(Result<Success, Failure>)
1215
        /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error.
1216
        /// Associated `Completion` value contains the final state.
1217
        case complete(Completion)
1218
    }
1219
 
1220
    /// Value containing the state of a `DataStreamRequest` when the stream was completed.
1221
    public struct Completion {
1222
        /// Last `URLRequest` issued by the instance.
1223
        public let request: URLRequest?
1224
        /// Last `HTTPURLResponse` received by the instance.
1225
        public let response: HTTPURLResponse?
1226
        /// Last `URLSessionTaskMetrics` produced for the instance.
1227
        public let metrics: URLSessionTaskMetrics?
1228
        /// `AFError` produced for the instance, if any.
1229
        public let error: AFError?
1230
    }
1231
 
1232
    /// Type used to cancel an ongoing stream.
1233
    public struct CancellationToken {
1234
        weak var request: DataStreamRequest?
1235
 
1236
        init(_ request: DataStreamRequest) {
1237
            self.request = request
1238
        }
1239
 
1240
        /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
1241
        public func cancel() {
1242
            request?.cancel()
1243
        }
1244
    }
1245
 
1246
    /// `URLRequestConvertible` value used to create `URLRequest`s for this instance.
1247
    public let convertible: URLRequestConvertible
1248
    /// Whether or not the instance will be cancelled if stream parsing encounters an error.
1249
    public let automaticallyCancelOnStreamError: Bool
1250
 
1251
    /// Internal mutable state specific to this type.
1252
    struct StreamMutableState {
1253
        /// `OutputStream` bound to the `InputStream` produced by `asInputStream`, if it has been called.
1254
        var outputStream: OutputStream?
1255
        /// Stream closures called as `Data` is received.
1256
        var streams: [(_ data: Data) -> Void] = []
1257
        /// Number of currently executing streams. Used to ensure completions are only fired after all streams are
1258
        /// enqueued.
1259
        var numberOfExecutingStreams = 0
1260
        /// Completion calls enqueued while streams are still executing.
1261
        var enqueuedCompletionEvents: [() -> Void] = []
1262
    }
1263
 
1264
    @Protected
1265
    var streamMutableState = StreamMutableState()
1266
 
1267
    /// Creates a `DataStreamRequest` using the provided parameters.
1268
    ///
1269
    /// - Parameters:
1270
    ///   - id:                               `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()`
1271
    ///                                        by default.
1272
    ///   - convertible:                      `URLRequestConvertible` value used to create `URLRequest`s for this
1273
    ///                                        instance.
1274
    ///   - automaticallyCancelOnStreamError: `Bool` indicating whether the instance will be cancelled when an `Error`
1275
    ///                                       is thrown while serializing stream `Data`.
1276
    ///   - underlyingQueue:                  `DispatchQueue` on which all internal `Request` work is performed.
1277
    ///   - serializationQueue:               `DispatchQueue` on which all serialization work is performed. By default
1278
    ///                                       targets
1279
    ///                                       `underlyingQueue`, but can be passed another queue from a `Session`.
1280
    ///   - eventMonitor:                     `EventMonitor` called for event callbacks from internal `Request` actions.
1281
    ///   - interceptor:                      `RequestInterceptor` used throughout the request lifecycle.
1282
    ///   - delegate:                         `RequestDelegate` that provides an interface to actions not performed by
1283
    ///                                       the `Request`.
1284
    init(id: UUID = UUID(),
1285
         convertible: URLRequestConvertible,
1286
         automaticallyCancelOnStreamError: Bool,
1287
         underlyingQueue: DispatchQueue,
1288
         serializationQueue: DispatchQueue,
1289
         eventMonitor: EventMonitor?,
1290
         interceptor: RequestInterceptor?,
1291
         delegate: RequestDelegate) {
1292
        self.convertible = convertible
1293
        self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError
1294
 
1295
        super.init(id: id,
1296
                   underlyingQueue: underlyingQueue,
1297
                   serializationQueue: serializationQueue,
1298
                   eventMonitor: eventMonitor,
1299
                   interceptor: interceptor,
1300
                   delegate: delegate)
1301
    }
1302
 
1303
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
1304
        let copiedRequest = request
1305
        return session.dataTask(with: copiedRequest)
1306
    }
1307
 
1308
    override func finish(error: AFError? = nil) {
1309
        $streamMutableState.write { state in
1310
            state.outputStream?.close()
1311
        }
1312
 
1313
        super.finish(error: error)
1314
    }
1315
 
1316
    func didReceive(data: Data) {
1317
        $streamMutableState.write { state in
1318
            #if !(os(Linux) || os(Windows))
1319
            if let stream = state.outputStream {
1320
                underlyingQueue.async {
1321
                    var bytes = Array(data)
1322
                    stream.write(&bytes, maxLength: bytes.count)
1323
                }
1324
            }
1325
            #endif
1326
            state.numberOfExecutingStreams += state.streams.count
1327
            let localState = state
1328
            underlyingQueue.async { localState.streams.forEach { $0(data) } }
1329
        }
1330
    }
1331
 
1332
    /// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure.
1333
    ///
1334
    /// - Parameter validation: `Validation` closure used to validate the request and response.
1335
    ///
1336
    /// - Returns:              The `DataStreamRequest`.
1337
    @discardableResult
1338
    public func validate(_ validation: @escaping Validation) -> Self {
1339
        let validator: () -> Void = { [unowned self] in
1340
            guard self.error == nil, let response = self.response else { return }
1341
 
1342
            let result = validation(self.request, response)
1343
 
1344
            if case let .failure(error) = result {
1345
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
1346
            }
1347
 
1348
            self.eventMonitor?.request(self,
1349
                                       didValidateRequest: self.request,
1350
                                       response: response,
1351
                                       withResult: result)
1352
        }
1353
 
1354
        $validators.write { $0.append(validator) }
1355
 
1356
        return self
1357
    }
1358
 
1359
    #if !(os(Linux) || os(Windows))
1360
    /// Produces an `InputStream` that receives the `Data` received by the instance.
1361
    ///
1362
    /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`.
1363
    ///         Additionally, this method will automatically call `resume()` on the instance, regardless of whether or
1364
    ///         not the creating session has `startRequestsImmediately` set to `true`.
1365
    ///
1366
    /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`.
1367
    ///
1368
    /// - Returns:              The `InputStream` bound to the internal `OutboundStream`.
1369
    public func asInputStream(bufferSize: Int = 1024) -> InputStream? {
1370
        defer { resume() }
1371
 
1372
        var inputStream: InputStream?
1373
        $streamMutableState.write { state in
1374
            Foundation.Stream.getBoundStreams(withBufferSize: bufferSize,
1375
                                              inputStream: &inputStream,
1376
                                              outputStream: &state.outputStream)
1377
            state.outputStream?.open()
1378
        }
1379
 
1380
        return inputStream
1381
    }
1382
    #endif
1383
 
1384
    func capturingError(from closure: () throws -> Void) {
1385
        do {
1386
            try closure()
1387
        } catch {
1388
            self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
1389
            cancel()
1390
        }
1391
    }
1392
 
1393
    func appendStreamCompletion<Success, Failure>(on queue: DispatchQueue,
1394
                                                  stream: @escaping Handler<Success, Failure>) {
1395
        appendResponseSerializer {
1396
            self.underlyingQueue.async {
1397
                self.responseSerializerDidComplete {
1398
                    self.$streamMutableState.write { state in
1399
                        guard state.numberOfExecutingStreams == 0 else {
1400
                            state.enqueuedCompletionEvents.append {
1401
                                self.enqueueCompletion(on: queue, stream: stream)
1402
                            }
1403
 
1404
                            return
1405
                        }
1406
 
1407
                        self.enqueueCompletion(on: queue, stream: stream)
1408
                    }
1409
                }
1410
            }
1411
        }
1412
    }
1413
 
1414
    func enqueueCompletion<Success, Failure>(on queue: DispatchQueue,
1415
                                             stream: @escaping Handler<Success, Failure>) {
1416
        queue.async {
1417
            do {
1418
                let completion = Completion(request: self.request,
1419
                                            response: self.response,
1420
                                            metrics: self.metrics,
1421
                                            error: self.error)
1422
                try stream(.init(event: .complete(completion), token: .init(self)))
1423
            } catch {
1424
                // Ignore error, as errors on Completion can't be handled anyway.
1425
            }
1426
        }
1427
    }
1428
}
1429
 
1430
extension DataStreamRequest.Stream {
1431
    /// Incoming `Result` values from `Event.stream`.
1432
    public var result: Result<Success, Failure>? {
1433
        guard case let .stream(result) = event else { return nil }
1434
 
1435
        return result
1436
    }
1437
 
1438
    /// `Success` value of the instance, if any.
1439
    public var value: Success? {
1440
        guard case let .success(value) = result else { return nil }
1441
 
1442
        return value
1443
    }
1444
 
1445
    /// `Failure` value of the instance, if any.
1446
    public var error: Failure? {
1447
        guard case let .failure(error) = result else { return nil }
1448
 
1449
        return error
1450
    }
1451
 
1452
    /// `Completion` value of the instance, if any.
1453
    public var completion: DataStreamRequest.Completion? {
1454
        guard case let .complete(completion) = event else { return nil }
1455
 
1456
        return completion
1457
    }
1458
}
1459
 
1460
// MARK: - DownloadRequest
1461
 
1462
/// `Request` subclass which downloads `Data` to a file on disk using `URLSessionDownloadTask`.
1463
public class DownloadRequest: Request {
1464
    /// A set of options to be executed prior to moving a downloaded file from the temporary `URL` to the destination
1465
    /// `URL`.
1466
    public struct Options: OptionSet {
1467
        /// Specifies that intermediate directories for the destination URL should be created.
1468
        public static let createIntermediateDirectories = Options(rawValue: 1 << 0)
1469
        /// Specifies that any previous file at the destination `URL` should be removed.
1470
        public static let removePreviousFile = Options(rawValue: 1 << 1)
1471
 
1472
        public let rawValue: Int
1473
 
1474
        public init(rawValue: Int) {
1475
            self.rawValue = rawValue
1476
        }
1477
    }
1478
 
1479
    // MARK: Destination
1480
 
1481
    /// A closure executed once a `DownloadRequest` has successfully completed in order to determine where to move the
1482
    /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
1483
    /// and the `HTTPURLResponse`, and returns two values: the file URL where the temporary file should be moved and
1484
    /// the options defining how the file should be moved.
1485
    ///
1486
    /// - Note: Downloads from a local `file://` `URL`s do not use the `Destination` closure, as those downloads do not
1487
    ///         return an `HTTPURLResponse`. Instead the file is merely moved within the temporary directory.
1488
    public typealias Destination = (_ temporaryURL: URL,
1489
                                    _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)
1490
 
1491
    /// Creates a download file destination closure which uses the default file manager to move the temporary file to a
1492
    /// file URL in the first available directory with the specified search path directory and search path domain mask.
1493
    ///
1494
    /// - Parameters:
1495
    ///   - directory: The search path directory. `.documentDirectory` by default.
1496
    ///   - domain:    The search path domain mask. `.userDomainMask` by default.
1497
    ///   - options:   `DownloadRequest.Options` used when moving the downloaded file to its destination. None by
1498
    ///                default.
1499
    /// - Returns: The `Destination` closure.
1500
    public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory,
1501
                                                   in domain: FileManager.SearchPathDomainMask = .userDomainMask,
1502
                                                   options: Options = []) -> Destination {
1503
        { temporaryURL, response in
1504
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
1505
            let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL
1506
 
1507
            return (url, options)
1508
        }
1509
    }
1510
 
1511
    /// Default `Destination` used by Alamofire to ensure all downloads persist. This `Destination` prepends
1512
    /// `Alamofire_` to the automatically generated download name and moves it within the temporary directory. Files
1513
    /// with this destination must be additionally moved if they should survive the system reclamation of temporary
1514
    /// space.
1515
    static let defaultDestination: Destination = { url, _ in
1516
        (defaultDestinationURL(url), [])
1517
    }
1518
 
1519
    /// Default `URL` creation closure. Creates a `URL` in the temporary directory with `Alamofire_` prepended to the
1520
    /// provided file name.
1521
    static let defaultDestinationURL: (URL) -> URL = { url in
1522
        let filename = "Alamofire_\(url.lastPathComponent)"
1523
        let destination = url.deletingLastPathComponent().appendingPathComponent(filename)
1524
 
1525
        return destination
1526
    }
1527
 
1528
    // MARK: Downloadable
1529
 
1530
    /// Type describing the source used to create the underlying `URLSessionDownloadTask`.
1531
    public enum Downloadable {
1532
        /// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value.
1533
        case request(URLRequestConvertible)
1534
        /// Download should be started from the associated resume `Data` value.
1535
        case resumeData(Data)
1536
    }
1537
 
1538
    // MARK: Mutable State
1539
 
1540
    /// Type containing all mutable state for `DownloadRequest` instances.
1541
    private struct DownloadRequestMutableState {
1542
        /// Possible resume `Data` produced when cancelling the instance.
1543
        var resumeData: Data?
1544
        /// `URL` to which `Data` is being downloaded.
1545
        var fileURL: URL?
1546
    }
1547
 
1548
    /// Protected mutable state specific to `DownloadRequest`.
1549
    @Protected
1550
    private var mutableDownloadState = DownloadRequestMutableState()
1551
 
1552
    /// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download
1553
    /// using the `download(resumingWith data:)` API.
1554
    ///
1555
    /// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel).
1556
    public var resumeData: Data? {
1557
        #if !(os(Linux) || os(Windows))
1558
        return $mutableDownloadState.resumeData ?? error?.downloadResumeData
1559
        #else
1560
        return $mutableDownloadState.resumeData
1561
        #endif
1562
    }
1563
 
1564
    /// If the download is successful, the `URL` where the file was downloaded.
1565
    public var fileURL: URL? { $mutableDownloadState.fileURL }
1566
 
1567
    // MARK: Initial State
1568
 
1569
    /// `Downloadable` value used for this instance.
1570
    public let downloadable: Downloadable
1571
    /// The `Destination` to which the downloaded file is moved.
1572
    let destination: Destination
1573
 
1574
    /// Creates a `DownloadRequest` using the provided parameters.
1575
    ///
1576
    /// - Parameters:
1577
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
1578
    ///   - downloadable:       `Downloadable` value used to create `URLSessionDownloadTasks` for the instance.
1579
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
1580
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
1581
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
1582
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
1583
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
1584
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`
1585
    ///   - destination:        `Destination` closure used to move the downloaded file to its final location.
1586
    init(id: UUID = UUID(),
1587
         downloadable: Downloadable,
1588
         underlyingQueue: DispatchQueue,
1589
         serializationQueue: DispatchQueue,
1590
         eventMonitor: EventMonitor?,
1591
         interceptor: RequestInterceptor?,
1592
         delegate: RequestDelegate,
1593
         destination: @escaping Destination) {
1594
        self.downloadable = downloadable
1595
        self.destination = destination
1596
 
1597
        super.init(id: id,
1598
                   underlyingQueue: underlyingQueue,
1599
                   serializationQueue: serializationQueue,
1600
                   eventMonitor: eventMonitor,
1601
                   interceptor: interceptor,
1602
                   delegate: delegate)
1603
    }
1604
 
1605
    override func reset() {
1606
        super.reset()
1607
 
1608
        $mutableDownloadState.write {
1609
            $0.resumeData = nil
1610
            $0.fileURL = nil
1611
        }
1612
    }
1613
 
1614
    /// Called when a download has finished.
1615
    ///
1616
    /// - Parameters:
1617
    ///   - task:   `URLSessionTask` that finished the download.
1618
    ///   - result: `Result` of the automatic move to `destination`.
1619
    func didFinishDownloading(using task: URLSessionTask, with result: Result<URL, AFError>) {
1620
        eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result)
1621
 
1622
        switch result {
1623
        case let .success(url): $mutableDownloadState.fileURL = url
1624
        case let .failure(error): self.error = error
1625
        }
1626
    }
1627
 
1628
    /// Updates the `downloadProgress` using the provided values.
1629
    ///
1630
    /// - Parameters:
1631
    ///   - bytesWritten:              Total bytes written so far.
1632
    ///   - totalBytesExpectedToWrite: Total bytes expected to write.
1633
    func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
1634
        downloadProgress.totalUnitCount = totalBytesExpectedToWrite
1635
        downloadProgress.completedUnitCount += bytesWritten
1636
 
1637
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
1638
    }
1639
 
1640
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
1641
        session.downloadTask(with: request)
1642
    }
1643
 
1644
    /// Creates a `URLSessionTask` from the provided resume data.
1645
    ///
1646
    /// - Parameters:
1647
    ///   - data:    `Data` used to resume the download.
1648
    ///   - session: `URLSession` used to create the `URLSessionTask`.
1649
    ///
1650
    /// - Returns:   The `URLSessionTask` created.
1651
    public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask {
1652
        session.downloadTask(withResumeData: data)
1653
    }
1654
 
1655
    /// Cancels the instance. Once cancelled, a `DownloadRequest` can no longer be resumed or suspended.
1656
    ///
1657
    /// - Note: This method will NOT produce resume data. If you wish to cancel and produce resume data, use
1658
    ///         `cancel(producingResumeData:)` or `cancel(byProducingResumeData:)`.
1659
    ///
1660
    /// - Returns: The instance.
1661
    @discardableResult
1662
    override public func cancel() -> Self {
1663
        cancel(producingResumeData: false)
1664
    }
1665
 
1666
    /// Cancels the instance, optionally producing resume data. Once cancelled, a `DownloadRequest` can no longer be
1667
    /// resumed or suspended.
1668
    ///
1669
    /// - Note: If `producingResumeData` is `true`, the `resumeData` property will be populated with any resume data, if
1670
    ///         available.
1671
    ///
1672
    /// - Returns: The instance.
1673
    @discardableResult
1674
    public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self {
1675
        cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil)
1676
    }
1677
 
1678
    /// Cancels the instance while producing resume data. Once cancelled, a `DownloadRequest` can no longer be resumed
1679
    /// or suspended.
1680
    ///
1681
    /// - Note: The resume data passed to the completion handler will also be available on the instance's `resumeData`
1682
    ///         property.
1683
    ///
1684
    /// - Parameter completionHandler: The completion handler that is called when the download has been successfully
1685
    ///                                cancelled. It is not guaranteed to be called on a particular queue, so you may
1686
    ///                                want use an appropriate queue to perform your work.
1687
    ///
1688
    /// - Returns:                     The instance.
1689
    @discardableResult
1690
    public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self {
1691
        cancel(optionallyProducingResumeData: completionHandler)
1692
    }
1693
 
1694
    /// Internal implementation of cancellation that optionally takes a resume data handler. If no handler is passed,
1695
    /// cancellation is performed without producing resume data.
1696
    ///
1697
    /// - Parameter completionHandler: Optional resume data handler.
1698
    ///
1699
    /// - Returns:                     The instance.
1700
    private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self {
1701
        $mutableState.write { mutableState in
1702
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
1703
 
1704
            mutableState.state = .cancelled
1705
 
1706
            underlyingQueue.async { self.didCancel() }
1707
 
1708
            guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else {
1709
                underlyingQueue.async { self.finish() }
1710
                return
1711
            }
1712
 
1713
            if let completionHandler = completionHandler {
1714
                // Resume to ensure metrics are gathered.
1715
                task.resume()
1716
                task.cancel { resumeData in
1717
                    self.$mutableDownloadState.resumeData = resumeData
1718
                    self.underlyingQueue.async { self.didCancelTask(task) }
1719
                    completionHandler(resumeData)
1720
                }
1721
            } else {
1722
                // Resume to ensure metrics are gathered.
1723
                task.resume()
1724
                task.cancel()
1725
                self.underlyingQueue.async { self.didCancelTask(task) }
1726
            }
1727
        }
1728
 
1729
        return self
1730
    }
1731
 
1732
    /// Validates the request, using the specified closure.
1733
    ///
1734
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
1735
    ///
1736
    /// - Parameter validation: `Validation` closure to validate the response.
1737
    ///
1738
    /// - Returns:              The instance.
1739
    @discardableResult
1740
    public func validate(_ validation: @escaping Validation) -> Self {
1741
        let validator: () -> Void = { [unowned self] in
1742
            guard self.error == nil, let response = self.response else { return }
1743
 
1744
            let result = validation(self.request, response, self.fileURL)
1745
 
1746
            if case let .failure(error) = result {
1747
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
1748
            }
1749
 
1750
            self.eventMonitor?.request(self,
1751
                                       didValidateRequest: self.request,
1752
                                       response: response,
1753
                                       fileURL: self.fileURL,
1754
                                       withResult: result)
1755
        }
1756
 
1757
        $validators.write { $0.append(validator) }
1758
 
1759
        return self
1760
    }
1761
}
1762
 
1763
// MARK: - UploadRequest
1764
 
1765
/// `DataRequest` subclass which handles `Data` upload from memory, file, or stream using `URLSessionUploadTask`.
1766
public class UploadRequest: DataRequest {
1767
    /// Type describing the origin of the upload, whether `Data`, file, or stream.
1768
    public enum Uploadable {
1769
        /// Upload from the provided `Data` value.
1770
        case data(Data)
1771
        /// Upload from the provided file `URL`, as well as a `Bool` determining whether the source file should be
1772
        /// automatically removed once uploaded.
1773
        case file(URL, shouldRemove: Bool)
1774
        /// Upload from the provided `InputStream`.
1775
        case stream(InputStream)
1776
    }
1777
 
1778
    // MARK: Initial State
1779
 
1780
    /// The `UploadableConvertible` value used to produce the `Uploadable` value for this instance.
1781
    public let upload: UploadableConvertible
1782
 
1783
    /// `FileManager` used to perform cleanup tasks, including the removal of multipart form encoded payloads written
1784
    /// to disk.
1785
    public let fileManager: FileManager
1786
 
1787
    // MARK: Mutable State
1788
 
1789
    /// `Uploadable` value used by the instance.
1790
    public var uploadable: Uploadable?
1791
 
1792
    /// Creates an `UploadRequest` using the provided parameters.
1793
    ///
1794
    /// - Parameters:
1795
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
1796
    ///   - convertible:        `UploadConvertible` value used to determine the type of upload to be performed.
1797
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
1798
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
1799
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
1800
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
1801
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
1802
    ///   - fileManager:        `FileManager` used to perform cleanup tasks, including the removal of multipart form
1803
    ///                         encoded payloads written to disk.
1804
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
1805
    init(id: UUID = UUID(),
1806
         convertible: UploadConvertible,
1807
         underlyingQueue: DispatchQueue,
1808
         serializationQueue: DispatchQueue,
1809
         eventMonitor: EventMonitor?,
1810
         interceptor: RequestInterceptor?,
1811
         fileManager: FileManager,
1812
         delegate: RequestDelegate) {
1813
        upload = convertible
1814
        self.fileManager = fileManager
1815
 
1816
        super.init(id: id,
1817
                   convertible: convertible,
1818
                   underlyingQueue: underlyingQueue,
1819
                   serializationQueue: serializationQueue,
1820
                   eventMonitor: eventMonitor,
1821
                   interceptor: interceptor,
1822
                   delegate: delegate)
1823
    }
1824
 
1825
    /// Called when the `Uploadable` value has been created from the `UploadConvertible`.
1826
    ///
1827
    /// - Parameter uploadable: The `Uploadable` that was created.
1828
    func didCreateUploadable(_ uploadable: Uploadable) {
1829
        self.uploadable = uploadable
1830
 
1831
        eventMonitor?.request(self, didCreateUploadable: uploadable)
1832
    }
1833
 
1834
    /// Called when the `Uploadable` value could not be created.
1835
    ///
1836
    /// - Parameter error: `AFError` produced by the failure.
1837
    func didFailToCreateUploadable(with error: AFError) {
1838
        self.error = error
1839
 
1840
        eventMonitor?.request(self, didFailToCreateUploadableWithError: error)
1841
 
1842
        retryOrFinish(error: error)
1843
    }
1844
 
1845
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
1846
        guard let uploadable = uploadable else {
1847
            fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.")
1848
        }
1849
 
1850
        switch uploadable {
1851
        case let .data(data): return session.uploadTask(with: request, from: data)
1852
        case let .file(url, _): return session.uploadTask(with: request, fromFile: url)
1853
        case .stream: return session.uploadTask(withStreamedRequest: request)
1854
        }
1855
    }
1856
 
1857
    override func reset() {
1858
        // Uploadable must be recreated on every retry.
1859
        uploadable = nil
1860
 
1861
        super.reset()
1862
    }
1863
 
1864
    /// Produces the `InputStream` from `uploadable`, if it can.
1865
    ///
1866
    /// - Note: Calling this method with a non-`.stream` `Uploadable` is a logic error and will crash.
1867
    ///
1868
    /// - Returns: The `InputStream`.
1869
    func inputStream() -> InputStream {
1870
        guard let uploadable = uploadable else {
1871
            fatalError("Attempting to access the input stream but the uploadable doesn't exist.")
1872
        }
1873
 
1874
        guard case let .stream(stream) = uploadable else {
1875
            fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.")
1876
        }
1877
 
1878
        eventMonitor?.request(self, didProvideInputStream: stream)
1879
 
1880
        return stream
1881
    }
1882
 
1883
    override public func cleanup() {
1884
        defer { super.cleanup() }
1885
 
1886
        guard
1887
            let uploadable = uploadable,
1888
            case let .file(url, shouldRemove) = uploadable,
1889
            shouldRemove
1890
        else { return }
1891
 
1892
        try? fileManager.removeItem(at: url)
1893
    }
1894
}
1895
 
1896
/// A type that can produce an `UploadRequest.Uploadable` value.
1897
public protocol UploadableConvertible {
1898
    /// Produces an `UploadRequest.Uploadable` value from the instance.
1899
    ///
1900
    /// - Returns: The `UploadRequest.Uploadable`.
1901
    /// - Throws:  Any `Error` produced during creation.
1902
    func createUploadable() throws -> UploadRequest.Uploadable
1903
}
1904
 
1905
extension UploadRequest.Uploadable: UploadableConvertible {
1906
    public func createUploadable() throws -> UploadRequest.Uploadable {
1907
        self
1908
    }
1909
}
1910
 
1911
/// A type that can be converted to an upload, whether from an `UploadRequest.Uploadable` or `URLRequestConvertible`.
1912
public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {}