2026-07-15 23:47:19 +08:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
pub const PROXY_TUNNEL_PATH: &str = "/api/internal/proxy-tunnel";
|
|
|
|
|
pub const TUNNEL_HEARTBEAT_PATH: &str = "/api/internal/tunnel/heartbeat";
|
|
|
|
|
pub const TUNNEL_NODE_STATUS_PATH: &str = "/api/internal/tunnel/node-status";
|
|
|
|
|
pub const TUNNEL_RELAY_PATH_PATTERN: &str = "/api/internal/tunnel/relay/{node_id}";
|
|
|
|
|
pub const TUNNEL_ROUTE_FAMILY: &str = "tunnel_manage";
|
|
|
|
|
|
|
|
|
|
pub const DEFAULT_TUNNEL_PROBE_BODY_LIMIT_BYTES: usize = 64 * 1024;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct TunnelAttachmentRecord {
|
|
|
|
|
pub gateway_instance_id: String,
|
|
|
|
|
pub relay_base_url: String,
|
2026-09-04 03:45:52 +08:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub tunnel_generation: String,
|
2026-07-15 23:47:19 +08:00
|
|
|
pub conn_count: usize,
|
|
|
|
|
pub observed_at_unix_secs: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TunnelAttachmentRecord {
|
|
|
|
|
pub fn is_routable(&self, now_unix_secs: u64, ttl_secs: u64) -> bool {
|
|
|
|
|
self.conn_count > 0
|
|
|
|
|
&& !self.relay_base_url.trim().is_empty()
|
2026-09-04 03:45:52 +08:00
|
|
|
&& !self.tunnel_generation.trim().is_empty()
|
2026-07-15 23:47:19 +08:00
|
|
|
&& self.observed_at_unix_secs.saturating_add(ttl_secs) >= now_unix_secs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_owned_by(&self, gateway_instance_id: &str) -> bool {
|
|
|
|
|
self.gateway_instance_id == gateway_instance_id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_tunnel_heartbeat_path(path: &str) -> bool {
|
|
|
|
|
path == TUNNEL_HEARTBEAT_PATH
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_tunnel_node_status_path(path: &str) -> bool {
|
|
|
|
|
path == TUNNEL_NODE_STATUS_PATH
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::TunnelAttachmentRecord;
|
|
|
|
|
|
|
|
|
|
fn record() -> TunnelAttachmentRecord {
|
|
|
|
|
TunnelAttachmentRecord {
|
|
|
|
|
gateway_instance_id: "gateway-a".to_string(),
|
|
|
|
|
relay_base_url: "http://gateway-a.internal".to_string(),
|
2026-09-04 03:45:52 +08:00
|
|
|
tunnel_generation: "generation-a".to_string(),
|
2026-07-15 23:47:19 +08:00
|
|
|
conn_count: 1,
|
|
|
|
|
observed_at_unix_secs: 100,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn attachment_is_routable_until_ttl_boundary() {
|
|
|
|
|
let record = record();
|
|
|
|
|
assert!(record.is_routable(190, 90));
|
|
|
|
|
assert!(!record.is_routable(191, 90));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn attachment_requires_connection_and_relay_url() {
|
|
|
|
|
let mut record = record();
|
|
|
|
|
record.conn_count = 0;
|
|
|
|
|
assert!(!record.is_routable(100, 90));
|
|
|
|
|
record.conn_count = 1;
|
|
|
|
|
record.relay_base_url.clear();
|
|
|
|
|
assert!(!record.is_routable(100, 90));
|
|
|
|
|
}
|
|
|
|
|
}
|