1use std::collections::HashMap;
22use std::net::SocketAddr;
23use std::sync::atomic::{AtomicU64, Ordering};
24use std::sync::Arc;
25use std::time::Instant;
26
27use parking_lot::RwLock;
28use tracing::{debug, warn};
29
30#[derive(Debug, Clone)]
32pub struct ConnectionLimits {
33 pub max_connections: u64,
35 pub max_per_ip: u64,
37 pub max_streams_per_connection: u32,
39}
40
41impl Default for ConnectionLimits {
42 fn default() -> Self {
43 Self {
44 max_connections: 10_000,
45 max_per_ip: 100,
46 max_streams_per_connection: 100,
47 }
48 }
49}
50
51impl ConnectionLimits {
52 pub fn new(max_connections: u64, max_per_ip: u64) -> Self {
54 Self {
55 max_connections,
56 max_per_ip,
57 ..Default::default()
58 }
59 }
60
61 pub fn with_max_streams(mut self, max: u32) -> Self {
63 self.max_streams_per_connection = max;
64 self
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct ConnectionInfo {
71 pub id: u64,
73 pub remote_addr: Option<SocketAddr>,
75 pub connected_at: Instant,
77 pub active_streams: u32,
79}
80
81#[derive(Debug)]
85pub struct ConnectionTracker {
86 inner: Arc<ConnectionTrackerInner>,
87}
88
89#[derive(Debug)]
90struct ConnectionTrackerInner {
91 limits: ConnectionLimits,
92 next_id: AtomicU64,
94 active: AtomicU64,
96 per_ip: RwLock<HashMap<std::net::IpAddr, u64>>,
98 connections: RwLock<HashMap<u64, ConnectionInfo>>,
100}
101
102impl Clone for ConnectionTracker {
103 fn clone(&self) -> Self {
104 Self {
105 inner: Arc::clone(&self.inner),
106 }
107 }
108}
109
110impl Default for ConnectionTracker {
111 fn default() -> Self {
112 Self::new(ConnectionLimits::default())
113 }
114}
115
116impl ConnectionTracker {
117 pub fn new(limits: ConnectionLimits) -> Self {
119 Self {
120 inner: Arc::new(ConnectionTrackerInner {
121 limits,
122 next_id: AtomicU64::new(1),
123 active: AtomicU64::new(0),
124 per_ip: RwLock::new(HashMap::new()),
125 connections: RwLock::new(HashMap::new()),
126 }),
127 }
128 }
129
130 pub fn limits(&self) -> &ConnectionLimits {
132 &self.inner.limits
133 }
134
135 pub fn active_connections(&self) -> u64 {
137 self.inner.active.load(Ordering::Relaxed)
138 }
139
140 pub fn connections_for_ip(&self, ip: std::net::IpAddr) -> u64 {
142 self.inner
143 .per_ip
144 .read()
145 .get(&ip)
146 .copied()
147 .unwrap_or(0)
148 }
149
150 pub fn try_acquire(&self, remote_addr: Option<SocketAddr>) -> Option<ConnectionGuard> {
154 let current = self.inner.active.load(Ordering::Relaxed);
156 if current >= self.inner.limits.max_connections {
157 warn!(
158 current = current,
159 limit = self.inner.limits.max_connections,
160 "connection rejected: at max connections"
161 );
162 return None;
163 }
164
165 if let Some(addr) = remote_addr {
167 let ip = addr.ip();
168 let mut per_ip = self.inner.per_ip.write();
169 let ip_count = per_ip.get(&ip).copied().unwrap_or(0);
170
171 if ip_count >= self.inner.limits.max_per_ip {
172 warn!(
173 ip = %ip,
174 current = ip_count,
175 limit = self.inner.limits.max_per_ip,
176 "connection rejected: at max per-IP limit"
177 );
178 return None;
179 }
180
181 *per_ip.entry(ip).or_insert(0) += 1;
183 }
184
185 self.inner.active.fetch_add(1, Ordering::Relaxed);
187
188 let id = self.inner.next_id.fetch_add(1, Ordering::Relaxed);
190 let info = ConnectionInfo {
191 id,
192 remote_addr,
193 connected_at: Instant::now(),
194 active_streams: 0,
195 };
196
197 self.inner.connections.write().insert(id, info);
198
199 debug!(
200 id = id,
201 remote_addr = ?remote_addr,
202 active = self.active_connections(),
203 "connection accepted"
204 );
205
206 Some(ConnectionGuard {
207 tracker: self.clone(),
208 id,
209 remote_addr,
210 })
211 }
212
213 pub fn list_connections(&self) -> Vec<ConnectionInfo> {
215 self.inner
216 .connections
217 .read()
218 .values()
219 .cloned()
220 .collect()
221 }
222
223 pub fn get_connection(&self, id: u64) -> Option<ConnectionInfo> {
225 self.inner.connections.read().get(&id).cloned()
226 }
227
228 fn release(&self, id: u64, remote_addr: Option<SocketAddr>) {
229 self.inner.active.fetch_sub(1, Ordering::Relaxed);
231
232 if let Some(addr) = remote_addr {
234 let ip = addr.ip();
235 let mut per_ip = self.inner.per_ip.write();
236 if let Some(count) = per_ip.get_mut(&ip) {
237 *count = count.saturating_sub(1);
238 if *count == 0 {
239 per_ip.remove(&ip);
240 }
241 }
242 }
243
244 self.inner.connections.write().remove(&id);
246
247 debug!(
248 id = id,
249 remote_addr = ?remote_addr,
250 active = self.active_connections(),
251 "connection released"
252 );
253 }
254}
255
256#[derive(Debug)]
260pub struct ConnectionGuard {
261 tracker: ConnectionTracker,
262 id: u64,
263 remote_addr: Option<SocketAddr>,
264}
265
266impl ConnectionGuard {
267 pub fn id(&self) -> u64 {
269 self.id
270 }
271
272 pub fn remote_addr(&self) -> Option<SocketAddr> {
274 self.remote_addr
275 }
276
277 pub fn add_stream(&self) -> bool {
279 let mut connections = self.tracker.inner.connections.write();
280 if let Some(info) = connections.get_mut(&self.id) {
281 if info.active_streams >= self.tracker.inner.limits.max_streams_per_connection {
282 return false;
283 }
284 info.active_streams += 1;
285 true
286 } else {
287 false
288 }
289 }
290
291 pub fn remove_stream(&self) {
293 let mut connections = self.tracker.inner.connections.write();
294 if let Some(info) = connections.get_mut(&self.id) {
295 info.active_streams = info.active_streams.saturating_sub(1);
296 }
297 }
298}
299
300impl Drop for ConnectionGuard {
301 fn drop(&mut self) {
302 self.tracker.release(self.id, self.remote_addr);
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use std::net::{IpAddr, Ipv4Addr};
310
311 #[test]
312 fn connection_limits_default() {
313 let limits = ConnectionLimits::default();
314 assert_eq!(limits.max_connections, 10_000);
315 assert_eq!(limits.max_per_ip, 100);
316 }
317
318 #[test]
319 fn tracker_basic() {
320 let tracker = ConnectionTracker::new(ConnectionLimits::new(10, 5));
321 assert_eq!(tracker.active_connections(), 0);
322
323 let guard = tracker.try_acquire(None).expect("should acquire connection");
324 assert_eq!(tracker.active_connections(), 1);
325 assert!(guard.id() > 0);
326
327 drop(guard);
328 assert_eq!(tracker.active_connections(), 0);
329 }
330
331 #[test]
332 fn tracker_max_connections() {
333 let tracker = ConnectionTracker::new(ConnectionLimits::new(2, 10));
334
335 let _g1 = tracker.try_acquire(None).expect("should acquire first connection");
336 let _g2 = tracker.try_acquire(None).expect("should acquire second connection");
337
338 assert!(tracker.try_acquire(None).is_none());
340 }
341
342 #[test]
343 fn tracker_per_ip_limit() {
344 let tracker = ConnectionTracker::new(ConnectionLimits::new(100, 2));
345
346 let addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
347 let addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)), 8080);
348
349 let _g1 = tracker.try_acquire(Some(addr1)).expect("should acquire first connection");
350 let _g2 = tracker.try_acquire(Some(addr1)).expect("should acquire second connection");
351
352 assert!(tracker.try_acquire(Some(addr1)).is_none());
354
355 let _g3 = tracker.try_acquire(Some(addr2)).expect("should acquire from different IP");
357 }
358
359 #[test]
360 fn tracker_per_ip_release() {
361 let tracker = ConnectionTracker::new(ConnectionLimits::new(100, 2));
362
363 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
364
365 {
366 let _g1 = tracker.try_acquire(Some(addr)).expect("should acquire first");
367 let _g2 = tracker.try_acquire(Some(addr)).expect("should acquire second");
368 assert!(tracker.try_acquire(Some(addr)).is_none());
369 }
370
371 let _g = tracker.try_acquire(Some(addr)).expect("should acquire after release");
373 assert_eq!(tracker.connections_for_ip(addr.ip()), 1);
374 }
375
376 #[test]
377 fn tracker_stream_counting() {
378 let tracker = ConnectionTracker::new(ConnectionLimits::new(10, 10).with_max_streams(2));
379
380 let guard = tracker.try_acquire(None).expect("should acquire connection");
381
382 assert!(guard.add_stream());
383 assert!(guard.add_stream());
384 assert!(!guard.add_stream()); guard.remove_stream();
387 assert!(guard.add_stream()); }
389
390 #[test]
391 fn tracker_list_connections() {
392 let tracker = ConnectionTracker::new(ConnectionLimits::default());
393
394 let _g1 = tracker.try_acquire(None).expect("should acquire first");
395 let _g2 = tracker.try_acquire(None).expect("should acquire second");
396
397 let connections = tracker.list_connections();
398 assert_eq!(connections.len(), 2);
399 }
400}