feat(quic,http3): add HTTP/3 proxy handling and hot-reload QUIC TLS configuration
This commit is contained in:
@@ -340,6 +340,17 @@ impl RustProxy {
|
||||
self.cancel_token.clone(),
|
||||
);
|
||||
|
||||
// Construct H3ProxyService for HTTP/3 request handling
|
||||
let h3_svc = rustproxy_http::h3_service::H3ProxyService::new(
|
||||
Arc::new(ArcSwap::from(Arc::clone(&*self.route_table.load()))),
|
||||
Arc::clone(&self.metrics),
|
||||
Arc::new(rustproxy_http::connection_pool::ConnectionPool::new()),
|
||||
Arc::new(rustproxy_http::protocol_cache::ProtocolCache::new()),
|
||||
rustproxy_passthrough::tls_handler::shared_backend_tls_config(),
|
||||
std::time::Duration::from_secs(30),
|
||||
);
|
||||
udp_mgr.set_h3_service(Arc::new(h3_svc));
|
||||
|
||||
for port in &udp_ports {
|
||||
udp_mgr.add_port_with_tls(*port, quic_tls_config.clone()).await?;
|
||||
}
|
||||
@@ -772,13 +783,21 @@ impl RustProxy {
|
||||
}
|
||||
}
|
||||
|
||||
// Build TLS config for QUIC before taking mutable borrow on udp_mgr
|
||||
let quic_tls = if new_udp_ports.iter().any(|p| !old_udp_ports.contains(p)) {
|
||||
let tls_configs = self.current_tls_configs().await;
|
||||
Self::build_quic_tls_config(&tls_configs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ref mut udp_mgr) = self.udp_listener_manager {
|
||||
udp_mgr.update_routes(Arc::clone(&new_manager));
|
||||
|
||||
// Add new UDP ports
|
||||
// Add new UDP ports (with TLS for QUIC)
|
||||
for port in &new_udp_ports {
|
||||
if !old_udp_ports.contains(port) {
|
||||
udp_mgr.add_port(*port).await?;
|
||||
udp_mgr.add_port_with_tls(*port, quic_tls.clone()).await?;
|
||||
}
|
||||
}
|
||||
// Remove old UDP ports
|
||||
@@ -1005,6 +1024,33 @@ impl RustProxy {
|
||||
Some(Arc::new(tls_config))
|
||||
}
|
||||
|
||||
/// Build the current full TLS config map from all sources (route configs, loaded certs, cert manager).
|
||||
async fn current_tls_configs(&self) -> HashMap<String, TlsCertConfig> {
|
||||
let mut configs = Self::extract_tls_configs(&self.options.routes);
|
||||
|
||||
// Merge dynamically loaded certs (from loadCertificate IPC)
|
||||
for (d, c) in &self.loaded_certs {
|
||||
if !configs.contains_key(d) {
|
||||
configs.insert(d.clone(), c.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Merge certs from cert manager store
|
||||
if let Some(ref cm_arc) = self.cert_manager {
|
||||
let cm = cm_arc.lock().await;
|
||||
for (d, b) in cm.store().iter() {
|
||||
if !configs.contains_key(d) {
|
||||
configs.insert(d.clone(), TlsCertConfig {
|
||||
cert_pem: b.cert_pem.clone(),
|
||||
key_pem: b.key_pem.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
/// Set the Unix domain socket path for relaying UDP datagrams to TypeScript datagramHandler callbacks.
|
||||
pub async fn set_datagram_handler_relay_path(&mut self, path: Option<String>) {
|
||||
info!("Datagram handler relay path set to: {:?}", path);
|
||||
@@ -1055,37 +1101,21 @@ impl RustProxy {
|
||||
key_pem: key_pem.clone(),
|
||||
});
|
||||
|
||||
// Hot-swap TLS config on the listener
|
||||
if let Some(ref mut listener) = self.listener_manager {
|
||||
let mut tls_configs = Self::extract_tls_configs(&self.options.routes);
|
||||
// Hot-swap TLS config on TCP and QUIC listeners
|
||||
let tls_configs = self.current_tls_configs().await;
|
||||
|
||||
// Add the new cert
|
||||
tls_configs.insert(domain.to_string(), TlsCertConfig {
|
||||
cert_pem: cert_pem.clone(),
|
||||
key_pem: key_pem.clone(),
|
||||
});
|
||||
|
||||
// Also include all existing certs from cert manager
|
||||
if let Some(ref cm_arc) = self.cert_manager {
|
||||
let cm = cm_arc.lock().await;
|
||||
for (d, b) in cm.store().iter() {
|
||||
if !tls_configs.contains_key(d) {
|
||||
tls_configs.insert(d.clone(), TlsCertConfig {
|
||||
cert_pem: b.cert_pem.clone(),
|
||||
key_pem: b.key_pem.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge dynamically loaded certs from previous loadCertificate calls
|
||||
for (d, c) in &self.loaded_certs {
|
||||
if !tls_configs.contains_key(d) {
|
||||
tls_configs.insert(d.clone(), c.clone());
|
||||
}
|
||||
}
|
||||
if let Some(ref listener) = self.listener_manager {
|
||||
// Build QUIC TLS config before TCP consumes the map
|
||||
let quic_tls = Self::build_quic_tls_config(&tls_configs);
|
||||
|
||||
listener.set_tls_configs(tls_configs);
|
||||
|
||||
// Also update QUIC endpoints with the new certs
|
||||
if let Some(ref udp_mgr) = self.udp_listener_manager {
|
||||
if let Some(quic_config) = quic_tls {
|
||||
udp_mgr.update_quic_tls(quic_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Certificate loaded and TLS config updated for {}", domain);
|
||||
|
||||
Reference in New Issue
Block a user