Skip to main content

moqtap_proxy/
types.rs

1//! The words the rest of the crate keys on.
2//!
3//! Five leaf types, each a plain `Copy` value with no behaviour: which side of
4//! the proxy a frame moves over, which of the two connections a call is about,
5//! which kind of data stream is being read, what a framed object is, and why
6//! the framer stopped framing one. Twenty-eight `use` edges from fourteen
7//! modules reach for them.
8//!
9//! Before this module existed each lived wherever it was first needed, so those
10//! edges ran upward: `framer` took a field of its own output type from
11//! `parser::data`, and `event` took two fields of an event from `framer` and
12//! `transport`. Nothing here imports anything from this crate, which is the
13//! property that makes those edges disappear rather than reverse.
14//!
15//! **Every one of the five is still re-exported where it used to live**, so
16//! `event::ProxySide`, `transport::Leg`, `framer::ObjectMeta`,
17//! `framer::BypassReason` and `parser::data::DataStreamType` all still resolve.
18//! A downstream match on `ProxySide` with no wildcard arm keeps compiling, and
19//! nothing outside this crate has to move.
20
21use moqtap_codec::dispatch::AnyFetchEndOfRange;
22use moqtap_codec::version::DraftVersion;
23
24/// Which side of the proxy a message originates from.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ProxySide {
27    /// Client → Proxy (downstream ingress).
28    ClientToProxy,
29    /// Proxy → Relay (upstream egress).
30    ProxyToRelay,
31    /// Relay → Proxy (upstream ingress).
32    RelayToProxy,
33    /// Proxy → Client (downstream egress).
34    ProxyToClient,
35}
36
37/// Which of the proxy's two connections a call is about.
38///
39/// **Not [`ProxySide`]**, which this crate also
40/// has and which names something else entirely. A leg is a *connection*:
41/// the proxy holds exactly two, one to the client and one to the upstream
42/// relay, and each has its own endpoint, its own socket, its own
43/// certificate and its own transport parameters. A side is a *direction of
44/// travel* over a leg, which is why `ProxySide` has four variants where
45/// this has two — `ClientToProxy` and `ProxyToClient` are the two
46/// directions of the client leg, `ProxyToRelay` and `RelayToProxy` the two
47/// of the upstream leg.
48///
49/// The two are worth keeping straight because the compiler will not: both are
50/// small `Copy` enums that a reader skims as *which part of the proxy*. The
51/// test is what the thing being described belongs to. Anything QUIC settles
52/// once for a whole connection — a window, an MTU, a congestion controller, a
53/// socket — is a leg. Anything a single frame can be observed in or acted on —
54/// an event, a shaping rule, a hook site — is a side.
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum Leg {
57    /// The connection between a client and this proxy.
58    Client,
59    /// The connection between this proxy and the upstream relay.
60    Upstream,
61}
62
63/// The expected type of data stream.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum DataStreamType {
66    /// Subgroup data stream (most common).
67    Subgroup,
68    /// Fetch response data stream.
69    Fetch,
70}
71
72/// A framed object's identity and framing, without its payload.
73///
74/// Every field is a primitive, so an observer never has to name a
75/// per-draft codec type to key on an object. Produced by
76/// [`ObjectFramer`](crate::framer::ObjectFramer).
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct ObjectMeta {
79    /// The draft this stream was parsed as.
80    pub draft: DraftVersion,
81    /// Whether this object came from a subgroup or a fetch stream.
82    pub stream_kind: DataStreamType,
83    /// Track alias from the stream header. `None` on fetch streams, whose
84    /// headers carry a request ID instead.
85    pub track_alias: Option<u64>,
86    /// Group ID: from the stream header on subgroup streams, from the
87    /// object itself on fetch streams.
88    pub group_id: u64,
89    /// Subgroup ID. `None` when the frame has none to report, which
90    /// happens two ways.
91    /// On a subgroup stream, when the stream type encodes an implicit subgroup
92    /// ID that this draft never resolves — eight drafts (11, 12, 13, 14, 16,
93    /// 17, 18 and 19) define a *subgroup ID is the first object's ID* mode that
94    /// the codec stores as zero, and reporting that zero would mis-key any
95    /// matcher.
96    ///
97    /// On a fetch stream, when the frame carried no Subgroup ID at all:
98    /// from draft-15 a fetch object may be marked as having been forwarded
99    /// over a datagram, which has no subgroup, and an End of Range
100    /// indicator names a Location rather than an object. Both cases reach
101    /// this crate as `has_subgroup_id: false` on the codec's meta, behind
102    /// a subgroup ID field that holds a placeholder — the same zero, and
103    /// the same mis-keying if it were forwarded.
104    pub subgroup_id: Option<u64>,
105    /// Absolute Object ID, resolved from delta encoding on drafts 14-19.
106    pub object_id: u64,
107    /// Publisher priority. `None` when the header set a default-priority
108    /// flag and omitted the field (drafts 15+).
109    pub publisher_priority: Option<u8>,
110    /// Zero-based index of this object within its stream.
111    pub index_in_stream: u64,
112    /// Declared payload length in bytes.
113    pub payload_len: u64,
114    /// Object Status wire code; `None` when a non-empty payload followed.
115    pub status: Option<u64>,
116    /// Which End of Range indicator this frame is, or `None` for an object.
117    ///
118    /// Drafts 16-19 let a fetch stream state that a run of Objects was not
119    /// serialized instead of sending them, and those frames arrive through
120    /// the same reader call as objects do. They are **not** objects: they
121    /// carry no payload and no content, and one of them standing in a
122    /// count of objects is a count that is wrong. An observer that means
123    /// "objects" filters on this being `None`; one that means "frames"
124    /// does not.
125    ///
126    /// Always `None` on a subgroup stream, and on every fetch stream of
127    /// drafts 07-15, which have no such frame.
128    pub end_of_range: Option<AnyFetchEndOfRange>,
129}
130
131/// Why [`ObjectFramer`](crate::framer::ObjectFramer) stopped parsing a stream.
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133#[non_exhaustive]
134pub enum BypassReason {
135    /// The header decoded but the object reader rejected this subgroup
136    /// stream type.
137    UnsupportedSubgroupStreamType,
138    /// This build compiled no object reader for the fetch stream's draft,
139    /// so the stream is forwarded intact from its header on.
140    ///
141    /// Defensive rather than routine. The stream *header* decodes first and
142    /// fails first on a draft that was not compiled, reporting
143    /// [`Self::DecodeError`], so a session reaches this only if the header
144    /// dispatch and the object-reader dispatch ever disagree about which
145    /// drafts this binary speaks.
146    ///
147    /// It used to mean something else and much more common: a fetch stream
148    /// on any of drafts 15-19, none of which this crate would address. Three
149    /// of those five are now framed, and the two that are not report
150    /// [`Self::FetchGroupOrderUnknown`], which says why.
151    NoFetchObjectCodec,
152    /// A fetch stream on draft-18 or draft-19 naming a request this session
153    /// never saw asked for.
154    ///
155    /// Those two drafts write an Object's Group ID as a difference from the
156    /// previous Object's, and the fetch's Group Order decides which way the
157    /// difference points — draft-19 Section 11.4.4.1: "If the Group Order is
158    /// Ascending, the Group ID is the prior Object's Group ID plus the Group
159    /// ID Delta + 1. If the Group Order is Descending, the Group ID is the
160    /// prior Object's Group ID minus the (Group ID Delta + 1)."
161    ///
162    /// Nothing on the data stream states the order. It is on the FETCH the
163    /// stream answers — draft-19 Section 10.2.8: "If omitted from FETCH, the
164    /// receiver uses Ascending (0x1)" — so a session that carried the FETCH
165    /// knows it, files it under that Request ID, and hands it to the framer
166    /// when the response stream opens. See
167    /// [`FetchGroupOrders`](crate::framer::FetchGroupOrders).
168    ///
169    /// What is left for this variant is the stream whose FETCH never came
170    /// past: a publisher answering a request nobody made, a session whose
171    /// control plane is a byte pump because nothing frames its data either,
172    /// or a hook that rewrote a FETCH into bytes that no longer decode.
173    ///
174    /// Guessing would not fail loudly, which is why an unanswered stream is
175    /// bypassed rather than read against the draft's default. A descending
176    /// stream read as ascending decodes every Object and every field of it;
177    /// only the Group IDs are wrong, walking up where the publisher sent them
178    /// walking down. Every event reported off this stream and every shaping
179    /// rule keyed on a group would then be wrong with nothing to say so. The
180    /// bytes are forwarded untouched instead.
181    FetchGroupOrderUnknown,
182    /// An object could not be measured within the buffer cap's reach.
183    ObjectBeyondMeasuringReach,
184    /// A header or object failed to decode. Also reported as
185    /// [`FramerOut::Error`](crate::framer::FramerOut::Error).
186    DecodeError,
187}