Skip to main content

moqtap_client/draft20/
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 10.14: "A publisher MAY send Objects in
6/// response to a FETCH before the FETCH_OK message is sent, but the FETCH_OK
7/// MUST NOT be sent until the End Location is known."
8///
9/// Section 10.13 states the other direction of the same freedom: "The
10/// FETCH_OK or REQUEST_ERROR can come at any time relative to object delivery."
11/// So the 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
42/// first, and Idle -> Pending -> Unanswered -> Done when the response stream
43/// ends first.
44pub struct FetchStateMachine {
45    state: FetchState,
46}
47
48impl Default for FetchStateMachine {
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54impl FetchStateMachine {
55    /// Creates a new state machine in the [`FetchState::Idle`] state.
56    pub fn new() -> Self {
57        Self { state: FetchState::Idle }
58    }
59
60    /// Returns the current state of the fetch request.
61    pub fn state(&self) -> FetchState {
62        self.state
63    }
64
65    /// Idle -> Pending (FETCH sent).
66    pub fn on_fetch_sent(&mut self) -> Result<(), FetchError> {
67        match self.state {
68            FetchState::Idle => {
69                self.state = FetchState::Pending;
70                Ok(())
71            }
72            _ => Err(FetchError::InvalidTransition {
73                from: self.state,
74                event: "on_fetch_sent".to_string(),
75            }),
76        }
77    }
78
79    /// Pending -> Receiving, Unanswered -> Done (FETCH_OK received).
80    ///
81    /// From Unanswered the objects have already been delivered, so the FETCH_OK
82    /// describing them is the last thing the fetch was waiting for.
83    pub fn on_fetch_ok(&mut self) -> Result<(), FetchError> {
84        match self.state {
85            FetchState::Pending => {
86                self.state = FetchState::Receiving;
87                Ok(())
88            }
89            FetchState::Unanswered => {
90                self.state = FetchState::Done;
91                Ok(())
92            }
93            _ => Err(FetchError::InvalidTransition {
94                from: self.state,
95                event: "on_fetch_ok".to_string(),
96            }),
97        }
98    }
99
100    /// Pending | Unanswered -> Done (REQUEST_ERROR received).
101    pub fn on_fetch_error(&mut self) -> Result<(), FetchError> {
102        match self.state {
103            FetchState::Pending | FetchState::Unanswered => {
104                self.state = FetchState::Done;
105                Ok(())
106            }
107            _ => Err(FetchError::InvalidTransition {
108                from: self.state,
109                event: "on_fetch_error".to_string(),
110            }),
111        }
112    }
113
114    /// Pending | Receiving | Unanswered -> Done, Done -> Done (this fetch's
115    /// request stream was cancelled).
116    ///
117    /// This draft carries no FETCH_CANCEL message. Section 3.3.3: "Once a request
118    /// stream has been opened, the request MAY be cancelled by either endpoint."
119    ///
120    /// `Idle` is refused, on the other half of the same sentence: nothing has
121    /// been written, so there is no stream to terminate. `Done` stays `Done` —
122    /// nothing finishes a request stream's send half on the ordinary path, so a
123    /// caller that walks away from a request that has already ended still
124    /// resets the stream, and that reset is an ordinary end rather than a
125    /// fault.
126    pub fn on_request_cancelled(&mut self) -> Result<(), FetchError> {
127        match self.state {
128            FetchState::Pending | FetchState::Receiving | FetchState::Unanswered => {
129                self.state = FetchState::Done;
130                Ok(())
131            }
132            FetchState::Done => Ok(()),
133            FetchState::Idle => Err(FetchError::InvalidTransition {
134                from: self.state,
135                event: "on_request_cancelled".to_string(),
136            }),
137        }
138    }
139
140    /// Receiving -> Done, Pending -> Unanswered (stream FIN received).
141    ///
142    /// A fetch that has already ended ignores the close of its response stream,
143    /// whichever of QUIC's two endings it is. Section 10.13: a relay whose
144    /// upstream FETCH failed "sends a REQUEST_ERROR and can reset the
145    /// unidirectional stream", and may "wait until the cached objects have been
146    /// delivered before resetting the stream", so that close arrives after the
147    /// error ended the fetch. This draft has no FETCH_CANCEL message; Section
148    /// 3.3.3 cancels a request by terminating the directions of its stream,
149    /// which reaches this machine the same way.
150    pub fn on_stream_fin(&mut self) -> Result<(), FetchError> {
151        match self.state {
152            FetchState::Receiving => {
153                self.state = FetchState::Done;
154                Ok(())
155            }
156            FetchState::Pending => {
157                self.state = FetchState::Unanswered;
158                Ok(())
159            }
160            FetchState::Done => Ok(()),
161            _ => Err(FetchError::InvalidTransition {
162                from: self.state,
163                event: "on_stream_fin".to_string(),
164            }),
165        }
166    }
167
168    /// Receiving -> Done, Pending -> Unanswered (stream RESET received).
169    ///
170    /// The same tolerance of a late close as
171    /// [`FetchStateMachine::on_stream_fin`], for the same reason.
172    pub fn on_stream_reset(&mut self) -> Result<(), FetchError> {
173        match self.state {
174            FetchState::Receiving => {
175                self.state = FetchState::Done;
176                Ok(())
177            }
178            FetchState::Pending => {
179                self.state = FetchState::Unanswered;
180                Ok(())
181            }
182            FetchState::Done => Ok(()),
183            _ => Err(FetchError::InvalidTransition {
184                from: self.state,
185                event: "on_stream_reset".to_string(),
186            }),
187        }
188    }
189}