Skip to main content

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}