Skip to main content

moqtap_client/draft23/
fetch.rs

1/// Fetch lifecycle states.
2///
3/// A fetch settles two things and settles them independently: the publisher's
4/// answer, which arrives on the control stream, and the response stream that
5/// carries the objects. Section 3.2: "The FETCH_OK or FETCH_ERROR can come at
6/// any time relative to object delivery. The publisher MAY send Objects in
7/// response to a FETCH before the FETCH_OK message is sent, but the FETCH_OK MUST NOT be sent until
8/// the publisher knows whether Largest Object is within the requested range." So the fetch is over
9/// when both have settled, in whichever order they settle.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum FetchState {
12    /// Initial state before any FETCH message is sent.
13    Idle,
14    /// FETCH has been sent; the answer is owed and the response stream
15    /// has not ended.
16    Pending,
17    /// FETCH_OK received; data is being received on the stream.
18    Receiving,
19    /// The response stream has ended and the answer is still owed.
20    Unanswered,
21    /// Fetch has ended (error, cancel, FIN, or reset).
22    Done,
23}
24
25/// Errors that can occur during fetch state transitions.
26#[derive(Debug, thiserror::Error, PartialEq, Eq)]
27pub enum FetchError {
28    /// An event was received that is not valid for the current state.
29    #[error("invalid transition from {from:?} on event {event}")]
30    InvalidTransition {
31        /// The state the machine was in when the invalid event arrived.
32        from: FetchState,
33        /// The name of the event that was rejected.
34        event: String,
35    },
36}
37
38/// Pure state machine for a MoQT fetch request.
39/// Transitions: Idle -> Pending -> Receiving -> Done when the answer comes
40/// first, and Idle -> Pending -> Unanswered -> Done when the response stream
41/// ends first.
42pub struct FetchStateMachine {
43    state: FetchState,
44}
45
46impl Default for FetchStateMachine {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl FetchStateMachine {
53    /// Creates a new state machine in the [`FetchState::Idle`] state.
54    pub fn new() -> Self {
55        Self { state: FetchState::Idle }
56    }
57
58    /// Returns the current state of the fetch request.
59    pub fn state(&self) -> FetchState {
60        self.state
61    }
62
63    /// Idle -> Pending (FETCH sent).
64    pub fn on_fetch_sent(&mut self) -> Result<(), FetchError> {
65        match self.state {
66            FetchState::Idle => {
67                self.state = FetchState::Pending;
68                Ok(())
69            }
70            _ => Err(FetchError::InvalidTransition {
71                from: self.state,
72                event: "on_fetch_sent".to_string(),
73            }),
74        }
75    }
76
77    /// Pending -> Receiving, Unanswered -> Done (FETCH_OK received).
78    ///
79    /// From Unanswered the objects have already been delivered, so the FETCH_OK
80    /// describing them is the last thing the fetch was waiting for.
81    pub fn on_fetch_ok(&mut self) -> Result<(), FetchError> {
82        match self.state {
83            FetchState::Pending => {
84                self.state = FetchState::Receiving;
85                Ok(())
86            }
87            FetchState::Unanswered => {
88                self.state = FetchState::Done;
89                Ok(())
90            }
91            _ => Err(FetchError::InvalidTransition {
92                from: self.state,
93                event: "on_fetch_ok".to_string(),
94            }),
95        }
96    }
97
98    /// Pending | Unanswered -> Done (REQUEST_ERROR received).
99    pub fn on_fetch_error(&mut self) -> Result<(), FetchError> {
100        match self.state {
101            FetchState::Pending | FetchState::Unanswered => {
102                self.state = FetchState::Done;
103                Ok(())
104            }
105            _ => Err(FetchError::InvalidTransition {
106                from: self.state,
107                event: "on_fetch_error".to_string(),
108            }),
109        }
110    }
111
112    /// Pending | Receiving | Unanswered -> Done, Done -> Done (this fetch's
113    /// request stream was cancelled).
114    ///
115    /// This draft carries no FETCH_CANCEL message. Section 6.4.2.3: "Once a request
116    /// stream has been opened, the request MAY be cancelled by either endpoint."
117    ///
118    /// `Idle` is refused, on the other half of the same sentence: nothing has
119    /// been written, so there is no stream to terminate. `Done` stays `Done` —
120    /// nothing finishes a request stream's send half on the ordinary path, so a
121    /// caller that walks away from a request that has already ended still
122    /// resets the stream, and that reset is an ordinary end rather than a
123    /// fault.
124    pub fn on_request_cancelled(&mut self) -> Result<(), FetchError> {
125        match self.state {
126            FetchState::Pending | FetchState::Receiving | FetchState::Unanswered => {
127                self.state = FetchState::Done;
128                Ok(())
129            }
130            FetchState::Done => Ok(()),
131            FetchState::Idle => Err(FetchError::InvalidTransition {
132                from: self.state,
133                event: "on_request_cancelled".to_string(),
134            }),
135        }
136    }
137
138    /// Receiving -> Done, Pending -> Unanswered (stream FIN received).
139    ///
140    /// A fetch that has already ended ignores the close of its response stream,
141    /// whichever of QUIC's two endings it is: a reset can arrive after a
142    /// REQUEST_ERROR ended the fetch. This draft has no FETCH_CANCEL message;
143    /// Section 6.4.2.3 cancels a request by terminating the directions of its
144    /// stream, which reaches this machine the same way.
145    pub fn on_stream_fin(&mut self) -> Result<(), FetchError> {
146        match self.state {
147            FetchState::Receiving => {
148                self.state = FetchState::Done;
149                Ok(())
150            }
151            FetchState::Pending => {
152                self.state = FetchState::Unanswered;
153                Ok(())
154            }
155            FetchState::Done => Ok(()),
156            _ => Err(FetchError::InvalidTransition {
157                from: self.state,
158                event: "on_stream_fin".to_string(),
159            }),
160        }
161    }
162
163    /// Receiving -> Done, Pending -> Unanswered (stream RESET received).
164    ///
165    /// The same tolerance of a late close as
166    /// [`FetchStateMachine::on_stream_fin`], for the same reason.
167    pub fn on_stream_reset(&mut self) -> Result<(), FetchError> {
168        match self.state {
169            FetchState::Receiving => {
170                self.state = FetchState::Done;
171                Ok(())
172            }
173            FetchState::Pending => {
174                self.state = FetchState::Unanswered;
175                Ok(())
176            }
177            FetchState::Done => Ok(()),
178            _ => Err(FetchError::InvalidTransition {
179                from: self.state,
180                event: "on_stream_reset".to_string(),
181            }),
182        }
183    }
184}