From 9ebb8ee7a831967c5fd2408c54945965fa8c997e Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Fri, 12 Jun 2026 06:14:46 +0000 Subject: [PATCH] Fix HTTP/2 support: use ALPN-based protocol detection and fallback to URI host Two changes to properly support HTTP/2 clients: 1. server.rs: Detect ALPN protocol after TLS handshake and use hyper::server::conn::http2::Builder for H2 connections instead of the auto::Builder which failed to detect HTTP/2 over TLS. The auto::Builder's ReadVersion mechanism doesn't work reliably with tokio-rustls TlsStreams. For H1 connections, continue using auto::Builder with upgrade support. 2. handler.rs: Fallback to URI host when Host header is missing. In HTTP/2, the host is conveyed via :authority pseudo-header which hyper represents as the URI host, not a Host header. --- src/proxy/handler.rs | 11 +++++++---- src/server.rs | 46 +++++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/proxy/handler.rs b/src/proxy/handler.rs index 261dd34..358b5fe 100644 --- a/src/proxy/handler.rs +++ b/src/proxy/handler.rs @@ -39,11 +39,14 @@ async fn proxy_handler( let host = req .headers() .get(axum::http::header::HOST) - .and_then(|v| v.to_str().ok()); + .and_then(|v| v.to_str().ok()) + .or_else(|| req.uri().host()) + .unwrap_or_default(); - let host = match host { - Some(h) => h, - None => return ProxyError::MissingHost.into_response(), + let host = if host.is_empty() { + return ProxyError::MissingHost.into_response(); + } else { + host }; let config = state.config.load(); diff --git a/src/server.rs b/src/server.rs index 7046a9d..de1b76f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,6 +6,7 @@ use axum::extract::ConnectInfo; use axum::http::Request; use axum::response::Response; use axum::Router; +use hyper::body::Incoming; use hyper_util::rt::TokioExecutor; use hyper_util::service::TowerToHyperService; use tokio::net::TcpListener; @@ -80,24 +81,40 @@ pub async fn serve_https_listener( } }; + let alpn = tls_stream.get_ref().1.alpn_protocol(); + let is_h2 = alpn == Some(b"h2"); + let svc = ConnectInfoService { - inner: router.into_service::(), + inner: router.into_service::(), remote_addr, }; - let svc = TowerToHyperService::new(svc); + let io = hyper_util::rt::TokioIo::new(tls_stream); - if let Err(e) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new()) - .serve_connection_with_upgrades(io, svc) - .await - { - if let Some(hyper_err) = e.downcast_ref::() { - if hyper_err.is_incomplete_message() { - return; - } + if is_h2 { + let mut builder = hyper::server::conn::http2::Builder::new(TokioExecutor::new()); + if let Err(e) = builder + .enable_connect_protocol() + .serve_connection(io, svc) + .await + { + error!(error = %e, "HTTPS/2 connection error"); + } + } else { + let mut builder = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new()); + builder.http2().enable_connect_protocol(); + if let Err(e) = builder + .serve_connection_with_upgrades(io, svc) + .await + { + if let Some(hyper_err) = e.downcast_ref::() { + if hyper_err.is_incomplete_message() { + return; + } + } + error!(error = %e, "HTTPS connection error"); } - error!(error = %e, "HTTPS connection error"); } }); } @@ -135,11 +152,10 @@ struct ConnectInfoService { remote_addr: SocketAddr, } -impl Service> for ConnectInfoService +impl Service> for ConnectInfoService where - S: Service, Response = Response> + Clone + Send + 'static, + S: Service, Response = Response> + Clone + Send + 'static, S::Future: Send + 'static, - B: Send + 'static, { type Response = S::Response; type Error = S::Error; @@ -152,7 +168,7 @@ where self.inner.poll_ready(cx) } - fn call(&mut self, mut req: Request) -> Self::Future { + fn call(&mut self, mut req: Request) -> Self::Future { req.extensions_mut().insert(ConnectInfo(self.remote_addr)); self.inner.call(req) }