Procházet zdrojové kódy

Retrier is now called when downloads encounter adapt error.

Christian Noon před 9 roky
rodič
revize
136419bd9b
3 změnil soubory, kde provedl 80 přidání a 20 odebrání
  1. 13 9
      Source/Request.swift
  2. 32 11
      Source/SessionManager.swift
  3. 35 0
      Tests/SessionManagerTests.swift

+ 13 - 9
Source/Request.swift

@@ -445,17 +445,21 @@ open class DownloadRequest: Request {
         case resumeData(Data)
 
         func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
-            let task: URLSessionTask
+            do {
+                let task: URLSessionTask
+
+                switch self {
+                case let .request(urlRequest):
+                    let urlRequest = try urlRequest.adapt(using: adapter)
+                    task = queue.syncResult { session.downloadTask(with: urlRequest) }
+                case let .resumeData(resumeData):
+                    task = queue.syncResult { session.downloadTask(withResumeData: resumeData) }
+                }
 
-            switch self {
-            case let .request(urlRequest):
-                let urlRequest = try urlRequest.adapt(using: adapter)
-                task = queue.syncResult { session.downloadTask(with: urlRequest) }
-            case let .resumeData(resumeData):
-                task = queue.syncResult { session.downloadTask(withResumeData: resumeData) }
+                return task
+            } catch {
+                throw AdaptError(error: error)
             }
-
-            return task
         }
     }
 

+ 32 - 11
Source/SessionManager.swift

@@ -328,7 +328,7 @@ open class SessionManager {
             let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
             return download(encodedURLRequest, to: destination)
         } catch {
-            return download(failedWith: error)
+            return download(nil, to: destination, failedWith: error)
         }
     }
 
@@ -354,7 +354,7 @@ open class SessionManager {
             let urlRequest = try urlRequest.asURLRequest()
             return download(.request(urlRequest), to: destination)
         } catch {
-            return download(failedWith: error)
+            return download(nil, to: destination, failedWith: error)
         }
     }
 
@@ -399,23 +399,44 @@ open class SessionManager {
     {
         do {
             let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
-            let request = DownloadRequest(session: session, requestTask: .download(downloadable, task))
+            let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
 
-            request.downloadDelegate.destination = destination
+            download.downloadDelegate.destination = destination
 
-            delegate[task] = request
+            delegate[task] = download
 
-            if startRequestsImmediately { request.resume() }
+            if startRequestsImmediately { download.resume() }
 
-            return request
+            return download
         } catch {
-            return download(failedWith: error)
+            return download(downloadable, to: destination, failedWith: error)
         }
     }
 
-    private func download(failedWith error: Error) -> DownloadRequest {
-        let download = DownloadRequest(session: session, requestTask: .download(nil, nil), error: error)
-        if startRequestsImmediately { download.resume() }
+    private func download(
+        _ downloadable: DownloadRequest.Downloadable?,
+        to destination: DownloadRequest.DownloadFileDestination?,
+        failedWith error: Error)
+        -> DownloadRequest
+    {
+        var downloadTask: Request.RequestTask = .download(nil, nil)
+
+        if let downloadable = downloadable {
+            downloadTask = .download(downloadable, nil)
+        }
+
+        let isAdaptError = error is AdaptError
+        let error = error.extractedAdaptError
+
+        let download = DownloadRequest(session: session, requestTask: downloadTask, error: error)
+        download.downloadDelegate.destination = destination
+
+        if let retrier = retrier, isAdaptError {
+            allowRetrier(retrier, toRetry: download, with: error)
+        } else {
+            if startRequestsImmediately { download.resume() }
+        }
+
         return download
     }
 

+ 35 - 0
Tests/SessionManagerTests.swift

@@ -612,6 +612,41 @@ class SessionManagerTestCase: BaseTestCase {
         XCTAssertEqual(response?.result.isSuccess, true)
     }
 
+    func testThatSessionManagerCallsRequestRetrierWhenDownloadInitiallyEncountersAdaptError() {
+        // Given
+        let handler = RequestHandler()
+        handler.adaptedCount = 1
+        handler.throwsErrorOnSecondAdapt = true
+        handler.shouldApplyAuthorizationHeader = true
+
+        let sessionManager = SessionManager()
+        sessionManager.adapter = handler
+        sessionManager.retrier = handler
+
+        let expectation = self.expectation(description: "request should eventually fail")
+        var response: DownloadResponse<Any>?
+
+        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
+            let fileURL = self.testDirectoryURL.appendingPathComponent("test-output.json")
+            return (fileURL, [.removePreviousFile])
+        }
+
+        // When
+        sessionManager.download("https://httpbin.org/basic-auth/user/password", to: destination)
+            .validate()
+            .responseJSON { jsonResponse in
+                response = jsonResponse
+                expectation.fulfill()
+            }
+
+        waitForExpectations(timeout: timeout, handler: nil)
+
+        // Then
+        XCTAssertEqual(handler.adaptedCount, 2)
+        XCTAssertEqual(handler.retryCount, 1)
+        XCTAssertEqual(response?.result.isSuccess, true)
+    }
+
     func testThatSessionManagerCallsAdapterWhenRequestIsRetried() {
         // Given
         let handler = RequestHandler()