##KituraLimiter Rate limiting middleware for Kitura applications built on redis
import Foundation import Kitura import SwiftRedis import KituraLimiter let redis = Redis() // remember to make sure a redis instance is running redis.connect(host: "localhost", port: 6379) { (redisError: NSError?) in if let error = redisError { print("Could not connect to Redis: \(error)") } else { print("Redis connected") // Create a new router let router = Router() // By default, Limiter allows 150 requests/hour for each IP address router.all(middleware: Limiter(redis: redis)) // Handle HTTP GET requests to / router.get("/") { request, response, next in response.send("Hello, World!") next() } // Add an HTTP server and connect it to the router Kitura.addHTTPServer(onPort: 8090, with: router) // Start the Kitura runloop (this call never returns) Kitura.run() } }
###API options
by:(_ request: RouterRequest) -> StringFunction which returns a unique key to identify the client in redis (the default fn uses the client'srequest.remoteAddress)total:Int(default150) Allowed number of requests before getting rate limitedexpire:Int(default:1000*60*60) Amount of time inmsbefore the rate limit is resetwhitelist:func(_ request: RouterRequest) -> BoolOptional param allowing the ability to whitelist client requests. Returntrueto whitelist,falseto pass through to Limiter.skipHeaders:BoolWhentrue, theresponse.headersfor rate limiting info (e.g"Retry-After") are not set. Default isfalseonRateLimited:RouteHandleroptional custom handler for rate-limited clients (default returns a429response and tries toend())ignoreErrors:Boolignore any errors and pass requests to the next() middleware if something goes wrongonRedisError:RouteHandleroptional custom handler for when redis throws an error, (default returns a500response and tried toend())
###Examples
func apiToken(request: RouterRequest) -> String { // use api token from middleware, cookies, etc. return request.userInfo["apiToken"] } func isAdmin(request: RouterRequest) -> Bool { return request.userInfo["isAdmin"] } func onRedisError(request: RouterRequest, response: RouterResponse, next: @escaping NextFunc) throws { response.status(HTTPStatusCode.internalServerError).send("This service is awful!") try response.end() } func onRateLimited(request: RouterRequest, response: RouterResponse, next: @escaping NextFunc) throws { response.status(HTTPStatusCode.tooManyRequests).send("I need you to calm down") try response.end() } router.all(middleware: Limiter( redis: redis, by: apiToken, // limit by api token instead of default ip address total: 2, // 2 req/second expire: 1000, // 1000 ms whitelist: isAdmin, // admin users shouldn't be limited ignoreErrors: true, // is rate limiting really *that* important? onRedisError: onRedisError, // this is ignored because ignoreErrors is true skipHeaders: true, // I don't care if my clients know when they can make more requests! onRateLimited: onRateLimited ) )