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//! Nothing here imports anything from this crate, which is the property that
10//! keeps those edges pointing one way. A leaf type living wherever it was
11//! first needed reverses them instead: `framer` taking a field of its own
12//! output type from `parser::data`, `event` taking two fields of an event
13//! from `framer` and `transport`.
14//!
15//! **Every one of the five is also re-exported from the module that owns its
16//! subject**, so `event::ProxySide`, `transport::Leg`, `framer::ObjectMeta`,
17//! `framer::BypassReason` and `parser::data::DataStreamType` all 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 — ten drafts (11 through 20) define a
93 /// *subgroup ID is the first object's ID* mode that the codec stores as
94 /// zero, and reporting that zero would mis-key any matcher.
95 ///
96 /// On a fetch stream, when the frame carried no Subgroup ID at all:
97 /// from draft-15 a fetch object may be marked as having been forwarded
98 /// over a datagram, which has no subgroup, and an End of Range
99 /// indicator names a Location rather than an object. Both cases reach
100 /// this crate as `has_subgroup_id: false` on the codec's meta, behind
101 /// a subgroup ID field that holds a placeholder — the same zero, and
102 /// the same mis-keying if it were forwarded.
103 pub subgroup_id: Option<u64>,
104 /// Absolute Object ID, resolved from delta encoding on drafts 14-21.
105 pub object_id: u64,
106 /// Publisher priority. `None` when the header set a default-priority
107 /// flag and omitted the field (drafts 15+).
108 pub publisher_priority: Option<u8>,
109 /// Zero-based index of this object within its stream.
110 pub index_in_stream: u64,
111 /// Declared payload length in bytes.
112 pub payload_len: u64,
113 /// Object Status wire code; `None` when a non-empty payload followed.
114 pub status: Option<u64>,
115 /// Which End of Range indicator this frame is, or `None` for an object.
116 ///
117 /// Drafts 16-19 let a fetch stream state that a run of Objects was not
118 /// serialized instead of sending them, and those frames arrive through
119 /// the same reader call as objects do. They are **not** objects: they
120 /// carry no payload and no content, and one of them standing in a
121 /// count of objects is a count that is wrong. An observer that means
122 /// "objects" filters on this being `None`; one that means "frames"
123 /// does not.
124 ///
125 /// Always `None` on a subgroup stream, and on every fetch stream of
126 /// drafts 07-15, which have no such frame.
127 pub end_of_range: Option<AnyFetchEndOfRange>,
128}
129
130/// Why [`ObjectFramer`](crate::framer::ObjectFramer) stopped parsing a stream.
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132#[non_exhaustive]
133pub enum BypassReason {
134 /// The header decoded but the object reader rejected this subgroup
135 /// stream type.
136 UnsupportedSubgroupStreamType,
137 /// This build compiled no object reader for the fetch stream's draft,
138 /// so the stream is forwarded intact from its header on.
139 ///
140 /// Defensive rather than routine. The stream *header* decodes first and
141 /// fails first on a draft that was not compiled, reporting
142 /// [`Self::DecodeError`], so a session reaches this only if the header
143 /// dispatch and the object-reader dispatch ever disagree about which
144 /// drafts this binary speaks.
145 NoFetchObjectCodec,
146 /// A fetch stream on draft-18, draft-19 or draft-20 naming a request this
147 /// session never saw asked for.
148 ///
149 /// Those three drafts write an Object's Group ID as a difference from the
150 /// previous Object's, and the fetch's Group Order decides which way the
151 /// difference points — draft-19 Section 11.4.4.1: "If the Group Order is
152 /// Ascending, the Group ID is the prior Object's Group ID plus the Group
153 /// ID Delta + 1. If the Group Order is Descending, the Group ID is the
154 /// prior Object's Group ID minus the (Group ID Delta + 1)."
155 ///
156 /// Nothing on the data stream states the order. It is on the FETCH the
157 /// stream answers — draft-19 Section 10.2.8: "If omitted from FETCH, the
158 /// receiver uses Ascending (0x1)" — so a session that carried the FETCH
159 /// knows it, files it under that Request ID, and hands it to the framer
160 /// when the response stream opens. See
161 /// [`FetchGroupOrders`](crate::framer::FetchGroupOrders).
162 ///
163 /// What is left for this variant is the stream whose FETCH never came
164 /// past: a publisher answering a request nobody made, a session whose
165 /// control plane is a byte pump because nothing frames its data either,
166 /// or a hook that rewrote a FETCH into bytes that no longer decode.
167 ///
168 /// Guessing would not fail loudly, which is why an unanswered stream is
169 /// bypassed rather than read against the draft's default. A descending
170 /// stream read as ascending decodes every Object and every field of it;
171 /// only the Group IDs are wrong, walking up where the publisher sent them
172 /// walking down. Every event reported off this stream and every shaping
173 /// rule keyed on a group would then be wrong with nothing to say so. The
174 /// bytes are forwarded untouched instead.
175 FetchGroupOrderUnknown,
176 /// An object could not be measured within the buffer cap's reach.
177 ObjectBeyondMeasuringReach,
178 /// A header or object failed to decode. Also reported as
179 /// [`FramerOut::Error`](crate::framer::FramerOut::Error).
180 DecodeError,
181}