Trait governor::middleware::RateLimitingMiddleware

source ·
pub trait RateLimitingMiddleware<P: Reference>: Debug {
    type PositiveOutcome: Sized;
    type NegativeOutcome: Sized;

    // Required methods
    fn allow<K>(
        key: &K,
        state: impl Into<StateSnapshot>,
    ) -> Self::PositiveOutcome;
    fn disallow<K>(
        key: &K,
        limiter: impl Into<StateSnapshot>,
        start_time: P,
    ) -> Self::NegativeOutcome;
}
Expand description

Defines the behavior and return values of rate limiting decisions.

While the rate limiter defines whether a decision is positive, the middleware defines what additional values (other than Ok or Err) are returned from the RateLimiter’s check methods.

The default middleware in this crate is NoOpMiddleware (which does nothing in the positive case and returns NotUntil in the negative) - so it does only the smallest amount of work it needs to do in order to be useful to users.

Other middleware gets to adjust these trade-offs: The pre-made StateInformationMiddleware returns quota and burst capacity information, while custom middleware could return a set of HTTP headers or increment counters per each rate limiter key’s decision.

§Defining your own middleware

Here’s an example of a rate limiting middleware that does no computations at all on positive and negative outcomes: All the information that a caller will receive is that a request should be allowed or disallowed. This can allow for faster negative outcome handling, and is useful if you don’t need to tell users when they can try again (or anything at all about their rate limiting status).

use governor::{middleware::{RateLimitingMiddleware, StateSnapshot},
               Quota, RateLimiter, clock::Reference};
#[derive(Debug)]
struct NullMiddleware;

impl<P: Reference> RateLimitingMiddleware<P> for NullMiddleware {
    type PositiveOutcome = ();
    type NegativeOutcome = ();

    fn allow<K>(_key: &K, _state: impl Into<StateSnapshot>) -> Self::PositiveOutcome {}
    fn disallow<K>(_: &K, _: impl Into<StateSnapshot>, _: P) -> Self::NegativeOutcome {}
}

let lim = RateLimiter::direct(Quota::per_hour(nonzero!(1_u32)))
    .with_middleware::<NullMiddleware>();

assert_eq!(lim.check(), Ok(()));
assert_eq!(lim.check(), Err(()));

Required Associated Types§

source

type PositiveOutcome: Sized

The type that’s returned by the rate limiter when a cell is allowed.

By default, rate limiters return Ok(()), which does not give much information. By using custom middleware, users can obtain more information about the rate limiter state that was used to come to a decision. That state can then be used to pass information downstream about, e.g. how much burst capacity is remaining.

source

type NegativeOutcome: Sized

The type that’s returned by the rate limiter when a cell is not allowed.

By default, rate limiters return Err(NotUntil), which allows interrogating the minimum amount of time to wait until a client can expect to have a cell allowed again.

Required Methods§

source

fn allow<K>(key: &K, state: impl Into<StateSnapshot>) -> Self::PositiveOutcome

Called when a positive rate-limiting decision is made.

This function is able to affect the return type of RateLimiter.check (and others) in the Ok case: Whatever is returned here is the value of the Ok result returned from the check functions.

The function is passed a snapshot of the rate-limiting state updated to after the decision was reached: E.g., if there was one cell left in the burst capacity before the decision was reached, the StateSnapshot::remaining_burst_capacity method will return 0.

source

fn disallow<K>( key: &K, limiter: impl Into<StateSnapshot>, start_time: P, ) -> Self::NegativeOutcome

Called when a negative rate-limiting decision is made (the “not allowed but OK” case).

This method returns whatever value is returned inside the Err variant a RateLimiter’s check method returns.

Object Safety§

This trait is not object safe.

Implementors§