1use std::collections::{HashMap, VecDeque};
68use std::sync::Arc;
69use std::time::Duration as StdDuration;
70
71use chrono::{DateTime, Duration, Utc};
72use dashmap::DashMap;
73use parking_lot::RwLock;
74use serde::{Deserialize, Serialize};
75use tokio::time::{Interval, interval};
76
77use crate::context::{
78 ClientIdExtractor, ClientSession, CompletionContext, ElicitationContext, RequestInfo,
79};
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct SessionConfig {
84 pub max_sessions: usize,
86 pub session_timeout: Duration,
88 pub max_request_history: usize,
90 pub max_requests_per_session: Option<usize>,
92 pub cleanup_interval: StdDuration,
94 pub enable_analytics: bool,
96}
97
98impl Default for SessionConfig {
99 fn default() -> Self {
100 Self {
101 max_sessions: 1000,
102 session_timeout: Duration::hours(24),
103 max_request_history: 1000,
104 max_requests_per_session: None,
105 cleanup_interval: StdDuration::from_secs(300), enable_analytics: true,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct SessionAnalytics {
114 pub total_sessions: usize,
116 pub active_sessions: usize,
118 pub total_requests: usize,
120 pub successful_requests: usize,
122 pub failed_requests: usize,
124 pub avg_session_duration: Duration,
126 pub top_clients: Vec<(String, usize)>,
128 pub top_methods: Vec<(String, usize)>,
130 pub requests_per_minute: f64,
132}
133
134#[derive(Debug)]
136pub struct SessionManager {
137 config: SessionConfig,
139 sessions: Arc<DashMap<String, ClientSession>>,
141 client_extractor: Arc<ClientIdExtractor>,
143 request_history: Arc<RwLock<VecDeque<RequestInfo>>>,
145 session_history: Arc<RwLock<VecDeque<SessionEvent>>>,
147 cleanup_timer: Arc<RwLock<Option<Interval>>>,
149 stats: Arc<RwLock<SessionStats>>,
151 pending_elicitations: Arc<DashMap<String, Vec<ElicitationContext>>>,
153 active_completions: Arc<DashMap<String, Vec<CompletionContext>>>,
155}
156
157#[derive(Debug, Default)]
159struct SessionStats {
160 total_sessions: usize,
161 total_requests: usize,
162 successful_requests: usize,
163 failed_requests: usize,
164 total_session_duration: Duration,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct SessionEvent {
170 pub timestamp: DateTime<Utc>,
172 pub client_id: String,
174 pub event_type: SessionEventType,
176 pub metadata: HashMap<String, serde_json::Value>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub enum SessionEventType {
183 Created,
185 Authenticated,
187 Updated,
189 Expired,
191 Terminated,
193}
194
195impl SessionManager {
196 #[must_use]
198 pub fn new(config: SessionConfig) -> Self {
199 Self {
200 config,
201 sessions: Arc::new(DashMap::new()),
202 client_extractor: Arc::new(ClientIdExtractor::new()),
203 request_history: Arc::new(RwLock::new(VecDeque::new())),
204 session_history: Arc::new(RwLock::new(VecDeque::new())),
205 cleanup_timer: Arc::new(RwLock::new(None)),
206 stats: Arc::new(RwLock::new(SessionStats::default())),
207 pending_elicitations: Arc::new(DashMap::new()),
208 active_completions: Arc::new(DashMap::new()),
209 }
210 }
211
212 pub fn start(&self) {
219 let mut timer_guard = self.cleanup_timer.write();
220 if timer_guard.is_some() {
221 return;
223 }
224 *timer_guard = Some(interval(self.config.cleanup_interval));
225 drop(timer_guard);
226
227 let sessions = self.sessions.clone();
229 let config = self.config.clone();
230 let session_history = self.session_history.clone();
231 let stats = self.stats.clone();
232 let pending_elicitations = self.pending_elicitations.clone();
233 let active_completions = self.active_completions.clone();
234
235 tokio::spawn(async move {
236 let mut timer = interval(config.cleanup_interval);
237 loop {
238 timer.tick().await;
239 Self::cleanup_expired_sessions(
240 &sessions,
241 &config,
242 &session_history,
243 &stats,
244 &pending_elicitations,
245 &active_completions,
246 );
247 }
248 });
249 }
250
251 #[must_use]
253 pub fn get_or_create_session(
254 &self,
255 client_id: String,
256 transport_type: String,
257 ) -> ClientSession {
258 self.sessions.get(&client_id).map_or_else(
259 || {
260 self.enforce_capacity();
262
263 let session = ClientSession::new(client_id.clone(), transport_type);
264 self.sessions.insert(client_id.clone(), session.clone());
265
266 let mut stats = self.stats.write();
268 stats.total_sessions += 1;
269 drop(stats);
270
271 self.record_session_event(client_id, SessionEventType::Created, HashMap::new());
272
273 session
274 },
275 |session| session.clone(),
276 )
277 }
278
279 pub fn update_client_activity(&self, client_id: &str) {
281 if let Some(mut session) = self.sessions.get_mut(client_id) {
282 session.update_activity();
283
284 if let Some(cap) = self.config.max_requests_per_session
286 && session.request_count > cap
287 {
288 drop(session);
291 let _ = self.terminate_session(client_id);
292 }
293 }
294 }
295
296 #[must_use]
298 pub fn authenticate_client(
299 &self,
300 client_id: &str,
301 client_name: Option<String>,
302 token: Option<String>,
303 ) -> bool {
304 if let Some(mut session) = self.sessions.get_mut(client_id) {
305 session.authenticate(client_name.clone());
306
307 if let Some(token) = token {
308 self.client_extractor
309 .register_token(token, client_id.to_string());
310 }
311
312 let mut metadata = HashMap::new();
313 if let Some(name) = client_name {
314 metadata.insert("client_name".to_string(), serde_json::json!(name));
315 }
316
317 self.record_session_event(
318 client_id.to_string(),
319 SessionEventType::Authenticated,
320 metadata,
321 );
322
323 return true;
324 }
325 false
326 }
327
328 pub fn record_request(&self, mut request_info: RequestInfo) {
330 if !self.config.enable_analytics {
331 return;
332 }
333
334 self.update_client_activity(&request_info.client_id);
336
337 let mut stats = self.stats.write();
339 stats.total_requests += 1;
340 if request_info.success {
341 stats.successful_requests += 1;
342 } else {
343 stats.failed_requests += 1;
344 }
345 drop(stats);
346
347 let mut history = self.request_history.write();
349 if history.len() >= self.config.max_request_history {
350 history.pop_front();
351 }
352
353 request_info.parameters = self.sanitize_parameters(request_info.parameters);
355 history.push_back(request_info);
356 }
357
358 #[must_use]
360 pub fn get_analytics(&self) -> SessionAnalytics {
361 let sessions = self.sessions.clone();
362
363 let active_sessions = sessions.len();
365
366 let total_duration = sessions
368 .iter()
369 .map(|entry| entry.session_duration())
370 .reduce(|acc, dur| acc + dur)
371 .unwrap_or_else(Duration::zero);
372
373 let avg_session_duration = if active_sessions > 0 {
374 total_duration / active_sessions as i32
375 } else {
376 Duration::zero()
377 };
378
379 let mut client_requests: HashMap<String, usize> = HashMap::new();
381 let mut method_requests: HashMap<String, usize> = HashMap::new();
382
383 let (recent_requests, top_clients, top_methods) = {
384 let history = self.request_history.read();
385 for request in history.iter() {
386 *client_requests
387 .entry(request.client_id.clone())
388 .or_insert(0) += 1;
389 *method_requests
390 .entry(request.method_name.clone())
391 .or_insert(0) += 1;
392 }
393
394 let mut top_clients: Vec<(String, usize)> = client_requests.into_iter().collect();
395 top_clients.sort_by_key(|entry| std::cmp::Reverse(entry.1));
396 top_clients.truncate(10);
397
398 let mut top_methods: Vec<(String, usize)> = method_requests.into_iter().collect();
399 top_methods.sort_by_key(|entry| std::cmp::Reverse(entry.1));
400 top_methods.truncate(10);
401
402 let one_hour_ago = Utc::now() - Duration::hours(1);
404 let recent_requests = history
405 .iter()
406 .filter(|req| req.timestamp > one_hour_ago)
407 .count();
408 drop(history);
409
410 (recent_requests, top_clients, top_methods)
411 };
412 let requests_per_minute = recent_requests as f64 / 60.0;
413
414 let stats = self.stats.read();
415 SessionAnalytics {
416 total_sessions: stats.total_sessions,
417 active_sessions,
418 total_requests: stats.total_requests,
419 successful_requests: stats.successful_requests,
420 failed_requests: stats.failed_requests,
421 avg_session_duration,
422 top_clients,
423 top_methods,
424 requests_per_minute,
425 }
426 }
427
428 #[must_use]
430 pub fn get_active_sessions(&self) -> Vec<ClientSession> {
431 self.sessions
432 .iter()
433 .map(|entry| entry.value().clone())
434 .collect()
435 }
436
437 #[must_use]
439 pub fn get_session(&self, client_id: &str) -> Option<ClientSession> {
440 self.sessions.get(client_id).map(|session| session.clone())
441 }
442
443 #[must_use]
445 pub fn client_extractor(&self) -> Arc<ClientIdExtractor> {
446 self.client_extractor.clone()
447 }
448
449 #[must_use]
451 pub fn terminate_session(&self, client_id: &str) -> bool {
452 if let Some((_, session)) = self.sessions.remove(client_id) {
453 let mut stats = self.stats.write();
454 stats.total_session_duration += session.session_duration();
455 drop(stats);
456
457 self.pending_elicitations.remove(client_id);
459 self.active_completions.remove(client_id);
460
461 self.record_session_event(
462 client_id.to_string(),
463 SessionEventType::Terminated,
464 HashMap::new(),
465 );
466
467 true
468 } else {
469 false
470 }
471 }
472
473 #[must_use]
475 pub fn get_request_history(&self, limit: Option<usize>) -> Vec<RequestInfo> {
476 let history = self.request_history.read();
477 let limit = limit.unwrap_or(100);
478
479 history.iter().rev().take(limit).cloned().collect()
480 }
481
482 #[must_use]
484 pub fn get_session_events(&self, limit: Option<usize>) -> Vec<SessionEvent> {
485 let events = self.session_history.read();
486 let limit = limit.unwrap_or(100);
487
488 events.iter().rev().take(limit).cloned().collect()
489 }
490
491 pub fn add_pending_elicitation(&self, client_id: String, elicitation: ElicitationContext) {
495 self.pending_elicitations
496 .entry(client_id)
497 .or_default()
498 .push(elicitation);
499 }
500
501 #[must_use]
503 pub fn get_pending_elicitations(&self, client_id: &str) -> Vec<ElicitationContext> {
504 self.pending_elicitations
505 .get(client_id)
506 .map(|entry| entry.clone())
507 .unwrap_or_default()
508 }
509
510 pub fn update_elicitation_state(
512 &self,
513 client_id: &str,
514 elicitation_id: &str,
515 state: crate::context::ElicitationState,
516 ) -> bool {
517 if let Some(mut elicitations) = self.pending_elicitations.get_mut(client_id) {
518 for elicitation in elicitations.iter_mut() {
519 if elicitation.elicitation_id == elicitation_id {
520 elicitation.set_state(state);
521 return true;
522 }
523 }
524 }
525 false
526 }
527
528 pub fn remove_completed_elicitations(&self, client_id: &str) {
530 if let Some(mut elicitations) = self.pending_elicitations.get_mut(client_id) {
531 elicitations.retain(|e| !e.is_complete());
532 }
533 }
534
535 pub fn clear_elicitations(&self, client_id: &str) {
537 self.pending_elicitations.remove(client_id);
538 }
539
540 pub fn add_active_completion(&self, client_id: String, completion: CompletionContext) {
544 self.active_completions
545 .entry(client_id)
546 .or_default()
547 .push(completion);
548 }
549
550 #[must_use]
552 pub fn get_active_completions(&self, client_id: &str) -> Vec<CompletionContext> {
553 self.active_completions
554 .get(client_id)
555 .map(|entry| entry.clone())
556 .unwrap_or_default()
557 }
558
559 pub fn remove_completion(&self, client_id: &str, completion_id: &str) -> bool {
561 if let Some(mut completions) = self.active_completions.get_mut(client_id) {
562 let original_len = completions.len();
563 completions.retain(|c| c.completion_id != completion_id);
564 return completions.len() < original_len;
565 }
566 false
567 }
568
569 pub fn clear_completions(&self, client_id: &str) {
571 self.active_completions.remove(client_id);
572 }
573
574 #[must_use]
576 pub fn get_enhanced_analytics(&self) -> SessionAnalytics {
577 let analytics = self.get_analytics();
578
579 let mut _total_elicitations = 0;
581 let mut _pending_elicitations = 0;
582 let mut _total_completions = 0;
583
584 for entry in self.pending_elicitations.iter() {
585 let elicitations = entry.value();
586 _total_elicitations += elicitations.len();
587 _pending_elicitations += elicitations.iter().filter(|e| !e.is_complete()).count();
588 }
589
590 for entry in self.active_completions.iter() {
591 _total_completions += entry.value().len();
592 }
593
594 analytics
598 }
599
600 fn cleanup_expired_sessions(
603 sessions: &Arc<DashMap<String, ClientSession>>,
604 config: &SessionConfig,
605 session_history: &Arc<RwLock<VecDeque<SessionEvent>>>,
606 stats: &Arc<RwLock<SessionStats>>,
607 pending_elicitations: &Arc<DashMap<String, Vec<ElicitationContext>>>,
608 active_completions: &Arc<DashMap<String, Vec<CompletionContext>>>,
609 ) {
610 let cutoff_time = Utc::now() - config.session_timeout;
611 let mut expired_sessions = Vec::new();
612
613 for entry in sessions.iter() {
614 if entry.last_activity < cutoff_time {
615 expired_sessions.push(entry.client_id.clone());
616 }
617 }
618
619 for client_id in expired_sessions {
620 if let Some((_, session)) = sessions.remove(&client_id) {
621 let mut stats_guard = stats.write();
623 stats_guard.total_session_duration += session.session_duration();
624 drop(stats_guard);
625
626 pending_elicitations.remove(&client_id);
628 active_completions.remove(&client_id);
629
630 let event = SessionEvent {
632 timestamp: Utc::now(),
633 client_id,
634 event_type: SessionEventType::Expired,
635 metadata: HashMap::new(),
636 };
637
638 let mut history = session_history.write();
639 if history.len() >= 1000 {
640 history.pop_front();
641 }
642 history.push_back(event);
643 }
644 }
645 }
646
647 fn record_session_event(
648 &self,
649 client_id: String,
650 event_type: SessionEventType,
651 metadata: HashMap<String, serde_json::Value>,
652 ) {
653 let event = SessionEvent {
654 timestamp: Utc::now(),
655 client_id,
656 event_type,
657 metadata,
658 };
659
660 let mut history = self.session_history.write();
661 if history.len() >= 1000 {
662 history.pop_front();
663 }
664 history.push_back(event);
665 }
666
667 fn enforce_capacity(&self) {
670 let target = self.config.max_sessions;
671 if self.sessions.len() < target {
673 return;
674 }
675
676 let mut entries: Vec<_> = self
678 .sessions
679 .iter()
680 .map(|entry| (entry.key().clone(), entry.last_activity))
681 .collect();
682 entries.sort_by_key(|(_, ts)| *ts);
683
684 let mut to_evict = self.sessions.len().saturating_sub(target) + 1; for (client_id, _) in entries {
687 if to_evict == 0 {
688 break;
689 }
690 if let Some((_, session)) = self.sessions.remove(&client_id) {
691 let mut stats = self.stats.write();
692 stats.total_session_duration += session.session_duration();
693 drop(stats);
694
695 let event = SessionEvent {
697 timestamp: Utc::now(),
698 client_id: client_id.clone(),
699 event_type: SessionEventType::Terminated,
700 metadata: {
701 let mut m = HashMap::new();
702 m.insert("reason".to_string(), serde_json::json!("capacity_eviction"));
703 m
704 },
705 };
706 {
707 let mut history = self.session_history.write();
708 if history.len() >= 1000 {
709 history.pop_front();
710 }
711 history.push_back(event);
712 } to_evict = to_evict.saturating_sub(1);
714 }
715 }
716 }
717
718 fn sanitize_parameters(&self, mut params: serde_json::Value) -> serde_json::Value {
719 let _ = self; if let Some(obj) = params.as_object_mut() {
722 let sensitive_keys = &["password", "token", "api_key", "secret", "auth"];
723 for key in sensitive_keys {
724 if obj.contains_key(*key) {
725 obj.insert(
726 (*key).to_string(),
727 serde_json::Value::String("[REDACTED]".to_string()),
728 );
729 }
730 }
731 }
732 params
733 }
734}
735
736impl Default for SessionManager {
737 fn default() -> Self {
738 Self::new(SessionConfig::default())
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[tokio::test]
747 async fn test_session_creation() {
748 let manager = SessionManager::new(SessionConfig::default());
749
750 let session = manager.get_or_create_session("client-1".to_string(), "http".to_string());
751
752 assert_eq!(session.client_id, "client-1");
753 assert_eq!(session.transport_type, "http");
754 assert!(!session.authenticated);
755
756 let analytics = manager.get_analytics();
757 assert_eq!(analytics.total_sessions, 1);
758 assert_eq!(analytics.active_sessions, 1);
759 }
760
761 #[tokio::test]
762 async fn test_session_authentication() {
763 let manager = SessionManager::new(SessionConfig::default());
764
765 let session = manager.get_or_create_session("client-1".to_string(), "http".to_string());
766 assert!(!session.authenticated);
767
768 let success = manager.authenticate_client(
769 "client-1",
770 Some("Test Client".to_string()),
771 Some("token123".to_string()),
772 );
773
774 assert!(success);
775
776 let updated_session = manager.get_session("client-1").unwrap();
777 assert!(updated_session.authenticated);
778 assert_eq!(updated_session.client_name, Some("Test Client".to_string()));
779 }
780
781 #[tokio::test]
782 async fn test_request_recording() {
783 let mut manager = SessionManager::new(SessionConfig::default());
784 manager.config.enable_analytics = true;
785
786 let request = RequestInfo::new(
787 "client-1".to_string(),
788 "test_method".to_string(),
789 serde_json::json!({"param": "value"}),
790 )
791 .complete_success(100);
792
793 manager.record_request(request);
794
795 let analytics = manager.get_analytics();
796 assert_eq!(analytics.total_requests, 1);
797 assert_eq!(analytics.successful_requests, 1);
798 assert_eq!(analytics.failed_requests, 0);
799
800 let history = manager.get_request_history(Some(10));
801 assert_eq!(history.len(), 1);
802 assert_eq!(history[0].method_name, "test_method");
803 }
804
805 #[tokio::test]
806 async fn test_session_termination() {
807 let manager = SessionManager::new(SessionConfig::default());
808
809 let _ = manager.get_or_create_session("client-1".to_string(), "http".to_string());
810 assert!(manager.get_session("client-1").is_some());
811
812 let terminated = manager.terminate_session("client-1");
813 assert!(terminated);
814 assert!(manager.get_session("client-1").is_none());
815
816 let analytics = manager.get_analytics();
817 assert_eq!(analytics.active_sessions, 0);
818 }
819
820 #[tokio::test]
821 async fn test_parameter_sanitization() {
822 let manager = SessionManager::new(SessionConfig::default());
823
824 let sensitive_params = serde_json::json!({
825 "username": "testuser",
826 "password": "secret123",
827 "api_key": "key456",
828 "data": "normal_data"
829 });
830
831 let sanitized = manager.sanitize_parameters(sensitive_params);
832 let obj = sanitized.as_object().unwrap();
833
834 assert_eq!(
835 obj["username"],
836 serde_json::Value::String("testuser".to_string())
837 );
838 assert_eq!(
839 obj["password"],
840 serde_json::Value::String("[REDACTED]".to_string())
841 );
842 assert_eq!(
843 obj["api_key"],
844 serde_json::Value::String("[REDACTED]".to_string())
845 );
846 assert_eq!(
847 obj["data"],
848 serde_json::Value::String("normal_data".to_string())
849 );
850 }
851}