瀏覽代碼

Added RedirectHandler protocol to support redirects per session and per request

Christian Noon 7 年之前
父節點
當前提交
20cae7fdad
共有 5 個文件被更改,包括 92 次插入6 次删除
  1. 10 0
      Alamofire.xcodeproj/project.pbxproj
  2. 46 0
      Source/RedirectHandler.swift
  3. 21 1
      Source/Request.swift
  4. 9 4
      Source/Session.swift
  5. 6 1
      Source/SessionStateProvider.swift

+ 10 - 0
Alamofire.xcodeproj/project.pbxproj

@@ -164,6 +164,10 @@
 		4C43669C1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C43669A1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift */; };
 		4C43669D1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C43669A1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift */; };
 		4C43669E1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C43669A1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift */; };
+		4C4466E621F2866000AC9703 /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4466E521F2866000AC9703 /* RedirectHandler.swift */; };
+		4C4466E721F2866000AC9703 /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4466E521F2866000AC9703 /* RedirectHandler.swift */; };
+		4C4466E821F2866000AC9703 /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4466E521F2866000AC9703 /* RedirectHandler.swift */; };
+		4C4466E921F2866000AC9703 /* RedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4466E521F2866000AC9703 /* RedirectHandler.swift */; };
 		4C743CF61C22772D00BCB23E /* certDER.cer in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F831C1A72F8002DA1A9 /* certDER.cer */; };
 		4C743CF71C22772D00BCB23E /* certDER.crt in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F841C1A72F8002DA1A9 /* certDER.crt */; };
 		4C743CF81C22772D00BCB23E /* certDER.der in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F851C1A72F8002DA1A9 /* certDER.der */; };
@@ -378,6 +382,7 @@
 		4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachabilityManager.swift; sourceTree = "<group>"; };
 		4C3D00571C66A8B900D1F709 /* NetworkReachabilityManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachabilityManagerTests.swift; sourceTree = "<group>"; };
 		4C43669A1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Alamofire.swift"; sourceTree = "<group>"; };
+		4C4466E521F2866000AC9703 /* RedirectHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedirectHandler.swift; sourceTree = "<group>"; };
 		4C811F8C1B51856D00E0F59A /* ServerTrustEvaluation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustEvaluation.swift; sourceTree = "<group>"; };
 		4C812C3A1B535F220017E0BF /* alamofire-root-ca.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = "alamofire-root-ca.cer"; path = "alamofire.org/alamofire-root-ca.cer"; sourceTree = "<group>"; };
 		4C812C3D1B535F2E0017E0BF /* alamofire-signing-ca1.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = "alamofire-signing-ca1.cer"; path = "alamofire.org/alamofire-signing-ca1.cer"; sourceTree = "<group>"; };
@@ -697,6 +702,7 @@
 				4C23EB421B327C5B0090E0BC /* MultipartFormData.swift */,
 				311B198F20B0D3B40036823B /* MultipartUpload.swift */,
 				4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */,
+				4C4466E521F2866000AC9703 /* RedirectHandler.swift */,
 				319917AE209CE34E00103A19 /* RequestAdapter.swift */,
 				319917B3209CE36E00103A19 /* RequestRetrier.swift */,
 				4CDE2C451AF89FF300BABAE5 /* ResponseSerialization.swift */,
@@ -1294,6 +1300,7 @@
 				4C43669D1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */,
 				4C3D00561C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				311B199220B0E3480036823B /* MultipartUpload.swift in Sources */,
+				4C4466E821F2866000AC9703 /* RedirectHandler.swift in Sources */,
 				319917A2209CDA7F00103A19 /* SessionStateProvider.swift in Sources */,
 				4CF6270A1BA7CBF60011A099 /* ParameterEncoding.swift in Sources */,
 				31991796209CDA7F00103A19 /* Request.swift in Sources */,
@@ -1359,6 +1366,7 @@
 				4C43669C1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */,
 				4C811F8E1B51856D00E0F59A /* ServerTrustEvaluation.swift in Sources */,
 				311B199120B0E3470036823B /* MultipartUpload.swift in Sources */,
+				4C4466E721F2866000AC9703 /* RedirectHandler.swift in Sources */,
 				319917A1209CDA7F00103A19 /* SessionStateProvider.swift in Sources */,
 				4C3D00551C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				31991795209CDA7F00103A19 /* Request.swift in Sources */,
@@ -1393,6 +1401,7 @@
 				4C43669E1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */,
 				E4202FD41B667AA100C997FB /* Alamofire.swift in Sources */,
 				311B199320B0E3480036823B /* MultipartUpload.swift in Sources */,
+				4C4466E921F2866000AC9703 /* RedirectHandler.swift in Sources */,
 				319917A3209CDA7F00103A19 /* SessionStateProvider.swift in Sources */,
 				E4202FD51B667AA100C997FB /* MultipartFormData.swift in Sources */,
 				31991797209CDA7F00103A19 /* Request.swift in Sources */,
@@ -1427,6 +1436,7 @@
 				4C43669B1D7BB93D00C38AAD /* DispatchQueue+Alamofire.swift in Sources */,
 				4C3D00541C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				311B199020B0D3B40036823B /* MultipartUpload.swift in Sources */,
+				4C4466E621F2866000AC9703 /* RedirectHandler.swift in Sources */,
 				319917A0209CDA7F00103A19 /* SessionStateProvider.swift in Sources */,
 				4CDE2C431AF89F0900BABAE5 /* Validation.swift in Sources */,
 				31991794209CDA7F00103A19 /* Request.swift in Sources */,

+ 46 - 0
Source/RedirectHandler.swift

@@ -0,0 +1,46 @@
+//
+//  RedirectHandler.swift
+//
+//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+//
+
+import Foundation
+
+/// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request.
+public protocol RedirectHandler {
+    /// Determines how the HTTP redirect response should be redirected to the new request.
+    ///
+    /// The `completion` closure should be passed one of three possible options:
+    ///
+    ///   1. The new request specified by the redirect (this is the most common use case).
+    ///   2. A modified version of the new request (you may want to route it somewhere else).
+    ///   3. A `nil` value to deny the redirect request and return the body of the redirect response.
+    ///
+    /// - Parameters:
+    ///   - task:       The task whose request resulted in a redirect.
+    ///   - request:    The URL request object to the new location specified by the redirect response.
+    ///   - response:   The response containing the server's response to the original request.
+    ///   - completion: The closure to execute containing the new request, a modified request, or `nil`.
+    func task(_ task: URLSessionTask,
+              willBeRedirectedTo request: URLRequest,
+              for response: HTTPURLResponse,
+              completion: @escaping (URLRequest?) -> Void)
+}

+ 21 - 1
Source/Request.swift

@@ -80,6 +80,8 @@ open class Request {
         var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
         /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks.
         var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
+        /// `RetryHandler` provided for redirect responses.
+        var redirectHandler: RedirectHandler?
         /// `URLCredential` used for authentication challenges.
         var credential: URLCredential?
         /// All `URLRequest`s created by Alamofire on behalf of the `Request`.
@@ -132,6 +134,13 @@ open class Request {
         set { protectedMutableState.write { $0.downloadProgressHandler = newValue } }
     }
 
+    // Redirects
+
+    public private(set) var redirectHandler: RedirectHandler? {
+        get { return protectedMutableState.directValue.redirectHandler }
+        set { protectedMutableState.write { $0.redirectHandler = newValue } }
+    }
+
     // Credential
 
     /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods.
@@ -198,7 +207,6 @@ open class Request {
         set { protectedMutableState.write { $0.error = newValue } }
     }
 
-
     /// Default initializer for the `Request` superclass.
     ///
     /// - Parameters:
@@ -486,6 +494,18 @@ open class Request {
 
         return self
     }
+
+    // MARK: - Redirects
+
+    /// Sets the redirect handler for the `Request` which will be used if a redirect response is encountered.
+    ///
+    /// - Parameter handler: The redirect handler.
+    /// - Returns: The `Request`.
+    @discardableResult
+    open func redirect(with handler: RedirectHandler) -> Self {
+        protectedMutableState.write { $0.redirectHandler = handler }
+        return self
+    }
 }
 
 // MARK: - Protocol Conformances

+ 9 - 4
Source/Session.swift

@@ -34,6 +34,7 @@ open class Session {
     public let adapter: RequestAdapter?
     public let retrier: RequestRetrier?
     public let serverTrustManager: ServerTrustManager?
+    public let redirectHandler: RedirectHandler?
 
     public let session: URLSession
     public let eventMonitor: CompositeEventMonitor
@@ -49,8 +50,9 @@ open class Session {
                 requestQueue: DispatchQueue? = nil,
                 serializationQueue: DispatchQueue? = nil,
                 adapter: RequestAdapter? = nil,
-                serverTrustManager: ServerTrustManager? = nil,
                 retrier: RequestRetrier? = nil,
+                serverTrustManager: ServerTrustManager? = nil,
+                redirectHandler: RedirectHandler? = nil,
                 eventMonitors: [EventMonitor] = []) {
         precondition(session.delegate === delegate,
                      "SessionManager(session:) initializer must be passed the delegate that has been assigned to the URLSession as the SessionDataProvider.")
@@ -66,6 +68,7 @@ open class Session {
         self.adapter = adapter
         self.retrier = retrier
         self.serverTrustManager = serverTrustManager
+        self.redirectHandler = redirectHandler
         eventMonitor = CompositeEventMonitor(monitors: defaultEventMonitors + eventMonitors)
         delegate.eventMonitor = eventMonitor
         delegate.stateProvider = self
@@ -78,8 +81,9 @@ open class Session {
                             requestQueue: DispatchQueue? = nil,
                             serializationQueue: DispatchQueue? = nil,
                             adapter: RequestAdapter? = nil,
-                            serverTrustManager: ServerTrustManager? = nil,
                             retrier: RequestRetrier? = nil,
+                            serverTrustManager: ServerTrustManager? = nil,
+                            redirectHandler: RedirectHandler? = nil,
                             eventMonitors: [EventMonitor] = []) {
         let delegateQueue = OperationQueue(maxConcurrentOperationCount: 1, underlyingQueue: rootQueue, name: "org.alamofire.sessionManager.sessionDelegateQueue")
         let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
@@ -90,8 +94,9 @@ open class Session {
                   requestQueue: requestQueue,
                   serializationQueue: serializationQueue,
                   adapter: adapter,
-                  serverTrustManager: serverTrustManager,
                   retrier: retrier,
+                  serverTrustManager: serverTrustManager,
+                  redirectHandler: redirectHandler,
                   eventMonitors: eventMonitors)
     }
 
@@ -525,7 +530,7 @@ extension Session: RequestDelegate {
     }
 }
 
-// MARK: - SessionDelegateDelegate
+// MARK: - SessionStateProvider
 
 extension Session: SessionStateProvider {
     public func request(for task: URLSessionTask) -> Request? {

+ 6 - 1
Source/SessionStateProvider.swift

@@ -28,6 +28,7 @@ public protocol SessionStateProvider: AnyObject {
     func request(for task: URLSessionTask) -> Request?
     func didCompleteTask(_ task: URLSessionTask)
     var serverTrustManager: ServerTrustManager? { get }
+    var redirectHandler: RedirectHandler? { get }
     func credential(for task: URLSessionTask, protectionSpace: URLProtectionSpace) -> URLCredential?
 }
 
@@ -145,7 +146,11 @@ extension SessionDelegate: URLSessionTaskDelegate {
                          completionHandler: @escaping (URLRequest?) -> Void) {
         eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request)
 
-        completionHandler(request)
+        if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler {
+            redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler)
+        } else {
+            completionHandler(request)
+        }
     }
 
     open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {