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)
    })
}
1

There are 1 best solutions below

0
On

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 call check_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.