diff --git a/src/tcp2udp.rs b/src/tcp2udp.rs index b158c67..f816ef2 100644 --- a/src/tcp2udp.rs +++ b/src/tcp2udp.rs @@ -2,6 +2,7 @@ //! to UDP. use crate::logging::Redact; +use core::cmp::min; use err_context::{BoxedErrorExt as _, ErrorExt as _, ResultExt as _}; use std::convert::Infallible; use std::fmt; @@ -9,6 +10,9 @@ use std::io; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; use tokio::net::{TcpListener, TcpSocket, TcpStream, UdpSocket}; +use tokio::time::sleep; + +const OS_ERROR_TOO_MANY_OPEN_FILES: i32 = 24; #[derive(Debug)] #[cfg_attr(feature = "clap", derive(clap::Parser))] @@ -142,6 +146,30 @@ fn create_listening_socket( Ok(tcp_listener) } +struct Cooldown { + index: usize, + delays: [u64; N], +} + +impl Cooldown { + pub fn new(start: u64) -> Cooldown { + Cooldown:: { + index: 0, + delays: core::array::from_fn(|i| start * (1 << i)), + } + } + + pub fn rewind(&mut self) { + self.index = 0 + } + + pub fn step(&mut self) -> u64 { + let delay = self.delays[self.index]; + self.index = min(self.index + 1, self.delays.len() - 1); + delay + } +} + async fn process_tcp_listener( tcp_listener: TcpListener, udp_bind_ip: IpAddr, @@ -149,9 +177,11 @@ async fn process_tcp_listener( tcp_recv_timeout: Option, tcp_nodelay: bool, ) -> ! { + let mut cooldown = Cooldown::<5>::new(100); loop { match tcp_listener.accept().await { Ok((tcp_stream, tcp_peer_addr)) => { + cooldown.rewind(); log::debug!("Incoming connection from {}/TCP", Redact(tcp_peer_addr)); if let Err(error) = crate::tcp_options::set_nodelay(&tcp_stream, tcp_nodelay) { log::error!("Error: {}", error.display("\nCaused by: ")); @@ -170,7 +200,12 @@ async fn process_tcp_listener( } }); } - Err(error) => log::error!("Error when accepting incoming TCP connection: {}", error), + Err(error) => { + log::error!("Error when accepting incoming TCP connection: {}", error); + if error.raw_os_error().map_or(false, |err|err == OS_ERROR_TOO_MANY_OPEN_FILES) { + sleep(Duration::from_millis(cooldown.step())).await; + } + } } } }