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}