Skip to main content

moqtap_client/draft13/
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 8.17: "A publisher MAY send Objects in response
6/// to a FETCH before the FETCH_OK message is sent, but the FETCH_OK MUST NOT be
7/// sent until the End Location is known."
8///
9/// Section 8.16 states the other direction of the same freedom: "The FETCH_OK
10/// or FETCH_ERROR can come at any time relative to object delivery." So the
11/// fetch is over when both have settled, in whichever order they settle.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FetchState {
14    /// Initial state before any FETCH message is sent.
15    Idle,
16    /// FETCH has been sent; the answer is owed and the response stream
17    /// has not ended.
18    Pending,
19    /// FETCH_OK received; data is being received on the stream.
20    Receiving,
21    /// The response stream has ended and the answer is still owed.
22    Unanswered,
23    /// Fetch has ended (error, cancel, FIN, or reset).
24    Done,
25}
26
27/// Errors that can occur during fetch state transitions.
28#[derive(Debug, thiserror::Error, PartialEq, Eq)]
29pub enum FetchError {
30    /// An event was received that is not valid for the current state.
31    #[error("invalid transition from {from:?} on event {event}")]
32    InvalidTransition {
33        /// The state the machine was in when the invalid event arrived.
34        from: FetchState,
35        /// The name of the event that was rejected.
36        event: String,
37    },
38}
39
40/// Pure state machine for a MoQT fetch request.
41/// Transitions: Idle → Pending → Receiving → Done when the answer comes first,
42/// and Idle → Pending → Unanswered → Done when the response stream ends first.
43pub struct FetchStateMachine {
44    state: FetchState,
45}
46
47impl Default for FetchStateMachine {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl FetchStateMachine {
54    /// Creates a new state machine in the [`FetchState::Idle`] state.
55    pub fn new() -> Self {
56        Self { state: FetchState::Idle }
57    }
58
59    /// Returns the current state of the fetch request.
60    pub fn state(&self) -> FetchState {
61        self.state
62    }
63
64    /// Idle → Pending (FETCH sent).
65    pub fn on_fetch_sent(&mut self) -> Result<(), FetchError> {
66        match self.state {
67            FetchState::Idle => {
68                self.state = FetchState::Pending;
69                Ok(())
70            }
71            _ => Err(FetchError::InvalidTransition {
72                from: self.state,
73                event: "on_fetch_sent".to_string(),
74            }),
75        }
76    }
77
78    /// Pending → Receiving, Unanswered → Done (FETCH_OK received).
79    ///
80    /// From Unanswered the objects have already been delivered, so the FETCH_OK
81    /// describing them is the last thing the fetch was waiting for.
82    pub fn on_fetch_ok(&mut self) -> Result<(), FetchError> {
83        match self.state {
84            FetchState::Pending => {
85                self.state = FetchState::Receiving;
86                Ok(())
87            }
88            FetchState::Unanswered => {
89                self.state = FetchState::Done;
90                Ok(())
91            }
92            _ => Err(FetchError::InvalidTransition {
93                from: self.state,
94                event: "on_fetch_ok".to_string(),
95            }),
96        }
97    }
98
99    /// Pending | Unanswered → Done (FETCH_ERROR received).
100    pub fn on_fetch_error(&mut self) -> Result<(), FetchError> {
101        match self.state {
102            FetchState::Pending | FetchState::Unanswered => {
103                self.state = FetchState::Done;
104                Ok(())
105            }
106            _ => Err(FetchError::InvalidTransition {
107                from: self.state,
108                event: "on_fetch_error".to_string(),
109            }),
110        }
111    }
112
113    /// Pending | Receiving | Unanswered → Done (FETCH_CANCEL sent).
114    pub fn on_fetch_cancel(&mut self) -> Result<(), FetchError> {
115        match self.state {
116            FetchState::Pending | FetchState::Receiving | FetchState::Unanswered => {
117                self.state = FetchState::Done;
118                Ok(())
119            }
120            _ => Err(FetchError::InvalidTransition {
121                from: self.state,
122                event: "on_fetch_cancel".to_string(),
123            }),
124        }
125    }
126
127    /// Receiving → Done, Pending → Unanswered (stream FIN received).
128    ///
129    /// A fetch that has already ended ignores the close of its response stream,
130    /// whichever of QUIC's two endings it is, and both of the ways a fetch ends
131    /// early leave one behind. Section 8.19: the publisher of a cancelled fetch
132    /// "SHOULD promptly close the unidirectional stream, even if it is in the
133    /// middle of delivering an object". Section 8.16: a relay whose upstream
134    /// FETCH failed "sends a FETCH_ERROR and can reset the unidirectional
135    /// stream", and may "wait until the cached objects have been delivered
136    /// before resetting the stream".
137    pub fn on_stream_fin(&mut self) -> Result<(), FetchError> {
138        match self.state {
139            FetchState::Receiving => {
140                self.state = FetchState::Done;
141                Ok(())
142            }
143            FetchState::Pending => {
144                self.state = FetchState::Unanswered;
145                Ok(())
146            }
147            FetchState::Done => Ok(()),
148            _ => Err(FetchError::InvalidTransition {
149                from: self.state,
150                event: "on_stream_fin".to_string(),
151            }),
152        }
153    }
154
155    /// Receiving → Done, Pending → Unanswered (stream RESET received).
156    ///
157    /// The same tolerance of a late close as
158    /// [`FetchStateMachine::on_stream_fin`], for the same reason.
159    pub fn on_stream_reset(&mut self) -> Result<(), FetchError> {
160        match self.state {
161            FetchState::Receiving => {
162                self.state = FetchState::Done;
163                Ok(())
164            }
165            FetchState::Pending => {
166                self.state = FetchState::Unanswered;
167                Ok(())
168            }
169            FetchState::Done => Ok(()),
170            _ => Err(FetchError::InvalidTransition {
171                from: self.state,
172                event: "on_stream_reset".to_string(),
173            }),
174        }
175    }
176}
177
178/// The same transitions, named for the end that serves the fetch.
179///
180/// A fetch this endpoint answers passes through the states in the same order
181/// as one it makes, with every message going the other way: the FETCH arrives
182/// instead of leaving, the answer leaves instead of arriving. Sharing the
183/// transitions and not the names is what lets a refusal say which event was
184/// refused, rather than naming the mirror image of it.
185impl FetchStateMachine {
186    /// Idle -> Pending (FETCH received from the peer).
187    pub fn on_fetch_received(&mut self) -> Result<(), FetchError> {
188        self.on_fetch_sent().map_err(|_| FetchError::InvalidTransition {
189            from: self.state(),
190            event: "on_fetch_received".to_string(),
191        })
192    }
193
194    /// Pending -> Receiving, Unanswered -> Done (FETCH_OK sent).
195    ///
196    /// The state is named for the requester's view; for the end answering, the
197    /// same node means the objects are being served rather than received. It is
198    /// the same node in the graph, with the same edges, so it keeps its name.
199    pub fn on_fetch_ok_sent(&mut self) -> Result<(), FetchError> {
200        self.on_fetch_ok().map_err(|_| FetchError::InvalidTransition {
201            from: self.state(),
202            event: "on_fetch_ok_sent".to_string(),
203        })
204    }
205
206    /// Pending | Unanswered -> Done (FETCH_ERROR sent).
207    pub fn on_fetch_error_sent(&mut self) -> Result<(), FetchError> {
208        self.on_fetch_error().map_err(|_| FetchError::InvalidTransition {
209            from: self.state(),
210            event: "on_fetch_error_sent".to_string(),
211        })
212    }
213
214    /// Pending | Receiving | Unanswered -> Done (FETCH_CANCEL received).
215    pub fn on_fetch_cancel_received(&mut self) -> Result<(), FetchError> {
216        self.on_fetch_cancel().map_err(|_| FetchError::InvalidTransition {
217            from: self.state(),
218            event: "on_fetch_cancel_received".to_string(),
219        })
220    }
221
222    /// Receiving -> Done, Pending -> Unanswered (this endpoint finished the
223    /// fetch data stream).
224    pub fn on_stream_fin_sent(&mut self) -> Result<(), FetchError> {
225        self.on_stream_fin().map_err(|_| FetchError::InvalidTransition {
226            from: self.state(),
227            event: "on_stream_fin_sent".to_string(),
228        })
229    }
230}