Sources/OSS/Internal/RetryerMiddleware.swift (66 lines of code) (raw):

import Foundation protocol RetryHandler { func retrying(request: RequestMessage, context: ExecuteContext, error: Error) } struct FixTimeRetryHandler: RetryHandler { func retrying(request _: RequestMessage, context: ExecuteContext, error: Error) { // fix time guard let serverError = error as? ServerError else { return } if serverError.code == "RequestTimeTooSkewed" || (serverError.code == "InvalidArgument" && serverError.message == "Invalid signing date in Authorization header.") { if let time = serverError.errorFields["ServerTime"] { let data = DateFormatter.iso8601DateTimeSeconds.date(from: time) context.signingContext?.clockOffset = data?.timeIntervalSince(Date()) } else if let time = serverError.headers[caseInsensitive: "Date"] { let data = DateFormatter.rfc5322DateTime.date(from: time) context.signingContext?.clockOffset = data?.timeIntervalSince(Date()) } } } } class RetryerMiddleware: ExecuteMiddleware { let nextHandler: ExecuteMiddleware let retryer: Retryer let maxAttempts: Int let logger: LogAgent? let retryHandler: RetryHandler? init(nextHandler: ExecuteMiddleware, retryer: Retryer, logger: LogAgent? = nil, retryHandler: RetryHandler? = nil) { self.nextHandler = nextHandler self.retryer = retryer maxAttempts = max(self.retryer.maxAttempts(), 1) self.logger = logger self.retryHandler = retryHandler } public func execute(request: RequestMessage, context: ExecuteContext) async throws -> ResponseMessage { var attempt = 0 while true { do { try Task.checkCancellation() return try await nextHandler.execute(request: request, context: context) } catch { attempt += 1 if attempt >= maxAttempts { throw error } if let isSeekable = request.content?.isSeekable, !isSeekable { throw error } let retry = retryer.isErrorRetryable(error: error) if !retry { throw error } let delay = retryer.retryDelay(attempt: attempt, error: error) try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) retryHandler?.retrying(request: request, context: context, error: error) logger?.debug("Should retry. Current attempt: \(attempt), delay: \(delay), error: \(error.localizedDescription)") } } } }