Skip to content

Commit 0397558

Browse files
committed
do not remove session
1 parent 795725e commit 0397558

File tree

5 files changed

+43
-139
lines changed

5 files changed

+43
-139
lines changed

Sources/Auth/AuthError.swift

-10
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,3 @@ public enum AuthError: LocalizedError, Equatable {
298298
return lhs == rhs
299299
}
300300
}
301-
302-
extension AuthError: RetryableError {
303-
package var shouldRetry: Bool {
304-
switch self {
305-
case .api(_, _, _, let response):
306-
defaultRetryableHTTPStatusCodes.contains(response.statusCode)
307-
default: false
308-
}
309-
}
310-
}

Sources/Auth/Internal/SessionManager.swift

+16-44
Original file line numberDiff line numberDiff line change
@@ -78,52 +78,24 @@ private actor LiveSessionManager {
7878
logger?.debug("Refresh task ended")
7979
}
8080

81-
do {
82-
let session = try await api.execute(
83-
HTTPRequest(
84-
url: configuration.url.appendingPathComponent("token"),
85-
method: .post,
86-
query: [
87-
URLQueryItem(name: "grant_type", value: "refresh_token")
88-
],
89-
body: configuration.encoder.encode(
90-
UserCredentials(refreshToken: refreshToken)
91-
)
81+
let session = try await api.execute(
82+
HTTPRequest(
83+
url: configuration.url.appendingPathComponent("token"),
84+
method: .post,
85+
query: [
86+
URLQueryItem(name: "grant_type", value: "refresh_token")
87+
],
88+
body: configuration.encoder.encode(
89+
UserCredentials(refreshToken: refreshToken)
9290
)
9391
)
94-
.decoded(as: Session.self, decoder: configuration.decoder)
95-
96-
update(session)
97-
eventEmitter.emit(.tokenRefreshed, session: session)
98-
99-
return session
100-
} catch {
101-
logger?.debug("Refresh token failed with error: \(error)")
102-
103-
if let error = error as? URLError, error.code == .cancelled {
104-
throw error
105-
}
106-
107-
if error is CancellationError {
108-
throw error
109-
}
110-
111-
// DO NOT remove session in case it is an error that should be retried.
112-
// i.e. server instability, connection issues, ...
113-
//
114-
// Need to do this check here, because not all RetryableError's should be retried.
115-
// URLError conforms to RetryableError, but only a subset of URLError should be retried,
116-
// the same is true for AuthError.
117-
if let error = error as? URLError, error.shouldRetry {
118-
throw error
119-
} else if let error = error as? any RetryableError, error.shouldRetry {
120-
throw error
121-
} else {
122-
logger?.debug("No retryable error, removing session")
123-
remove()
124-
throw error
125-
}
126-
}
92+
)
93+
.decoded(as: Session.self, decoder: configuration.decoder)
94+
95+
update(session)
96+
eventEmitter.emit(.tokenRefreshed, session: session)
97+
98+
return session
12799
}
128100

129101
return try await inFlightRefreshTask!.value

Sources/Helpers/HTTP/RetryRequestInterceptor.swift

+27-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ package actor RetryRequestInterceptor: HTTPClientInterceptor {
3030
.delete, .get, .head, .options, .put, .trace,
3131
]
3232

33+
/// The default set of retryable URL error codes.
34+
package static let defaultRetryableURLErrorCodes: Set<URLError.Code> = [
35+
.backgroundSessionInUseByAnotherProcess, .backgroundSessionWasDisconnected,
36+
.badServerResponse, .callIsActive, .cannotConnectToHost, .cannotFindHost,
37+
.cannotLoadFromNetwork, .dataNotAllowed, .dnsLookupFailed,
38+
.downloadDecodingFailedMidStream, .downloadDecodingFailedToComplete,
39+
.internationalRoamingOff, .networkConnectionLost, .notConnectedToInternet,
40+
.secureConnectionFailed, .serverCertificateHasBadDate,
41+
.serverCertificateNotYetValid, .timedOut,
42+
]
43+
44+
/// The default set of retryable HTTP status codes.
45+
package static let defaultRetryableHTTPStatusCodes: Set<Int> = [
46+
408, 500, 502, 503, 504,
47+
]
48+
3349
/// The maximum number of retries.
3450
package let retryLimit: Int
3551
/// The base value for exponential backoff.
@@ -56,12 +72,14 @@ package actor RetryRequestInterceptor: HTTPClientInterceptor {
5672
retryLimit: Int = RetryRequestInterceptor.defaultRetryLimit,
5773
exponentialBackoffBase: UInt = RetryRequestInterceptor.defaultExponentialBackoffBase,
5874
exponentialBackoffScale: Double = RetryRequestInterceptor.defaultExponentialBackoffScale,
59-
retryableHTTPMethods: Set<HTTPTypes.HTTPRequest.Method> = RetryRequestInterceptor.defaultRetryableHTTPMethods,
60-
retryableHTTPStatusCodes: Set<Int> = defaultRetryableHTTPStatusCodes,
61-
retryableErrorCodes: Set<URLError.Code> = defaultRetryableURLErrorCodes
75+
retryableHTTPMethods: Set<HTTPTypes.HTTPRequest.Method> = RetryRequestInterceptor
76+
.defaultRetryableHTTPMethods,
77+
retryableHTTPStatusCodes: Set<Int> = RetryRequestInterceptor.defaultRetryableHTTPStatusCodes,
78+
retryableErrorCodes: Set<URLError.Code> = RetryRequestInterceptor.defaultRetryableURLErrorCodes
6279
) {
6380
precondition(
64-
exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2."
81+
exponentialBackoffBase >= 2,
82+
"The `exponentialBackoffBase` must be a minimum of 2."
6583
)
6684

6785
self.retryLimit = retryLimit
@@ -114,10 +132,11 @@ package actor RetryRequestInterceptor: HTTPClientInterceptor {
114132
}
115133

116134
if retryCount < retryLimit, shouldRetry(request: request, result: result) {
117-
let retryDelay = pow(
118-
Double(exponentialBackoffBase),
119-
Double(retryCount)
120-
) * exponentialBackoffScale
135+
let retryDelay =
136+
pow(
137+
Double(exponentialBackoffBase),
138+
Double(retryCount)
139+
) * exponentialBackoffScale
121140

122141
let nanoseconds = UInt64(retryDelay)
123142
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * nanoseconds)

Sources/Helpers/RetryableError.swift

-35
This file was deleted.

Tests/AuthTests/SessionManagerTests.swift

-42
Original file line numberDiff line numberDiff line change
@@ -115,46 +115,4 @@ final class SessionManagerTests: XCTestCase {
115115
XCTAssertEqual(refreshSessionCallCount.value, 1)
116116
XCTAssertEqual(try result.map { try $0.get() }, (0..<10).map { _ in validSession })
117117
}
118-
119-
func testRefreshSession_shouldNotRemoveSession_whenErrorIsURLErrorCancelled() async throws {
120-
let currentSession = Session.validSession
121-
Dependencies[clientID].sessionStorage.store(currentSession)
122-
123-
await http.when(
124-
{ $0.url.path.contains("/token") },
125-
return: { _ in
126-
throw URLError(.cancelled)
127-
}
128-
)
129-
130-
do {
131-
_ = try await sut.refreshSession(currentSession.refreshToken)
132-
XCTFail("Expected a \(URLError.cancelled) failure")
133-
} catch let error as URLError {
134-
XCTAssertEqual(error.code, .cancelled)
135-
}
136-
137-
XCTAssertNotNil(Dependencies[clientID].sessionStorage.get())
138-
}
139-
140-
func testRefreshSession_shouldNotRemoveSession_whenErrorIsCancellationError() async throws {
141-
let currentSession = Session.validSession
142-
Dependencies[clientID].sessionStorage.store(currentSession)
143-
144-
await http.when(
145-
{ $0.url.path.contains("/token") },
146-
return: { _ in
147-
throw CancellationError()
148-
}
149-
)
150-
151-
do {
152-
_ = try await sut.refreshSession(currentSession.refreshToken)
153-
XCTFail("Expected a \(CancellationError.self) failure")
154-
} catch is CancellationError {
155-
// no-op
156-
}
157-
158-
XCTAssertNotNil(Dependencies[clientID].sessionStorage.get())
159-
}
160118
}

0 commit comments

Comments
 (0)