Skip to main content

moqtap_client/draft08/session/
state.rs

1/// Session lifecycle states.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum SessionState {
4    /// Initial state before the QUIC connection is established.
5    Connecting,
6    /// Exchanging CLIENT_SETUP and SERVER_SETUP messages.
7    SetupExchange,
8    /// Session is fully established and can exchange data.
9    Active,
10    /// GOAWAY received; finishing in-flight requests before closing.
11    Draining,
12    /// Session is terminated.
13    Closed,
14}
15
16/// Errors arising from invalid session state transitions.
17#[derive(Debug, thiserror::Error, PartialEq, Eq)]
18pub enum SessionError {
19    /// Attempted a state transition that is not allowed.
20    #[error("invalid state transition from {from:?} to {to:?}")]
21    InvalidTransition {
22        /// The state the session was in.
23        from: SessionState,
24        /// The state the transition targeted.
25        to: SessionState,
26    },
27}
28
29/// Pure state machine for MoQT session lifecycle.
30/// Transitions: Connecting → SetupExchange → Active → Draining → Closed.
31pub struct SessionStateMachine {
32    state: SessionState,
33}
34
35impl Default for SessionStateMachine {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl SessionStateMachine {
42    /// Create a new state machine starting in the `Connecting` state.
43    pub fn new() -> Self {
44        Self { state: SessionState::Connecting }
45    }
46
47    /// Returns the current session state.
48    pub fn state(&self) -> SessionState {
49        self.state
50    }
51
52    /// Transition: Connecting → SetupExchange.
53    pub fn on_connect(&mut self) -> Result<(), SessionError> {
54        if self.state == SessionState::Connecting {
55            self.state = SessionState::SetupExchange;
56            Ok(())
57        } else {
58            Err(SessionError::InvalidTransition {
59                from: self.state,
60                to: SessionState::SetupExchange,
61            })
62        }
63    }
64
65    /// Transition: SetupExchange → Active.
66    pub fn on_setup_complete(&mut self) -> Result<(), SessionError> {
67        if self.state == SessionState::SetupExchange {
68            self.state = SessionState::Active;
69            Ok(())
70        } else {
71            Err(SessionError::InvalidTransition { from: self.state, to: SessionState::Active })
72        }
73    }
74
75    /// Transition: Active → Draining (GOAWAY received).
76    pub fn on_goaway(&mut self) -> Result<(), SessionError> {
77        if self.state == SessionState::Active {
78            self.state = SessionState::Draining;
79            Ok(())
80        } else {
81            Err(SessionError::InvalidTransition { from: self.state, to: SessionState::Draining })
82        }
83    }
84
85    /// Transition: Active|Draining → Closed.
86    pub fn on_close(&mut self) -> Result<(), SessionError> {
87        if self.state == SessionState::Active || self.state == SessionState::Draining {
88            self.state = SessionState::Closed;
89            Ok(())
90        } else {
91            Err(SessionError::InvalidTransition { from: self.state, to: SessionState::Closed })
92        }
93    }
94}