I am trying to get rate limiting to work with Warp using Governor crate. However, when I try using rate_limiter instance wrapped in Arc
part of a closure, I keep getting "expected a closure that implements the Fn
trait, but this closure only implements FnOnce
"
I tried cloning the rate_limiter instance inside and outside the closure, but it still complains. Can someone help me out with this?
use crate::rejections::RateLimitFailure;
use dashmap::DashMap;
use governor::{
clock::{QuantaClock, QuantaInstant},
middleware::NoOpMiddleware,
state::InMemoryState,
Quota, RateLimiter,
};
use nonzero_ext::nonzero;
use std::collections::hash_map::RandomState;
use std::sync::Arc;
use warp::{Filter, Rejection};
const LIMIT: u32 = 50;
#[derive(Debug, Clone)]
pub struct FridayRateLimiter<'a> {
pub lim: Arc<
RateLimiter<
&'a str,
DashMap<&'a str, InMemoryState, RandomState>,
QuantaClock,
NoOpMiddleware<QuantaInstant>,
>,
>,
}
impl<'a> FridayRateLimiter<'a> {
pub fn new() -> Self {
let lim = Arc::new(RateLimiter::keyed(Quota::per_second(nonzero!(LIMIT))));
FridayRateLimiter { lim }
}
}
pub fn with_rate_limiter(
rate_limiter: FridayRateLimiter,
) -> impl Filter<Extract = (bool,), Error = Rejection> + Clone {
let addr = warp::header::<String>("HTTP_X_FORWARDED_FOR");
let rate_limiter = rate_limiter.clone();
addr.and_then(|ip: String| async move {
let rate_limiter = rate_limiter.clone();
if rate_limiter.lim.check_key(&ip.as_str()).is_err() {
return Err(warp::reject::custom(RateLimitFailure));
}
Ok(true)
})
}
From here: When does a closure implement Fn, FnMut and FnOnce?
Cloning the rate limiter inside the closure seems wrong, for starters.
Next, I don't see why you'd need to take ownership of anything, so I'd drop the
move
. You really only need an immutable borrow to callcheck_key
on the rate limiter, don't you?And that's the condition for implementing
Fn
: Something that won't mess up its environment because it only needs&
-access to it.