1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Errors for governed requests which implement 
//! [Responder](rocket::response::Responder).

use super::{header::Header, Quota};
use rocket::{
    response::{self, Responder},
    Request,
};

mod catcher;

/// Errors for governed requests which implement 
/// [Responder](rocket::response::Responder).
#[derive(Clone, Debug)]
pub enum LimitError {
    /// Any other undefined LimitError
    Error,

    // TODO: Check ratelimit headers DRAFT for publication
    /// Governed request for the next provided seconds.
    /// Provided `Quota` will be used for setting additional
    /// HTTP headers defined by
    /// [draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers).
    /// These headers could be used in service clients to use the service
    /// in a more compliant way for its resources.
    GovernedRequest(u64, Quota),

    /// There is no remote client IP address known in the request. Might be 
    /// a misconfigured server environment.
    MissingClientIpAddr,

    /// Route is not available which might be only the case in fairings
    MissingRoute,

    /// There is a route without name and this can not be matched for 
    /// rate limiting
    MissingRouteName,
}

/// Implements [Responder](rocket::response::Responder) to provide 
/// [Result](rocket::response::Result) possibilities.
impl<'r, 'o: 'r> Responder<'r, 'o> for &LimitError {
    fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> {
        let mut handler = catcher::too_many_requests_handler(request);

        match self {
            LimitError::Error => {
                handler.set_header(Header::XRateLimitError("rate limiter error"));
            }
            LimitError::GovernedRequest(wait_time, quota) => {
                handler.set_header(Header::RetryAfter(*wait_time));
                // TODO: x-ratelimit-limit can describe the time window of limit
                //       https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers#section-5.1
                handler.set_header(Header::XRateLimitLimit(quota.burst_size().get() as u64));
                // XRateLimitRemaining makes no sense here in LimitError
                // because `state.remaining_burst_capacity()` should be 
                // always 0 
                handler.set_header(Header::XRateLimitReset(
                    quota.burst_size_replenished_in().as_secs(),
                ));
            }
            LimitError::MissingClientIpAddr => {
                handler.set_header(Header::XRateLimitError(
                    "application not retrieving client ip",
                ));
            }
            LimitError::MissingRoute => {
                handler.set_header(Header::XRateLimitError("routing failure"));
            }
            LimitError::MissingRouteName => {
                handler.set_header(Header::XRateLimitError("route without name"));
            }
        };

        Ok(handler)
    }
}