use std::sync::Arc;
use std::time::Duration;
use tower_governor::governor::{GovernorConfig, GovernorConfigBuilder};
use tower_governor::key_extractor::SmartIpKeyExtractor;
pub use tower_governor::GovernorLayer;
type HeaderMiddleware = ::governor::middleware::StateInformationMiddleware;
pub type RateLimitConfig = GovernorConfig<SmartIpKeyExtractor, HeaderMiddleware>;
pub fn build_rate_limit_config(burst: u32) -> anyhow::Result<Arc<RateLimitConfig>> {
let mut builder = GovernorConfigBuilder::default();
builder.per_second(u64::from(burst));
builder.burst_size(burst);
let mut builder = builder.key_extractor(SmartIpKeyExtractor);
let mut builder = builder.use_headers();
let config = Arc::new(
builder
.finish()
.ok_or_else(|| anyhow::anyhow!("failed to build rate limiter configuration"))?,
);
spawn_limiter_cleanup(&config);
Ok(config)
}
fn spawn_limiter_cleanup(config: &Arc<RateLimitConfig>) {
let limiter = config.limiter().clone();
let interval = Duration::from_secs(60);
std::thread::spawn(move || loop {
std::thread::sleep(interval);
tracing::debug!("rate limiter cleanup: {} tracked IPs", limiter.len());
limiter.retain_recent();
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_rate_limit_config_succeeds() {
let config = build_rate_limit_config(100);
assert!(
config.is_ok(),
"governor config should build with burst=100"
);
}
#[test]
fn test_build_rate_limit_config_burst_one() {
let config = build_rate_limit_config(1);
assert!(config.is_ok(), "governor config should build with burst=1");
}
#[test]
fn test_rate_limit_replenishment_scales_with_burst() {
let config = build_rate_limit_config(100).expect("config should build");
let limiter = config.limiter();
let key = std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);
assert!(
limiter.check_key(&key).is_ok(),
"first request should succeed"
);
assert!(
limiter.check_key(&key).is_ok(),
"second request should succeed (burst=100 allows many concurrent)"
);
}
}