Compare commits

...

4 Commits

Author SHA1 Message Date
81de611255 v25.17.3
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-20 02:54:44 +00:00
91598b3be9 fix(repository): no changes detected 2026-03-20 02:54:44 +00:00
4e3c548012 v25.17.2
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-03-20 02:53:41 +00:00
1a2d7529db fix(rustproxy-http): enable TLS connections for HTTP/3 upstream requests when backend re-encryption or TLS is configured 2026-03-20 02:53:41 +00:00
4 changed files with 51 additions and 16 deletions

View File

@@ -1,5 +1,16 @@
# Changelog # Changelog
## 2026-03-20 - 25.17.3 - fix(repository)
no changes detected
## 2026-03-20 - 25.17.2 - fix(rustproxy-http)
enable TLS connections for HTTP/3 upstream requests when backend re-encryption or TLS is configured
- Pass backend TLS client configuration into the HTTP/3 request handler.
- Detect TLS-required upstream targets using route and target TLS settings before connecting.
- Build backend request URIs with the correct http or https scheme to match the upstream connection.
## 2026-03-20 - 25.17.1 - fix(rustproxy-routing) ## 2026-03-20 - 25.17.1 - fix(rustproxy-routing)
allow QUIC UDP TLS connections without SNI to match domain-restricted routes allow QUIC UDP TLS connections without SNI to match domain-restricted routes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartproxy", "name": "@push.rocks/smartproxy",
"version": "25.17.1", "version": "25.17.3",
"private": false, "private": false,
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.", "description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",

View File

@@ -36,7 +36,6 @@ pub struct H3ProxyService {
protocol_cache: Arc<ProtocolCache>, protocol_cache: Arc<ProtocolCache>,
#[allow(dead_code)] #[allow(dead_code)]
upstream_selector: UpstreamSelector, upstream_selector: UpstreamSelector,
#[allow(dead_code)]
backend_tls_config: Arc<rustls::ClientConfig>, backend_tls_config: Arc<rustls::ClientConfig>,
connect_timeout: Duration, connect_timeout: Duration,
} }
@@ -98,12 +97,14 @@ impl H3ProxyService {
let rm = self.route_manager.load(); let rm = self.route_manager.load();
let pool = Arc::clone(&self.connection_pool); let pool = Arc::clone(&self.connection_pool);
let metrics = Arc::clone(&self.metrics); let metrics = Arc::clone(&self.metrics);
let backend_tls = Arc::clone(&self.backend_tls_config);
let connect_timeout = self.connect_timeout; let connect_timeout = self.connect_timeout;
let client_ip = client_ip.clone(); let client_ip = client_ip.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = handle_h3_request( if let Err(e) = handle_h3_request(
request, stream, port, &client_ip, &rm, &pool, &metrics, connect_timeout, request, stream, port, &client_ip, &rm, &pool, &metrics,
&backend_tls, connect_timeout,
).await { ).await {
debug!("HTTP/3 request error from {}: {}", client_ip, e); debug!("HTTP/3 request error from {}: {}", client_ip, e);
} }
@@ -133,6 +134,7 @@ async fn handle_h3_request(
route_manager: &RouteManager, route_manager: &RouteManager,
_connection_pool: &ConnectionPool, _connection_pool: &ConnectionPool,
metrics: &MetricsCollector, metrics: &MetricsCollector,
backend_tls_config: &Arc<rustls::ClientConfig>,
connect_timeout: Duration, connect_timeout: Duration,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let method = request.method().clone(); let method = request.method().clone();
@@ -173,7 +175,15 @@ async fn handle_h3_request(
let backend_port = target.port.resolve(port); let backend_port = target.port.resolve(port);
let backend_addr = format!("{}:{}", backend_host, backend_port); let backend_addr = format!("{}:{}", backend_host, backend_port);
// Connect to backend via TCP HTTP/1.1 with timeout // Determine if backend requires TLS (same logic as proxy_service.rs)
let mut use_tls = target.tls.is_some();
if let Some(ref tls) = route.action.tls {
if tls.mode == rustproxy_config::TlsMode::TerminateAndReencrypt {
use_tls = true;
}
}
// Connect to backend via TCP with timeout
let tcp_stream = tokio::time::timeout( let tcp_stream = tokio::time::timeout(
connect_timeout, connect_timeout,
tokio::net::TcpStream::connect(&backend_addr), tokio::net::TcpStream::connect(&backend_addr),
@@ -183,15 +193,27 @@ async fn handle_h3_request(
let _ = tcp_stream.set_nodelay(true); let _ = tcp_stream.set_nodelay(true);
let io = hyper_util::rt::TokioIo::new(tcp_stream); // Branch: wrap in TLS if backend requires it, then HTTP/1.1 handshake.
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await // hyper's SendRequest<B> is NOT generic over the IO type, so both branches
.map_err(|e| anyhow::anyhow!("Backend handshake failed: {}", e))?; // produce the same type and can be unified.
let mut sender = if use_tls {
tokio::spawn(async move { let connector = tokio_rustls::TlsConnector::from(Arc::clone(backend_tls_config));
if let Err(e) = conn.await { let server_name = rustls::pki_types::ServerName::try_from(backend_host.to_string())
debug!("Backend connection closed: {}", e); .map_err(|e| anyhow::anyhow!("Invalid backend SNI '{}': {}", backend_host, e))?;
} let tls_stream = connector.connect(server_name, tcp_stream).await
}); .map_err(|e| anyhow::anyhow!("Backend TLS handshake to {} failed: {}", backend_addr, e))?;
let io = hyper_util::rt::TokioIo::new(tls_stream);
let (sender, conn) = hyper::client::conn::http1::handshake(io).await
.map_err(|e| anyhow::anyhow!("Backend handshake failed: {}", e))?;
tokio::spawn(async move { let _ = conn.await; });
sender
} else {
let io = hyper_util::rt::TokioIo::new(tcp_stream);
let (sender, conn) = hyper::client::conn::http1::handshake(io).await
.map_err(|e| anyhow::anyhow!("Backend handshake failed: {}", e))?;
tokio::spawn(async move { let _ = conn.await; });
sender
};
// Stream request body from H3 client to backend via an mpsc channel. // Stream request body from H3 client to backend via an mpsc channel.
// This avoids buffering the entire request body in memory. // This avoids buffering the entire request body in memory.
@@ -214,7 +236,7 @@ async fn handle_h3_request(
// Create a body that polls from the mpsc receiver // Create a body that polls from the mpsc receiver
let body = H3RequestBody { receiver: body_rx }; let body = H3RequestBody { receiver: body_rx };
let backend_req = build_backend_request(&method, &backend_addr, &path, &host, &request, body)?; let backend_req = build_backend_request(&method, &backend_addr, &path, &host, &request, body, use_tls)?;
let response = sender.send_request(backend_req).await let response = sender.send_request(backend_req).await
.map_err(|e| anyhow::anyhow!("Backend request failed: {}", e))?; .map_err(|e| anyhow::anyhow!("Backend request failed: {}", e))?;
@@ -294,10 +316,12 @@ fn build_backend_request<B>(
host: &str, host: &str,
original_request: &hyper::Request<()>, original_request: &hyper::Request<()>,
body: B, body: B,
use_tls: bool,
) -> anyhow::Result<hyper::Request<B>> { ) -> anyhow::Result<hyper::Request<B>> {
let scheme = if use_tls { "https" } else { "http" };
let mut req = hyper::Request::builder() let mut req = hyper::Request::builder()
.method(method) .method(method)
.uri(format!("http://{}{}", backend_addr, path)) .uri(format!("{}://{}{}", scheme, backend_addr, path))
.header("host", host); .header("host", host);
// Forward non-pseudo headers // Forward non-pseudo headers

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '25.17.1', version: '25.17.3',
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
} }