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:
@@ -364,6 +364,10 @@ impl TcpListenerManager {
|
||||
// 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,
|
||||
// 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 {
|
||||
port,
|
||||
@@ -384,7 +388,28 @@ impl TcpListenerManager {
|
||||
|
||||
// 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 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_port = target.port.resolve(port);
|
||||
let route_id = quick_match.route.id.as_deref();
|
||||
@@ -562,6 +587,17 @@ impl TcpListenerManager {
|
||||
Some(rm) => rm,
|
||||
None => {
|
||||
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(());
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user