1 // Copyright (c) 2023 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/moqt/moqt_framer.h"
6
7 #include <cstddef>
8 #include <cstdint>
9 #include <cstdlib>
10 #include <optional>
11 #include <type_traits>
12 #include <utility>
13
14 #include "absl/container/inlined_vector.h"
15 #include "absl/status/status.h"
16 #include "absl/status/statusor.h"
17 #include "absl/strings/string_view.h"
18 #include "quiche/quic/core/quic_data_writer.h"
19 #include "quiche/quic/core/quic_time.h"
20 #include "quiche/quic/moqt/moqt_messages.h"
21 #include "quiche/quic/platform/api/quic_bug_tracker.h"
22 #include "quiche/common/platform/api/quiche_bug_tracker.h"
23 #include "quiche/common/quiche_buffer_allocator.h"
24 #include "quiche/common/quiche_data_writer.h"
25 #include "quiche/common/simple_buffer_allocator.h"
26 #include "quiche/common/wire_serialization.h"
27
28 namespace moqt {
29
30 namespace {
31
32 using ::quiche::QuicheBuffer;
33 using ::quiche::WireBytes;
34 using ::quiche::WireOptional;
35 using ::quiche::WireSpan;
36 using ::quiche::WireStringWithVarInt62Length;
37 using ::quiche::WireUint8;
38 using ::quiche::WireVarInt62;
39
40 // Encoding for MOQT Locations:
41 // https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-subscribe-locations
42 class WireLocation {
43 public:
44 using DataType = std::optional<MoqtSubscribeLocation>;
WireLocation(const DataType & location)45 explicit WireLocation(const DataType& location) : location_(location) {}
46
GetLengthOnWire()47 size_t GetLengthOnWire() {
48 return quiche::ComputeLengthOnWire(
49 WireVarInt62(GetModeForSubscribeLocation(location_)),
50 WireOptional<WireVarInt62>(LocationOffsetOnTheWire(location_)));
51 }
SerializeIntoWriter(quiche::QuicheDataWriter & writer)52 absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) {
53 return quiche::SerializeIntoWriter(
54 writer, WireVarInt62(GetModeForSubscribeLocation(location_)),
55 WireOptional<WireVarInt62>(LocationOffsetOnTheWire(location_)));
56 }
57
58 private:
59 // For all location types other than None, we record a single varint after the
60 // type; this function computes the value of that varint.
LocationOffsetOnTheWire(std::optional<MoqtSubscribeLocation> location)61 static std::optional<uint64_t> LocationOffsetOnTheWire(
62 std::optional<MoqtSubscribeLocation> location) {
63 if (!location.has_value()) {
64 return std::nullopt;
65 }
66 if (location->absolute) {
67 return location->absolute_value;
68 }
69 return location->relative_value <= 0 ? -location->relative_value
70 : location->relative_value + 1;
71 }
72
73 const DataType& location_;
74 };
75
76 // Encoding for string parameters as described in
77 // https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-parameters
78 struct StringParameter {
79 template <typename Enum>
StringParametermoqt::__anona1cacf650111::StringParameter80 StringParameter(Enum type, absl::string_view data)
81 : type(static_cast<uint64_t>(type)), data(data) {
82 static_assert(std::is_enum_v<Enum>);
83 }
84
85 uint64_t type;
86 absl::string_view data;
87 };
88 class WireStringParameter {
89 public:
90 using DataType = StringParameter;
91
WireStringParameter(const StringParameter & parameter)92 explicit WireStringParameter(const StringParameter& parameter)
93 : parameter_(parameter) {}
GetLengthOnWire()94 size_t GetLengthOnWire() {
95 return quiche::ComputeLengthOnWire(
96 WireVarInt62(parameter_.type),
97 WireStringWithVarInt62Length(parameter_.data));
98 }
SerializeIntoWriter(quiche::QuicheDataWriter & writer)99 absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) {
100 return quiche::SerializeIntoWriter(
101 writer, WireVarInt62(parameter_.type),
102 WireStringWithVarInt62Length(parameter_.data));
103 }
104
105 private:
106 const StringParameter& parameter_;
107 };
108
109 // Encoding for integer parameters as described in
110 // https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-parameters
111 struct IntParameter {
112 template <typename Enum, typename Param>
IntParametermoqt::__anona1cacf650111::IntParameter113 IntParameter(Enum type, Param value)
114 : type(static_cast<uint64_t>(type)), value(static_cast<uint64_t>(value)) {
115 static_assert(std::is_enum_v<Enum>);
116 static_assert(std::is_enum_v<Param> || std::is_unsigned_v<Param>);
117 }
118
119 uint64_t type;
120 uint64_t value;
121 };
122 class WireIntParameter {
123 public:
124 using DataType = IntParameter;
125
WireIntParameter(const IntParameter & parameter)126 explicit WireIntParameter(const IntParameter& parameter)
127 : parameter_(parameter) {}
GetLengthOnWire()128 size_t GetLengthOnWire() {
129 return quiche::ComputeLengthOnWire(
130 WireVarInt62(parameter_.type),
131 WireVarInt62(NeededVarIntLen(parameter_.value)),
132 WireVarInt62(parameter_.value));
133 }
SerializeIntoWriter(quiche::QuicheDataWriter & writer)134 absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) {
135 return quiche::SerializeIntoWriter(
136 writer, WireVarInt62(parameter_.type),
137 WireVarInt62(NeededVarIntLen(parameter_.value)),
138 WireVarInt62(parameter_.value));
139 }
140
141 private:
NeededVarIntLen(const uint64_t value)142 size_t NeededVarIntLen(const uint64_t value) {
143 return static_cast<size_t>(quic::QuicDataWriter::GetVarInt62Len(value));
144 }
145
146 const IntParameter& parameter_;
147 };
148
149 // Serializes data into buffer using the default allocator. Invokes QUICHE_BUG
150 // on failure.
151 template <typename... Ts>
Serialize(Ts...data)152 QuicheBuffer Serialize(Ts... data) {
153 absl::StatusOr<QuicheBuffer> buffer = quiche::SerializeIntoBuffer(
154 quiche::SimpleBufferAllocator::Get(), data...);
155 if (!buffer.ok()) {
156 QUICHE_BUG(moqt_failed_serialization)
157 << "Failed to serialize MoQT frame: " << buffer.status();
158 return QuicheBuffer();
159 }
160 return *std::move(buffer);
161 }
162
163 } // namespace
164
SerializeObjectHeader(const MoqtObject & message,bool is_first_in_stream)165 quiche::QuicheBuffer MoqtFramer::SerializeObjectHeader(
166 const MoqtObject& message, bool is_first_in_stream) {
167 if (!message.payload_length.has_value() &&
168 !(message.forwarding_preference == MoqtForwardingPreference::kObject ||
169 message.forwarding_preference == MoqtForwardingPreference::kDatagram)) {
170 QUIC_BUG(quic_bug_serialize_object_input_01)
171 << "Track or Group forwarding preference requires knowing the object "
172 "length in advance";
173 return quiche::QuicheBuffer();
174 }
175 if (!is_first_in_stream) {
176 switch (message.forwarding_preference) {
177 case MoqtForwardingPreference::kTrack:
178 return Serialize(WireVarInt62(message.group_id),
179 WireVarInt62(message.object_id),
180 WireVarInt62(*message.payload_length));
181 case MoqtForwardingPreference::kGroup:
182 return Serialize(WireVarInt62(message.object_id),
183 WireVarInt62(*message.payload_length));
184 default:
185 QUIC_BUG(quic_bug_serialize_object_input_02)
186 << "Object or Datagram forwarding_preference must be first in "
187 "stream";
188 return quiche::QuicheBuffer();
189 }
190 }
191 MoqtMessageType message_type =
192 GetMessageTypeForForwardingPreference(message.forwarding_preference);
193 switch (message.forwarding_preference) {
194 case MoqtForwardingPreference::kTrack:
195 return Serialize(
196 WireVarInt62(message_type), WireVarInt62(message.subscribe_id),
197 WireVarInt62(message.track_alias),
198 WireVarInt62(message.object_send_order),
199 WireVarInt62(message.group_id), WireVarInt62(message.object_id),
200 WireVarInt62(*message.payload_length));
201 case MoqtForwardingPreference::kGroup:
202 return Serialize(
203 WireVarInt62(message_type), WireVarInt62(message.subscribe_id),
204 WireVarInt62(message.track_alias), WireVarInt62(message.group_id),
205 WireVarInt62(message.object_send_order),
206 WireVarInt62(message.object_id),
207 WireVarInt62(*message.payload_length));
208 case MoqtForwardingPreference::kObject:
209 case MoqtForwardingPreference::kDatagram:
210 return Serialize(
211 WireVarInt62(message_type), WireVarInt62(message.subscribe_id),
212 WireVarInt62(message.track_alias), WireVarInt62(message.group_id),
213 WireVarInt62(message.object_id),
214 WireVarInt62(message.object_send_order));
215 }
216 }
217
SerializeObjectDatagram(const MoqtObject & message,absl::string_view payload)218 quiche::QuicheBuffer MoqtFramer::SerializeObjectDatagram(
219 const MoqtObject& message, absl::string_view payload) {
220 return Serialize(
221 WireVarInt62(MoqtMessageType::kObjectDatagram),
222 WireVarInt62(message.subscribe_id), WireVarInt62(message.track_alias),
223 WireVarInt62(message.group_id), WireVarInt62(message.object_id),
224 WireVarInt62(message.object_send_order), WireBytes(payload));
225 }
226
SerializeClientSetup(const MoqtClientSetup & message)227 quiche::QuicheBuffer MoqtFramer::SerializeClientSetup(
228 const MoqtClientSetup& message) {
229 absl::InlinedVector<IntParameter, 1> int_parameters;
230 absl::InlinedVector<StringParameter, 1> string_parameters;
231 if (message.role.has_value()) {
232 int_parameters.push_back(
233 IntParameter(MoqtSetupParameter::kRole, *message.role));
234 }
235 if (!using_webtrans_ && message.path.has_value()) {
236 string_parameters.push_back(
237 StringParameter(MoqtSetupParameter::kPath, *message.path));
238 }
239 return Serialize(
240 WireVarInt62(MoqtMessageType::kClientSetup),
241 WireVarInt62(message.supported_versions.size()),
242 WireSpan<WireVarInt62, MoqtVersion>(message.supported_versions),
243 WireVarInt62(string_parameters.size() + int_parameters.size()),
244 WireSpan<WireIntParameter>(int_parameters),
245 WireSpan<WireStringParameter>(string_parameters));
246 }
247
SerializeServerSetup(const MoqtServerSetup & message)248 quiche::QuicheBuffer MoqtFramer::SerializeServerSetup(
249 const MoqtServerSetup& message) {
250 absl::InlinedVector<IntParameter, 1> int_parameters;
251 if (message.role.has_value()) {
252 int_parameters.push_back(
253 IntParameter(MoqtSetupParameter::kRole, *message.role));
254 }
255 return Serialize(WireVarInt62(MoqtMessageType::kServerSetup),
256 WireVarInt62(message.selected_version),
257 WireVarInt62(int_parameters.size()),
258 WireSpan<WireIntParameter>(int_parameters));
259 }
260
SerializeSubscribe(const MoqtSubscribe & message)261 quiche::QuicheBuffer MoqtFramer::SerializeSubscribe(
262 const MoqtSubscribe& message) {
263 if (!message.start_group.has_value() || !message.start_object.has_value()) {
264 QUICHE_BUG(MoqtFramer_start_group_missing)
265 << "start_group or start_object is missing";
266 return quiche::QuicheBuffer();
267 }
268 if (message.end_group.has_value() != message.end_object.has_value()) {
269 QUICHE_BUG(MoqtFramer_end_mismatch)
270 << "end_group and end_object must both be None or both non-None";
271 return quiche::QuicheBuffer();
272 }
273 absl::InlinedVector<StringParameter, 1> string_params;
274 if (message.authorization_info.has_value()) {
275 string_params.push_back(
276 StringParameter(MoqtTrackRequestParameter::kAuthorizationInfo,
277 *message.authorization_info));
278 }
279 return Serialize(
280 WireVarInt62(MoqtMessageType::kSubscribe),
281 WireVarInt62(message.subscribe_id), WireVarInt62(message.track_alias),
282 WireStringWithVarInt62Length(message.track_namespace),
283 WireStringWithVarInt62Length(message.track_name),
284 WireLocation(message.start_group), WireLocation(message.start_object),
285 WireLocation(message.end_group), WireLocation(message.end_object),
286 WireVarInt62(string_params.size()),
287 WireSpan<WireStringParameter>(string_params));
288 }
289
SerializeSubscribeOk(const MoqtSubscribeOk & message)290 quiche::QuicheBuffer MoqtFramer::SerializeSubscribeOk(
291 const MoqtSubscribeOk& message) {
292 if (message.largest_id.has_value()) {
293 return Serialize(WireVarInt62(MoqtMessageType::kSubscribeOk),
294 WireVarInt62(message.subscribe_id),
295 WireVarInt62(message.expires.ToMilliseconds()),
296 WireUint8(1), WireVarInt62(message.largest_id->group),
297 WireVarInt62(message.largest_id->object));
298 }
299 return Serialize(WireVarInt62(MoqtMessageType::kSubscribeOk),
300 WireVarInt62(message.subscribe_id),
301 WireVarInt62(message.expires.ToMilliseconds()),
302 WireUint8(0));
303 }
304
SerializeSubscribeError(const MoqtSubscribeError & message)305 quiche::QuicheBuffer MoqtFramer::SerializeSubscribeError(
306 const MoqtSubscribeError& message) {
307 return Serialize(WireVarInt62(MoqtMessageType::kSubscribeError),
308 WireVarInt62(message.subscribe_id),
309 WireVarInt62(message.error_code),
310 WireStringWithVarInt62Length(message.reason_phrase),
311 WireVarInt62(message.track_alias));
312 }
313
SerializeUnsubscribe(const MoqtUnsubscribe & message)314 quiche::QuicheBuffer MoqtFramer::SerializeUnsubscribe(
315 const MoqtUnsubscribe& message) {
316 return Serialize(WireVarInt62(MoqtMessageType::kUnsubscribe),
317 WireVarInt62(message.subscribe_id));
318 }
319
SerializeSubscribeDone(const MoqtSubscribeDone & message)320 quiche::QuicheBuffer MoqtFramer::SerializeSubscribeDone(
321 const MoqtSubscribeDone& message) {
322 if (message.final_id.has_value()) {
323 return Serialize(WireVarInt62(MoqtMessageType::kSubscribeDone),
324 WireVarInt62(message.subscribe_id),
325 WireVarInt62(message.status_code),
326 WireStringWithVarInt62Length(message.reason_phrase),
327 WireUint8(1), WireVarInt62(message.final_id->group),
328 WireVarInt62(message.final_id->object));
329 }
330 return Serialize(
331 WireVarInt62(MoqtMessageType::kSubscribeDone),
332 WireVarInt62(message.subscribe_id), WireVarInt62(message.status_code),
333 WireStringWithVarInt62Length(message.reason_phrase), WireUint8(0));
334 }
335
SerializeAnnounce(const MoqtAnnounce & message)336 quiche::QuicheBuffer MoqtFramer::SerializeAnnounce(
337 const MoqtAnnounce& message) {
338 absl::InlinedVector<StringParameter, 1> string_params;
339 if (message.authorization_info.has_value()) {
340 string_params.push_back(
341 StringParameter(MoqtTrackRequestParameter::kAuthorizationInfo,
342 *message.authorization_info));
343 }
344 return Serialize(
345 WireVarInt62(static_cast<uint64_t>(MoqtMessageType::kAnnounce)),
346 WireStringWithVarInt62Length(message.track_namespace),
347 WireVarInt62(string_params.size()),
348 WireSpan<WireStringParameter>(string_params));
349 }
350
SerializeAnnounceOk(const MoqtAnnounceOk & message)351 quiche::QuicheBuffer MoqtFramer::SerializeAnnounceOk(
352 const MoqtAnnounceOk& message) {
353 return Serialize(WireVarInt62(MoqtMessageType::kAnnounceOk),
354 WireStringWithVarInt62Length(message.track_namespace));
355 }
356
SerializeAnnounceError(const MoqtAnnounceError & message)357 quiche::QuicheBuffer MoqtFramer::SerializeAnnounceError(
358 const MoqtAnnounceError& message) {
359 return Serialize(WireVarInt62(MoqtMessageType::kAnnounceError),
360 WireStringWithVarInt62Length(message.track_namespace),
361 WireVarInt62(message.error_code),
362 WireStringWithVarInt62Length(message.reason_phrase));
363 }
364
SerializeUnannounce(const MoqtUnannounce & message)365 quiche::QuicheBuffer MoqtFramer::SerializeUnannounce(
366 const MoqtUnannounce& message) {
367 return Serialize(WireVarInt62(MoqtMessageType::kUnannounce),
368 WireStringWithVarInt62Length(message.track_namespace));
369 }
370
SerializeGoAway(const MoqtGoAway & message)371 quiche::QuicheBuffer MoqtFramer::SerializeGoAway(const MoqtGoAway& message) {
372 return Serialize(WireVarInt62(MoqtMessageType::kGoAway),
373 WireStringWithVarInt62Length(message.new_session_uri));
374 }
375
376 } // namespace moqt
377