3

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)
    })
}
Bhargav
  • 31
  • 4
  • Can you please post a compiler output? – Konstantin Tikhonov Jan 13 '23 at 16:01
  • --> src/rate_limiter.rs:41:19 | 41 | addr.and_then(|ip: String| async move { | -------- ^^^^^^^^^^^^ this closure implements `FnOnce`, not `Fn` | | | the requirement to implement `Fn` derives from here 42 | let rate_limiter = rate_limiter.clone(); | ------------ closure is `FnOnce` because it moves the variable `rate_limiter` out of its environment | = note: required for `[closure@src/rate_limiter.rs:41:19: 41:31]` to implement `warp::generic::Func<(std::string::String,)>` – Bhargav Jan 13 '23 at 16:07

1 Answers1

2

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.

cadolphs
  • 9,014
  • 1
  • 24
  • 41