http_tunnel_common/protocol/
request.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Represents an HTTP request forwarded from the public endpoint to the agent
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct HttpRequest {
7    /// Unique identifier to correlate request and response
8    pub request_id: String,
9
10    /// HTTP method (GET, POST, PUT, DELETE, etc.)
11    pub method: String,
12
13    /// Request URI including path and query string
14    /// Example: "/api/v1/users?limit=10"
15    pub uri: String,
16
17    /// HTTP headers as a map of header name to list of values
18    /// Multiple values per header are supported
19    pub headers: HashMap<String, Vec<String>>,
20
21    /// Request body encoded in Base64
22    /// Empty string for requests without body
23    #[serde(default)]
24    pub body: String,
25
26    /// Timestamp when request was received (Unix epoch in milliseconds)
27    pub timestamp: u64,
28}
29
30impl HttpRequest {
31    /// Create a new HTTP request
32    pub fn new(method: String, uri: String, request_id: String, timestamp: u64) -> Self {
33        Self {
34            request_id,
35            method,
36            uri,
37            headers: HashMap::new(),
38            body: String::new(),
39            timestamp,
40        }
41    }
42
43    /// Check if the request has a body
44    pub fn has_body(&self) -> bool {
45        !self.body.is_empty()
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_http_request_creation() {
55        let req = HttpRequest::new(
56            "GET".to_string(),
57            "/api/users".to_string(),
58            "req_123".to_string(),
59            1234567890,
60        );
61
62        assert_eq!(req.method, "GET");
63        assert_eq!(req.uri, "/api/users");
64        assert_eq!(req.request_id, "req_123");
65        assert_eq!(req.timestamp, 1234567890);
66        assert!(req.headers.is_empty());
67        assert!(!req.has_body());
68    }
69
70    #[test]
71    fn test_http_request_with_headers() {
72        let mut headers = HashMap::new();
73        headers.insert(
74            "content-type".to_string(),
75            vec!["application/json".to_string()],
76        );
77        headers.insert(
78            "authorization".to_string(),
79            vec!["Bearer token123".to_string()],
80        );
81
82        let req = HttpRequest {
83            request_id: "req_123".to_string(),
84            method: "POST".to_string(),
85            uri: "/api/data".to_string(),
86            headers,
87            body: "eyJ0ZXN0IjoidmFsdWUifQ==".to_string(), // {"test":"value"}
88            timestamp: 1234567890,
89        };
90
91        assert_eq!(req.headers.len(), 2);
92        assert!(req.has_body());
93    }
94
95    #[test]
96    fn test_http_request_serialization() {
97        let mut headers = HashMap::new();
98        headers.insert("host".to_string(), vec!["example.com".to_string()]);
99
100        let req = HttpRequest {
101            request_id: "req_abc123".to_string(),
102            method: "GET".to_string(),
103            uri: "/path?query=value".to_string(),
104            headers,
105            body: String::new(),
106            timestamp: 1234567890000,
107        };
108
109        let json = serde_json::to_string(&req).unwrap();
110        assert!(json.contains(r#""request_id":"req_abc123"#));
111        assert!(json.contains(r#""method":"GET"#));
112        assert!(json.contains(r#""uri":"/path?query=value"#));
113
114        let parsed: HttpRequest = serde_json::from_str(&json).unwrap();
115        assert_eq!(parsed.request_id, req.request_id);
116        assert_eq!(parsed.method, req.method);
117        assert_eq!(parsed.uri, req.uri);
118        assert_eq!(parsed.timestamp, req.timestamp);
119    }
120
121    #[test]
122    fn test_http_request_multiple_header_values() {
123        let mut headers = HashMap::new();
124        headers.insert(
125            "cookie".to_string(),
126            vec!["session=abc".to_string(), "token=xyz".to_string()],
127        );
128
129        let req = HttpRequest {
130            request_id: "req_123".to_string(),
131            method: "GET".to_string(),
132            uri: "/".to_string(),
133            headers,
134            body: String::new(),
135            timestamp: 1234567890,
136        };
137
138        assert_eq!(req.headers.get("cookie").unwrap().len(), 2);
139
140        let json = serde_json::to_string(&req).unwrap();
141        let parsed: HttpRequest = serde_json::from_str(&json).unwrap();
142        assert_eq!(parsed.headers.get("cookie").unwrap().len(), 2);
143    }
144
145    #[test]
146    fn test_http_request_body_default() {
147        let json = r#"{
148            "request_id": "req_123",
149            "method": "GET",
150            "uri": "/test",
151            "headers": {},
152            "timestamp": 1234567890
153        }"#;
154
155        let parsed: HttpRequest = serde_json::from_str(json).unwrap();
156        assert_eq!(parsed.body, "");
157        assert!(!parsed.has_body());
158    }
159}