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}