Skip to main content

openauth_core/options/
rate_limit.rs

1use std::fmt;
2use std::sync::Arc;
3
4use http::Request;
5
6use crate::error::OpenAuthError;
7
8/// Rate limiting defaults.
9#[derive(Clone)]
10pub struct RateLimitOptions {
11    pub enabled: Option<bool>,
12    pub window: u64,
13    pub max: u64,
14    pub storage: RateLimitStorageOption,
15    pub custom_rules: Vec<RateLimitPathRule>,
16    pub dynamic_rules: Vec<DynamicRateLimitPathRule>,
17    pub custom_storage: Option<Arc<dyn RateLimitStorage>>,
18}
19
20impl Default for RateLimitOptions {
21    fn default() -> Self {
22        Self {
23            enabled: None,
24            window: 10,
25            max: 100,
26            storage: RateLimitStorageOption::Memory,
27            custom_rules: Vec::new(),
28            dynamic_rules: Vec::new(),
29            custom_storage: None,
30        }
31    }
32}
33
34impl fmt::Debug for RateLimitOptions {
35    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36        formatter
37            .debug_struct("RateLimitOptions")
38            .field("enabled", &self.enabled)
39            .field("window", &self.window)
40            .field("max", &self.max)
41            .field("storage", &self.storage)
42            .field("custom_rules", &self.custom_rules)
43            .field("dynamic_rules", &self.dynamic_rules)
44            .field(
45                "custom_storage",
46                &self.custom_storage.as_ref().map(|_| "<custom-storage>"),
47            )
48            .finish()
49    }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct RateLimitRule {
54    pub window: u64,
55    pub max: u64,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct RateLimitPathRule {
60    pub path: String,
61    pub rule: Option<RateLimitRule>,
62}
63
64pub trait RateLimitRuleProvider: Send + Sync + 'static {
65    fn resolve(
66        &self,
67        request: &Request<Vec<u8>>,
68        current_rule: &RateLimitRule,
69    ) -> Result<Option<RateLimitRule>, OpenAuthError>;
70}
71
72impl<F> RateLimitRuleProvider for F
73where
74    F: Fn(&Request<Vec<u8>>, &RateLimitRule) -> Result<Option<RateLimitRule>, OpenAuthError>
75        + Send
76        + Sync
77        + 'static,
78{
79    fn resolve(
80        &self,
81        request: &Request<Vec<u8>>,
82        current_rule: &RateLimitRule,
83    ) -> Result<Option<RateLimitRule>, OpenAuthError> {
84        self(request, current_rule)
85    }
86}
87
88#[derive(Clone)]
89pub struct DynamicRateLimitPathRule {
90    pub path: String,
91    pub provider: Arc<dyn RateLimitRuleProvider>,
92}
93
94impl DynamicRateLimitPathRule {
95    pub fn new<P>(path: impl Into<String>, provider: P) -> Self
96    where
97        P: RateLimitRuleProvider,
98    {
99        Self {
100            path: path.into(),
101            provider: Arc::new(provider),
102        }
103    }
104}
105
106impl fmt::Debug for DynamicRateLimitPathRule {
107    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
108        formatter
109            .debug_struct("DynamicRateLimitPathRule")
110            .field("path", &self.path)
111            .field("provider", &"<request-aware>")
112            .finish()
113    }
114}
115
116/// Rate limit storage record.
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct RateLimitRecord {
119    pub key: String,
120    pub count: u64,
121    pub last_request: i64,
122}
123
124/// Synchronous storage contract for router-level rate limiting.
125pub trait RateLimitStorage: Send + Sync + 'static {
126    fn get(&self, key: &str) -> Result<Option<RateLimitRecord>, OpenAuthError>;
127    fn set(
128        &self,
129        key: &str,
130        value: RateLimitRecord,
131        ttl_seconds: u64,
132        update: bool,
133    ) -> Result<(), OpenAuthError>;
134}
135
136/// Rate limit storage selector.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum RateLimitStorageOption {
139    Memory,
140    Database,
141    SecondaryStorage,
142}