rocket/listener/
bounced.rs

1use std::{io, time::Duration};
2
3use crate::listener::{Listener, Endpoint};
4
5static DURATION: Duration = Duration::from_millis(250);
6
7pub struct Bounced<L> {
8    listener: L,
9}
10
11pub trait BouncedExt: Sized {
12    fn bounced(self) -> Bounced<Self> {
13        Bounced { listener: self }
14    }
15}
16
17impl<L> BouncedExt for L { }
18
19fn is_recoverable(e: &io::Error) -> bool {
20    matches!(e.kind(),
21        | io::ErrorKind::ConnectionRefused
22        | io::ErrorKind::ConnectionAborted
23        | io::ErrorKind::ConnectionReset)
24}
25
26impl<L: Listener + Sync> Bounced<L> {
27    #[inline]
28    pub async fn accept_next(&self) -> <Self as Listener>::Accept {
29        loop {
30            match self.listener.accept().await {
31                Ok(accept) => return accept,
32                Err(e) if is_recoverable(&e) => warn!("recoverable connection error: {e}"),
33                Err(e) => {
34                    warn!("accept error: {e} [retrying in {}ms]", DURATION.as_millis());
35                    tokio::time::sleep(DURATION).await;
36                }
37            };
38        }
39    }
40}
41
42impl<L: Listener + Sync> Listener for Bounced<L> {
43    type Accept = L::Accept;
44
45    type Connection = L::Connection;
46
47    async fn accept(&self) -> io::Result<Self::Accept> {
48        Ok(self.accept_next().await)
49    }
50
51    async fn connect(&self, accept: Self::Accept) -> io::Result<Self::Connection> {
52        self.listener.connect(accept).await
53    }
54
55    fn endpoint(&self) -> io::Result<Endpoint> {
56        self.listener.endpoint()
57    }
58}