flexd 0.2.3

Hardened web server and reverse proxy: TLS/HTTP3, ACME, rate limiting, host policy, GeoIP, A/B splitting, privilege dropping
Documentation
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

#[derive(Clone)]
pub struct RateLimiter {
    state: Arc<Mutex<HashMap<String, TokenBucket>>>,
    max_tokens: usize,
    refill_interval: Duration,
}

struct TokenBucket {
    tokens: usize,
    last_refill: Instant,
}

impl RateLimiter {
    pub fn new(max_tokens: usize, window: Duration) -> Self {
        RateLimiter {
            state: Arc::new(Mutex::new(HashMap::new())),
            max_tokens,
            refill_interval: window,
        }
    }

    pub fn allow(&self, key: &str) -> bool {
        let now = Instant::now();
        let mut state = self.state.lock().unwrap();
        let bucket = state.entry(key.to_string()).or_insert_with(|| TokenBucket {
            tokens: self.max_tokens,
            last_refill: now,
        });

        let elapsed = now.duration_since(bucket.last_refill);
        if elapsed >= self.refill_interval {
            bucket.tokens = self.max_tokens;
            bucket.last_refill = now;
        }

        if bucket.tokens > 0 {
            bucket.tokens -= 1;
            true
        } else {
            false
        }
    }

    pub fn cleanup_stale(&self, max_age: Duration) {
        let now = Instant::now();
        let mut state = self.state.lock().unwrap();
        state.retain(|_, bucket| now.duration_since(bucket.last_refill) < max_age);
    }
}