1use std::collections::{HashMap, HashSet};
42use std::net::SocketAddr;
43use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
44use std::sync::Mutex;
45use std::time::{Duration, Instant};
46
47static CLOSED: AtomicU64 = AtomicU64::new(0);
49
50static REFUSED: AtomicU64 = AtomicU64::new(0);
52
53static DEADLINE_SECONDS: AtomicU64 = AtomicU64::new(0);
55
56#[derive(
70 Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
71)]
72pub struct ConnectionCounts {
73 pub read_deadline_seconds: u64,
75 pub closed_by_deadline: u64,
77 pub refused_busy: u64,
79}
80
81pub fn counts() -> Option<ConnectionCounts> {
89 let seconds = DEADLINE_SECONDS.load(Ordering::SeqCst);
90 (seconds > 0).then(|| ConnectionCounts {
91 read_deadline_seconds: seconds,
92 closed_by_deadline: CLOSED.load(Ordering::SeqCst),
93 refused_busy: REFUSED.load(Ordering::SeqCst),
94 })
95}
96
97pub(crate) fn refused() {
99 REFUSED.fetch_add(1, Ordering::SeqCst);
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104enum Phase {
105 Waiting(Instant),
108 Reading(Instant),
110 Busy,
112 Live,
114 Closed,
116}
117
118pub(crate) struct Watch {
120 local: SocketAddr,
121 deadline: Duration,
122 peers: Mutex<HashMap<SocketAddr, Phase>>,
123}
124
125impl Watch {
126 pub(crate) fn new(local: SocketAddr, deadline: Duration) -> Self {
128 DEADLINE_SECONDS.store(deadline.as_secs().max(1), Ordering::SeqCst);
129 Self {
130 local,
131 deadline,
132 peers: Mutex::new(HashMap::new()),
133 }
134 }
135
136 fn peers(&self) -> std::sync::MutexGuard<'_, HashMap<SocketAddr, Phase>> {
137 self.peers.lock().unwrap_or_else(|e| e.into_inner())
138 }
139
140 pub(crate) fn reading(&self, peer: Option<&SocketAddr>) {
142 if let Some(peer) = peer {
143 let mut peers = self.peers();
145 let phase = peers.entry(*peer).or_insert(Phase::Busy);
146 if *phase != Phase::Closed {
147 *phase = Phase::Reading(Instant::now());
148 }
149 }
150 }
151
152 pub(crate) fn busy(&self, peer: Option<&SocketAddr>) -> bool {
156 let Some(peer) = peer else { return true };
157 let mut peers = self.peers();
158 match peers.get_mut(peer) {
159 Some(Phase::Closed) => false,
160 Some(phase) => {
161 *phase = Phase::Busy;
162 true
163 }
164 None => {
165 peers.insert(*peer, Phase::Busy);
166 true
167 }
168 }
169 }
170
171 pub(crate) fn live(&self, peer: Option<&SocketAddr>) {
173 if let Some(peer) = peer {
174 self.peers().insert(*peer, Phase::Live);
175 }
176 }
177
178 pub(crate) fn hung_up(&self, peer: Option<&SocketAddr>) -> impl Fn() -> bool + Send + 'static {
188 let descriptor = peer.and_then(|peer| sockets::descriptor(self.local, *peer));
189 move || descriptor.is_some_and(sockets::hung_up)
190 }
191
192 pub(crate) fn answered(&self, peer: Option<&SocketAddr>) {
194 if let Some(peer) = peer {
195 if let Some(phase) = self.peers().get_mut(peer) {
196 if *phase == Phase::Busy {
197 *phase = Phase::Waiting(Instant::now());
198 }
199 }
200 }
201 }
202
203 pub(crate) fn stop_reading(&self, peer: Option<&SocketAddr>) {
209 if let Some(peer) = peer {
210 sockets::shut_reading(self.local, *peer);
211 }
212 }
213
214 pub(crate) fn interval(&self) -> Duration {
216 (self.deadline / 4).clamp(Duration::from_millis(100), Duration::from_secs(1))
217 }
218
219 pub(crate) fn run(&self, stopping: &AtomicBool) {
221 let step = Duration::from_millis(50);
222 let mut next = Instant::now() + self.interval();
223 while !stopping.load(Ordering::SeqCst) {
224 std::thread::sleep(step);
225 if Instant::now() >= next {
226 self.sweep(Instant::now());
227 next = Instant::now() + self.interval();
228 }
229 }
230 }
231
232 fn sweep(&self, now: Instant) {
235 self.sweep_with(now, sockets::connected_to(self.local), sockets::shut_down);
236 }
237
238 fn sweep_with(
241 &self,
242 now: Instant,
243 open: Vec<(i32, SocketAddr)>,
244 close: impl Fn(i32, SocketAddr, SocketAddr) -> bool,
245 ) {
246 let mut peers = self.peers();
247 let mut seen = HashSet::new();
248 for (descriptor, peer) in open {
249 if !seen.insert(peer) {
252 continue;
253 }
254 let phase = peers.entry(peer).or_insert(Phase::Waiting(now));
255 let since = match *phase {
256 Phase::Waiting(since) | Phase::Reading(since) => since,
257 Phase::Busy | Phase::Live | Phase::Closed => continue,
258 };
259 if now.saturating_duration_since(since) < self.deadline {
260 continue;
261 }
262 if close(descriptor, self.local, peer) {
263 *phase = Phase::Closed;
264 CLOSED.fetch_add(1, Ordering::SeqCst);
265 tracing::info!(
266 peer = %peer,
267 deadline_seconds = self.deadline.as_secs(),
268 "closed a connection that read no request within the deadline"
269 );
270 }
271 }
272 peers.retain(|peer, _| seen.contains(peer));
273 }
274}
275
276#[cfg(unix)]
277mod sockets {
278 use std::mem::ManuallyDrop;
279 use std::net::{Shutdown, SocketAddr, TcpStream};
280 use std::os::fd::{FromRawFd, RawFd};
281
282 fn descriptors() -> Vec<RawFd> {
284 ["/dev/fd", "/proc/self/fd"]
285 .iter()
286 .find_map(|dir| std::fs::read_dir(dir).ok())
287 .map(|entries| {
288 entries
289 .filter_map(Result::ok)
290 .filter_map(|e| descriptor_of(&e.file_name()))
291 .collect()
292 })
293 .unwrap_or_default()
294 }
295
296 pub(super) fn descriptor_of(name: &std::ffi::OsStr) -> Option<RawFd> {
298 name.to_str()?.parse().ok()
299 }
300
301 fn addresses(descriptor: RawFd) -> Option<(SocketAddr, SocketAddr)> {
303 let stream = ManuallyDrop::new(unsafe { TcpStream::from_raw_fd(descriptor) });
307 Some((stream.local_addr().ok()?, stream.peer_addr().ok()?))
308 }
309
310 fn served(local: SocketAddr, mine: SocketAddr) -> bool {
311 mine.port() == local.port() && (local.ip().is_unspecified() || mine.ip() == local.ip())
312 }
313
314 pub(super) fn connected_to(local: SocketAddr) -> Vec<(RawFd, SocketAddr)> {
316 descriptors()
317 .into_iter()
318 .filter_map(|d| {
319 let (mine, peer) = addresses(d)?;
320 served(local, mine).then_some((d, peer))
321 })
322 .collect()
323 }
324
325 pub(super) fn shut_reading(local: SocketAddr, peer: SocketAddr) {
327 for (descriptor, theirs) in connected_to(local) {
328 if theirs == peer {
329 let stream = ManuallyDrop::new(unsafe { TcpStream::from_raw_fd(descriptor) });
331 let _ = stream.shutdown(Shutdown::Read);
332 return;
333 }
334 }
335 }
336
337 pub(super) fn descriptor(local: SocketAddr, peer: SocketAddr) -> Option<RawFd> {
342 connected_to(local)
343 .into_iter()
344 .find_map(|(descriptor, theirs)| (theirs == peer).then_some(descriptor))
345 }
346
347 pub(super) fn hung_up(descriptor: RawFd) -> bool {
357 let mut buffer = [0u8; 4096];
358 for _ in 0..64 {
360 let read = unsafe {
363 libc::recv(
364 descriptor,
365 buffer.as_mut_ptr().cast(),
366 buffer.len(),
367 libc::MSG_DONTWAIT,
368 )
369 };
370 match read {
371 0 => return true,
372 n if n > 0 => continue,
373 _ => {
374 let error = std::io::Error::last_os_error();
375 return !matches!(
376 error.kind(),
377 std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted
378 );
379 }
380 }
381 }
382 false
383 }
384
385 pub(super) fn shut_down(descriptor: RawFd, local: SocketAddr, peer: SocketAddr) -> bool {
388 match addresses(descriptor) {
389 Some((mine, now)) if served(local, mine) && now == peer => {
390 let stream = ManuallyDrop::new(unsafe { TcpStream::from_raw_fd(descriptor) });
393 stream.shutdown(Shutdown::Both).is_ok()
394 }
395 _ => false,
396 }
397 }
398}
399
400#[cfg(not(unix))]
401mod sockets {
402 use std::net::SocketAddr;
403
404 pub(super) fn connected_to(_: SocketAddr) -> Vec<(i32, SocketAddr)> {
405 Vec::new()
406 }
407
408 pub(super) fn shut_down(_: i32, _: SocketAddr, _: SocketAddr) -> bool {
409 false
410 }
411
412 pub(super) fn shut_reading(_: SocketAddr, _: SocketAddr) {}
413
414 pub(super) fn descriptor(_: SocketAddr, _: SocketAddr) -> Option<i32> {
415 None
416 }
417
418 pub(super) fn hung_up(_: i32) -> bool {
419 false
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 fn watch() -> Watch {
428 Watch {
429 local: "127.0.0.1:1".parse().unwrap(),
430 deadline: Duration::from_secs(2),
431 peers: Mutex::new(HashMap::new()),
432 }
433 }
434
435 #[test]
436 fn a_body_cut_short_by_the_sweeper_is_never_answered() {
437 let w = watch();
438 let peer: SocketAddr = "127.0.0.1:5000".parse().unwrap();
439 w.reading(Some(&peer));
440 w.peers().insert(peer, Phase::Closed);
441 assert!(!w.busy(Some(&peer)));
442 w.reading(Some(&peer));
444 assert_eq!(w.peers()[&peer], Phase::Closed);
445 }
446
447 #[test]
448 fn an_answered_request_starts_the_wait_for_the_next() {
449 let w = watch();
450 let peer: SocketAddr = "127.0.0.1:5001".parse().unwrap();
451 w.reading(Some(&peer));
452 assert!(w.busy(Some(&peer)));
453 assert_eq!(w.peers()[&peer], Phase::Busy);
454 w.answered(Some(&peer));
455 assert!(matches!(w.peers()[&peer], Phase::Waiting(_)));
456 }
457
458 #[test]
459 fn a_live_channel_outlives_the_answer_that_opened_it() {
460 let w = watch();
461 let peer: SocketAddr = "127.0.0.1:5002".parse().unwrap();
462 w.reading(Some(&peer));
463 w.live(Some(&peer));
464 w.answered(Some(&peer));
465 assert_eq!(w.peers()[&peer], Phase::Live);
466 }
467
468 #[test]
469 fn the_sweeper_looks_often_enough_to_keep_the_deadline() {
470 let mut w = watch();
471 assert_eq!(w.interval(), Duration::from_millis(500));
472 w.deadline = Duration::from_secs(60);
473 assert_eq!(w.interval(), Duration::from_secs(1));
474 }
475
476 #[test]
477 fn a_request_whose_peer_is_unknown_is_answered_and_never_tracked() {
478 let w = watch();
479 w.reading(None);
482 assert!(w.busy(None));
483 w.live(None);
484 w.answered(None);
485 w.stop_reading(None);
486 assert!(w.peers().is_empty());
487 }
488
489 #[test]
490 fn a_request_read_before_its_head_was_seen_is_busy_from_then_on() {
491 let w = watch();
492 let peer: SocketAddr = "127.0.0.1:5003".parse().unwrap();
493 assert!(w.busy(Some(&peer)));
495 assert_eq!(w.peers()[&peer], Phase::Busy);
496 let other: SocketAddr = "127.0.0.1:5004".parse().unwrap();
498 w.answered(Some(&other));
499 assert!(!w.peers().contains_key(&other));
500 }
501
502 #[test]
503 fn a_watch_poisoned_by_a_panic_still_tracks_its_connections() {
504 let w = std::sync::Arc::new(watch());
505 let held = std::sync::Arc::clone(&w);
506 let _ = std::thread::spawn(move || {
507 let _guard = held.peers.lock().unwrap();
508 panic!("a handler panicked while it held the watch");
509 })
510 .join();
511 assert!(w.peers.is_poisoned());
512 let peer: SocketAddr = "127.0.0.1:5005".parse().unwrap();
513 w.reading(Some(&peer));
514 assert!(matches!(w.peers()[&peer], Phase::Reading(_)));
515 assert!(w.busy(Some(&peer)));
516 }
517
518 #[derive(Clone, Default)]
520 struct Captured(std::sync::Arc<Mutex<Vec<u8>>>);
521
522 impl std::io::Write for Captured {
523 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
524 self.0
525 .lock()
526 .unwrap_or_else(|e| e.into_inner())
527 .extend_from_slice(bytes);
528 Ok(bytes.len())
529 }
530
531 fn flush(&mut self) -> std::io::Result<()> {
532 Ok(())
533 }
534 }
535
536 impl Captured {
537 fn text(&self) -> String {
538 String::from_utf8_lossy(&self.0.lock().unwrap_or_else(|e| e.into_inner())).into_owned()
539 }
540 }
541
542 #[cfg(unix)]
543 #[test]
544 fn a_connection_idle_past_the_deadline_is_closed_counted_and_logged() {
545 use std::io::Read;
546 use std::net::{TcpListener, TcpStream};
547
548 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
549 let local = listener.local_addr().unwrap();
550 let w = Watch::new(local, Duration::from_secs(2));
551 let mut client = TcpStream::connect(local).unwrap();
552 let (accepted, peer) = listener.accept().unwrap();
553 let twin = accepted.try_clone().unwrap();
555
556 let start = Instant::now();
557 w.sweep(start);
558 assert_eq!(
559 w.peers()[&peer],
560 Phase::Waiting(start),
561 "a connection first seen by the sweeper waits from then on"
562 );
563 w.sweep(start + Duration::from_secs(1));
564 assert_eq!(
565 w.peers()[&peer],
566 Phase::Waiting(start),
567 "within the deadline"
568 );
569
570 let before = CLOSED.load(Ordering::SeqCst);
571 let captured = Captured::default();
572 let writer = captured.clone();
573 let subscriber = tracing_subscriber::fmt()
574 .with_max_level(tracing::Level::INFO)
575 .with_writer(move || writer.clone())
576 .finish();
577 tracing::subscriber::with_default(subscriber, || {
578 w.sweep(start + Duration::from_secs(3));
579 });
580 assert_eq!(w.peers()[&peer], Phase::Closed, "past the deadline");
581 assert!(
582 CLOSED.load(Ordering::SeqCst) > before,
583 "the close is counted"
584 );
585 let log = captured.text();
586 assert!(
587 log.contains("closed a connection that read no request within the deadline"),
588 "{log}"
589 );
590 assert!(log.contains("deadline_seconds=2"), "{log}");
591 assert!(log.contains(&peer.to_string()), "the peer is named: {log}");
592
593 client
595 .set_read_timeout(Some(Duration::from_secs(30)))
596 .unwrap();
597 let mut buffer = [0u8; 16];
598 assert_eq!(client.read(&mut buffer).unwrap_or(0), 0);
599
600 drop(client);
602 drop(accepted);
603 drop(twin);
604 w.sweep(start + Duration::from_secs(4));
605 assert!(!w.peers().contains_key(&peer));
606 }
607
608 #[test]
609 fn a_connection_the_system_will_not_close_stays_as_it_was_and_a_gone_one_is_forgotten() {
610 let w = watch();
611 let peer: SocketAddr = "127.0.0.1:40000".parse().unwrap();
612 let start = Instant::now();
613 let asked = std::cell::Cell::new(0);
614 let refuse = |descriptor: i32, _: SocketAddr, at: SocketAddr| {
615 assert_eq!((descriptor, at), (7, peer));
616 asked.set(asked.get() + 1);
617 false
618 };
619 w.sweep_with(start, vec![(7, peer), (8, peer)], refuse);
621 assert_eq!(asked.get(), 0, "within the deadline nothing is closed");
622 w.sweep_with(start + Duration::from_secs(3), vec![(7, peer)], refuse);
623 assert_eq!(asked.get(), 1, "past the deadline the system is asked once");
624 assert_eq!(
625 w.peers()[&peer],
626 Phase::Waiting(start),
627 "a connection the system did not close is not called closed"
628 );
629 w.sweep_with(start + Duration::from_secs(4), vec![], |_, _, _| {
630 unreachable!("nothing is open")
631 });
632 assert!(
633 w.peers().is_empty(),
634 "a connection that is gone is forgotten"
635 );
636 }
637
638 #[cfg(unix)]
639 #[test]
640 fn only_a_number_names_a_descriptor() {
641 use std::ffi::OsStr;
642 use std::os::unix::ffi::OsStrExt;
643 assert_eq!(sockets::descriptor_of(OsStr::new("12")), Some(12));
644 assert_eq!(sockets::descriptor_of(OsStr::new("cwd")), None);
645 assert_eq!(sockets::descriptor_of(OsStr::from_bytes(b"\xff")), None);
646 }
647
648 #[cfg(unix)]
649 #[test]
650 fn a_descriptor_that_is_not_the_listed_connection_is_left_alone() {
651 use std::io::{Read, Write};
652 use std::net::{TcpListener, TcpStream};
653 use std::os::fd::AsRawFd;
654
655 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
656 let local = listener.local_addr().unwrap();
657 let mut client = TcpStream::connect(local).unwrap();
658 let (mut accepted, peer) = listener.accept().unwrap();
659
660 assert!(!sockets::shut_down(listener.as_raw_fd(), local, peer));
662 let other: SocketAddr = "127.0.0.1:9".parse().unwrap();
664 assert!(!sockets::shut_down(accepted.as_raw_fd(), local, other));
665 let elsewhere: SocketAddr = "127.0.0.1:1".parse().unwrap();
667 assert!(!sockets::shut_down(accepted.as_raw_fd(), elsewhere, peer));
668
669 accepted.write_all(b"x").unwrap();
671 client
672 .set_read_timeout(Some(Duration::from_secs(30)))
673 .unwrap();
674 let mut byte = [0u8; 1];
675 client.read_exact(&mut byte).unwrap();
676 assert_eq!(&byte, b"x");
677
678 assert!(sockets::shut_down(accepted.as_raw_fd(), local, peer));
680 assert_eq!(client.read(&mut byte).unwrap_or(0), 0);
681 }
682}