governor/state/
direct.rs

1//! Direct rate limiters (those that can only hold one state).
2//!
3//! Rate limiters based on these types are constructed with
4//! [the `RateLimiter` constructors](../struct.RateLimiter.html#direct-in-memory-rate-limiters---constructors)
5
6use std::prelude::v1::*;
7
8use std::num::NonZeroU32;
9
10use crate::{
11    clock,
12    errors::InsufficientCapacity,
13    middleware::{NoOpMiddleware, RateLimitingMiddleware},
14    state::InMemoryState,
15    Quota,
16};
17
18/// The "this state store does not use keys" key type.
19///
20/// It's possible to use this to create a "direct" rate limiter. It explicitly does not implement
21/// [`Hash`][std::hash::Hash] so that it is possible to tell apart from "hashable" key types.
22#[derive(PartialEq, Debug, Eq)]
23pub enum NotKeyed {
24    /// The value given to state stores' methods.
25    NonKey,
26}
27
28/// A trait for state stores that only keep one rate limiting state.
29///
30/// This is blanket-implemented by all [`StateStore`]s with [`NotKeyed`] key associated types.
31pub trait DirectStateStore: StateStore<Key = NotKeyed> {}
32
33impl<T> DirectStateStore for T where T: StateStore<Key = NotKeyed> {}
34
35/// # Direct in-memory rate limiters - Constructors
36///
37/// Here we construct an in-memory rate limiter that makes direct (un-keyed)
38/// rate-limiting decisions. Direct rate limiters can be used to
39/// e.g. regulate the transmission of packets on a single connection,
40/// or to ensure that an API client stays within a service's rate
41/// limit.
42#[cfg(feature = "std")]
43impl RateLimiter<NotKeyed, InMemoryState, clock::DefaultClock, NoOpMiddleware> {
44    /// Constructs a new in-memory direct rate limiter for a quota with the default real-time clock.
45    pub fn direct(
46        quota: Quota,
47    ) -> RateLimiter<NotKeyed, InMemoryState, clock::DefaultClock, NoOpMiddleware> {
48        let clock = clock::DefaultClock::default();
49        Self::direct_with_clock(quota, &clock)
50    }
51}
52
53impl<C> RateLimiter<NotKeyed, InMemoryState, C, NoOpMiddleware<C::Instant>>
54where
55    C: clock::Clock,
56{
57    /// Constructs a new direct rate limiter for a quota with a custom clock.
58    pub fn direct_with_clock(quota: Quota, clock: &C) -> Self {
59        let state: InMemoryState = Default::default();
60        RateLimiter::new(quota, state, clock)
61    }
62}
63
64/// # Direct rate limiters - Manually checking cells
65impl<S, C, MW> RateLimiter<NotKeyed, S, C, MW>
66where
67    S: DirectStateStore,
68    C: clock::Clock,
69    MW: RateLimitingMiddleware<C::Instant>,
70{
71    /// Allow a single cell through the rate limiter.
72    ///
73    /// If the rate limit is reached, `check` returns information about the earliest
74    /// time that a cell might be allowed through again.
75    pub fn check(&self) -> Result<MW::PositiveOutcome, MW::NegativeOutcome> {
76        self.gcra.test_and_update::<NotKeyed, C::Instant, S, MW>(
77            self.start,
78            &NotKeyed::NonKey,
79            &self.state,
80            self.clock.now(),
81        )
82    }
83
84    /// Allow *only all* `n` cells through the rate limiter.
85    ///
86    /// This method can succeed in only one way and fail in two ways:
87    /// * Success: If all `n` cells can be accommodated, it returns `Ok(())`.
88    /// * Failure (but ok): Not all cells can make it through at the current time.
89    ///   The result is `Err(NegativeMultiDecision::BatchNonConforming(NotUntil))`, which can
90    ///   be interrogated about when the batch might next conform.
91    /// * Failure (the batch can never go through): The rate limit quota's burst size is too low
92    ///   for the given number of cells to ever be allowed through.
93    ///
94    /// ### Performance
95    /// This method diverges a little from the GCRA algorithm, using
96    /// multiplication to determine the next theoretical arrival time, and so
97    /// is not as fast as checking a single cell.
98    pub fn check_n(
99        &self,
100        n: NonZeroU32,
101    ) -> Result<Result<MW::PositiveOutcome, MW::NegativeOutcome>, InsufficientCapacity> {
102        self.gcra
103            .test_n_all_and_update::<NotKeyed, C::Instant, S, MW>(
104                self.start,
105                &NotKeyed::NonKey,
106                n,
107                &self.state,
108                self.clock.now(),
109            )
110    }
111}
112
113#[cfg(feature = "std")]
114mod future;
115
116#[cfg(feature = "std")]
117mod sinks;
118#[cfg(feature = "std")]
119pub use sinks::*;
120
121#[cfg(feature = "std")]
122mod streams;
123
124use crate::state::{RateLimiter, StateStore};
125#[cfg(feature = "std")]
126pub use streams::*;
127
128#[cfg(test)]
129mod test {
130    use super::*;
131
132    #[test]
133    fn not_keyed_impls_coverage() {
134        assert_eq!(NotKeyed::NonKey, NotKeyed::NonKey);
135    }
136}