1 |
efrain |
1 |
//
|
|
|
2 |
// RetryPolicy.swift
|
|
|
3 |
//
|
|
|
4 |
// Copyright (c) 2019-2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
|
5 |
//
|
|
|
6 |
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
7 |
// of this software and associated documentation files (the "Software"), to deal
|
|
|
8 |
// in the Software without restriction, including without limitation the rights
|
|
|
9 |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
10 |
// copies of the Software, and to permit persons to whom the Software is
|
|
|
11 |
// furnished to do so, subject to the following conditions:
|
|
|
12 |
//
|
|
|
13 |
// The above copyright notice and this permission notice shall be included in
|
|
|
14 |
// all copies or substantial portions of the Software.
|
|
|
15 |
//
|
|
|
16 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
17 |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
18 |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19 |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
20 |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
21 |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
22 |
// THE SOFTWARE.
|
|
|
23 |
//
|
|
|
24 |
|
|
|
25 |
import Foundation
|
|
|
26 |
|
|
|
27 |
/// A retry policy that retries requests using an exponential backoff for allowed HTTP methods and HTTP status codes
|
|
|
28 |
/// as well as certain types of networking errors.
|
|
|
29 |
open class RetryPolicy: RequestInterceptor {
|
|
|
30 |
/// The default retry limit for retry policies.
|
|
|
31 |
public static let defaultRetryLimit: UInt = 2
|
|
|
32 |
|
|
|
33 |
/// The default exponential backoff base for retry policies (must be a minimum of 2).
|
|
|
34 |
public static let defaultExponentialBackoffBase: UInt = 2
|
|
|
35 |
|
|
|
36 |
/// The default exponential backoff scale for retry policies.
|
|
|
37 |
public static let defaultExponentialBackoffScale: Double = 0.5
|
|
|
38 |
|
|
|
39 |
/// The default HTTP methods to retry.
|
|
|
40 |
/// See [RFC 2616 - Section 9.1.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more information.
|
|
|
41 |
public static let defaultRetryableHTTPMethods: Set<HTTPMethod> = [.delete, // [Delete](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) - not always idempotent
|
|
|
42 |
.get, // [GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) - generally idempotent
|
|
|
43 |
.head, // [HEAD](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4) - generally idempotent
|
|
|
44 |
.options, // [OPTIONS](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2) - inherently idempotent
|
|
|
45 |
.put, // [PUT](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) - not always idempotent
|
|
|
46 |
.trace // [TRACE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8) - inherently idempotent
|
|
|
47 |
]
|
|
|
48 |
|
|
|
49 |
/// The default HTTP status codes to retry.
|
|
|
50 |
/// See [RFC 2616 - Section 10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10) for more information.
|
|
|
51 |
public static let defaultRetryableHTTPStatusCodes: Set<Int> = [408, // [Request Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9)
|
|
|
52 |
500, // [Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1)
|
|
|
53 |
502, // [Bad Gateway](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3)
|
|
|
54 |
503, // [Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4)
|
|
|
55 |
504 // [Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5)
|
|
|
56 |
]
|
|
|
57 |
|
|
|
58 |
/// The default URL error codes to retry.
|
|
|
59 |
public static let defaultRetryableURLErrorCodes: Set<URLError.Code> = [// [Security] App Transport Security disallowed a connection because there is no secure network connection.
|
|
|
60 |
// - [Disabled] ATS settings do not change at runtime.
|
|
|
61 |
// .appTransportSecurityRequiresSecureConnection,
|
|
|
62 |
|
|
|
63 |
// [System] An app or app extension attempted to connect to a background session that is already connected to a
|
|
|
64 |
// process.
|
|
|
65 |
// - [Enabled] The other process could release the background session.
|
|
|
66 |
.backgroundSessionInUseByAnotherProcess,
|
|
|
67 |
|
|
|
68 |
// [System] The shared container identifier of the URL session configuration is needed but has not been set.
|
|
|
69 |
// - [Disabled] Cannot change at runtime.
|
|
|
70 |
// .backgroundSessionRequiresSharedContainer,
|
|
|
71 |
|
|
|
72 |
// [System] The app is suspended or exits while a background data task is processing.
|
|
|
73 |
// - [Enabled] App can be foregrounded or launched to recover.
|
|
|
74 |
.backgroundSessionWasDisconnected,
|
|
|
75 |
|
|
|
76 |
// [Network] The URL Loading system received bad data from the server.
|
|
|
77 |
// - [Enabled] Server could return valid data when retrying.
|
|
|
78 |
.badServerResponse,
|
|
|
79 |
|
|
|
80 |
// [Resource] A malformed URL prevented a URL request from being initiated.
|
|
|
81 |
// - [Disabled] URL was most likely constructed incorrectly.
|
|
|
82 |
// .badURL,
|
|
|
83 |
|
|
|
84 |
// [System] A connection was attempted while a phone call is active on a network that does not support
|
|
|
85 |
// simultaneous phone and data communication (EDGE or GPRS).
|
|
|
86 |
// - [Enabled] Phone call could be ended to allow request to recover.
|
|
|
87 |
.callIsActive,
|
|
|
88 |
|
|
|
89 |
// [Client] An asynchronous load has been canceled.
|
|
|
90 |
// - [Disabled] Request was cancelled by the client.
|
|
|
91 |
// .cancelled,
|
|
|
92 |
|
|
|
93 |
// [File System] A download task couldn’t close the downloaded file on disk.
|
|
|
94 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
95 |
// .cannotCloseFile,
|
|
|
96 |
|
|
|
97 |
// [Network] An attempt to connect to a host failed.
|
|
|
98 |
// - [Enabled] Server or DNS lookup could recover during retry.
|
|
|
99 |
.cannotConnectToHost,
|
|
|
100 |
|
|
|
101 |
// [File System] A download task couldn’t create the downloaded file on disk because of an I/O failure.
|
|
|
102 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
103 |
// .cannotCreateFile,
|
|
|
104 |
|
|
|
105 |
// [Data] Content data received during a connection request had an unknown content encoding.
|
|
|
106 |
// - [Disabled] Server is unlikely to modify the content encoding during a retry.
|
|
|
107 |
// .cannotDecodeContentData,
|
|
|
108 |
|
|
|
109 |
// [Data] Content data received during a connection request could not be decoded for a known content encoding.
|
|
|
110 |
// - [Disabled] Server is unlikely to modify the content encoding during a retry.
|
|
|
111 |
// .cannotDecodeRawData,
|
|
|
112 |
|
|
|
113 |
// [Network] The host name for a URL could not be resolved.
|
|
|
114 |
// - [Enabled] Server or DNS lookup could recover during retry.
|
|
|
115 |
.cannotFindHost,
|
|
|
116 |
|
|
|
117 |
// [Network] A request to load an item only from the cache could not be satisfied.
|
|
|
118 |
// - [Enabled] Cache could be populated during a retry.
|
|
|
119 |
.cannotLoadFromNetwork,
|
|
|
120 |
|
|
|
121 |
// [File System] A download task was unable to move a downloaded file on disk.
|
|
|
122 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
123 |
// .cannotMoveFile,
|
|
|
124 |
|
|
|
125 |
// [File System] A download task was unable to open the downloaded file on disk.
|
|
|
126 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
127 |
// .cannotOpenFile,
|
|
|
128 |
|
|
|
129 |
// [Data] A task could not parse a response.
|
|
|
130 |
// - [Disabled] Invalid response is unlikely to recover with retry.
|
|
|
131 |
// .cannotParseResponse,
|
|
|
132 |
|
|
|
133 |
// [File System] A download task was unable to remove a downloaded file from disk.
|
|
|
134 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
135 |
// .cannotRemoveFile,
|
|
|
136 |
|
|
|
137 |
// [File System] A download task was unable to write to the downloaded file on disk.
|
|
|
138 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
139 |
// .cannotWriteToFile,
|
|
|
140 |
|
|
|
141 |
// [Security] A client certificate was rejected.
|
|
|
142 |
// - [Disabled] Client certificate is unlikely to change with retry.
|
|
|
143 |
// .clientCertificateRejected,
|
|
|
144 |
|
|
|
145 |
// [Security] A client certificate was required to authenticate an SSL connection during a request.
|
|
|
146 |
// - [Disabled] Client certificate is unlikely to be provided with retry.
|
|
|
147 |
// .clientCertificateRequired,
|
|
|
148 |
|
|
|
149 |
// [Data] The length of the resource data exceeds the maximum allowed.
|
|
|
150 |
// - [Disabled] Resource will likely still exceed the length maximum on retry.
|
|
|
151 |
// .dataLengthExceedsMaximum,
|
|
|
152 |
|
|
|
153 |
// [System] The cellular network disallowed a connection.
|
|
|
154 |
// - [Enabled] WiFi connection could be established during retry.
|
|
|
155 |
.dataNotAllowed,
|
|
|
156 |
|
|
|
157 |
// [Network] The host address could not be found via DNS lookup.
|
|
|
158 |
// - [Enabled] DNS lookup could succeed during retry.
|
|
|
159 |
.dnsLookupFailed,
|
|
|
160 |
|
|
|
161 |
// [Data] A download task failed to decode an encoded file during the download.
|
|
|
162 |
// - [Enabled] Server could correct the decoding issue with retry.
|
|
|
163 |
.downloadDecodingFailedMidStream,
|
|
|
164 |
|
|
|
165 |
// [Data] A download task failed to decode an encoded file after downloading.
|
|
|
166 |
// - [Enabled] Server could correct the decoding issue with retry.
|
|
|
167 |
.downloadDecodingFailedToComplete,
|
|
|
168 |
|
|
|
169 |
// [File System] A file does not exist.
|
|
|
170 |
// - [Disabled] File system error is unlikely to recover with retry.
|
|
|
171 |
// .fileDoesNotExist,
|
|
|
172 |
|
|
|
173 |
// [File System] A request for an FTP file resulted in the server responding that the file is not a plain file,
|
|
|
174 |
// but a directory.
|
|
|
175 |
// - [Disabled] FTP directory is not likely to change to a file during a retry.
|
|
|
176 |
// .fileIsDirectory,
|
|
|
177 |
|
|
|
178 |
// [Network] A redirect loop has been detected or the threshold for number of allowable redirects has been
|
|
|
179 |
// exceeded (currently 16).
|
|
|
180 |
// - [Disabled] The redirect loop is unlikely to be resolved within the retry window.
|
|
|
181 |
// .httpTooManyRedirects,
|
|
|
182 |
|
|
|
183 |
// [System] The attempted connection required activating a data context while roaming, but international roaming
|
|
|
184 |
// is disabled.
|
|
|
185 |
// - [Enabled] WiFi connection could be established during retry.
|
|
|
186 |
.internationalRoamingOff,
|
|
|
187 |
|
|
|
188 |
// [Connectivity] A client or server connection was severed in the middle of an in-progress load.
|
|
|
189 |
// - [Enabled] A network connection could be established during retry.
|
|
|
190 |
.networkConnectionLost,
|
|
|
191 |
|
|
|
192 |
// [File System] A resource couldn’t be read because of insufficient permissions.
|
|
|
193 |
// - [Disabled] Permissions are unlikely to be granted during retry.
|
|
|
194 |
// .noPermissionsToReadFile,
|
|
|
195 |
|
|
|
196 |
// [Connectivity] A network resource was requested, but an internet connection has not been established and
|
|
|
197 |
// cannot be established automatically.
|
|
|
198 |
// - [Enabled] A network connection could be established during retry.
|
|
|
199 |
.notConnectedToInternet,
|
|
|
200 |
|
|
|
201 |
// [Resource] A redirect was specified by way of server response code, but the server did not accompany this
|
|
|
202 |
// code with a redirect URL.
|
|
|
203 |
// - [Disabled] The redirect URL is unlikely to be supplied during a retry.
|
|
|
204 |
// .redirectToNonExistentLocation,
|
|
|
205 |
|
|
|
206 |
// [Client] A body stream is needed but the client did not provide one.
|
|
|
207 |
// - [Disabled] The client will be unlikely to supply a body stream during retry.
|
|
|
208 |
// .requestBodyStreamExhausted,
|
|
|
209 |
|
|
|
210 |
// [Resource] A requested resource couldn’t be retrieved.
|
|
|
211 |
// - [Disabled] The resource is unlikely to become available during the retry window.
|
|
|
212 |
// .resourceUnavailable,
|
|
|
213 |
|
|
|
214 |
// [Security] An attempt to establish a secure connection failed for reasons that can’t be expressed more
|
|
|
215 |
// specifically.
|
|
|
216 |
// - [Enabled] The secure connection could be established during a retry given the lack of specificity
|
|
|
217 |
// provided by the error.
|
|
|
218 |
.secureConnectionFailed,
|
|
|
219 |
|
|
|
220 |
// [Security] A server certificate had a date which indicates it has expired, or is not yet valid.
|
|
|
221 |
// - [Enabled] The server certificate could become valid within the retry window.
|
|
|
222 |
.serverCertificateHasBadDate,
|
|
|
223 |
|
|
|
224 |
// [Security] A server certificate was not signed by any root server.
|
|
|
225 |
// - [Disabled] The server certificate is unlikely to change during the retry window.
|
|
|
226 |
// .serverCertificateHasUnknownRoot,
|
|
|
227 |
|
|
|
228 |
// [Security] A server certificate is not yet valid.
|
|
|
229 |
// - [Enabled] The server certificate could become valid within the retry window.
|
|
|
230 |
.serverCertificateNotYetValid,
|
|
|
231 |
|
|
|
232 |
// [Security] A server certificate was signed by a root server that isn’t trusted.
|
|
|
233 |
// - [Disabled] The server certificate is unlikely to become trusted within the retry window.
|
|
|
234 |
// .serverCertificateUntrusted,
|
|
|
235 |
|
|
|
236 |
// [Network] An asynchronous operation timed out.
|
|
|
237 |
// - [Enabled] The request timed out for an unknown reason and should be retried.
|
|
|
238 |
.timedOut
|
|
|
239 |
|
|
|
240 |
// [System] The URL Loading System encountered an error that it can’t interpret.
|
|
|
241 |
// - [Disabled] The error could not be interpreted and is unlikely to be recovered from during a retry.
|
|
|
242 |
// .unknown,
|
|
|
243 |
|
|
|
244 |
// [Resource] A properly formed URL couldn’t be handled by the framework.
|
|
|
245 |
// - [Disabled] The URL is unlikely to change during a retry.
|
|
|
246 |
// .unsupportedURL,
|
|
|
247 |
|
|
|
248 |
// [Client] Authentication is required to access a resource.
|
|
|
249 |
// - [Disabled] The user authentication is unlikely to be provided by retrying.
|
|
|
250 |
// .userAuthenticationRequired,
|
|
|
251 |
|
|
|
252 |
// [Client] An asynchronous request for authentication has been canceled by the user.
|
|
|
253 |
// - [Disabled] The user cancelled authentication and explicitly took action to not retry.
|
|
|
254 |
// .userCancelledAuthentication,
|
|
|
255 |
|
|
|
256 |
// [Resource] A server reported that a URL has a non-zero content length, but terminated the network connection
|
|
|
257 |
// gracefully without sending any data.
|
|
|
258 |
// - [Disabled] The server is unlikely to provide data during the retry window.
|
|
|
259 |
// .zeroByteResource,
|
|
|
260 |
]
|
|
|
261 |
|
|
|
262 |
/// The total number of times the request is allowed to be retried.
|
|
|
263 |
public let retryLimit: UInt
|
|
|
264 |
|
|
|
265 |
/// The base of the exponential backoff policy (should always be greater than or equal to 2).
|
|
|
266 |
public let exponentialBackoffBase: UInt
|
|
|
267 |
|
|
|
268 |
/// The scale of the exponential backoff.
|
|
|
269 |
public let exponentialBackoffScale: Double
|
|
|
270 |
|
|
|
271 |
/// The HTTP methods that are allowed to be retried.
|
|
|
272 |
public let retryableHTTPMethods: Set<HTTPMethod>
|
|
|
273 |
|
|
|
274 |
/// The HTTP status codes that are automatically retried by the policy.
|
|
|
275 |
public let retryableHTTPStatusCodes: Set<Int>
|
|
|
276 |
|
|
|
277 |
/// The URL error codes that are automatically retried by the policy.
|
|
|
278 |
public let retryableURLErrorCodes: Set<URLError.Code>
|
|
|
279 |
|
|
|
280 |
/// Creates a `RetryPolicy` from the specified parameters.
|
|
|
281 |
///
|
|
|
282 |
/// - Parameters:
|
|
|
283 |
/// - retryLimit: The total number of times the request is allowed to be retried. `2` by default.
|
|
|
284 |
/// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default.
|
|
|
285 |
/// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default.
|
|
|
286 |
/// - retryableHTTPMethods: The HTTP methods that are allowed to be retried.
|
|
|
287 |
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
|
288 |
/// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy.
|
|
|
289 |
/// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default.
|
|
|
290 |
/// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy.
|
|
|
291 |
/// `RetryPolicy.defaultRetryableURLErrorCodes` by default.
|
|
|
292 |
public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
|
293 |
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
|
294 |
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
|
295 |
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods,
|
|
|
296 |
retryableHTTPStatusCodes: Set<Int> = RetryPolicy.defaultRetryableHTTPStatusCodes,
|
|
|
297 |
retryableURLErrorCodes: Set<URLError.Code> = RetryPolicy.defaultRetryableURLErrorCodes) {
|
|
|
298 |
precondition(exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2.")
|
|
|
299 |
|
|
|
300 |
self.retryLimit = retryLimit
|
|
|
301 |
self.exponentialBackoffBase = exponentialBackoffBase
|
|
|
302 |
self.exponentialBackoffScale = exponentialBackoffScale
|
|
|
303 |
self.retryableHTTPMethods = retryableHTTPMethods
|
|
|
304 |
self.retryableHTTPStatusCodes = retryableHTTPStatusCodes
|
|
|
305 |
self.retryableURLErrorCodes = retryableURLErrorCodes
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
open func retry(_ request: Request,
|
|
|
309 |
for session: Session,
|
|
|
310 |
dueTo error: Error,
|
|
|
311 |
completion: @escaping (RetryResult) -> Void) {
|
|
|
312 |
if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) {
|
|
|
313 |
completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale))
|
|
|
314 |
} else {
|
|
|
315 |
completion(.doNotRetry)
|
|
|
316 |
}
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
/// Determines whether or not to retry the provided `Request`.
|
|
|
320 |
///
|
|
|
321 |
/// - Parameters:
|
|
|
322 |
/// - request: `Request` that failed due to the provided `Error`.
|
|
|
323 |
/// - error: `Error` encountered while executing the `Request`.
|
|
|
324 |
///
|
|
|
325 |
/// - Returns: `Bool` determining whether or not to retry the `Request`.
|
|
|
326 |
open func shouldRetry(request: Request, dueTo error: Error) -> Bool {
|
|
|
327 |
guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false }
|
|
|
328 |
|
|
|
329 |
if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) {
|
|
|
330 |
return true
|
|
|
331 |
} else {
|
|
|
332 |
let errorCode = (error as? URLError)?.code
|
|
|
333 |
let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code
|
|
|
334 |
|
|
|
335 |
guard let code = errorCode ?? afErrorCode else { return false }
|
|
|
336 |
|
|
|
337 |
return retryableURLErrorCodes.contains(code)
|
|
|
338 |
}
|
|
|
339 |
}
|
|
|
340 |
}
|
|
|
341 |
|
|
|
342 |
#if swift(>=5.5)
|
|
|
343 |
extension RequestInterceptor where Self == RetryPolicy {
|
|
|
344 |
/// Provides a default `RetryPolicy` instance.
|
|
|
345 |
public static var retryPolicy: RetryPolicy { RetryPolicy() }
|
|
|
346 |
|
|
|
347 |
/// Creates an `RetryPolicy` from the specified parameters.
|
|
|
348 |
///
|
|
|
349 |
/// - Parameters:
|
|
|
350 |
/// - retryLimit: The total number of times the request is allowed to be retried. `2` by default.
|
|
|
351 |
/// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default.
|
|
|
352 |
/// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default.
|
|
|
353 |
/// - retryableHTTPMethods: The HTTP methods that are allowed to be retried.
|
|
|
354 |
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
|
355 |
/// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy.
|
|
|
356 |
/// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default.
|
|
|
357 |
/// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy.
|
|
|
358 |
/// `RetryPolicy.defaultRetryableURLErrorCodes` by default.
|
|
|
359 |
///
|
|
|
360 |
/// - Returns: The `RetryPolicy`
|
|
|
361 |
public static func retryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
|
362 |
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
|
363 |
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
|
364 |
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods,
|
|
|
365 |
retryableHTTPStatusCodes: Set<Int> = RetryPolicy.defaultRetryableHTTPStatusCodes,
|
|
|
366 |
retryableURLErrorCodes: Set<URLError.Code> = RetryPolicy.defaultRetryableURLErrorCodes) -> RetryPolicy {
|
|
|
367 |
RetryPolicy(retryLimit: retryLimit,
|
|
|
368 |
exponentialBackoffBase: exponentialBackoffBase,
|
|
|
369 |
exponentialBackoffScale: exponentialBackoffScale,
|
|
|
370 |
retryableHTTPMethods: retryableHTTPMethods,
|
|
|
371 |
retryableHTTPStatusCodes: retryableHTTPStatusCodes,
|
|
|
372 |
retryableURLErrorCodes: retryableURLErrorCodes)
|
|
|
373 |
}
|
|
|
374 |
}
|
|
|
375 |
#endif
|
|
|
376 |
|
|
|
377 |
// MARK: -
|
|
|
378 |
|
|
|
379 |
/// A retry policy that automatically retries idempotent requests for network connection lost errors. For more
|
|
|
380 |
/// information about retrying network connection lost errors, please refer to Apple's
|
|
|
381 |
/// [technical document](https://developer.apple.com/library/content/qa/qa1941/_index.html).
|
|
|
382 |
open class ConnectionLostRetryPolicy: RetryPolicy {
|
|
|
383 |
/// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters.
|
|
|
384 |
///
|
|
|
385 |
/// - Parameters:
|
|
|
386 |
/// - retryLimit: The total number of times the request is allowed to be retried.
|
|
|
387 |
/// `RetryPolicy.defaultRetryLimit` by default.
|
|
|
388 |
/// - exponentialBackoffBase: The base of the exponential backoff policy.
|
|
|
389 |
/// `RetryPolicy.defaultExponentialBackoffBase` by default.
|
|
|
390 |
/// - exponentialBackoffScale: The scale of the exponential backoff.
|
|
|
391 |
/// `RetryPolicy.defaultExponentialBackoffScale` by default.
|
|
|
392 |
/// - retryableHTTPMethods: The idempotent http methods to retry.
|
|
|
393 |
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
|
394 |
public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
|
395 |
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
|
396 |
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
|
397 |
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods) {
|
|
|
398 |
super.init(retryLimit: retryLimit,
|
|
|
399 |
exponentialBackoffBase: exponentialBackoffBase,
|
|
|
400 |
exponentialBackoffScale: exponentialBackoffScale,
|
|
|
401 |
retryableHTTPMethods: retryableHTTPMethods,
|
|
|
402 |
retryableHTTPStatusCodes: [],
|
|
|
403 |
retryableURLErrorCodes: [.networkConnectionLost])
|
|
|
404 |
}
|
|
|
405 |
}
|
|
|
406 |
|
|
|
407 |
#if swift(>=5.5)
|
|
|
408 |
extension RequestInterceptor where Self == ConnectionLostRetryPolicy {
|
|
|
409 |
/// Provides a default `ConnectionLostRetryPolicy` instance.
|
|
|
410 |
public static var connectionLostRetryPolicy: ConnectionLostRetryPolicy { ConnectionLostRetryPolicy() }
|
|
|
411 |
|
|
|
412 |
/// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters.
|
|
|
413 |
///
|
|
|
414 |
/// - Parameters:
|
|
|
415 |
/// - retryLimit: The total number of times the request is allowed to be retried.
|
|
|
416 |
/// `RetryPolicy.defaultRetryLimit` by default.
|
|
|
417 |
/// - exponentialBackoffBase: The base of the exponential backoff policy.
|
|
|
418 |
/// `RetryPolicy.defaultExponentialBackoffBase` by default.
|
|
|
419 |
/// - exponentialBackoffScale: The scale of the exponential backoff.
|
|
|
420 |
/// `RetryPolicy.defaultExponentialBackoffScale` by default.
|
|
|
421 |
/// - retryableHTTPMethods: The idempotent http methods to retry.
|
|
|
422 |
///
|
|
|
423 |
/// - Returns: The `ConnectionLostRetryPolicy`.
|
|
|
424 |
public static func connectionLostRetryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
|
425 |
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
|
426 |
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
|
427 |
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods) -> ConnectionLostRetryPolicy {
|
|
|
428 |
ConnectionLostRetryPolicy(retryLimit: retryLimit,
|
|
|
429 |
exponentialBackoffBase: exponentialBackoffBase,
|
|
|
430 |
exponentialBackoffScale: exponentialBackoffScale,
|
|
|
431 |
retryableHTTPMethods: retryableHTTPMethods)
|
|
|
432 |
}
|
|
|
433 |
}
|
|
|
434 |
#endif
|