Skip to main content

ant_quic/constrained/
state.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! Connection state machine for the constrained protocol
9//!
10//! The state machine follows a simplified TCP-like model:
11//!
12//! ```text
13//!            SYN_SENT
14//!               ↓
15//! CLOSED → SYN_RCVD → ESTABLISHED → FIN_WAIT → CLOSING → TIME_WAIT → CLOSED
16//!               ↑                      ↓
17//!               └─────── RST ─────────┘
18//! ```
19
20use super::types::ConstrainedError;
21use std::fmt;
22use std::time::{Duration, Instant};
23
24/// Connection state for the constrained protocol
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub enum ConnectionState {
27    /// Connection is closed (initial or final state)
28    #[default]
29    Closed,
30    /// SYN sent, waiting for SYN-ACK
31    SynSent,
32    /// SYN received, SYN-ACK sent, waiting for ACK
33    SynReceived,
34    /// Connection established, data can flow
35    Established,
36    /// FIN sent, waiting for ACK
37    FinWait,
38    /// Received FIN, sent ACK, waiting to close
39    Closing,
40    /// Waiting for enough time to pass before reusing connection ID
41    TimeWait,
42}
43
44impl ConnectionState {
45    /// Check if this state allows sending data
46    pub const fn can_send_data(&self) -> bool {
47        matches!(self, Self::Established | Self::FinWait)
48    }
49
50    /// Check if this state allows receiving data
51    pub const fn can_receive_data(&self) -> bool {
52        matches!(self, Self::Established | Self::FinWait | Self::Closing)
53    }
54
55    /// Check if connection is considered open
56    pub const fn is_open(&self) -> bool {
57        matches!(
58            self,
59            Self::SynSent | Self::SynReceived | Self::Established | Self::FinWait | Self::Closing
60        )
61    }
62
63    /// Check if connection is closed or closing
64    pub const fn is_closed(&self) -> bool {
65        matches!(self, Self::Closed | Self::TimeWait)
66    }
67
68    /// Check if connection is fully established
69    pub const fn is_established(&self) -> bool {
70        matches!(self, Self::Established)
71    }
72
73    /// Get timeout duration for this state
74    ///
75    /// Returns how long to wait in this state before timing out.
76    pub fn timeout(&self) -> Duration {
77        match self {
78            Self::Closed => Duration::MAX,           // No timeout for closed
79            Self::SynSent => Duration::from_secs(5), // Connection setup timeout
80            Self::SynReceived => Duration::from_secs(5),
81            Self::Established => Duration::from_secs(300), // 5 minute idle timeout
82            Self::FinWait => Duration::from_secs(30),      // Wait for FIN-ACK
83            Self::Closing => Duration::from_secs(30),
84            Self::TimeWait => Duration::from_secs(4), // 2*MSL equivalent for constrained
85        }
86    }
87}
88
89impl fmt::Display for ConnectionState {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        let name = match self {
92            Self::Closed => "CLOSED",
93            Self::SynSent => "SYN_SENT",
94            Self::SynReceived => "SYN_RCVD",
95            Self::Established => "ESTABLISHED",
96            Self::FinWait => "FIN_WAIT",
97            Self::Closing => "CLOSING",
98            Self::TimeWait => "TIME_WAIT",
99        };
100        write!(f, "{}", name)
101    }
102}
103
104/// Events that can trigger state transitions
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub enum StateEvent {
107    /// Application requested connection open
108    Open,
109    /// Received SYN from peer
110    RecvSyn,
111    /// Received SYN-ACK from peer
112    RecvSynAck,
113    /// Received ACK
114    RecvAck,
115    /// Received FIN from peer
116    RecvFin,
117    /// Received RST from peer
118    RecvRst,
119    /// Application requested close
120    Close,
121    /// Timeout expired
122    Timeout,
123}
124
125impl fmt::Display for StateEvent {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        let name = match self {
128            Self::Open => "OPEN",
129            Self::RecvSyn => "RECV_SYN",
130            Self::RecvSynAck => "RECV_SYN_ACK",
131            Self::RecvAck => "RECV_ACK",
132            Self::RecvFin => "RECV_FIN",
133            Self::RecvRst => "RECV_RST",
134            Self::Close => "CLOSE",
135            Self::Timeout => "TIMEOUT",
136        };
137        write!(f, "{}", name)
138    }
139}
140
141/// Connection state machine with transition validation
142#[derive(Debug)]
143pub struct StateMachine {
144    /// Current state
145    state: ConnectionState,
146    /// When we entered the current state
147    state_entered: Instant,
148    /// Transition history for debugging (last 8 transitions)
149    history: Vec<(ConnectionState, StateEvent, ConnectionState)>,
150}
151
152impl StateMachine {
153    /// Create a new state machine in Closed state
154    pub fn new() -> Self {
155        Self {
156            state: ConnectionState::Closed,
157            state_entered: Instant::now(),
158            history: Vec::with_capacity(8),
159        }
160    }
161
162    /// Get current state
163    pub fn state(&self) -> ConnectionState {
164        self.state
165    }
166
167    /// Get time spent in current state
168    pub fn time_in_state(&self) -> Duration {
169        self.state_entered.elapsed()
170    }
171
172    /// Check if current state has timed out
173    pub fn is_timed_out(&self) -> bool {
174        self.time_in_state() > self.state.timeout()
175    }
176
177    /// Check if data can be sent
178    pub fn can_send_data(&self) -> bool {
179        self.state.can_send_data()
180    }
181
182    /// Check if data can be received
183    pub fn can_receive_data(&self) -> bool {
184        self.state.can_receive_data()
185    }
186
187    /// Process an event and transition to new state
188    ///
189    /// Returns the new state if transition is valid, or an error if invalid.
190    pub fn transition(&mut self, event: StateEvent) -> Result<ConnectionState, ConstrainedError> {
191        let old_state = self.state;
192        let new_state = self.next_state(event)?;
193
194        // Record transition in history
195        if self.history.len() >= 8 {
196            self.history.remove(0);
197        }
198        self.history.push((old_state, event, new_state));
199
200        // Update state
201        self.state = new_state;
202        self.state_entered = Instant::now();
203
204        tracing::trace!(
205            from = %old_state,
206            event = %event,
207            to = %new_state,
208            "State transition"
209        );
210
211        Ok(new_state)
212    }
213
214    /// Calculate next state for an event without actually transitioning
215    fn next_state(&self, event: StateEvent) -> Result<ConnectionState, ConstrainedError> {
216        use ConnectionState::*;
217        use StateEvent::*;
218
219        let new_state = match (self.state, event) {
220            // From Closed
221            (Closed, Open) => SynSent,
222            (Closed, RecvSyn) => SynReceived,
223
224            // From SynSent
225            (SynSent, RecvSynAck) => Established,
226            (SynSent, RecvRst) => Closed,
227            (SynSent, Timeout) => Closed,
228            (SynSent, Close) => Closed,
229
230            // From SynReceived
231            (SynReceived, RecvAck) => Established,
232            (SynReceived, RecvRst) => Closed,
233            (SynReceived, Timeout) => Closed,
234            (SynReceived, Close) => Closed,
235
236            // From Established
237            (Established, RecvFin) => Closing,
238            (Established, Close) => FinWait,
239            (Established, RecvRst) => Closed,
240            (Established, Timeout) => Closed,
241
242            // From FinWait
243            (FinWait, RecvAck) => Closing,
244            (FinWait, RecvFin) => TimeWait,
245            (FinWait, RecvRst) => Closed,
246            (FinWait, Timeout) => Closed,
247
248            // From Closing
249            (Closing, RecvAck) => TimeWait,
250            (Closing, RecvFin) => TimeWait,
251            (Closing, RecvRst) => Closed,
252            (Closing, Timeout) => Closed,
253
254            // From TimeWait
255            (TimeWait, Timeout) => Closed,
256            (TimeWait, RecvRst) => Closed,
257
258            // Invalid transitions
259            _ => {
260                return Err(ConstrainedError::InvalidStateTransition {
261                    from: self.state.to_string(),
262                    to: format!("{} -> ?", event),
263                });
264            }
265        };
266
267        Ok(new_state)
268    }
269
270    /// Force transition to a specific state (for testing or recovery)
271    #[cfg(test)]
272    pub fn force_state(&mut self, state: ConnectionState) {
273        self.state = state;
274        self.state_entered = Instant::now();
275    }
276
277    /// Get transition history
278    pub fn history(&self) -> &[(ConnectionState, StateEvent, ConnectionState)] {
279        &self.history
280    }
281}
282
283impl Default for StateMachine {
284    fn default() -> Self {
285        Self::new()
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn test_state_display() {
295        assert_eq!(format!("{}", ConnectionState::Closed), "CLOSED");
296        assert_eq!(format!("{}", ConnectionState::Established), "ESTABLISHED");
297        assert_eq!(format!("{}", ConnectionState::SynSent), "SYN_SENT");
298    }
299
300    #[test]
301    fn test_state_properties() {
302        assert!(!ConnectionState::Closed.can_send_data());
303        assert!(ConnectionState::Established.can_send_data());
304        assert!(ConnectionState::FinWait.can_send_data());
305
306        assert!(ConnectionState::Closed.is_closed());
307        assert!(ConnectionState::TimeWait.is_closed());
308        assert!(!ConnectionState::Established.is_closed());
309
310        assert!(ConnectionState::Established.is_established());
311        assert!(!ConnectionState::SynSent.is_established());
312    }
313
314    #[test]
315    fn test_state_machine_new() {
316        let sm = StateMachine::new();
317        assert_eq!(sm.state(), ConnectionState::Closed);
318    }
319
320    #[test]
321    fn test_normal_connection_flow() {
322        let mut sm = StateMachine::new();
323
324        // Initiator side: CLOSED -> SYN_SENT -> ESTABLISHED
325        assert_eq!(
326            sm.transition(StateEvent::Open).unwrap(),
327            ConnectionState::SynSent
328        );
329        assert_eq!(
330            sm.transition(StateEvent::RecvSynAck).unwrap(),
331            ConnectionState::Established
332        );
333
334        // Close: ESTABLISHED -> FIN_WAIT -> TIME_WAIT -> CLOSED
335        assert_eq!(
336            sm.transition(StateEvent::Close).unwrap(),
337            ConnectionState::FinWait
338        );
339        assert_eq!(
340            sm.transition(StateEvent::RecvFin).unwrap(),
341            ConnectionState::TimeWait
342        );
343        assert_eq!(
344            sm.transition(StateEvent::Timeout).unwrap(),
345            ConnectionState::Closed
346        );
347    }
348
349    #[test]
350    fn test_responder_flow() {
351        let mut sm = StateMachine::new();
352
353        // Responder side: CLOSED -> SYN_RCVD -> ESTABLISHED
354        assert_eq!(
355            sm.transition(StateEvent::RecvSyn).unwrap(),
356            ConnectionState::SynReceived
357        );
358        assert_eq!(
359            sm.transition(StateEvent::RecvAck).unwrap(),
360            ConnectionState::Established
361        );
362    }
363
364    #[test]
365    fn test_reset_from_any_state() {
366        let mut sm = StateMachine::new();
367
368        sm.transition(StateEvent::Open).unwrap();
369        assert_eq!(sm.state(), ConnectionState::SynSent);
370
371        assert_eq!(
372            sm.transition(StateEvent::RecvRst).unwrap(),
373            ConnectionState::Closed
374        );
375    }
376
377    #[test]
378    fn test_invalid_transition() {
379        let mut sm = StateMachine::new();
380
381        // Can't receive SYN-ACK from Closed state
382        let result = sm.transition(StateEvent::RecvSynAck);
383        assert!(result.is_err());
384        match result {
385            Err(ConstrainedError::InvalidStateTransition { from, .. }) => {
386                assert_eq!(from, "CLOSED");
387            }
388            _ => panic!("Expected InvalidStateTransition error"),
389        }
390    }
391
392    #[test]
393    fn test_timeout_detection() {
394        let sm = StateMachine::new();
395        // Closed state has Duration::MAX timeout, so should never timeout
396        assert!(!sm.is_timed_out());
397    }
398
399    #[test]
400    fn test_history_tracking() {
401        let mut sm = StateMachine::new();
402
403        sm.transition(StateEvent::Open).unwrap();
404        sm.transition(StateEvent::RecvSynAck).unwrap();
405
406        let history = sm.history();
407        assert_eq!(history.len(), 2);
408        assert_eq!(history[0].0, ConnectionState::Closed);
409        assert_eq!(history[0].1, StateEvent::Open);
410        assert_eq!(history[0].2, ConnectionState::SynSent);
411    }
412
413    #[test]
414    fn test_event_display() {
415        assert_eq!(format!("{}", StateEvent::Open), "OPEN");
416        assert_eq!(format!("{}", StateEvent::RecvSyn), "RECV_SYN");
417        assert_eq!(format!("{}", StateEvent::Close), "CLOSE");
418    }
419
420    #[test]
421    fn test_state_timeout_durations() {
422        // Verify timeout durations are reasonable
423        assert!(ConnectionState::SynSent.timeout() < Duration::from_secs(60));
424        assert!(ConnectionState::Established.timeout() >= Duration::from_secs(60));
425        assert!(ConnectionState::TimeWait.timeout() < Duration::from_secs(60));
426    }
427}