rust_web_server/rate_limit/
mod.rs1#[cfg(test)]
2mod tests;
3
4use std::collections::{HashMap, VecDeque};
5use std::sync::{Mutex, OnceLock};
6use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
7use std::time::{Duration, Instant};
8
9pub struct RateLimiter {
32 state: Mutex<HashMap<String, VecDeque<Instant>>>,
33 max_requests: AtomicU32,
34 window_secs: AtomicU64,
35}
36
37impl RateLimiter {
38 pub fn new(max_requests: u32, window_secs: u64) -> Self {
40 RateLimiter {
41 state: Mutex::new(HashMap::new()),
42 max_requests: AtomicU32::new(max_requests),
43 window_secs: AtomicU64::new(window_secs),
44 }
45 }
46
47 pub fn set_limits(&self, max_requests: u32, window_secs: u64) {
52 self.max_requests.store(max_requests, Ordering::Relaxed);
53 self.window_secs.store(window_secs, Ordering::Relaxed);
54 }
55
56 fn window(&self) -> Duration {
57 Duration::from_secs(self.window_secs.load(Ordering::Relaxed))
58 }
59
60 fn max(&self) -> u32 {
61 self.max_requests.load(Ordering::Relaxed)
62 }
63
64 pub fn check(&self, key: &str) -> bool {
69 let now = Instant::now();
70 let window = self.window();
71 let max = self.max();
72 let mut guard = self.state.lock().unwrap();
73 let timestamps = guard.entry(key.to_string()).or_default();
74
75 while timestamps.front().map(|t| now.duration_since(*t) > window).unwrap_or(false) {
77 timestamps.pop_front();
78 }
79
80 if (timestamps.len() as u32) < max {
81 timestamps.push_back(now);
82 true
83 } else {
84 false
85 }
86 }
87
88 pub fn remaining(&self, key: &str) -> u32 {
90 let now = Instant::now();
91 let window = self.window();
92 let max = self.max();
93 let mut guard = self.state.lock().unwrap();
94 let timestamps = guard.entry(key.to_string()).or_default();
95 while timestamps.front().map(|t| now.duration_since(*t) > window).unwrap_or(false) {
96 timestamps.pop_front();
97 }
98 max.saturating_sub(timestamps.len() as u32)
99 }
100
101 pub fn reset(&self, key: &str) {
103 self.state.lock().unwrap().remove(key);
104 }
105}
106
107static GLOBAL_LIMITER: OnceLock<RateLimiter> = OnceLock::new();
108
109pub fn global() -> &'static RateLimiter {
118 GLOBAL_LIMITER.get_or_init(|| {
119 let max: u32 = std::env::var("RWS_CONFIG_RATE_LIMIT_MAX_REQUESTS")
120 .ok()
121 .and_then(|v| v.parse().ok())
122 .unwrap_or(1000);
123 let window: u64 = std::env::var("RWS_CONFIG_RATE_LIMIT_WINDOW_SECS")
124 .ok()
125 .and_then(|v| v.parse().ok())
126 .unwrap_or(60);
127 RateLimiter::new(max, window)
128 })
129}