bindcar/rate_limit.rs
1//! Rate limiting middleware for HTTP requests
2//!
3//! This module provides rate limiting functionality using the tower-governor crate,
4//! which implements the Generic Cell Rate Algorithm (GCRA). Rate limits are
5//! keyed on the real TCP peer IP ([`PeerIpKeyExtractor`]) rather than the
6//! spoofable `X-Forwarded-For` family of headers, so a client cannot evade the
7//! limit or exhaust another client's bucket by forging a forwarding header.
8
9// Re-export commonly used types for convenience
10pub use tower_governor::{
11 governor::GovernorConfigBuilder, key_extractor::PeerIpKeyExtractor, GovernorLayer,
12};
13
14/// Rate limiting configuration
15#[derive(Debug, Clone)]
16pub struct RateLimitConfig {
17 /// Maximum requests per period
18 pub requests_per_period: u32,
19 /// Period duration in seconds
20 pub period_secs: u64,
21 /// Burst size (max requests at once)
22 pub burst_size: u32,
23 /// Whether rate limiting is enabled
24 pub enabled: bool,
25}
26
27impl Default for RateLimitConfig {
28 fn default() -> Self {
29 Self {
30 requests_per_period: 100,
31 period_secs: 60,
32 burst_size: 10,
33 enabled: true,
34 }
35 }
36}
37
38impl RateLimitConfig {
39 /// Create configuration from environment variables
40 ///
41 /// Environment variables:
42 /// - `RATE_LIMIT_ENABLED`: Enable/disable rate limiting (default: true)
43 /// - `RATE_LIMIT_REQUESTS`: Max requests per period (default: 100)
44 /// - `RATE_LIMIT_PERIOD_SECS`: Period in seconds (default: 60)
45 /// - `RATE_LIMIT_BURST`: Burst size (default: 10)
46 pub fn from_env() -> Self {
47 let enabled = std::env::var("RATE_LIMIT_ENABLED")
48 .ok()
49 .and_then(|v| v.parse().ok())
50 .unwrap_or(true);
51
52 let requests_per_period = std::env::var("RATE_LIMIT_REQUESTS")
53 .ok()
54 .and_then(|v| v.parse().ok())
55 .unwrap_or(100);
56
57 let period_secs = std::env::var("RATE_LIMIT_PERIOD_SECS")
58 .ok()
59 .and_then(|v| v.parse().ok())
60 .unwrap_or(60);
61
62 let burst_size = std::env::var("RATE_LIMIT_BURST")
63 .ok()
64 .and_then(|v| v.parse().ok())
65 .unwrap_or(10);
66
67 Self {
68 requests_per_period,
69 period_secs,
70 burst_size,
71 enabled,
72 }
73 }
74
75 /// Validate configuration values
76 pub fn validate(&self) -> Result<(), String> {
77 if self.requests_per_period == 0 {
78 return Err("requests_per_period must be greater than 0".to_string());
79 }
80
81 if self.period_secs == 0 {
82 return Err("period_secs must be greater than 0".to_string());
83 }
84
85 if self.burst_size == 0 {
86 return Err("burst_size must be greater than 0".to_string());
87 }
88
89 Ok(())
90 }
91}