From 1c4e450f1da6590176fe2e961998020907e6c73c Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 6 Jul 2022 21:16:46 +0900 Subject: [PATCH] workaround for failure to bind tcp/quic listeners --- src/constants.rs | 2 ++ src/proxy/proxy_handler.rs | 6 +++++- src/proxy/proxy_main.rs | 22 ++++++++++++++++++++-- src/proxy/proxy_tls.rs | 27 ++++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 9fef23b..e137791 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -8,6 +8,8 @@ pub const MAX_CONCURRENT_STREAMS: u32 = 16; // #[cfg(feature = "tls")] pub const CERTS_WATCH_DELAY_SECS: u32 = 10; +pub const GET_LISTENER_RETRY_TIMEOUT_SEC: u64 = 2; + #[cfg(feature = "h3")] pub const H3_ALT_SVC_MAX_AGE: u32 = 60; #[cfg(feature = "h3")] diff --git a/src/proxy/proxy_handler.rs b/src/proxy/proxy_handler.rs index 0411c79..03ad055 100644 --- a/src/proxy/proxy_handler.rs +++ b/src/proxy/proxy_handler.rs @@ -32,10 +32,14 @@ where client_addr: SocketAddr, // アクセス制御用 ) -> Result> { info!( - "Handling {:?} request from {}: {} {} {:?}", + "Handling {:?} request from {}: {} {:?} {} {:?}", req.version(), client_addr, req.method(), + req + .headers() + .get("host") + .map_or_else(|| "", |h| h.to_str().unwrap()), req.uri(), req .headers() diff --git a/src/proxy/proxy_main.rs b/src/proxy/proxy_main.rs index 3c35693..2429db0 100644 --- a/src/proxy/proxy_main.rs +++ b/src/proxy/proxy_main.rs @@ -1,6 +1,6 @@ // use super::proxy_handler::handle_request; use super::Backends; -use crate::{error::*, globals::Globals, log::*}; +use crate::{constants::GET_LISTENER_RETRY_TIMEOUT_SEC, error::*, globals::Globals, log::*}; use hyper::{ client::connect::Connect, server::conn::Http, service::service_fn, Body, Client, Request, }; @@ -77,9 +77,27 @@ where }); } + // Work around to forcibly get tcp listener for "address already in use" + async fn try_bind_tcp_listener(&self) -> Result { + let fut = async { + loop { + if let Ok(listener) = TcpListener::bind(&self.listening_on).await { + break listener; + } + } + }; + tokio::time::timeout( + tokio::time::Duration::from_secs(GET_LISTENER_RETRY_TIMEOUT_SEC), + fut, + ) + .await + .map_err(|_e| anyhow!("Failed to get listener")) + } + async fn start_without_tls(self, server: Http) -> Result<()> { let listener_service = async { - let tcp_listener = TcpListener::bind(&self.listening_on).await?; + // let tcp_listener = TcpListener::bind(&self.listening_on).await?; + let tcp_listener = self.try_bind_tcp_listener().await?; info!( "Start TCP proxy serving with HTTP request for configured host names: {:?}", tcp_listener.local_addr()? diff --git a/src/proxy/proxy_tls.rs b/src/proxy/proxy_tls.rs index d70f230..d09b6f7 100644 --- a/src/proxy/proxy_tls.rs +++ b/src/proxy/proxy_tls.rs @@ -1,5 +1,5 @@ use super::proxy_main::{LocalExecutor, Proxy}; -use crate::{constants::CERTS_WATCH_DELAY_SECS, error::*, log::*}; +use crate::{constants::*, error::*, log::*}; #[cfg(feature = "h3")] use futures::StreamExt; use futures::{future::FutureExt, join, select}; @@ -99,8 +99,8 @@ where let server_crypto = backend_serve.get_tls_server_config().unwrap(); let server_config_h3 = quinn::ServerConfig::with_crypto(Arc::new(server_crypto)); - let (endpoint, incoming) = - quinn::Endpoint::server(server_config_h3, self.listening_on).unwrap(); + let (endpoint, incoming) = self.try_bind_quic_listener(server_config_h3).await?; + // quinn::Endpoint::server(server_config_h3, self.listening_on).unwrap(); info!( "Start UDP proxy serving with HTTP/3 request for configured host names: {:?}", endpoint.local_addr()? @@ -181,6 +181,27 @@ where } } + // Work around to forcibly get quic listener for "address already in use" + #[cfg(feature = "h3")] + async fn try_bind_quic_listener( + &self, + server_config: quinn::ServerConfig, + ) -> Result<(quinn::Endpoint, quinn::Incoming)> { + let fut = async { + loop { + if let Ok(listener) = quinn::Endpoint::server(server_config.clone(), self.listening_on) { + break listener; + } + } + }; + tokio::time::timeout( + tokio::time::Duration::from_secs(GET_LISTENER_RETRY_TIMEOUT_SEC), + fut, + ) + .await + .map_err(|_e| anyhow!("Failed to get listener")) + } + fn fetch_server_crypto(&self, server_name: &str) -> Option { let backend_serve = if let Some(backend_serve) = self.backends.apps.get(server_name) { backend_serve