nomad_protocol/server/
session.rs

1//! Server session management.
2//!
3//! Handles per-client session state including:
4//! - Session ID management
5//! - State synchronization tracking
6//! - Session lifecycle
7
8use std::net::SocketAddr;
9use std::time::Instant;
10
11use crate::core::SyncState;
12
13/// Session ID (48-bit, as per NOMAD spec).
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct ServerSessionId([u8; 6]);
16
17impl ServerSessionId {
18    /// Create a new session ID from bytes.
19    pub fn new(bytes: [u8; 6]) -> Self {
20        Self(bytes)
21    }
22
23    /// Generate a random session ID.
24    pub fn generate() -> Self {
25        // TODO: Use proper crypto RNG from crypto module
26        use std::time::{SystemTime, UNIX_EPOCH};
27
28        let seed = SystemTime::now()
29            .duration_since(UNIX_EPOCH)
30            .unwrap()
31            .as_nanos() as u64;
32
33        let mut id = [0u8; 6];
34        let mut state = seed;
35        for byte in &mut id {
36            state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
37            *byte = (state >> 33) as u8;
38        }
39
40        Self(id)
41    }
42
43    /// Get the session ID as bytes.
44    pub fn as_bytes(&self) -> &[u8; 6] {
45        &self.0
46    }
47
48    /// Convert to a u64 (zero-padded).
49    pub fn to_u64(&self) -> u64 {
50        let mut buf = [0u8; 8];
51        buf[..6].copy_from_slice(&self.0);
52        u64::from_le_bytes(buf)
53    }
54}
55
56impl std::fmt::Display for ServerSessionId {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        write!(f, "{:012x}", self.to_u64())
59    }
60}
61
62impl From<[u8; 6]> for ServerSessionId {
63    fn from(bytes: [u8; 6]) -> Self {
64        Self::new(bytes)
65    }
66}
67
68impl From<ServerSessionId> for [u8; 6] {
69    fn from(id: ServerSessionId) -> [u8; 6] {
70        id.0
71    }
72}
73
74/// Session state.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum SessionState {
77    /// Handshake in progress.
78    Handshaking,
79    /// Session is active and syncing.
80    Active,
81    /// Session is closing.
82    Closing,
83    /// Session is closed.
84    Closed,
85}
86
87/// Per-client session.
88#[derive(Debug)]
89pub struct ServerSession<S: SyncState> {
90    /// Session ID.
91    id: ServerSessionId,
92
93    /// Client's address.
94    client_addr: SocketAddr,
95
96    /// Client's static public key.
97    client_public_key: [u8; 32],
98
99    /// Session state.
100    state: SessionState,
101
102    /// Current server-side state.
103    server_state: S,
104
105    /// Last known client state version.
106    client_state_version: u64,
107
108    /// Last known server state version.
109    server_state_version: u64,
110
111    /// Last activity time.
112    last_activity: Instant,
113
114    /// Created time.
115    created_at: Instant,
116
117    /// Negotiated extensions.
118    extensions: Vec<u16>,
119}
120
121impl<S: SyncState> ServerSession<S> {
122    /// Create a new session.
123    pub fn new(
124        id: ServerSessionId,
125        client_addr: SocketAddr,
126        client_public_key: [u8; 32],
127        initial_state: S,
128    ) -> Self {
129        let now = Instant::now();
130        Self {
131            id,
132            client_addr,
133            client_public_key,
134            state: SessionState::Handshaking,
135            server_state: initial_state,
136            client_state_version: 0,
137            server_state_version: 0,
138            last_activity: now,
139            created_at: now,
140            extensions: Vec::new(),
141        }
142    }
143
144    /// Get the session ID.
145    pub fn id(&self) -> ServerSessionId {
146        self.id
147    }
148
149    /// Get the client address.
150    pub fn client_addr(&self) -> SocketAddr {
151        self.client_addr
152    }
153
154    /// Get the client's public key.
155    pub fn client_public_key(&self) -> &[u8; 32] {
156        &self.client_public_key
157    }
158
159    /// Get the session state.
160    pub fn state(&self) -> SessionState {
161        self.state
162    }
163
164    /// Set the session state.
165    pub fn set_state(&mut self, state: SessionState) {
166        self.state = state;
167    }
168
169    /// Get the current server state.
170    pub fn server_state(&self) -> &S {
171        &self.server_state
172    }
173
174    /// Get mutable access to the server state.
175    pub fn server_state_mut(&mut self) -> &mut S {
176        &mut self.server_state
177    }
178
179    /// Update the server state and increment version.
180    pub fn update_server_state(&mut self, state: S) {
181        self.server_state = state;
182        self.server_state_version += 1;
183    }
184
185    /// Get the client state version.
186    pub fn client_state_version(&self) -> u64 {
187        self.client_state_version
188    }
189
190    /// Update the client state version (from ack).
191    pub fn update_client_state_version(&mut self, version: u64) {
192        self.client_state_version = version;
193    }
194
195    /// Get the server state version.
196    pub fn server_state_version(&self) -> u64 {
197        self.server_state_version
198    }
199
200    /// Record activity.
201    pub fn touch(&mut self) {
202        self.last_activity = Instant::now();
203    }
204
205    /// Get time since last activity.
206    pub fn idle_time(&self) -> std::time::Duration {
207        self.last_activity.elapsed()
208    }
209
210    /// Get session age.
211    pub fn age(&self) -> std::time::Duration {
212        self.created_at.elapsed()
213    }
214
215    /// Check if session is active.
216    pub fn is_active(&self) -> bool {
217        self.state == SessionState::Active
218    }
219
220    /// Update the client address (for IP roaming).
221    pub fn update_client_addr(&mut self, addr: SocketAddr) {
222        self.client_addr = addr;
223        self.touch();
224    }
225
226    /// Set negotiated extensions.
227    pub fn set_extensions(&mut self, extensions: Vec<u16>) {
228        self.extensions = extensions;
229    }
230
231    /// Get negotiated extensions.
232    pub fn extensions(&self) -> &[u16] {
233        &self.extensions
234    }
235
236    /// Check if compression is enabled.
237    pub fn compression_enabled(&self) -> bool {
238        // Extension 0x0001 is compression
239        self.extensions.contains(&0x0001)
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_session_id_generate() {
249        let id1 = ServerSessionId::generate();
250        let id2 = ServerSessionId::generate();
251
252        // IDs should be different (with very high probability)
253        assert_ne!(id1, id2);
254    }
255
256    #[test]
257    fn test_session_id_display() {
258        let id = ServerSessionId::new([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
259        let display = format!("{}", id);
260        assert_eq!(display.len(), 12); // 48 bits = 12 hex chars
261    }
262}