Browse Source

Enable Interceptor to be built from other RequestInterceptors (#3155)

* Add Interceptor initializer from interceptors, docs.

* Put array of interceptors on existing init, not new.

* Update documentation.

* Add index link.
Jon Shier 5 years ago
parent
commit
505babfec2

+ 16 - 0
Documentation/AdvancedUsage.md

@@ -37,6 +37,7 @@
 * [Adapting and Retrying Requests with `RequestInterceptor`](#adapting-and-retrying-requests-with-requestinterceptor)
 * [Adapting and Retrying Requests with `RequestInterceptor`](#adapting-and-retrying-requests-with-requestinterceptor)
   + [`RequestAdapter`](#requestadapter)
   + [`RequestAdapter`](#requestadapter)
   + [`RequestRetrier`](#requestretrier)
   + [`RequestRetrier`](#requestretrier)
+  + [Using Multiple `RequestInterceptor`s](#using-multiple-requestinterceptors)
 * [Security](#security)
 * [Security](#security)
   + [Evaluating Server Trusts with `ServerTrustManager` and `ServerTrustEvaluating`](#evaluating-server-trusts-with-servertrustmanager-and-servertrustevaluating)
   + [Evaluating Server Trusts with `ServerTrustManager` and `ServerTrustEvaluating`](#evaluating-server-trusts-with-servertrustmanager-and-servertrustevaluating)
     - [`ServerTrustEvaluting`](#servertrustevaluting)
     - [`ServerTrustEvaluting`](#servertrustevaluting)
@@ -520,6 +521,21 @@ open func retry(_ request: Request, for session: Session, dueTo error: Error, co
 }
 }
 ```
 ```
 
 
+### Using Multiple `RequestInterceptor`s
+
+Alamofire supports the use of multiple `RequestInterceptor`s at both the `Session` and `Request` levels through the use of the `Interceptor` type. `Interceptor`s can be composed of adapter and retrier closures, a single combination of a `RequestAdapter` and `RequestRetrier`, or a combination of arrays of `RequestAdapter`s, `RequestRetrier`s, and `RequestInterceptor`s.
+
+```swift
+let adapter = // Some RequestAdapter
+let retrier = // Some RequestRetrier
+let interceptor = // Some RequestInterceptor
+
+let adapterAndRetrier = Interceptor(adapter: adapter, retrier: retrier)
+let composite = Interceptor(interceptors: [adapterAndRetrier, interceptor])
+```
+
+When composed of multiple `RequestAdapter`s, `Interceptor` will call each `RequestAdapter` in succession. If they all succeed, the final `URLRequest` out of the chain of `RequestAdapter`s will be used to perform the request. If one fails, adaptation stops and the `Request` fails with the error returned. Similarly, when composed of multiple `RequestRetrier`s, retries are executed in the same order as the retriers were added to the instance, until either all of them complete or one of them fails with an error.
+
 ## Security
 ## Security
 Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire receives the same automatic TLS certificate and certificate chain validation as `URLSession`. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by Alamofire’s `ServerTrustEvaluating` protocol.
 Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire receives the same automatic TLS certificate and certificate chain validation as `URLSession`. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by Alamofire’s `ServerTrustEvaluating` protocol.
 
 

+ 6 - 5
Source/RequestInterceptor.swift

@@ -181,11 +181,12 @@ open class Interceptor: RequestInterceptor {
     /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values.
     /// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
-    ///   - adapters: `RequestAdapter` values to be used.
-    ///   - retriers: `RequestRetrier` values to be used.
-    public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = []) {
-        self.adapters = adapters
-        self.retriers = retriers
+    ///   - adapters:     `RequestAdapter` values to be used.
+    ///   - retriers:     `RequestRetrier` values to be used.
+    ///   - interceptors: `RequestInterceptor`s to be used.
+    public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) {
+        self.adapters = adapters + interceptors
+        self.retriers = retriers + interceptors
     }
     }
 
 
     open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
     open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {

+ 32 - 18
Tests/RequestInterceptorTests.swift

@@ -80,7 +80,7 @@ final class RetryResultTestCase: BaseTestCase {
 final class AdapterTestCase: BaseTestCase {
 final class AdapterTestCase: BaseTestCase {
     func testThatAdapterCallsAdaptHandler() {
     func testThatAdapterCallsAdaptHandler() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
         var adapted = false
         var adapted = false
 
 
@@ -101,7 +101,7 @@ final class AdapterTestCase: BaseTestCase {
 
 
     func testThatAdapterCallsRequestRetrierDefaultImplementationInProtocolExtension() {
     func testThatAdapterCallsRequestRetrierDefaultImplementationInProtocolExtension() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -120,7 +120,7 @@ final class AdapterTestCase: BaseTestCase {
 
 
     func testThatAdapterCanBeImplementedAsynchronously() {
     func testThatAdapterCanBeImplementedAsynchronously() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
         var adapted = false
         var adapted = false
 
 
@@ -154,7 +154,7 @@ final class AdapterTestCase: BaseTestCase {
 final class RetrierTestCase: BaseTestCase {
 final class RetrierTestCase: BaseTestCase {
     func testThatRetrierCallsRetryHandler() {
     func testThatRetrierCallsRetryHandler() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
         var retried = false
         var retried = false
@@ -176,7 +176,7 @@ final class RetrierTestCase: BaseTestCase {
 
 
     func testThatRetrierCallsRequestAdapterDefaultImplementationInProtocolExtension() {
     func testThatRetrierCallsRequestAdapterDefaultImplementationInProtocolExtension() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
 
 
         let retrier = Retrier { _, _, _, completion in
         let retrier = Retrier { _, _, _, completion in
@@ -194,7 +194,7 @@ final class RetrierTestCase: BaseTestCase {
 
 
     func testThatRetrierCanBeImplementedAsynchronously() {
     func testThatRetrierCanBeImplementedAsynchronously() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
         var retried = false
         var retried = false
@@ -226,7 +226,7 @@ final class RetrierTestCase: BaseTestCase {
 
 
 // MARK: -
 // MARK: -
 
 
-final class InterceptorTestCase: BaseTestCase {
+final class InterceptorTests: BaseTestCase {
     func testAdaptHandlerAndRetryHandlerDefaultInitializer() {
     func testAdaptHandlerAndRetryHandlerDefaultInitializer() {
         // Given
         // Given
         let adaptHandler: AdaptHandler = { urlRequest, _, completion in completion(.success(urlRequest)) }
         let adaptHandler: AdaptHandler = { urlRequest, _, completion in completion(.success(urlRequest)) }
@@ -266,9 +266,23 @@ final class InterceptorTestCase: BaseTestCase {
         XCTAssertEqual(interceptor.retriers.count, 2)
         XCTAssertEqual(interceptor.retriers.count, 2)
     }
     }
 
 
+    func testThatInterceptorCanBeComposedOfMultipleRequestInterceptors() {
+        // Given
+        let adapter = Adapter { request, _, completion in completion(.success(request)) }
+        let retrier = Retrier { _, _, _, completion in completion(.doNotRetry) }
+        let inner = Interceptor(adapter: adapter, retrier: retrier)
+
+        // When
+        let interceptor = Interceptor(interceptors: [inner])
+
+        // Then
+        XCTAssertEqual(interceptor.adapters.count, 1)
+        XCTAssertEqual(interceptor.retriers.count, 1)
+    }
+
     func testThatInterceptorCanAdaptRequestWithNoAdapters() {
     func testThatInterceptorCanAdaptRequestWithNoAdapters() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
         let interceptor = Interceptor()
         let interceptor = Interceptor()
 
 
@@ -284,7 +298,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanAdaptRequestWithOneAdapter() {
     func testThatInterceptorCanAdaptRequestWithOneAdapter() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
 
 
         let adapter = Adapter { _, _, completion in completion(.failure(MockError())) }
         let adapter = Adapter { _, _, completion in completion(.failure(MockError())) }
@@ -302,7 +316,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanAdaptRequestWithMultipleAdapters() {
     func testThatInterceptorCanAdaptRequestWithMultipleAdapters() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
 
 
         let adapter1 = Adapter { urlRequest, _, completion in completion(.success(urlRequest)) }
         let adapter1 = Adapter { urlRequest, _, completion in completion(.success(urlRequest)) }
@@ -321,7 +335,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanAdaptRequestAsynchronously() {
     func testThatInterceptorCanAdaptRequestAsynchronously() {
         // Given
         // Given
-        let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
+        let urlRequest = URLRequest.makeHTTPBinRequest()
         let session = Session()
         let session = Session()
 
 
         let adapter = Adapter { _, _, completion in
         let adapter = Adapter { _, _, completion in
@@ -350,7 +364,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanRetryRequestWithNoRetriers() {
     func testThatInterceptorCanRetryRequestWithNoRetriers() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -367,7 +381,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanRetryRequestWithOneRetrier() {
     func testThatInterceptorCanRetryRequestWithOneRetrier() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -385,7 +399,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanRetryRequestWithMultipleRetriers() {
     func testThatInterceptorCanRetryRequestWithMultipleRetriers() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -404,7 +418,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorCanRetryRequestAsynchronously() {
     func testThatInterceptorCanRetryRequestAsynchronously() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -433,7 +447,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithRetryResult() {
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithRetryResult() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -455,7 +469,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithRetryWithDelayResult() {
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithRetryWithDelayResult() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)
 
 
@@ -478,7 +492,7 @@ final class InterceptorTestCase: BaseTestCase {
 
 
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithDoNotRetryResult() {
     func testThatInterceptorStopsIteratingThroughPendingRetriersWithDoNotRetryResult() {
         // Given
         // Given
-        let url = URL(string: "https://httpbin.org/get")!
+        let url = URL.makeHTTPBinURL()
         let session = Session(startRequestsImmediately: false)
         let session = Session(startRequestsImmediately: false)
         let request = session.request(url)
         let request = session.request(url)