xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/moqt/moqt_framer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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