cortex_runtime/acquisition/
http_session.rs1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{SystemTime, UNIX_EPOCH};
11
12static SESSION_COUNTER: AtomicU64 = AtomicU64::new(0);
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct HttpSession {
18 pub session_id: String,
20 pub domain: String,
22 pub cookies: HashMap<String, String>,
24 pub auth_headers: HashMap<String, String>,
26 pub csrf_token: Option<String>,
28 pub auth_type: AuthType,
30 pub created_at: f64,
32 pub expires_at: Option<f64>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
38pub enum AuthType {
39 Password,
41 OAuth(String), ApiKey,
45 Bearer,
47 None,
49}
50
51impl HttpSession {
52 pub fn new(domain: &str, auth_type: AuthType) -> Self {
57 let ts = SystemTime::now()
58 .duration_since(UNIX_EPOCH)
59 .unwrap_or_default()
60 .as_millis();
61 let counter = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed);
62 let session_id = format!("sess-{ts}-{counter}");
63 let created_at = ts as f64 / 1000.0;
64
65 Self {
66 session_id,
67 domain: domain.to_string(),
68 cookies: HashMap::new(),
69 auth_headers: HashMap::new(),
70 csrf_token: None,
71 auth_type,
72 created_at,
73 expires_at: None,
74 }
75 }
76
77 pub fn cookie_header(&self) -> String {
82 let mut pairs: Vec<_> = self.cookies.iter().collect();
83 pairs.sort_by_key(|(k, _)| (*k).clone());
84 pairs
85 .iter()
86 .map(|(k, v)| format!("{k}={v}"))
87 .collect::<Vec<_>>()
88 .join("; ")
89 }
90
91 pub fn is_expired(&self) -> bool {
95 if let Some(expires) = self.expires_at {
96 let now = SystemTime::now()
97 .duration_since(UNIX_EPOCH)
98 .unwrap_or_default()
99 .as_secs_f64();
100 now >= expires
101 } else {
102 false
103 }
104 }
105
106 pub fn add_cookie(&mut self, name: &str, value: &str) {
108 self.cookies.insert(name.to_string(), value.to_string());
109 }
110
111 pub fn add_auth_header(&mut self, name: &str, value: &str) {
113 self.auth_headers
114 .insert(name.to_string(), value.to_string());
115 }
116
117 pub fn set_expires(&mut self, unix_timestamp: f64) {
119 self.expires_at = Some(unix_timestamp);
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_session_creation() {
129 let session = HttpSession::new("example.com", AuthType::Password);
130
131 assert!(session.session_id.starts_with("sess-"));
132 assert_eq!(session.domain, "example.com");
133 assert_eq!(session.auth_type, AuthType::Password);
134 assert!(session.cookies.is_empty());
135 assert!(session.auth_headers.is_empty());
136 assert!(session.csrf_token.is_none());
137 assert!(session.expires_at.is_none());
138 assert!(session.created_at > 0.0);
139 }
140
141 #[test]
142 fn test_cookie_header_format() {
143 let mut session = HttpSession::new("example.com", AuthType::None);
144 session.add_cookie("session_id", "abc123");
145 session.add_cookie("csrftoken", "xyz789");
146
147 let header = session.cookie_header();
148 assert_eq!(header, "csrftoken=xyz789; session_id=abc123");
150 }
151
152 #[test]
153 fn test_is_expired() {
154 let mut session = HttpSession::new("example.com", AuthType::Bearer);
155
156 assert!(!session.is_expired());
158
159 session.set_expires(0.0);
161 assert!(session.is_expired());
162
163 session.set_expires(f64::MAX);
165 assert!(!session.is_expired());
166 }
167
168 #[test]
169 fn test_add_cookies_and_headers() {
170 let mut session = HttpSession::new("example.com", AuthType::ApiKey);
171
172 session.add_cookie("sid", "value1");
173 session.add_cookie("pref", "dark");
174 assert_eq!(session.cookies.len(), 2);
175 assert_eq!(session.cookies.get("sid").unwrap(), "value1");
176 assert_eq!(session.cookies.get("pref").unwrap(), "dark");
177
178 session.add_auth_header("X-Api-Key", "key123");
179 session.add_auth_header("X-Custom", "custom_val");
180 assert_eq!(session.auth_headers.len(), 2);
181 assert_eq!(session.auth_headers.get("X-Api-Key").unwrap(), "key123");
182 assert_eq!(session.auth_headers.get("X-Custom").unwrap(), "custom_val");
183 }
184}