fix(rustproxy): install default rustls crypto provider early; detect and skip raw fast-path for HTTP connections and return proper HTTP 502 when no route matches
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-02-12 - 23.1.3 - fix(rustproxy)
|
||||||
|
install default rustls crypto provider early; detect and skip raw fast-path for HTTP connections and return proper HTTP 502 when no route matches
|
||||||
|
|
||||||
|
- Install ring-based rustls crypto provider at startup to prevent panics from instant-acme/hyper-rustls calling ClientConfig::builder() before TLS listeners are initialized
|
||||||
|
- Add a non-blocking 10ms peek to detect HTTP traffic in the TCP passthrough fast-path to avoid misrouting HTTP and ensure HTTP proxy handles CORS, errors, and request-level routing
|
||||||
|
- Skip the fast-path and fall back to the HTTP proxy when HTTP is detected (with a debug log)
|
||||||
|
- When no route matches for detected HTTP connections, send an HTTP 502 Bad Gateway response and close the connection instead of silently dropping it
|
||||||
|
|
||||||
## 2026-02-11 - 23.1.2 - fix(core)
|
## 2026-02-11 - 23.1.2 - fix(core)
|
||||||
use node: scoped builtin imports and add route unit tests
|
use node: scoped builtin imports and add route unit tests
|
||||||
|
|
||||||
|
|||||||
@@ -364,6 +364,10 @@ impl TcpListenerManager {
|
|||||||
// doesn't send initial data (e.g., SMTP, greeting-based protocols).
|
// doesn't send initial data (e.g., SMTP, greeting-based protocols).
|
||||||
// If a route matches by port alone and doesn't need domain/path/TLS info,
|
// If a route matches by port alone and doesn't need domain/path/TLS info,
|
||||||
// we can forward immediately without waiting for client data.
|
// we can forward immediately without waiting for client data.
|
||||||
|
//
|
||||||
|
// IMPORTANT: HTTP connections must NOT use this path — they need the HTTP
|
||||||
|
// proxy for proper error responses, CORS handling, and request-level routing.
|
||||||
|
// We detect HTTP via a non-blocking peek before committing to raw forwarding.
|
||||||
{
|
{
|
||||||
let quick_ctx = rustproxy_routing::MatchContext {
|
let quick_ctx = rustproxy_routing::MatchContext {
|
||||||
port,
|
port,
|
||||||
@@ -384,7 +388,28 @@ impl TcpListenerManager {
|
|||||||
|
|
||||||
// Only use fast path for simple port-only forward routes with no TLS
|
// Only use fast path for simple port-only forward routes with no TLS
|
||||||
if has_no_domain && has_no_path && is_forward && has_no_tls {
|
if has_no_domain && has_no_path && is_forward && has_no_tls {
|
||||||
if let Some(target) = quick_match.target {
|
// Non-blocking peek: if client has already sent data that looks
|
||||||
|
// like HTTP, skip fast path and let the normal path handle it
|
||||||
|
// through the HTTP proxy (for CORS, error responses, path routing).
|
||||||
|
let is_likely_http = {
|
||||||
|
let mut probe = [0u8; 16];
|
||||||
|
// Brief peek: HTTP clients send data immediately after connect.
|
||||||
|
// Server-speaks-first protocols (SMTP etc.) send nothing initially.
|
||||||
|
// 10ms is ample for any HTTP client while negligible for
|
||||||
|
// server-speaks-first protocols (which wait seconds for greeting).
|
||||||
|
match tokio::time::timeout(
|
||||||
|
std::time::Duration::from_millis(10),
|
||||||
|
stream.peek(&mut probe),
|
||||||
|
).await {
|
||||||
|
Ok(Ok(n)) if n > 0 => sni_parser::is_http(&probe[..n]),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_likely_http {
|
||||||
|
debug!("Fast-path skipped: HTTP detected from {}, using HTTP proxy", peer_addr);
|
||||||
|
// Fall through to normal path for HTTP proxy handling
|
||||||
|
} else if let Some(target) = quick_match.target {
|
||||||
let target_host = target.host.first().to_string();
|
let target_host = target.host.first().to_string();
|
||||||
let target_port = target.port.resolve(port);
|
let target_port = target.port.resolve(port);
|
||||||
let route_id = quick_match.route.id.as_deref();
|
let route_id = quick_match.route.id.as_deref();
|
||||||
@@ -562,6 +587,17 @@ impl TcpListenerManager {
|
|||||||
Some(rm) => rm,
|
Some(rm) => rm,
|
||||||
None => {
|
None => {
|
||||||
debug!("No route matched for port {} domain {:?}", port, domain);
|
debug!("No route matched for port {} domain {:?}", port, domain);
|
||||||
|
if is_http {
|
||||||
|
// Send a proper HTTP error instead of dropping the connection
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
let body = "No route matched";
|
||||||
|
let resp = format!(
|
||||||
|
"HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||||
|
body.len(), body
|
||||||
|
);
|
||||||
|
let _ = stream.write_all(resp.as_bytes()).await;
|
||||||
|
let _ = stream.shutdown().await;
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ struct Cli {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
// Install the default CryptoProvider early, before any TLS or ACME code runs.
|
||||||
|
// This prevents panics from instant-acme/hyper-rustls calling ClientConfig::builder()
|
||||||
|
// before TLS listeners have started. Idempotent — later calls harmlessly return Err.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
// Initialize tracing - write to stderr so stdout is reserved for management IPC
|
// Initialize tracing - write to stderr so stdout is reserved for management IPC
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '23.1.2',
|
version: '23.1.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.'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user