generated from dankinsoid/iOSLibraryTemplate
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
24206a7
commit cfd9dcc
Showing
18 changed files
with
768 additions
and
737 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 58 additions & 58 deletions
116
Sources/SwiftAPIClient/Modifiers/RateLimitModifier.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,68 @@ | ||
import Foundation | ||
import HTTPTypes | ||
|
||
extension APIClient { | ||
public extension APIClient { | ||
|
||
/// When the rate limit is exceeded, the request will be repeated after the specified interval and all requests with the same identifier will be suspended. | ||
/// - Parameters: | ||
/// - id: The identifier to use for rate limiting. Default to the base URL of the request. | ||
/// - interval: The interval to wait before repeating the request. Default to 30 seconds. | ||
/// - statusCodes: The set of status codes that indicate a rate limit exceeded. Default to `[429]`. | ||
/// - maxRepeatCount: The maximum number of times the request can be repeated. Default to 3. | ||
public func waitIfRateLimitExceeded<ID: Hashable>( | ||
id: @escaping (HTTPRequestComponents) -> ID, | ||
interval: TimeInterval = 30, | ||
statusCodes: Set<HTTPResponse.Status> = [.tooManyRequests], | ||
maxRepeatCount: Int = 3 | ||
) -> Self { | ||
httpClientMiddleware(RateLimitMiddleware(id: id, interval: interval, statusCodes: statusCodes, maxCount: maxRepeatCount)) | ||
} | ||
/// When the rate limit is exceeded, the request will be repeated after the specified interval and all requests with the same identifier will be suspended. | ||
/// - Parameters: | ||
/// - id: The identifier to use for rate limiting. Default to the base URL of the request. | ||
/// - interval: The interval to wait before repeating the request. Default to 30 seconds. | ||
/// - statusCodes: The set of status codes that indicate a rate limit exceeded. Default to `[429]`. | ||
/// - maxRepeatCount: The maximum number of times the request can be repeated. Default to 3. | ||
func waitIfRateLimitExceeded<ID: Hashable>( | ||
id: @escaping (HTTPRequestComponents) -> ID, | ||
interval: TimeInterval = 30, | ||
statusCodes: Set<HTTPResponse.Status> = [.tooManyRequests], | ||
maxRepeatCount: Int = 3 | ||
) -> Self { | ||
httpClientMiddleware(RateLimitMiddleware(id: id, interval: interval, statusCodes: statusCodes, maxCount: maxRepeatCount)) | ||
} | ||
|
||
/// When the rate limit is exceeded, the request will be repeated after the specified interval and all requests with the same base URL will be suspended. | ||
/// - Parameters: | ||
/// - interval: The interval to wait before repeating the request. Default to 30 seconds. | ||
/// - statusCodes: The set of status codes that indicate a rate limit exceeded. Default to `[429]`. | ||
/// - maxRepeatCount: The maximum number of times the request can be repeated. Default to 3. | ||
public func waitIfRateLimitExceeded( | ||
interval: TimeInterval = 30, | ||
statusCodes: Set<HTTPResponse.Status> = [.tooManyRequests], | ||
maxRepeatCount: Int = 3 | ||
) -> Self { | ||
waitIfRateLimitExceeded( | ||
id: { $0.url?.host ?? UUID().uuidString }, | ||
interval: interval, | ||
statusCodes: statusCodes, | ||
maxRepeatCount: maxRepeatCount | ||
) | ||
} | ||
/// When the rate limit is exceeded, the request will be repeated after the specified interval and all requests with the same base URL will be suspended. | ||
/// - Parameters: | ||
/// - interval: The interval to wait before repeating the request. Default to 30 seconds. | ||
/// - statusCodes: The set of status codes that indicate a rate limit exceeded. Default to `[429]`. | ||
/// - maxRepeatCount: The maximum number of times the request can be repeated. Default to 3. | ||
func waitIfRateLimitExceeded( | ||
interval: TimeInterval = 30, | ||
statusCodes: Set<HTTPResponse.Status> = [.tooManyRequests], | ||
maxRepeatCount: Int = 3 | ||
) -> Self { | ||
waitIfRateLimitExceeded( | ||
id: { $0.url?.host ?? UUID().uuidString }, | ||
interval: interval, | ||
statusCodes: statusCodes, | ||
maxRepeatCount: maxRepeatCount | ||
) | ||
} | ||
} | ||
|
||
private struct RateLimitMiddleware<ID: Hashable>: HTTPClientMiddleware { | ||
|
||
let id: (HTTPRequestComponents) -> ID | ||
let interval: TimeInterval | ||
let statusCodes: Set<HTTPResponse.Status> | ||
let maxCount: Int | ||
|
||
func execute<T>( | ||
request: HTTPRequestComponents, | ||
configs: APIClient.Configs, | ||
next: @escaping (HTTPRequestComponents, APIClient.Configs) async throws -> (T, HTTPResponse) | ||
) async throws -> (T, HTTPResponse) { | ||
let id = id(request) | ||
await waitForSynchronizedAccess(id: id, of: Void.self) | ||
var res = try await next(request, configs) | ||
var count: UInt = 0 | ||
while | ||
statusCodes.contains(res.1.status), | ||
count < maxCount | ||
{ | ||
count += 1 | ||
try await withThrowingSynchronizedAccess(id: id) { | ||
try await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000)) | ||
} | ||
res = try await next(request, configs) | ||
} | ||
return res | ||
} | ||
let id: (HTTPRequestComponents) -> ID | ||
let interval: TimeInterval | ||
let statusCodes: Set<HTTPResponse.Status> | ||
let maxCount: Int | ||
|
||
func execute<T>( | ||
request: HTTPRequestComponents, | ||
configs: APIClient.Configs, | ||
next: @escaping (HTTPRequestComponents, APIClient.Configs) async throws -> (T, HTTPResponse) | ||
) async throws -> (T, HTTPResponse) { | ||
let id = id(request) | ||
await waitForSynchronizedAccess(id: id, of: Void.self) | ||
var res = try await next(request, configs) | ||
var count: UInt = 0 | ||
while | ||
statusCodes.contains(res.1.status), | ||
count < maxCount | ||
{ | ||
count += 1 | ||
try await withThrowingSynchronizedAccess(id: id) { | ||
try await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000)) | ||
} | ||
res = try await next(request, configs) | ||
} | ||
return res | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.