moqtap_client/draft20/event.rs
1//! Client event types emitted by a MoQT connection.
2
3use moqtap_codec::dispatch::{
4 AnyControlMessage, AnyDatagramHeader, AnyFetchHeader, AnySubgroupHeader,
5};
6use moqtap_codec::draft20::data_stream::{FetchHeader, SubgroupHeader, SubgroupObject};
7use moqtap_codec::kvp::KeyValuePair;
8
9/// Direction of a message or stream relative to this endpoint.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Direction {
12 /// Sent (outgoing).
13 Send,
14 /// Received (incoming).
15 Receive,
16}
17
18/// The kind of stream an event refers to.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum StreamKind {
21 /// Subgroup data stream.
22 Subgroup,
23 /// Fetch data stream.
24 Fetch,
25 /// Fill fetch stream, new in draft-20 (Section 5.1.3).
26 ///
27 /// Framed exactly as a fetch stream — a FETCH_HEADER and the fetch object
28 /// records after it — and told apart from one only by what its Request ID
29 /// names: a subscription that asked for a fill rather than a FETCH. It is
30 /// a separate kind here because it belongs to a subscription's lifetime
31 /// rather than to a request of its own, and because an observer that could
32 /// not tell the two apart would count a subscription's fill as a fetch the
33 /// application never made.
34 Fill,
35 /// Datagram.
36 Datagram,
37 /// Request stream: the bidirectional stream one request and its response
38 /// travel on.
39 ///
40 /// Draft-20 Section 3.3 moved requests off the control plane and gave each
41 /// one a bidirectional stream that begins with the request message. This
42 /// is the only kind here that is not a data stream, and it is named
43 /// because an observer that could not name it would see a request message
44 /// with no stream to attach it to.
45 Request,
46}
47
48/// Events emitted by a MoQT connection.
49///
50/// This enum is `#[non_exhaustive]` -- new variants may be added in minor
51/// releases. Downstream `match` arms should include a wildcard `_ =>` branch.
52#[non_exhaustive]
53#[derive(Debug, Clone)]
54pub enum ClientEvent {
55 /// MoQT setup handshake completed.
56 SetupComplete {
57 /// The negotiated MoQT version (from ALPN in draft-20).
58 negotiated_version: u64,
59 },
60
61 /// A control message was sent or received.
62 ControlMessage {
63 /// Whether the message was sent or received.
64 direction: Direction,
65 /// The decoded control message.
66 message: AnyControlMessage,
67 /// The transport-level identifier of the stream the message travelled
68 /// on when that stream is a request stream, and `None` when it is the
69 /// control stream.
70 ///
71 /// Draft-20 responses carry no request id: the stream is the
72 /// correlation. Without this an observer sees a SUBSCRIBE_OK with
73 /// nothing to say which SUBSCRIBE it answers, and cannot tell a
74 /// message on the control stream from one on a request stream.
75 stream_id: Option<u64>,
76 /// The raw wire bytes of the framed message (type + length + payload).
77 /// `None` if raw capture is not available.
78 raw: Option<Vec<u8>>,
79 },
80
81 /// A data stream was opened.
82 StreamOpened {
83 /// Whether we opened (Send) or accepted (Receive) the stream.
84 direction: Direction,
85 /// The type of data stream.
86 stream_kind: StreamKind,
87 /// Transport-level stream identifier.
88 stream_id: u64,
89 },
90
91 /// A data stream header was decoded after the stream opened.
92 DataStreamHeader {
93 /// Transport-level stream identifier.
94 stream_id: u64,
95 /// Whether we opened (Send) or accepted (Receive) the stream.
96 direction: Direction,
97 /// The parsed subgroup header.
98 header: AnySubgroupHeader,
99 },
100
101 /// The publisher reported that a subscription's state changed, other than
102 /// in answer to a REQUEST_UPDATE this endpoint sent.
103 ///
104 /// PUBLISH_STATE_NOTIFY, new in draft-20 (Section 10.10). Nothing is owed
105 /// in reply — "it is a unilateral notification: the receiver does not
106 /// respond with REQUEST_OK or REQUEST_ERROR, and the message is not subject
107 /// to the MAX_REQUEST_UPDATES limit" — and "no action is required by the
108 /// recipient". It is an event rather than a return value for that reason:
109 /// there is nothing for a caller to do with it except notice.
110 ///
111 /// It also arrives as an ordinary [`ClientEvent::ControlMessage`], as every
112 /// message does. This variant is the decoded form, so an observer does not
113 /// have to reach through `AnyControlMessage` to find out which subscription
114 /// changed.
115 PublishStateNotify {
116 /// The Request ID of the subscription whose state changed.
117 ///
118 /// The message itself carries no Request ID field: Section 10.10 makes
119 /// the bidirectional stream the correlation, exactly as it is for
120 /// SUBSCRIBE_OK, PUBLISH_DONE and FETCH_OK. This is the request that
121 /// stream belongs to.
122 request_id: u64,
123 /// The transport-level identifier of that stream.
124 stream_id: u64,
125 /// The parameters whose values changed. A parameter that is absent is
126 /// unchanged; the publisher "MUST include the LARGEST_OBJECT parameter"
127 /// if known, so a receiver can place the change in the track.
128 parameters: Vec<KeyValuePair>,
129 },
130
131 /// A fetch response stream header was decoded.
132 FetchStreamHeader {
133 /// Transport-level stream identifier.
134 stream_id: u64,
135 /// Whether we opened (Send) or accepted (Receive) the stream.
136 direction: Direction,
137 /// The parsed fetch header.
138 header: AnyFetchHeader,
139 },
140
141 /// A subgroup object header was decoded on a subgroup stream.
142 SubgroupObjectReceived {
143 /// Transport-level stream identifier.
144 stream_id: u64,
145 /// Direction (Send when emitted from a writer, Receive from a reader).
146 direction: Direction,
147 /// The decoded subgroup header (for context).
148 subgroup_header: SubgroupHeader,
149 /// The decoded subgroup object.
150 object: SubgroupObject,
151 },
152
153 /// A fetch header was decoded on a fetch stream.
154 FetchHeaderReceived {
155 /// Transport-level stream identifier.
156 stream_id: u64,
157 /// Direction (Send when emitted from a writer, Receive from a reader).
158 direction: Direction,
159 /// The decoded fetch header.
160 header: FetchHeader,
161 },
162
163 /// A datagram was sent or received.
164 DatagramReceived {
165 /// Whether sent or received.
166 direction: Direction,
167 /// The parsed datagram header.
168 header: AnyDatagramHeader,
169 /// Size of the payload in bytes.
170 payload_len: usize,
171 },
172
173 /// A data stream was closed.
174 StreamClosed {
175 /// Transport-level stream identifier.
176 stream_id: u64,
177 /// Error code (0 = clean close).
178 error_code: u64,
179 },
180
181 /// Session entered draining state (GOAWAY received).
182 Draining {
183 /// The new session URI from the GOAWAY message.
184 new_session_uri: Vec<u8>,
185 },
186
187 /// Connection was closed.
188 Closed {
189 /// Application error code.
190 code: u32,
191 /// Human-readable reason.
192 reason: Vec<u8>,
193 },
194
195 /// A transport or protocol error occurred.
196 Error {
197 /// Error description.
198 error: String,
199 },
200}