Skip to main content

ironsbe_server/
session.rs

1//! Session management.
2
3use parking_lot::RwLock;
4use std::collections::HashMap;
5use std::net::SocketAddr;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8/// Session information.
9#[derive(Debug, Clone)]
10pub struct Session {
11    /// Session ID.
12    pub id: u64,
13    /// Peer address.
14    pub peer_addr: SocketAddr,
15    /// Creation timestamp (nanos since epoch).
16    pub created_at: u64,
17    /// Last activity timestamp.
18    pub last_activity: u64,
19}
20
21/// Manages active sessions.
22pub struct SessionManager {
23    sessions: RwLock<HashMap<u64, Session>>,
24    next_id: AtomicU64,
25}
26
27impl SessionManager {
28    /// Creates a new session manager.
29    #[must_use]
30    pub fn new() -> Self {
31        Self {
32            sessions: RwLock::new(HashMap::new()),
33            next_id: AtomicU64::new(1),
34        }
35    }
36
37    /// Creates a new session and returns its ID.
38    pub fn create_session(&self, peer_addr: SocketAddr) -> u64 {
39        let id = self.next_id.fetch_add(1, Ordering::Relaxed);
40        let now = std::time::SystemTime::now()
41            .duration_since(std::time::UNIX_EPOCH)
42            .unwrap_or_default()
43            .as_nanos() as u64;
44
45        let session = Session {
46            id,
47            peer_addr,
48            created_at: now,
49            last_activity: now,
50        };
51
52        self.sessions.write().insert(id, session);
53        id
54    }
55
56    /// Closes a session.
57    pub fn close_session(&self, session_id: u64) -> Option<Session> {
58        self.sessions.write().remove(&session_id)
59    }
60
61    /// Gets a session by ID.
62    #[must_use]
63    pub fn get_session(&self, session_id: u64) -> Option<Session> {
64        self.sessions.read().get(&session_id).cloned()
65    }
66
67    /// Updates the last activity timestamp for a session.
68    pub fn touch_session(&self, session_id: u64) {
69        let now = std::time::SystemTime::now()
70            .duration_since(std::time::UNIX_EPOCH)
71            .unwrap_or_default()
72            .as_nanos() as u64;
73
74        if let Some(session) = self.sessions.write().get_mut(&session_id) {
75            session.last_activity = now;
76        }
77    }
78
79    /// Returns the number of active sessions.
80    #[must_use]
81    pub fn count(&self) -> usize {
82        self.sessions.read().len()
83    }
84
85    /// Returns all session IDs.
86    #[must_use]
87    pub fn session_ids(&self) -> Vec<u64> {
88        self.sessions.read().keys().copied().collect()
89    }
90
91    /// Iterates over all sessions.
92    pub fn for_each<F>(&self, mut f: F)
93    where
94        F: FnMut(&Session),
95    {
96        for session in self.sessions.read().values() {
97            f(session);
98        }
99    }
100}
101
102impl Default for SessionManager {
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_create_session() {
114        let manager = SessionManager::new();
115        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
116
117        let id1 = manager.create_session(addr);
118        let id2 = manager.create_session(addr);
119
120        assert_ne!(id1, id2);
121        assert_eq!(manager.count(), 2);
122    }
123
124    #[test]
125    fn test_close_session() {
126        let manager = SessionManager::new();
127        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
128
129        let id = manager.create_session(addr);
130        assert_eq!(manager.count(), 1);
131
132        let session = manager.close_session(id);
133        assert!(session.is_some());
134        assert_eq!(manager.count(), 0);
135    }
136
137    #[test]
138    fn test_get_session() {
139        let manager = SessionManager::new();
140        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
141
142        let id = manager.create_session(addr);
143        let session = manager.get_session(id).unwrap();
144
145        assert_eq!(session.id, id);
146        assert_eq!(session.peer_addr, addr);
147    }
148}