1use crate::error::{Error, Result};
34use crate::middleware::{Middleware, Next};
35use crate::request::Request;
36use crate::response::Response;
37use async_trait::async_trait;
38use std::collections::HashMap;
39use std::net::IpAddr;
40use std::sync::{Arc, Mutex};
41use std::time::Duration;
42use tokio::sync::Semaphore;
43
44pub struct Timeout {
46 limit: Duration,
47}
48
49impl Timeout {
50 pub fn new(limit: Duration) -> Self {
51 Self { limit }
52 }
53}
54
55#[async_trait]
56impl Middleware for Timeout {
57 async fn handle(&self, req: Request, next: Next) -> Result<Response> {
58 match tokio::time::timeout(self.limit, next.run(req)).await {
59 Ok(result) => result,
60 Err(_) => Ok(Response::from_error(Error::custom(
61 504,
62 "Request timed out",
63 ))),
64 }
65 }
66}
67
68pub struct ConcurrencyLimit {
74 permits: Arc<Semaphore>,
75 wait: Duration,
76 retry_after: u64,
77}
78
79impl ConcurrencyLimit {
80 pub fn new(max: usize) -> Self {
81 Self {
82 permits: Arc::new(Semaphore::new(max.max(1))),
83 wait: Duration::ZERO,
84 retry_after: 1,
85 }
86 }
87
88 pub fn queue_for(mut self, wait: Duration) -> Self {
90 self.wait = wait;
91 self
92 }
93
94 pub fn retry_after(mut self, seconds: u64) -> Self {
96 self.retry_after = seconds;
97 self
98 }
99
100 pub fn available_slots(&self) -> usize {
102 self.permits.available_permits()
103 }
104}
105
106#[async_trait]
107impl Middleware for ConcurrencyLimit {
108 async fn handle(&self, req: Request, next: Next) -> Result<Response> {
109 let permit = match self.permits.clone().try_acquire_owned() {
110 Ok(p) => Some(p),
111 Err(_) if self.wait.is_zero() => None,
112 Err(_) => tokio::time::timeout(self.wait, self.permits.clone().acquire_owned())
113 .await
114 .ok()
115 .and_then(|r| r.ok()),
116 };
117 let Some(_permit) = permit else {
118 return Ok(Response::from_error(Error::custom(
119 503,
120 "Server is busy, try again shortly",
121 ))
122 .with_header("retry-after", self.retry_after.to_string()));
123 };
124 next.run(req).await
125 }
126}
127
128type PerIp = Arc<Mutex<HashMap<IpAddr, usize>>>;
129
130pub(crate) struct ConnLimits {
132 total: Option<Arc<Semaphore>>,
133 per_ip: Option<(usize, PerIp)>,
134}
135
136pub(crate) struct ConnGuard {
138 _permit: Option<tokio::sync::OwnedSemaphorePermit>,
139 ip: Option<(IpAddr, PerIp)>,
140}
141
142impl Drop for ConnGuard {
143 fn drop(&mut self) {
144 if let Some((ip, map)) = self.ip.take() {
145 let mut map = map.lock().unwrap_or_else(|e| e.into_inner());
146 if let Some(n) = map.get_mut(&ip) {
147 *n -= 1;
148 if *n == 0 {
149 map.remove(&ip);
150 }
151 }
152 }
153 }
154}
155
156impl ConnLimits {
157 pub(crate) fn new(total: Option<usize>, per_ip: Option<usize>) -> Self {
158 Self {
159 total: total.map(|n| Arc::new(Semaphore::new(n))),
160 per_ip: per_ip.map(|n| (n, Arc::default())),
161 }
162 }
163
164 pub(crate) fn admit(&self, ip: IpAddr) -> Option<ConnGuard> {
166 let permit = match &self.total {
167 Some(sem) => Some(sem.clone().try_acquire_owned().ok()?),
168 None => None,
169 };
170 let ip = match &self.per_ip {
171 Some((max, map)) => {
172 let mut guard = map.lock().unwrap_or_else(|e| e.into_inner());
173 let n = guard.entry(ip).or_insert(0);
174 if *n >= *max {
175 return None; }
177 *n += 1;
178 Some((ip, map.clone()))
179 }
180 None => None,
181 };
182 Some(ConnGuard {
183 _permit: permit,
184 ip,
185 })
186 }
187}
188
189pub(crate) async fn refuse<S>(mut stream: S)
191where
192 S: tokio::io::AsyncWrite + Unpin,
193{
194 use tokio::io::AsyncWriteExt;
195 let reply = b"HTTP/1.1 503 Service Unavailable\r\nretry-after: 1\r\nconnection: close\r\ncontent-length: 0\r\n\r\n";
196 let _ = tokio::time::timeout(Duration::from_millis(500), async {
197 let _ = stream.write_all(reply).await;
198 let _ = stream.shutdown().await;
199 })
200 .await;
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 fn ip(n: u8) -> IpAddr {
208 IpAddr::from([10, 0, 0, n])
209 }
210
211 #[test]
212 fn total_cap_releases_on_drop() {
213 let limits = ConnLimits::new(Some(2), None);
214 let a = limits.admit(ip(1)).unwrap();
215 let _b = limits.admit(ip(2)).unwrap();
216 assert!(limits.admit(ip(3)).is_none());
217 drop(a);
218 assert!(limits.admit(ip(3)).is_some());
219 }
220
221 #[test]
222 fn per_ip_cap_is_independent_per_address() {
223 let limits = ConnLimits::new(None, Some(1));
224 let a = limits.admit(ip(1)).unwrap();
225 assert!(limits.admit(ip(1)).is_none());
226 assert!(limits.admit(ip(2)).is_some());
227 drop(a);
228 assert!(limits.admit(ip(1)).is_some());
229 }
230
231 #[test]
232 fn refused_per_ip_does_not_leak_a_total_slot() {
233 let limits = ConnLimits::new(Some(2), Some(1));
234 let _a = limits.admit(ip(1)).unwrap();
235 assert!(limits.admit(ip(1)).is_none());
236 assert!(limits.admit(ip(2)).is_some());
237 }
238
239 #[test]
240 fn no_caps_admit_everything() {
241 let limits = ConnLimits::new(None, None);
242 let _g: Vec<_> = (0..100).map(|_| limits.admit(ip(1)).unwrap()).collect();
243 }
244}