Răsfoiți Sursa

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 ani în urmă
părinte
comite
505babfec2

+ 16 - 0
Documentation/AdvancedUsage.md

@@ -37,6 +37,7 @@
 * [Adapting and Retrying Requests with `RequestInterceptor`](#adapting-and-retrying-requests-with-requestinterceptor)
   + [`RequestAdapter`](#requestadapter)
   + [`RequestRetrier`](#requestretrier)
+  + [Using Multiple `RequestInterceptor`s](#using-multiple-requestinterceptors)
 * [Security](#security)
   + [Evaluating Server Trusts with `ServerTrustManager` and `ServerTrustEvaluating`](#evaluating-server-trusts-with-servertrustmanager-and-servertrustevaluating)
     - [`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
 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.
     ///
     /// - 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) {

+ 32 - 18
Tests/RequestInterceptorTests.swift

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