http_tunnel_common/models/
connection.rs

1use serde::{Deserialize, Serialize};
2
3/// Connection metadata tracked in DynamoDB for active WebSocket connections
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct ConnectionMetadata {
6    /// API Gateway WebSocket connection ID
7    pub connection_id: String,
8
9    /// Unique tunnel ID assigned to this connection (path segment)
10    pub tunnel_id: String,
11
12    /// Primary public URL (subdomain if enabled, otherwise path-based)
13    pub public_url: String,
14
15    /// Subdomain-based URL (https://{tunnel_id}.{domain})
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub subdomain_url: Option<String>,
18
19    /// Path-based URL (https://{domain}/{tunnel_id}) for backward compatibility
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub path_based_url: Option<String>,
22
23    /// Timestamp when connection was established (Unix epoch seconds)
24    pub created_at: i64,
25
26    /// TTL timestamp for DynamoDB auto-deletion (Unix epoch seconds)
27    pub ttl: i64,
28
29    /// Optional metadata about the client
30    #[serde(default)]
31    pub client_info: Option<ClientInfo>,
32}
33
34impl ConnectionMetadata {
35    /// Create a new connection metadata entry
36    pub fn new(
37        connection_id: String,
38        tunnel_id: String,
39        public_url: String,
40        created_at: i64,
41        ttl: i64,
42    ) -> Self {
43        Self {
44            connection_id,
45            tunnel_id,
46            public_url,
47            subdomain_url: None,
48            path_based_url: None,
49            created_at,
50            ttl,
51            client_info: None,
52        }
53    }
54
55    /// Create a connection with client info
56    pub fn with_client_info(mut self, client_info: ClientInfo) -> Self {
57        self.client_info = Some(client_info);
58        self
59    }
60}
61
62/// Information about the client agent
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ClientInfo {
65    /// Client version string
66    pub version: String,
67
68    /// Platform/OS information
69    pub platform: String,
70}
71
72impl ClientInfo {
73    /// Create new client info
74    pub fn new(version: String, platform: String) -> Self {
75        Self { version, platform }
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_connection_metadata_creation() {
85        let metadata = ConnectionMetadata::new(
86            "conn_123".to_string(),
87            "abc123def456".to_string(),
88            "https://abc123def456.tunnel.example.com".to_string(),
89            1234567890,
90            1234574090,
91        );
92
93        assert_eq!(metadata.connection_id, "conn_123");
94        assert_eq!(metadata.tunnel_id, "abc123def456");
95        assert_eq!(metadata.created_at, 1234567890);
96        assert_eq!(metadata.ttl, 1234574090);
97        assert!(metadata.client_info.is_none());
98    }
99
100    #[test]
101    fn test_connection_metadata_with_client_info() {
102        let client_info = ClientInfo::new("1.0.0".to_string(), "linux-x86_64".to_string());
103
104        let metadata = ConnectionMetadata::new(
105            "conn_123".to_string(),
106            "abc123def456".to_string(),
107            "https://abc123def456.tunnel.example.com".to_string(),
108            1234567890,
109            1234574090,
110        )
111        .with_client_info(client_info);
112
113        assert!(metadata.client_info.is_some());
114        let info = metadata.client_info.unwrap();
115        assert_eq!(info.version, "1.0.0");
116        assert_eq!(info.platform, "linux-x86_64");
117    }
118
119    #[test]
120    fn test_connection_metadata_serialization() {
121        let metadata = ConnectionMetadata::new(
122            "conn_abc".to_string(),
123            "xyz789".to_string(),
124            "https://xyz789.tunnel.example.com".to_string(),
125            1234567890,
126            1234574090,
127        );
128
129        let json = serde_json::to_string(&metadata).unwrap();
130        assert!(json.contains(r#""connection_id":"conn_abc"#));
131        assert!(json.contains(r#""tunnel_id":"xyz789"#));
132
133        let parsed: ConnectionMetadata = serde_json::from_str(&json).unwrap();
134        assert_eq!(parsed.connection_id, metadata.connection_id);
135        assert_eq!(parsed.tunnel_id, metadata.tunnel_id);
136        assert_eq!(parsed.created_at, metadata.created_at);
137        assert_eq!(parsed.ttl, metadata.ttl);
138    }
139
140    #[test]
141    fn test_client_info_serialization() {
142        let info = ClientInfo::new("2.1.0".to_string(), "darwin-arm64".to_string());
143
144        let json = serde_json::to_string(&info).unwrap();
145        assert!(json.contains(r#""version":"2.1.0"#));
146        assert!(json.contains(r#""platform":"darwin-arm64"#));
147
148        let parsed: ClientInfo = serde_json::from_str(&json).unwrap();
149        assert_eq!(parsed.version, "2.1.0");
150        assert_eq!(parsed.platform, "darwin-arm64");
151    }
152
153    #[test]
154    fn test_connection_metadata_with_serialized_client_info() {
155        let client_info = ClientInfo::new("1.5.0".to_string(), "windows-x86_64".to_string());
156        let metadata = ConnectionMetadata::new(
157            "conn_123".to_string(),
158            "abc123".to_string(),
159            "https://abc123.tunnel.example.com".to_string(),
160            1234567890,
161            1234574090,
162        )
163        .with_client_info(client_info);
164
165        let json = serde_json::to_string(&metadata).unwrap();
166        let parsed: ConnectionMetadata = serde_json::from_str(&json).unwrap();
167
168        assert!(parsed.client_info.is_some());
169        let info = parsed.client_info.unwrap();
170        assert_eq!(info.version, "1.5.0");
171        assert_eq!(info.platform, "windows-x86_64");
172    }
173
174    #[test]
175    fn test_connection_metadata_default_client_info() {
176        let json = r#"{
177            "connection_id": "conn_123",
178            "tunnel_id": "abc123",
179            "public_url": "https://tunnel.example.com/abc123",
180            "created_at": 1234567890,
181            "ttl": 1234574090
182        }"#;
183
184        let parsed: ConnectionMetadata = serde_json::from_str(json).unwrap();
185        assert!(parsed.client_info.is_none());
186    }
187}