1use std::fmt;
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::Arc;
5use std::time::Duration;
6
7use http::Request;
8
9use crate::error::OpenAuthError;
10
11#[derive(Clone)]
13pub struct RateLimitOptions {
14 pub enabled: Option<bool>,
15 pub window: u64,
16 pub max: u64,
17 pub storage: RateLimitStorageOption,
18 pub custom_rules: Vec<RateLimitPathRule>,
19 pub dynamic_rules: Vec<DynamicRateLimitPathRule>,
20 pub custom_store: Option<Arc<dyn RateLimitStore>>,
21 pub custom_storage: Option<Arc<dyn RateLimitStorage>>,
22 pub hybrid: HybridRateLimitOptions,
23 pub memory_cleanup_interval: Option<Duration>,
24}
25
26impl Default for RateLimitOptions {
27 fn default() -> Self {
28 Self {
29 enabled: None,
30 window: 10,
31 max: 100,
32 storage: RateLimitStorageOption::Memory,
33 custom_rules: Vec::new(),
34 dynamic_rules: Vec::new(),
35 custom_store: None,
36 custom_storage: None,
37 hybrid: HybridRateLimitOptions::default(),
38 memory_cleanup_interval: Some(Duration::from_secs(60 * 60)),
39 }
40 }
41}
42
43impl RateLimitOptions {
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn builder() -> Self {
49 Self::new()
50 }
51
52 pub fn memory() -> Self {
53 Self {
54 storage: RateLimitStorageOption::Memory,
55 ..Self::default()
56 }
57 }
58
59 pub fn database<S>(store: S) -> Self
60 where
61 S: RateLimitStore,
62 {
63 Self::database_arc(Arc::new(store))
64 }
65
66 pub fn database_arc(store: Arc<dyn RateLimitStore>) -> Self {
67 Self {
68 storage: RateLimitStorageOption::Database,
69 custom_store: Some(store),
70 ..Self::default()
71 }
72 }
73
74 pub fn secondary_storage<S>(store: S) -> Self
75 where
76 S: RateLimitStore,
77 {
78 Self::secondary_storage_arc(Arc::new(store))
79 }
80
81 pub fn secondary_storage_arc(store: Arc<dyn RateLimitStore>) -> Self {
82 Self {
83 storage: RateLimitStorageOption::SecondaryStorage,
84 custom_store: Some(store),
85 ..Self::default()
86 }
87 }
88
89 #[must_use]
90 pub fn enabled(mut self, enabled: bool) -> Self {
91 self.enabled = Some(enabled);
92 self
93 }
94
95 #[must_use]
96 pub fn window(mut self, window: u64) -> Self {
97 self.window = window;
98 self
99 }
100
101 #[must_use]
102 pub fn max(mut self, max: u64) -> Self {
103 self.max = max;
104 self
105 }
106
107 #[must_use]
108 pub fn storage(mut self, storage: RateLimitStorageOption) -> Self {
109 self.storage = storage;
110 self
111 }
112
113 #[must_use]
114 pub fn custom_store<S>(mut self, store: S) -> Self
115 where
116 S: RateLimitStore,
117 {
118 self.custom_store = Some(Arc::new(store));
119 self
120 }
121
122 #[must_use]
123 pub fn custom_store_arc(mut self, store: Arc<dyn RateLimitStore>) -> Self {
124 self.custom_store = Some(store);
125 self
126 }
127
128 #[must_use]
129 pub fn custom_storage(mut self, storage: Arc<dyn RateLimitStorage>) -> Self {
130 self.custom_storage = Some(storage);
131 self
132 }
133
134 #[must_use]
135 pub fn custom_rule(mut self, path: impl Into<String>, rule: RateLimitRule) -> Self {
136 self.custom_rules.push(RateLimitPathRule {
137 path: path.into(),
138 rule: Some(rule),
139 });
140 self
141 }
142
143 #[must_use]
144 pub fn disabled_path(mut self, path: impl Into<String>) -> Self {
145 self.custom_rules.push(RateLimitPathRule {
146 path: path.into(),
147 rule: None,
148 });
149 self
150 }
151
152 #[must_use]
153 pub fn dynamic_rule<P>(mut self, path: impl Into<String>, provider: P) -> Self
154 where
155 P: RateLimitRuleProvider,
156 {
157 self.dynamic_rules
158 .push(DynamicRateLimitPathRule::new(path, provider));
159 self
160 }
161
162 #[must_use]
163 pub fn hybrid(mut self, hybrid: HybridRateLimitOptions) -> Self {
164 self.hybrid = hybrid;
165 self
166 }
167
168 #[must_use]
169 pub fn memory_cleanup_interval(mut self, interval: Option<Duration>) -> Self {
170 self.memory_cleanup_interval = interval;
171 self
172 }
173}
174
175impl fmt::Debug for RateLimitOptions {
176 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
177 formatter
178 .debug_struct("RateLimitOptions")
179 .field("enabled", &self.enabled)
180 .field("window", &self.window)
181 .field("max", &self.max)
182 .field("storage", &self.storage)
183 .field("custom_rules", &self.custom_rules)
184 .field("dynamic_rules", &self.dynamic_rules)
185 .field(
186 "custom_store",
187 &self.custom_store.as_ref().map(|_| "<custom-store>"),
188 )
189 .field(
190 "custom_storage",
191 &self.custom_storage.as_ref().map(|_| "<custom-storage>"),
192 )
193 .field("hybrid", &self.hybrid)
194 .field("memory_cleanup_interval", &self.memory_cleanup_interval)
195 .finish()
196 }
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct RateLimitRule {
201 pub window: u64,
202 pub max: u64,
203}
204
205impl RateLimitRule {
206 pub fn new(window: u64, max: u64) -> Self {
207 Self { window, max }
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct HybridRateLimitOptions {
213 pub enabled: bool,
214 pub local_multiplier: u64,
215}
216
217impl Default for HybridRateLimitOptions {
218 fn default() -> Self {
219 Self {
220 enabled: false,
221 local_multiplier: 2,
222 }
223 }
224}
225
226impl HybridRateLimitOptions {
227 pub fn new() -> Self {
228 Self::default()
229 }
230
231 pub fn builder() -> Self {
232 Self::new()
233 }
234
235 pub fn enabled() -> Self {
236 Self {
237 enabled: true,
238 ..Self::default()
239 }
240 }
241
242 pub fn disabled() -> Self {
243 Self::default()
244 }
245
246 #[must_use]
247 pub fn set_enabled(mut self, enabled: bool) -> Self {
248 self.enabled = enabled;
249 self
250 }
251
252 #[must_use]
253 pub fn local_multiplier(mut self, multiplier: u64) -> Self {
254 self.local_multiplier = multiplier;
255 self
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
260pub struct RateLimitPathRule {
261 pub path: String,
262 pub rule: Option<RateLimitRule>,
263}
264
265pub trait RateLimitRuleProvider: Send + Sync + 'static {
266 fn resolve(
267 &self,
268 request: &Request<Vec<u8>>,
269 current_rule: &RateLimitRule,
270 ) -> Result<Option<RateLimitRule>, OpenAuthError>;
271}
272
273impl<F> RateLimitRuleProvider for F
274where
275 F: Fn(&Request<Vec<u8>>, &RateLimitRule) -> Result<Option<RateLimitRule>, OpenAuthError>
276 + Send
277 + Sync
278 + 'static,
279{
280 fn resolve(
281 &self,
282 request: &Request<Vec<u8>>,
283 current_rule: &RateLimitRule,
284 ) -> Result<Option<RateLimitRule>, OpenAuthError> {
285 self(request, current_rule)
286 }
287}
288
289#[derive(Clone)]
290pub struct DynamicRateLimitPathRule {
291 pub path: String,
292 pub provider: Arc<dyn RateLimitRuleProvider>,
293}
294
295impl DynamicRateLimitPathRule {
296 pub fn new<P>(path: impl Into<String>, provider: P) -> Self
297 where
298 P: RateLimitRuleProvider,
299 {
300 Self {
301 path: path.into(),
302 provider: Arc::new(provider),
303 }
304 }
305}
306
307impl fmt::Debug for DynamicRateLimitPathRule {
308 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
309 formatter
310 .debug_struct("DynamicRateLimitPathRule")
311 .field("path", &self.path)
312 .field("provider", &"<request-aware>")
313 .finish()
314 }
315}
316
317#[derive(Debug, Clone, PartialEq, Eq)]
319pub struct RateLimitRecord {
320 pub key: String,
321 pub count: u64,
322 pub last_request: i64,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct RateLimitConsumeInput {
327 pub key: String,
328 pub rule: RateLimitRule,
329 pub now_ms: i64,
330}
331
332#[derive(Debug, Clone, PartialEq, Eq)]
333pub struct RateLimitDecision {
334 pub permitted: bool,
335 pub retry_after: u64,
336 pub limit: u64,
337 pub remaining: u64,
338 pub reset_after: u64,
339}
340
341pub type RateLimitFuture<'a> =
342 Pin<Box<dyn Future<Output = Result<RateLimitDecision, OpenAuthError>> + Send + 'a>>;
343
344pub trait RateLimitStore: Send + Sync + 'static {
349 fn consume<'a>(&'a self, input: RateLimitConsumeInput) -> RateLimitFuture<'a>;
350}
351
352pub trait RateLimitStorage: Send + Sync + 'static {
358 fn get(&self, key: &str) -> Result<Option<RateLimitRecord>, OpenAuthError>;
359 fn set(
360 &self,
361 key: &str,
362 value: RateLimitRecord,
363 ttl_seconds: u64,
364 update: bool,
365 ) -> Result<(), OpenAuthError>;
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
370pub enum RateLimitStorageOption {
371 Memory,
372 Database,
373 SecondaryStorage,
374}