feat(quic,http3): add HTTP/3 proxy handling and hot-reload QUIC TLS configuration
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
//! Bounded, TTL-based protocol detection cache for HTTP/2 auto-detection.
|
||||
//! Bounded, TTL-based protocol detection cache for backend protocol auto-detection.
|
||||
//!
|
||||
//! Caches the ALPN-negotiated protocol (H1 or H2) per backend endpoint and requested
|
||||
//! Caches the detected protocol (H1, H2, or H3) per backend endpoint and requested
|
||||
//! domain (host:port + requested_host). This prevents cache oscillation when multiple
|
||||
//! frontend domains share the same backend but differ in HTTP/2 support.
|
||||
//! frontend domains share the same backend but differ in protocol support.
|
||||
//!
|
||||
//! H3 detection uses the browser model: Alt-Svc headers from H1/H2 responses are
|
||||
//! parsed and cached, including the advertised H3 port (which may differ from TCP).
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -29,6 +32,14 @@ pub enum DetectedProtocol {
|
||||
H3,
|
||||
}
|
||||
|
||||
/// Result of a protocol cache lookup.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CachedProtocol {
|
||||
pub protocol: DetectedProtocol,
|
||||
/// For H3: the port advertised by Alt-Svc (may differ from TCP port).
|
||||
pub h3_port: Option<u16>,
|
||||
}
|
||||
|
||||
/// Key for the protocol cache: (host, port, requested_host).
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct ProtocolCacheKey {
|
||||
@@ -43,6 +54,8 @@ pub struct ProtocolCacheKey {
|
||||
struct CachedEntry {
|
||||
protocol: DetectedProtocol,
|
||||
detected_at: Instant,
|
||||
/// For H3: the port advertised by Alt-Svc (may differ from TCP port).
|
||||
h3_port: Option<u16>,
|
||||
}
|
||||
|
||||
/// Bounded, TTL-based protocol detection cache.
|
||||
@@ -75,11 +88,14 @@ impl ProtocolCache {
|
||||
|
||||
/// Look up the cached protocol for a backend endpoint.
|
||||
/// Returns `None` if not cached or expired (caller should probe via ALPN).
|
||||
pub fn get(&self, key: &ProtocolCacheKey) -> Option<DetectedProtocol> {
|
||||
pub fn get(&self, key: &ProtocolCacheKey) -> Option<CachedProtocol> {
|
||||
let entry = self.cache.get(key)?;
|
||||
if entry.detected_at.elapsed() < PROTOCOL_CACHE_TTL {
|
||||
debug!("Protocol cache hit: {:?} for {}:{} (requested: {:?})", entry.protocol, key.host, key.port, key.requested_host);
|
||||
Some(entry.protocol)
|
||||
Some(CachedProtocol {
|
||||
protocol: entry.protocol,
|
||||
h3_port: entry.h3_port,
|
||||
})
|
||||
} else {
|
||||
// Expired — remove and return None to trigger re-probe
|
||||
drop(entry); // release DashMap ref before remove
|
||||
@@ -91,6 +107,16 @@ impl ProtocolCache {
|
||||
/// Insert a detected protocol into the cache.
|
||||
/// If the cache is at capacity, evict the oldest entry first.
|
||||
pub fn insert(&self, key: ProtocolCacheKey, protocol: DetectedProtocol) {
|
||||
self.insert_with_h3_port(key, protocol, None);
|
||||
}
|
||||
|
||||
/// Insert an H3 detection result with the Alt-Svc advertised port.
|
||||
pub fn insert_h3(&self, key: ProtocolCacheKey, h3_port: u16) {
|
||||
self.insert_with_h3_port(key, DetectedProtocol::H3, Some(h3_port));
|
||||
}
|
||||
|
||||
/// Insert a protocol detection result with an optional H3 port.
|
||||
fn insert_with_h3_port(&self, key: ProtocolCacheKey, protocol: DetectedProtocol, h3_port: Option<u16>) {
|
||||
if self.cache.len() >= PROTOCOL_CACHE_MAX_ENTRIES && !self.cache.contains_key(&key) {
|
||||
// Evict the oldest entry to stay within bounds
|
||||
let oldest = self.cache.iter()
|
||||
@@ -103,6 +129,7 @@ impl ProtocolCache {
|
||||
self.cache.insert(key, CachedEntry {
|
||||
protocol,
|
||||
detected_at: Instant::now(),
|
||||
h3_port,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user