A Swift package providing rate limiting capabilities, suitable for various server-side applications. Includes integration middleware for the Hummingbird framework.
Import RateLimiter
and create an instance of PublicApiRateLimiter
. You'll typically need a Logger
instance.
import RateLimiter
import Logging // You'll need swift-log
// Assuming you have a logger configured
let logger = Logger(label: "com.example.MyApp.RateLimiter")
// Create the rate limiter (it's an actor)
let rateLimiter = PublicApiRateLimiter(logger: logger)
// Somewhere in your request handling logic:
let clientIP = "192.168.1.100" // Get the client IP address
do {
try await rateLimiter.check(ipAddress: clientIP)
// Request allowed, proceed with handling
print("Request from \(clientIP) allowed.")
} catch RateLimitingError.tooManyRequests {
// Request denied due to rate limiting
print("Rate limit exceeded for \(clientIP).")
// Return an appropriate error response (e.g., HTTP 429 Too Many Requests)
} catch {
// Handle other potential errors
print("An unexpected error occurred: \(error)")
}
The PublicApiRateLimiter
uses an in-memory store (a dictionary) and limits requests to 15 per second per IP address by default, which can be configured in the initialization. It automatically cleans up stale entries.
First, ensure your RequestContext
conforms to IPRequestContext
. This protocol requires you to provide a way to access the client's remote IP address.
import Hummingbird
import RateLimiterHummingbird // Import the Hummingbird integration
import Logging
import NIOCore // For SocketAddress
// Example Request Context
struct AppRequestContext: IPRequestContext {
var coreContext: CoreRequestContextStorage
// Required by Hummingbird.RemoteAddressRequestContext
var remoteAddress: SocketAddress? { coreContext.channel.remoteAddress }
// Required by IPRequestContext: Provide a way to get the remote address
var xForwardedFor: String?
init(channel: Channel, logger: Logger) {
self.coreContext = .init(channel: channel, logger: logger)
self.xForwardedFor = nil // Or parse from headers if applicable
}
}
Then, add the InMemoryRateLimitMiddleware
to your Hummingbird application router.
let logger = Logger(label: "my-hummingbird-app")
let router = Router(context: AppRequestContext.self)
// Add the rate limiting middleware
router.add(middleware: InMemoryRateLimitMiddleware(
logger: logger,
configuration: .init(
limitPerSecond: 10,
cleanupInterval: 60.0,
entryTTL: 300.0
)
))
// ... add your routes ...
let app = Application(
router: router,
context: AppRequestContext.init,
logger: logger
)
try await app.runService()
The InMemoryRateLimitMiddleware
uses the PublicApiRateLimiter
internally and automatically checks first the X-Forwarded-For
header, if nil then remoteAddress
from your IPRequestContext
. If the limit is exceeded, it throws an HTTPError(.tooManyRequests)
.
Add the RateLimiter
package to your Package.swift
dependencies:
dependencies: [
.package(url: "https://github.com/your-username/RateLimiter.git", from: "0.0.1")
]
And add RateLimiter
to your target's dependencies:
.target(
name: "YourApp",
dependencies: [
.product(name: "RateLimiter", package: "RateLimiter"),
// ... other dependencies
]
),
If you are using the Hummingbird integration, also add RateLimiterHummingbird
:
.target(
name: "YourApp",
dependencies: [
.product(name: "RateLimiterHummingbird", package: "RateLimiter"),
.product(name: "Hummingbird", package: "hummingbird"),
// ... other dependencies
]
),