mentedb_server/
rate_limit.rs1use std::collections::HashMap;
4use std::net::IpAddr;
5use std::sync::Arc;
6use std::task::{Context, Poll};
7use std::time::Instant;
8
9use axum::body::Body;
10use axum::http::{Request, StatusCode};
11use axum::response::IntoResponse;
12use serde_json::json;
13use tokio::sync::Mutex;
14use tower::{Layer, Service};
15
16struct Bucket {
18 tokens: u32,
19 last_refill: Instant,
20 max_tokens: u32,
21 refill_rate: u32,
22}
23
24impl Bucket {
25 fn new(max_tokens: u32, refill_rate: u32) -> Self {
26 Self {
27 tokens: max_tokens,
28 last_refill: Instant::now(),
29 max_tokens,
30 refill_rate,
31 }
32 }
33
34 fn try_consume(&mut self) -> bool {
36 let now = Instant::now();
37 let elapsed = now.duration_since(self.last_refill);
38 let refill = (elapsed.as_secs_f64() * f64::from(self.refill_rate)) as u32;
39 if refill > 0 {
40 self.tokens = (self.tokens + refill).min(self.max_tokens);
41 self.last_refill = now;
42 }
43 if self.tokens > 0 {
44 self.tokens -= 1;
45 true
46 } else {
47 false
48 }
49 }
50}
51
52#[derive(Clone)]
54pub struct RateLimiter {
55 buckets: Arc<Mutex<HashMap<IpAddr, Bucket>>>,
56 max_tokens: u32,
57 refill_rate: u32,
58}
59
60impl RateLimiter {
61 pub fn new(max_tokens: u32, refill_rate: u32) -> Self {
63 Self {
64 buckets: Arc::new(Mutex::new(HashMap::new())),
65 max_tokens,
66 refill_rate,
67 }
68 }
69}
70
71impl Default for RateLimiter {
72 fn default() -> Self {
73 Self::new(100, 10)
74 }
75}
76
77impl<S> Layer<S> for RateLimiter {
80 type Service = RateLimitService<S>;
81
82 fn layer(&self, inner: S) -> Self::Service {
83 RateLimitService {
84 inner,
85 limiter: self.clone(),
86 }
87 }
88}
89
90#[derive(Clone)]
93pub struct RateLimitService<S> {
94 inner: S,
95 limiter: RateLimiter,
96}
97
98impl<S> Service<Request<Body>> for RateLimitService<S>
99where
100 S: Service<Request<Body>, Response = axum::response::Response> + Clone + Send + 'static,
101 S::Future: Send + 'static,
102{
103 type Response = axum::response::Response;
104 type Error = S::Error;
105 type Future = std::pin::Pin<
106 Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>,
107 >;
108
109 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
110 self.inner.poll_ready(cx)
111 }
112
113 fn call(&mut self, req: Request<Body>) -> Self::Future {
114 let limiter = self.limiter.clone();
115 let mut inner = self.inner.clone();
116
117 let ip = req
119 .extensions()
120 .get::<axum::extract::ConnectInfo<std::net::SocketAddr>>()
121 .map(|ci| ci.0.ip())
122 .unwrap_or(IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED));
123
124 Box::pin(async move {
125 let allowed = {
126 let mut buckets = limiter.buckets.lock().await;
127 let bucket = buckets
128 .entry(ip)
129 .or_insert_with(|| Bucket::new(limiter.max_tokens, limiter.refill_rate));
130 bucket.try_consume()
131 };
132
133 if allowed {
134 inner.call(req).await
135 } else {
136 let body = json!({ "error": "too many requests" });
137 Ok((StatusCode::TOO_MANY_REQUESTS, axum::Json(body)).into_response())
138 }
139 })
140 }
141}