feat(rustproxy-passthrough): add selective connection recycling for route, security, and certificate updates

This commit is contained in:
2026-03-27 22:34:13 +00:00
parent 5be93c8d38
commit 3c24bf659b
9 changed files with 514 additions and 8 deletions

View File

@@ -30,6 +30,7 @@ use rustproxy_routing::{MatchContext, RouteManager};
use rustproxy_http::h3_service::H3ProxyService;
use crate::connection_tracker::ConnectionTracker;
use crate::connection_registry::{ConnectionEntry, ConnectionRegistry};
/// Create a QUIC server endpoint on the given port with the provided TLS config.
///
@@ -350,6 +351,8 @@ pub async fn quic_accept_loop(
cancel: CancellationToken,
h3_service: Option<Arc<H3ProxyService>>,
real_client_map: Option<Arc<DashMap<SocketAddr, SocketAddr>>>,
route_cancels: Arc<DashMap<String, CancellationToken>>,
connection_registry: Arc<ConnectionRegistry>,
) {
loop {
let incoming = tokio::select! {
@@ -406,17 +409,48 @@ pub async fn quic_accept_loop(
}
};
// Check route-level IP security (previously missing for QUIC)
if let Some(ref security) = route.security {
if !rustproxy_http::request_filter::RequestFilter::check_ip_security(
security, &ip,
) {
debug!("QUIC connection from {} blocked by route security", real_addr);
continue;
}
}
conn_tracker.connection_opened(&ip);
let route_id = route.name.clone().or(route.id.clone());
metrics.connection_opened(route_id.as_deref(), Some(&ip_str));
// Resolve per-route cancel token (child of global cancel)
let route_cancel = match route_id.as_deref() {
Some(id) => route_cancels.entry(id.to_string())
.or_insert_with(|| cancel.child_token())
.clone(),
None => cancel.child_token(),
};
// Per-connection child token for selective recycling
let conn_cancel = route_cancel.child_token();
// Register in connection registry
let registry = Arc::clone(&connection_registry);
let reg_entry = ConnectionEntry {
cancel: conn_cancel.clone(),
source_ip: ip,
domain: None, // QUIC Initial is encrypted, domain comes later via H3 :authority
route_id: route_id.clone(),
};
let metrics = Arc::clone(&metrics);
let conn_tracker = Arc::clone(&conn_tracker);
let cancel = cancel.child_token();
let h3_svc = h3_service.clone();
let real_client_addr = if real_addr != remote_addr { Some(real_addr) } else { None };
tokio::spawn(async move {
// Register in connection registry (RAII guard removes on drop)
let (_conn_id, _registry_guard) = registry.register(reg_entry);
// RAII guard: ensures metrics/tracker cleanup even on panic
struct QuicConnGuard {
tracker: Arc<ConnectionTracker>,
@@ -439,7 +473,7 @@ pub async fn quic_accept_loop(
route_id,
};
match handle_quic_connection(incoming, route, port, Arc::clone(&metrics), &cancel, h3_svc, real_client_addr).await {
match handle_quic_connection(incoming, route, port, Arc::clone(&metrics), &conn_cancel, h3_svc, real_client_addr).await {
Ok(()) => debug!("QUIC connection from {} completed", real_addr),
Err(e) => debug!("QUIC connection from {} error: {}", real_addr, e),
}