Skip to main content

moqtap_client/draft22/
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
8/// MUST NOT be sent until the End Location is known." 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. Section 3.2.2: a publisher that
142    /// resets the stream "can do so immediately or after previously sent
143    /// objects are delivered. If it has not yet sent FETCH_OK, it can send
144    /// FETCH_ERROR in addition to resetting the stream", so that close can
145    /// arrive after the error ended the fetch. This draft has no FETCH_CANCEL
146    /// message; Section 6.4.2.3 cancels a request by terminating the
147    /// directions of its stream, which reaches this machine the same way.
148    pub fn on_stream_fin(&mut self) -> Result<(), FetchError> {
149        match self.state {
150            FetchState::Receiving => {
151                self.state = FetchState::Done;
152                Ok(())
153            }
154            FetchState::Pending => {
155                self.state = FetchState::Unanswered;
156                Ok(())
157            }
158            FetchState::Done => Ok(()),
159            _ => Err(FetchError::InvalidTransition {
160                from: self.state,
161                event: "on_stream_fin".to_string(),
162            }),
163        }
164    }
165
166    /// Receiving -> Done, Pending -> Unanswered (stream RESET received).
167    ///
168    /// The same tolerance of a late close as
169    /// [`FetchStateMachine::on_stream_fin`], for the same reason.
170    pub fn on_stream_reset(&mut self) -> Result<(), FetchError> {
171        match self.state {
172            FetchState::Receiving => {
173                self.state = FetchState::Done;
174                Ok(())
175            }
176            FetchState::Pending => {
177                self.state = FetchState::Unanswered;
178                Ok(())
179            }
180            FetchState::Done => Ok(()),
181            _ => Err(FetchError::InvalidTransition {
182                from: self.state,
183                event: "on_stream_reset".to_string(),
184            }),
185        }
186    }
187}