1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-02-22 18:33:18 +01:00
2023-11-09 23:29:38 +01:00

83 lines
3.1 KiB
Rust

use std::{collections::HashMap, time::Duration};
use actix_limitation::{Limiter, RateLimiter};
use actix_web::{dev::ServiceRequest, get, put, web, App, HttpServer, Responder};
use redis::Client;
#[get("/")]
async fn index() -> impl Responder {
"index"
}
#[put("/sms")]
async fn send_sms() -> impl Responder {
"sending an expensive sms"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
pretty_env_logger::init();
// Create an Hashmap to store the multiples [Limiter](Limiter)
let mut limiters = HashMap::new();
// Create and connect a redis Client.
let redis_client = Client::open("redis://127.0.0.1/").expect("creation of the redis client");
// Create a default limiter
let default_limiter = Limiter::builder_with_redis_client(redis_client.clone())
// specifying with key_by that we take the user IP address as a identifier.
.key_by(|req: &ServiceRequest| {
req.connection_info()
.realip_remote_addr()
.map(|ip| ip.to_string())
})
// Allowing a maximum of 30 requests per minute
.limit(30)
.period(Duration::from_secs(60))
.build()
.unwrap();
limiters.insert("default", default_limiter);
let scope_limiter = Limiter::builder_with_redis_client(redis_client)
.key_by(|req: &ServiceRequest| {
req.connection_info()
.realip_remote_addr()
// ⚠️ we prepend "scoped" to the key in order to isolate this count from the default count
//
// If we were using the same key, a request to this route would always return too many requests
// in this context because the default limiter at the root would be reached first and would count 1 before we check for this.
// To mitigate this issue you could also specify a different namespace with the redis_client passed as parameter: `redis://127.0.0.1/2`
.map(|ip| format!("scoped-{}", ip))
})
// Allowing only 1 request per minute
.limit(1)
.period(Duration::from_secs(60))
.build()
.unwrap();
limiters.insert("scoped", scope_limiter);
// Passing this limiters as app_data so it can be accessed by the middleware.
let limiters = web::Data::new(limiters);
HttpServer::new(move || {
App::new()
// Using the default limiter for all the routes
// ⚠️ This limiter will count and apply the limits before the one in "/scoped"
.wrap(RateLimiter::scoped("default"))
.app_data(limiters.clone())
.service(
web::scope("/scoped")
// Wrapping only for this scope the scoped limiter
.wrap(RateLimiter::scoped("scoped"))
// This route will only be available 1 time every minutes
// Note: the root limiter default will also limit this route
.service(send_sms),
)
// This route is only limited by the default limiter
.service(index)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}