http_rate/
error.rs

1use core::fmt;
2
3use std::{error, time::Instant};
4
5use http::{HeaderName, HeaderValue, Response, StatusCode};
6
7use crate::{
8    gcra::NotUntil,
9    timer::{DefaultTimer, Timer},
10};
11
12/// Error happen when client exceeds rate limit.
13#[derive(Debug)]
14pub struct TooManyRequests {
15    after_seconds: u64,
16}
17
18impl fmt::Display for TooManyRequests {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        write!(f, "too many requests. wait for {}", self.after_seconds)
21    }
22}
23
24impl error::Error for TooManyRequests {}
25
26impl From<NotUntil<Instant>> for TooManyRequests {
27    fn from(e: NotUntil<Instant>) -> Self {
28        let after_seconds = e.wait_time_from(DefaultTimer.now()).as_secs();
29        Self { after_seconds }
30    }
31}
32
33const X_RT_AFTER: HeaderName = HeaderName::from_static("x-ratelimit-after");
34
35impl TooManyRequests {
36    /// extend response headers with status code and headers
37    /// StatusCode: 429
38    /// Header: `x-ratelimit-after: <num in second>`
39    pub fn extend_response<Ext>(&self, res: &mut Response<Ext>) {
40        *res.status_mut() = StatusCode::TOO_MANY_REQUESTS;
41        res.headers_mut()
42            .insert(X_RT_AFTER, HeaderValue::from(self.after_seconds));
43    }
44}
45
46/// Error indicating that the number of cells tested (the first
47/// argument) is larger than the bucket's capacity.
48///
49/// This means the decision can never have a conforming result. The
50/// argument gives the maximum number of cells that could ever have a
51/// conforming result.
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct InsufficientCapacity(pub u32);
54
55impl fmt::Display for InsufficientCapacity {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "required number of cells {} exceeds bucket's capacity", self.0)
58    }
59}
60
61impl error::Error for InsufficientCapacity {}
62
63#[cfg(test)]
64mod test {
65    use super::InsufficientCapacity;
66
67    #[test]
68    fn coverage() {
69        let display_output = format!("{}", InsufficientCapacity(3));
70        assert!(display_output.contains('3'));
71        let debug_output = format!("{:?}", InsufficientCapacity(3));
72        assert!(debug_output.contains('3'));
73        assert_eq!(InsufficientCapacity(3), InsufficientCapacity(3));
74    }
75}