feat(smart-proxy): add UDP transport support with QUIC/HTTP3 routing and datagram handler relay
This commit is contained in:
@@ -15,6 +15,7 @@ pub fn create_http_route(
|
||||
domains: Some(domains.into()),
|
||||
path: None,
|
||||
client_ip: None,
|
||||
transport: None,
|
||||
tls_version: None,
|
||||
headers: None,
|
||||
protocol: None,
|
||||
@@ -31,6 +32,7 @@ pub fn create_http_route(
|
||||
send_proxy_protocol: None,
|
||||
headers: None,
|
||||
advanced: None,
|
||||
backend_transport: None,
|
||||
priority: None,
|
||||
}]),
|
||||
tls: None,
|
||||
@@ -41,6 +43,7 @@ pub fn create_http_route(
|
||||
forwarding_engine: None,
|
||||
nftables: None,
|
||||
send_proxy_protocol: None,
|
||||
udp: None,
|
||||
},
|
||||
headers: None,
|
||||
security: None,
|
||||
@@ -107,6 +110,7 @@ pub fn create_http_to_https_redirect(
|
||||
domains: Some(domains),
|
||||
path: None,
|
||||
client_ip: None,
|
||||
transport: None,
|
||||
tls_version: None,
|
||||
headers: None,
|
||||
protocol: None,
|
||||
@@ -137,6 +141,7 @@ pub fn create_http_to_https_redirect(
|
||||
forwarding_engine: None,
|
||||
nftables: None,
|
||||
send_proxy_protocol: None,
|
||||
udp: None,
|
||||
},
|
||||
headers: None,
|
||||
security: None,
|
||||
@@ -187,6 +192,7 @@ pub fn create_load_balancer_route(
|
||||
send_proxy_protocol: None,
|
||||
headers: None,
|
||||
advanced: None,
|
||||
backend_transport: None,
|
||||
priority: None,
|
||||
})
|
||||
.collect();
|
||||
@@ -200,6 +206,7 @@ pub fn create_load_balancer_route(
|
||||
domains: Some(domains.into()),
|
||||
path: None,
|
||||
client_ip: None,
|
||||
transport: None,
|
||||
tls_version: None,
|
||||
headers: None,
|
||||
protocol: None,
|
||||
@@ -218,6 +225,7 @@ pub fn create_load_balancer_route(
|
||||
forwarding_engine: None,
|
||||
nftables: None,
|
||||
send_proxy_protocol: None,
|
||||
udp: None,
|
||||
},
|
||||
headers: None,
|
||||
security: None,
|
||||
|
||||
@@ -7,16 +7,24 @@ use crate::security_types::RouteSecurity;
|
||||
// ─── Port Range ──────────────────────────────────────────────────────
|
||||
|
||||
/// Port range specification format.
|
||||
/// Matches TypeScript: `type TPortRange = number | number[] | Array<{ from: number; to: number }>`
|
||||
/// Matches TypeScript: `type TPortRange = number | Array<number | { from: number; to: number }>`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PortRange {
|
||||
/// Single port number
|
||||
Single(u16),
|
||||
/// Array of port numbers
|
||||
List(Vec<u16>),
|
||||
/// Array of port ranges
|
||||
Ranges(Vec<PortRangeSpec>),
|
||||
/// Array of port numbers, ranges, or mixed
|
||||
List(Vec<PortRangeItem>),
|
||||
}
|
||||
|
||||
/// A single item in a port range array: either a number or a from-to range.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PortRangeItem {
|
||||
/// Single port number
|
||||
Port(u16),
|
||||
/// A from-to port range
|
||||
Range(PortRangeSpec),
|
||||
}
|
||||
|
||||
impl PortRange {
|
||||
@@ -24,9 +32,11 @@ impl PortRange {
|
||||
pub fn to_ports(&self) -> Vec<u16> {
|
||||
match self {
|
||||
PortRange::Single(p) => vec![*p],
|
||||
PortRange::List(ports) => ports.clone(),
|
||||
PortRange::Ranges(ranges) => {
|
||||
ranges.iter().flat_map(|r| r.from..=r.to).collect()
|
||||
PortRange::List(items) => {
|
||||
items.iter().flat_map(|item| match item {
|
||||
PortRangeItem::Port(p) => vec![*p],
|
||||
PortRangeItem::Range(r) => (r.from..=r.to).collect(),
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +105,10 @@ pub struct RouteMatch {
|
||||
/// Listen on these ports (required)
|
||||
pub ports: PortRange,
|
||||
|
||||
/// Transport protocol: tcp (default), udp, or all (both TCP and UDP)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub transport: Option<TransportProtocol>,
|
||||
|
||||
/// Optional domain patterns to match (default: all domains)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub domains: Option<DomainSpec>,
|
||||
@@ -115,7 +129,7 @@ pub struct RouteMatch {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
|
||||
/// Match specific protocol: "http" (includes h2 + websocket) or "tcp"
|
||||
/// Match specific protocol: "http", "tcp", "udp", "quic", "http3"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub protocol: Option<String>,
|
||||
}
|
||||
@@ -367,9 +381,19 @@ pub struct NfTablesOptions {
|
||||
pub enum BackendProtocol {
|
||||
Http1,
|
||||
Http2,
|
||||
Http3,
|
||||
Auto,
|
||||
}
|
||||
|
||||
/// Transport protocol for route matching.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TransportProtocol {
|
||||
Tcp,
|
||||
Udp,
|
||||
All,
|
||||
}
|
||||
|
||||
/// Action options.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -470,6 +494,10 @@ pub struct RouteTarget {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub advanced: Option<RouteAdvanced>,
|
||||
|
||||
/// Override transport for backend connection (e.g., receive QUIC but forward as TCP)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub backend_transport: Option<TransportProtocol>,
|
||||
|
||||
/// Priority for matching (higher values checked first, default: 0)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub priority: Option<i32>,
|
||||
@@ -524,6 +552,68 @@ pub struct RouteAction {
|
||||
/// PROXY protocol support (default for all targets)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub send_proxy_protocol: Option<bool>,
|
||||
|
||||
/// UDP-specific settings (session tracking, datagram limits, QUIC config)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub udp: Option<RouteUdp>,
|
||||
}
|
||||
|
||||
// ─── UDP & QUIC Config ──────────────────────────────────────────────
|
||||
|
||||
/// UDP-specific settings for route actions.
|
||||
/// Matches TypeScript: `IRouteUdp`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RouteUdp {
|
||||
/// Idle timeout for a UDP session/flow in ms. Default: 60000
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub session_timeout: Option<u64>,
|
||||
|
||||
/// Max concurrent UDP sessions per source IP. Default: 1000
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_sessions_per_ip: Option<u32>,
|
||||
|
||||
/// Max accepted datagram size in bytes. Default: 65535
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_datagram_size: Option<u32>,
|
||||
|
||||
/// QUIC-specific configuration
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub quic: Option<RouteQuic>,
|
||||
}
|
||||
|
||||
/// QUIC and HTTP/3 settings.
|
||||
/// Matches TypeScript: `IRouteQuic`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RouteQuic {
|
||||
/// QUIC connection idle timeout in ms. Default: 30000
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_idle_timeout: Option<u64>,
|
||||
|
||||
/// Max concurrent bidirectional streams per QUIC connection. Default: 100
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_concurrent_bidi_streams: Option<u32>,
|
||||
|
||||
/// Max concurrent unidirectional streams per QUIC connection. Default: 100
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_concurrent_uni_streams: Option<u32>,
|
||||
|
||||
/// Enable HTTP/3 over this QUIC endpoint. Default: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enable_http3: Option<bool>,
|
||||
|
||||
/// Port to advertise in Alt-Svc header on TCP HTTP responses
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub alt_svc_port: Option<u16>,
|
||||
|
||||
/// Max age for Alt-Svc advertisement in seconds. Default: 86400
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub alt_svc_max_age: Option<u64>,
|
||||
|
||||
/// Initial congestion window size in bytes
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub initial_congestion_window: Option<u32>,
|
||||
}
|
||||
|
||||
// ─── Route Config ────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user