Moqt MessageParameters are no longer Key-Value Pairs. SETUP message no longer contains number of options (Key-Value Pairs are now never length-prefixed). Part of the draft-18 update. PiperOrigin-RevId: 994738534
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc index e02f090..9c7abb0 100644 --- a/quiche/quic/moqt/moqt_framer.cc +++ b/quiche/quic/moqt/moqt_framer.cc
@@ -86,13 +86,10 @@ class WireKeyValuePairList { public: - explicit WireKeyValuePairList(const KeyValuePairList& list, - bool length_prefix = true) - : list_(list), length_prefix_(length_prefix) {} + explicit WireKeyValuePairList(const KeyValuePairList& list) : list_(list) {} size_t GetLengthOnWire() { - size_t total = - length_prefix_ ? WireMoqVarInt(list_.size()).GetLengthOnWire() : 0; + size_t total = 0; uint64_t last_key = 0; list_.ForEach([&](uint64_t key, std::variant<uint64_t, absl::string_view> value) { @@ -111,9 +108,6 @@ return total; } absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) { - if (length_prefix_) { - WireMoqVarInt(list_.size()).SerializeIntoWriter(writer); - } uint64_t last_key = 0; list_.ForEach( [&](uint64_t key, std::variant<uint64_t, absl::string_view> value) { @@ -135,7 +129,283 @@ private: const KeyValuePairList& list_; - const bool length_prefix_; +}; + +class WireLocation { + public: + explicit WireLocation(const Location& location) : location_(location) {} + size_t GetLengthOnWire() { + return quiche::ComputeLengthOnWire(WireMoqVarInt(location_.group), + WireMoqVarInt(location_.object)); + } + absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) { + return quiche::SerializeIntoWriter(writer, WireMoqVarInt(location_.group), + WireMoqVarInt(location_.object)); + } + + private: + const Location& location_; +}; + +class WireAuthToken { + public: + explicit WireAuthToken(const AuthToken& token) : token_(token) {} + size_t GetLengthOnWire() { + return quiche::ComputeLengthOnWire( + WireMoqVarInt(token_.alias_type), + WireOptional<WireMoqVarInt>(token_.alias), + WireOptional<WireMoqVarInt>(token_.type), + WireOptional<WireBytes>(token_.value)); + } + absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) { + return quiche::SerializeIntoWriter( + writer, WireMoqVarInt(token_.alias_type), + WireOptional<WireMoqVarInt>(token_.alias), + WireOptional<WireMoqVarInt>(token_.type), + WireOptional<WireBytes>(token_.value)); + } + + private: + const AuthToken& token_; +}; + +class WireSubscriptionFilter { + public: + explicit WireSubscriptionFilter(const SubscriptionFilter& filter) + : filter_(filter) {} + size_t GetLengthOnWire() { + switch (filter_.type()) { + case MoqtFilterType::kNextGroupStart: + case MoqtFilterType::kLargestObject: + return quiche::ComputeLengthOnWire(WireMoqVarInt(filter_.type())); + case MoqtFilterType::kAbsoluteStart: + return quiche::ComputeLengthOnWire(WireMoqVarInt(filter_.type()), + WireLocation(filter_.start())); + case MoqtFilterType::kAbsoluteRange: + return quiche::ComputeLengthOnWire( + WireMoqVarInt(filter_.type()), WireLocation(filter_.start()), + WireMoqVarInt(filter_.end_group() - filter_.start().group)); + } + } + absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) { + switch (filter_.type()) { + case MoqtFilterType::kNextGroupStart: + case MoqtFilterType::kLargestObject: + return quiche::SerializeIntoWriter(writer, + WireMoqVarInt(filter_.type())); + case MoqtFilterType::kAbsoluteStart: + return quiche::SerializeIntoWriter(writer, + WireMoqVarInt(filter_.type()), + WireLocation(filter_.start())); + case MoqtFilterType::kAbsoluteRange: + return quiche::SerializeIntoWriter( + writer, WireMoqVarInt(filter_.type()), + WireLocation(filter_.start()), + WireMoqVarInt(filter_.end_group() - filter_.start().group)); + } + } + + private: + const SubscriptionFilter& filter_; +}; + +uint64_t TimeDeltaToMilliseconds(const quic::QuicTimeDelta& time_delta) { + if (time_delta == quic::QuicTimeDelta::Infinite()) { + return 0ULL; + } + return std::max(time_delta.ToMilliseconds(), int64_t{1}); +} + +class WireMessageParameters { + public: + explicit WireMessageParameters(const MessageParameters& parameters) + : parameters_(parameters), num_parameters_(0) { + if (parameters_.object_delivery_timeout.has_value()) { + ++num_parameters_; + } + num_parameters_ += parameters_.authorization_tokens.size(); + if (parameters_.subgroup_delivery_timeout.has_value()) { + ++num_parameters_; + } + if (parameters_.expires.has_value()) { + ++num_parameters_; + } + if (parameters_.largest_object.has_value()) { + ++num_parameters_; + } + if (parameters_.forward_has_value()) { + ++num_parameters_; + } + if (parameters_.subscriber_priority.has_value()) { + ++num_parameters_; + } + if (parameters_.subscription_filter.has_value()) { + ++num_parameters_; + } + if (parameters_.group_order.has_value()) { + ++num_parameters_; + } + if (parameters_.new_group_request.has_value()) { + ++num_parameters_; + } + if (parameters_.oack_window_size.has_value()) { + ++num_parameters_; + } + } + + size_t GetLengthOnWire() { + size_t length = WireMoqVarInt(num_parameters_).GetLengthOnWire(); + uint64_t last_key = 0; + auto key_delta = [&](MessageParameter key) { + uint64_t delta = static_cast<uint64_t>(key) - last_key; + last_key = static_cast<uint64_t>(key); + return delta; + }; + if (parameters_.object_delivery_timeout.has_value()) { + length += quiche::ComputeLengthOnWire(WireKeyVarIntPair( + key_delta(MessageParameter::kObjectDeliveryTimeout), + TimeDeltaToMilliseconds(*parameters_.object_delivery_timeout))); + } + for (const AuthToken& token : parameters_.authorization_tokens) { + WireAuthToken wire_token(token); + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kAuthorizationToken)), + WireMoqVarInt(wire_token.GetLengthOnWire()), wire_token); + } + if (parameters_.subgroup_delivery_timeout.has_value()) { + length += + WireKeyVarIntPair( + key_delta(MessageParameter::kSubgroupDeliveryTimeout), + TimeDeltaToMilliseconds(*parameters_.subgroup_delivery_timeout)) + .GetLengthOnWire(); + } + if (parameters_.expires.has_value()) { + length += WireKeyVarIntPair(key_delta(MessageParameter::kExpires), + TimeDeltaToMilliseconds(*parameters_.expires)) + .GetLengthOnWire(); + } + if (parameters_.largest_object.has_value()) { + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kLargestObject)), + WireLocation(*parameters_.largest_object)); + } + if (parameters_.forward_has_value()) { + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kForward)), + WireUint8(parameters_.forward() ? 1ULL : 0ULL)); + } + if (parameters_.subscriber_priority.has_value()) { + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kSubscriberPriority)), + WireUint8(*parameters_.subscriber_priority)); + } + if (parameters_.subscription_filter.has_value()) { + WireSubscriptionFilter filter(*parameters_.subscription_filter); + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kSubscriptionFilter)), + WireMoqVarInt(filter.GetLengthOnWire()), filter); + } + if (parameters_.group_order.has_value()) { + length += quiche::ComputeLengthOnWire( + WireMoqVarInt(key_delta(MessageParameter::kGroupOrder)), + WireUint8(static_cast<uint8_t>(*parameters_.group_order))); + } + if (parameters_.new_group_request.has_value()) { + length += WireKeyVarIntPair(key_delta(MessageParameter::kNewGroupRequest), + *parameters_.new_group_request) + .GetLengthOnWire(); + } + if (parameters_.oack_window_size.has_value()) { + length += + WireKeyVarIntPair(key_delta(MessageParameter::kOackWindowSize), + parameters_.oack_window_size->ToMicroseconds()) + .GetLengthOnWire(); + } + return length; + } + + absl::Status SerializeIntoWriter(quiche::QuicheDataWriter& writer) { + QUICHE_RETURN_IF_ERROR( + quiche::SerializeIntoWriter(writer, WireMoqVarInt(num_parameters_))); + uint64_t last_key = 0; + auto key_delta = [&](MessageParameter key) { + uint64_t delta = static_cast<uint64_t>(key) - last_key; + last_key = static_cast<uint64_t>(key); + return delta; + }; + if (parameters_.object_delivery_timeout.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireKeyVarIntPair( + key_delta(MessageParameter::kObjectDeliveryTimeout), + TimeDeltaToMilliseconds(*parameters_.object_delivery_timeout)))); + } + for (const AuthToken& token : parameters_.authorization_tokens) { + WireAuthToken wire_token(token); + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireMoqVarInt(key_delta(MessageParameter::kAuthorizationToken)), + WireMoqVarInt(wire_token.GetLengthOnWire()), wire_token)); + } + if (parameters_.subgroup_delivery_timeout.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, WireKeyVarIntPair( + key_delta(MessageParameter::kSubgroupDeliveryTimeout), + TimeDeltaToMilliseconds( + *parameters_.subgroup_delivery_timeout)))); + } + if (parameters_.expires.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireKeyVarIntPair(key_delta(MessageParameter::kExpires), + TimeDeltaToMilliseconds(*parameters_.expires)))); + } + if (parameters_.largest_object.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, WireMoqVarInt(key_delta(MessageParameter::kLargestObject)), + WireLocation(*parameters_.largest_object))); + } + if (parameters_.forward_has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, WireMoqVarInt(key_delta(MessageParameter::kForward)), + WireUint8(parameters_.forward() ? 1ULL : 0ULL))); + } + if (parameters_.subscriber_priority.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireMoqVarInt(key_delta(MessageParameter::kSubscriberPriority)), + WireUint8(*parameters_.subscriber_priority))); + } + if (parameters_.subscription_filter.has_value()) { + WireSubscriptionFilter filter(*parameters_.subscription_filter); + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireMoqVarInt(key_delta(MessageParameter::kSubscriptionFilter)), + WireMoqVarInt(filter.GetLengthOnWire()), filter)); + } + if (parameters_.group_order.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, WireMoqVarInt(key_delta(MessageParameter::kGroupOrder)), + WireUint8(static_cast<uint8_t>(*parameters_.group_order)))); + } + if (parameters_.new_group_request.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireKeyVarIntPair(key_delta(MessageParameter::kNewGroupRequest), + *parameters_.new_group_request))); + } + if (parameters_.oack_window_size.has_value()) { + QUICHE_RETURN_IF_ERROR(quiche::SerializeIntoWriter( + writer, + WireKeyVarIntPair(key_delta(MessageParameter::kOackWindowSize), + parameters_.oack_window_size->ToMicroseconds()))); + } + return absl::OkStatus(); + } + + private: + const MessageParameters& parameters_; + uint64_t num_parameters_; }; class WireTrackNamespace { @@ -252,30 +522,6 @@ WireOptional<WireBytes>(token.value)); } -quiche::QuicheBuffer SerializeSubscriptionFilter( - const SubscriptionFilter& filter) { - switch (filter.type()) { - case MoqtFilterType::kNextGroupStart: - return Serialize(WireMoqVarInt(filter.type())); - case MoqtFilterType::kLargestObject: - return Serialize(WireMoqVarInt(filter.type())); - case MoqtFilterType::kAbsoluteStart: - return Serialize( - WireMoqVarInt(filter.type()), - WireKeyVarIntPair(filter.start().group, filter.start().object)); - case MoqtFilterType::kAbsoluteRange: - QUICHE_DCHECK_LE(filter.start().group, filter.end_group()); - return Serialize( - WireMoqVarInt(filter.type()), - WireKeyVarIntPair(filter.start().group, filter.start().object), - WireMoqVarInt(filter.end_group() - filter.start().group)); - } -} - -quiche::QuicheBuffer SerializeLocation(const Location& location) { - return Serialize(WireKeyVarIntPair(location.group, location.object)); -} - } // namespace KeyValuePairList SetupOptions::ToKeyValuePairList() const { @@ -305,64 +551,6 @@ return out; } -uint64_t TimeDeltaToMilliseconds(const quic::QuicTimeDelta& time_delta) { - if (time_delta == quic::QuicTimeDelta::Infinite()) { - return 0ULL; - } - return std::max(time_delta.ToMilliseconds(), int64_t{1}); -} - -KeyValuePairList MessageParameters::ToKeyValuePairList() const { - KeyValuePairList list; - if (object_delivery_timeout.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kObjectDeliveryTimeout), - TimeDeltaToMilliseconds(*object_delivery_timeout)); - } - for (const AuthToken& token : authorization_tokens) { - list.insert(static_cast<uint64_t>(MessageParameter::kAuthorizationToken), - SerializeAuthToken(token).AsStringView()); - } - if (subgroup_delivery_timeout.has_value()) { - list.insert( - static_cast<uint64_t>(MessageParameter::kSubgroupDeliveryTimeout), - TimeDeltaToMilliseconds(*subgroup_delivery_timeout)); - } - if (expires.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kExpires), - TimeDeltaToMilliseconds(*expires)); - } - if (largest_object.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kLargestObject), - SerializeLocation(*largest_object).AsStringView()); - } - if (forward_has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kForward), - forward() ? 1ULL : 0ULL); - } - if (subscriber_priority.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kSubscriberPriority), - *subscriber_priority); - } - if (subscription_filter.has_value()) { - list.insert( - static_cast<uint64_t>(MessageParameter::kSubscriptionFilter), - SerializeSubscriptionFilter(*subscription_filter).AsStringView()); - } - if (group_order.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kGroupOrder), - static_cast<uint64_t>(*group_order)); - } - if (new_group_request.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kNewGroupRequest), - *new_group_request); - } - if (oack_window_size.has_value()) { - list.insert(static_cast<uint64_t>(MessageParameter::kOackWindowSize), - static_cast<uint64_t>(oack_window_size->ToMicroseconds())); - } - return list; -} - quiche::QuicheBuffer MoqtFramer::SerializeObjectHeader( const MoqtObject& message, MoqtDataStreamType message_type, std::optional<PublishedObjectMetadata>& previous_object_in_stream) { @@ -511,18 +699,17 @@ quiche::QuicheBuffer MoqtFramer::SerializeRequestOk( const MoqtRequestOk& message) { - return SerializeControlMessage( - MoqtMessageType::kRequestOk, - WireKeyValuePairList(message.parameters.ToKeyValuePairList()), - WireKeyValuePairList(message.properties, false)); + return SerializeControlMessage(MoqtMessageType::kRequestOk, + WireMessageParameters(message.parameters), + WireKeyValuePairList(message.properties)); } quiche::QuicheBuffer MoqtFramer::SerializeSubscribe( const MoqtSubscribe& message, MoqtMessageType message_type) { - return SerializeControlMessage( - message_type, WireMoqVarInt(message.request_id), - WireFullTrackName(message.full_track_name), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + return SerializeControlMessage(message_type, + WireMoqVarInt(message.request_id), + WireFullTrackName(message.full_track_name), + WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializeSubscribeOk( @@ -532,10 +719,10 @@ << "Subscribe OK properties are ill-formed"; return quiche::QuicheBuffer(); } - return SerializeControlMessage( - message_type, WireMoqVarInt(message.track_alias), - WireKeyValuePairList(message.parameters.ToKeyValuePairList()), - WireKeyValuePairList(message.properties, false)); + return SerializeControlMessage(message_type, + WireMoqVarInt(message.track_alias), + WireMessageParameters(message.parameters), + WireKeyValuePairList(message.properties)); } quiche::QuicheBuffer MoqtFramer::SerializeRequestError( @@ -558,18 +745,18 @@ quiche::QuicheBuffer MoqtFramer::SerializeRequestUpdate( const MoqtRequestUpdate& message) { - return SerializeControlMessage( - MoqtMessageType::kRequestUpdate, WireMoqVarInt(message.request_id), - WireMoqVarInt(message.existing_request_id), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + return SerializeControlMessage(MoqtMessageType::kRequestUpdate, + WireMoqVarInt(message.request_id), + WireMoqVarInt(message.existing_request_id), + WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializePublishNamespace( const MoqtPublishNamespace& message) { - return SerializeControlMessage( - MoqtMessageType::kPublishNamespace, WireMoqVarInt(message.request_id), - WireTrackNamespace(message.track_namespace), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + return SerializeControlMessage(MoqtMessageType::kPublishNamespace, + WireMoqVarInt(message.request_id), + WireTrackNamespace(message.track_namespace), + WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializeNamespace( @@ -621,7 +808,7 @@ return SerializeControlMessage( MoqtMessageType::kSubscribeNamespace, WireMoqVarInt(message.request_id), WireTrackNamespace(message.track_namespace_prefix), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializeSubscribeTracks( @@ -629,7 +816,7 @@ return SerializeControlMessage( MoqtMessageType::kSubscribeTracks, WireMoqVarInt(message.request_id), WireTrackNamespace(message.track_namespace_prefix), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializeFetch(const MoqtFetch& message) { @@ -654,7 +841,7 @@ WireMoqVarInt(standalone_fetch.end_location.object == kMaxObjectId ? 0 : standalone_fetch.end_location.object + 1), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + WireMessageParameters(message.parameters)); } uint64_t request_id, joining_start; if (std::holds_alternative<JoiningFetchRelative>(message.fetch)) { @@ -671,8 +858,7 @@ return SerializeControlMessage( MoqtMessageType::kFetch, WireMoqVarInt(message.request_id), WireMoqVarInt(message.fetch.index() + 1), WireMoqVarInt(request_id), - WireMoqVarInt(joining_start), - WireKeyValuePairList(message.parameters.ToKeyValuePairList())); + WireMoqVarInt(joining_start), WireMessageParameters(message.parameters)); } quiche::QuicheBuffer MoqtFramer::SerializeFetchOk(const MoqtFetchOk& message) { @@ -682,18 +868,18 @@ WireMoqVarInt(message.end_location.object == kMaxObjectId ? 0 : (message.end_location.object + 1)), - WireKeyValuePairList(message.parameters.ToKeyValuePairList()), - WireKeyValuePairList(message.properties, false)); + WireMessageParameters(message.parameters), + WireKeyValuePairList(message.properties)); } quiche::QuicheBuffer MoqtFramer::SerializePublish(const MoqtPublish& message) { - return SerializeControlMessage( - MoqtMessageType::kPublish, WireMoqVarInt(message.request_id), - WireFullTrackName(message.full_track_name), - WireMoqVarInt(message.track_alias), - WireKeyValuePairList(message.parameters.ToKeyValuePairList()), - WireKeyValuePairList(message.properties, false)); + return SerializeControlMessage(MoqtMessageType::kPublish, + WireMoqVarInt(message.request_id), + WireFullTrackName(message.full_track_name), + WireMoqVarInt(message.track_alias), + WireMessageParameters(message.parameters), + WireKeyValuePairList(message.properties)); } quiche::QuicheBuffer MoqtFramer::SerializeObjectAck(
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc index 941c286..f4932a4 100644 --- a/quiche/quic/moqt/moqt_framer_test.cc +++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -43,15 +43,25 @@ std::vector<MoqtFramerTestParams> GetMoqtFramerTestParams() { std::vector<MoqtFramerTestParams> params; std::vector<MoqtMessageType> message_types = { - MoqtMessageType::kRequestOk, MoqtMessageType::kRequestError, - MoqtMessageType::kSubscribe, MoqtMessageType::kSubscribeOk, - MoqtMessageType::kPublishDone, MoqtMessageType::kPublishNamespace, - MoqtMessageType::kNamespace, MoqtMessageType::kNamespaceDone, - MoqtMessageType::kPublishSkipped, MoqtMessageType::kTrackStatus, - MoqtMessageType::kGoAway, MoqtMessageType::kSubscribeNamespace, - MoqtMessageType::kSubscribeTracks, MoqtMessageType::kFetch, - MoqtMessageType::kFetchOk, MoqtMessageType::kPublish, - MoqtMessageType::kObjectAck, MoqtMessageType::kSetup, + MoqtMessageType::kRequestOk, + MoqtMessageType::kRequestError, + MoqtMessageType::kSubscribe, + MoqtMessageType::kSubscribeOk, + MoqtMessageType::kRequestUpdate, + MoqtMessageType::kPublishDone, + MoqtMessageType::kPublishNamespace, + MoqtMessageType::kNamespace, + MoqtMessageType::kNamespaceDone, + MoqtMessageType::kPublishSkipped, + MoqtMessageType::kTrackStatus, + MoqtMessageType::kGoAway, + MoqtMessageType::kSubscribeNamespace, + MoqtMessageType::kSubscribeTracks, + MoqtMessageType::kFetch, + MoqtMessageType::kFetchOk, + MoqtMessageType::kPublish, + MoqtMessageType::kObjectAck, + MoqtMessageType::kSetup, }; for (const MoqtMessageType message_type : message_types) { if (message_type == MoqtMessageType::kSetup) { @@ -145,6 +155,10 @@ auto data = std::get<MoqtSubscribeOk>(structured_data); return framer_.SerializeSubscribeOk(data); } + case MoqtMessageType::kRequestUpdate: { + auto data = std::get<MoqtRequestUpdate>(structured_data); + return framer_.SerializeRequestUpdate(data); + } case MoqtMessageType::kPublishDone: { auto data = std::get<MoqtPublishDone>(structured_data); return framer_.SerializePublishDone(data); @@ -474,38 +488,35 @@ params.subgroup_delivery_timeout = quic::QuicTimeDelta::FromMicroseconds(500); params.expires = quic::QuicTimeDelta::Infinite(); - KeyValuePairList list = params.ToKeyValuePairList(); - std::optional<uint64_t> object_timeout; - std::optional<uint64_t> subgroup_timeout; - std::optional<uint64_t> expires; - list.ForEach( - [&](uint64_t key, std::variant<uint64_t, absl::string_view> value) { - if (key == - static_cast<uint64_t>(MessageParameter::kObjectDeliveryTimeout)) { - object_timeout = std::get<uint64_t>(value); - } else if (key == static_cast<uint64_t>( - MessageParameter::kSubgroupDeliveryTimeout)) { - subgroup_timeout = std::get<uint64_t>(value); - } else if (key == static_cast<uint64_t>(MessageParameter::kExpires)) { - expires = std::get<uint64_t>(value); - } - return true; - }); - EXPECT_EQ(object_timeout, 0ULL); - EXPECT_EQ(subgroup_timeout, 1ULL); - EXPECT_EQ(expires, 0ULL); + quiche::QuicheBuffer buffer = + framer_.SerializeRequestOk(MoqtRequestOk{params, TrackProperties()}); + const uint8_t expected_infinite_expires[] = { + 0x07, // MoqtMessageType::kRequestOk + 0x00, 0x07, // Payload length = 7 + 0x03, // 3 parameters + 0x02, 0x00, // kObjectDeliveryTimeout (0x02): Infinite -> 0 + 0x04, 0x01, // kSubgroupDeliveryTimeout (+0x04 = 0x06): 500us -> 1ms + 0x02, 0x00, // kExpires (+0x02 = 0x08): Infinite -> 0 + }; + quiche::test::CompareCharArraysWithHexError( + "frame encoding", buffer.data(), buffer.size(), + reinterpret_cast<const char*>(expected_infinite_expires), + sizeof(expected_infinite_expires)); params.expires = quic::QuicTimeDelta::FromMicroseconds(200); - list = params.ToKeyValuePairList(); - expires.reset(); - list.ForEach( - [&](uint64_t key, std::variant<uint64_t, absl::string_view> value) { - if (key == static_cast<uint64_t>(MessageParameter::kExpires)) { - expires = std::get<uint64_t>(value); - } - return true; - }); - EXPECT_EQ(expires, 1ULL); + buffer = framer_.SerializeRequestOk(MoqtRequestOk{params, TrackProperties()}); + const uint8_t expected_clamped_expires[] = { + 0x07, // MoqtMessageType::kRequestOk + 0x00, 0x07, // Payload length = 7 + 0x03, // 3 parameters + 0x02, 0x00, // kObjectDeliveryTimeout (0x02): Infinite -> 0 + 0x04, 0x01, // kSubgroupDeliveryTimeout (+0x04 = 0x06): 500us -> 1ms + 0x02, 0x01, // kExpires (+0x02 = 0x08): 200us -> 1ms + }; + quiche::test::CompareCharArraysWithHexError( + "frame encoding", buffer.data(), buffer.size(), + reinterpret_cast<const char*>(expected_clamped_expires), + sizeof(expected_clamped_expires)); } } // namespace moqt::test
diff --git a/quiche/quic/moqt/moqt_key_value_pair.h b/quiche/quic/moqt/moqt_key_value_pair.h index 10d7631..8a2eb5b 100644 --- a/quiche/quic/moqt/moqt_key_value_pair.h +++ b/quiche/quic/moqt/moqt_key_value_pair.h
@@ -25,7 +25,7 @@ namespace moqt { -// Encodes a list of key-value pairs common to both parameters and properties. +// Encodes a list of key-value pairs common to both Options and Properties. // If the key is odd, it is a length-prefixed string (which may encode further // item-specific structure). If the key is even, it is a varint. // This class does not interpret the semantic meaning of the keys and values. @@ -208,7 +208,6 @@ quic::QuicTimeDelta::Infinite(); constexpr quic::QuicTimeDelta kDefaultExpires = quic::QuicTimeDelta::Infinite(); constexpr bool kDefaultForward = true; -// TODO(martinduke): Refactor this to be more like TrackProperties. struct MessageParameters { MessageParameters() = default; MessageParameters(const MessageParameters&) = default; @@ -241,13 +240,6 @@ std::optional<quic::QuicTimeDelta> oack_window_size; bool operator==(const MessageParameters& other) const = default; - // Defined in moqt_framer.cc. - KeyValuePairList ToKeyValuePairList() const; - // Defined in moqt_parser.cc. - // If the class is not initialized with the default constructor, it is likely - // to return an error if a non-default field duplicates what is in |list|. - absl::Status FromKeyValuePairList(const KeyValuePairList& list); - private: // "if (forward)" is bug-prone because it returns forward_.has_value(). Make // it private and use public accessors instead.
diff --git a/quiche/quic/moqt/moqt_key_value_pair_test.cc b/quiche/quic/moqt/moqt_key_value_pair_test.cc index c04cb22..d13d4c6 100644 --- a/quiche/quic/moqt/moqt_key_value_pair_test.cc +++ b/quiche/quic/moqt/moqt_key_value_pair_test.cc
@@ -17,8 +17,6 @@ namespace moqt::test { -using ::quiche::test::StatusIs; - class LocationTest : public quic::test::QuicTest {}; TEST_F(LocationTest, LocationTests) { @@ -134,103 +132,6 @@ class MessageParametersTest : public quic::test::QuicTest {}; -TEST_F(MessageParametersTest, FromKeyValuePairList) { - KeyValuePairList list; - list.insert(static_cast<uint64_t>(MessageParameter::kObjectDeliveryTimeout), - 1ULL); - list.insert(static_cast<uint64_t>(MessageParameter::kForward), 0ULL); - list.insert(static_cast<uint64_t>(MessageParameter::kOackWindowSize), - 12345678ULL); - MessageParameters parameters; - QUICHE_EXPECT_OK(parameters.FromKeyValuePairList(list)); - EXPECT_EQ(parameters.object_delivery_timeout, - quic::QuicTimeDelta::FromMilliseconds(1)); - EXPECT_FALSE(parameters.forward()); - EXPECT_EQ(parameters.oack_window_size, - quic::QuicTimeDelta::FromMicroseconds(12345678)); -} - -TEST_F(MessageParametersTest, IllegalKeyValuePairs) { - KeyValuePairList list; - MessageParameters parameters; - list.insert(static_cast<uint64_t>(MessageParameter::kForward), 2ULL); - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); - list.clear(); - list.insert(static_cast<uint64_t>(MessageParameter::kSubscriberPriority), - 256ULL); - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); - list.clear(); - list.insert(static_cast<uint64_t>(MessageParameter::kGroupOrder), 0ULL); - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); - list.clear(); - list.insert(static_cast<uint64_t>(MessageParameter::kGroupOrder), 3ULL); - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Unknown MessageParameter. - list.clear(); - list.insert(0x12345678, 12345678ULL); - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST_F(MessageParametersTest, DuplicateParameters) { - for (MessageParameter param : - {MessageParameter::kObjectDeliveryTimeout, - MessageParameter::kSubgroupDeliveryTimeout, - // Auth token can be repeated. - MessageParameter::kExpires, MessageParameter::kLargestObject, - MessageParameter::kForward, MessageParameter::kSubscriberPriority, - MessageParameter::kSubscriptionFilter, MessageParameter::kGroupOrder, - MessageParameter::kNewGroupRequest, - MessageParameter::kOackWindowSize}) { - KeyValuePairList list; - MessageParameters parameters; - switch (param) { - case MessageParameter::kLargestObject: { - char largest_object[] = {0x00, 0x01}; - list.insert(static_cast<uint64_t>(param), - absl::string_view(largest_object, 2)); - largest_object[1] = 0x02; - list.insert(static_cast<uint64_t>(param), - absl::string_view(largest_object, 2)); - break; - } - case MessageParameter::kForward: { - list.insert(static_cast<uint64_t>(param), 0ULL); - list.insert(static_cast<uint64_t>(param), 1ULL); - break; - } - case MessageParameter::kSubscriberPriority: { - list.insert(static_cast<uint64_t>(param), 127ULL); - list.insert(static_cast<uint64_t>(param), 128ULL); - break; - } - case MessageParameter::kSubscriptionFilter: { - char filter[] = {0x01}; // kNextGroupStart - list.insert(static_cast<uint64_t>(param), absl::string_view(filter, 1)); - filter[0] = 0x02; // kLargestObject - list.insert(static_cast<uint64_t>(param), absl::string_view(filter, 1)); - break; - } - case MessageParameter::kGroupOrder: { - list.insert(static_cast<uint64_t>(param), 1ULL); - list.insert(static_cast<uint64_t>(param), 2ULL); - break; - } - default: { - list.insert(static_cast<uint64_t>(param), 1024ULL); - list.insert(static_cast<uint64_t>(param), 2048ULL); - break; - } - } - EXPECT_THAT(parameters.FromKeyValuePairList(list), - StatusIs(absl::StatusCode::kInvalidArgument)); - } -} - TEST_F(MessageParametersTest, Update) { MessageParameters p1; p1.object_delivery_timeout = quic::QuicTimeDelta::FromMilliseconds(10);
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc index 7a17db0..2f6ea27 100644 --- a/quiche/quic/moqt/moqt_parser.cc +++ b/quiche/quic/moqt/moqt_parser.cc
@@ -9,6 +9,7 @@ #include <cstddef> #include <cstdint> #include <cstring> +#include <limits> #include <optional> #include <string> #include <utility> @@ -112,74 +113,7 @@ return result; } -// Reads from |reader| to list. Returns false if there is a read error. -absl::Status ParseKeyValuePairList(quic::QuicDataReader& reader, - KeyValuePairList& list) { - list.clear(); - uint64_t num_params; - if (!reader.ReadMoqVarInt(&num_params)) { - return absl::InvalidArgumentError( - "Unable to parse key-value pair list element count"); - } - uint64_t type = 0; - for (uint64_t i = 0; i < num_params; ++i) { - uint64_t type_diff; - if (!reader.ReadMoqVarInt(&type_diff)) { - return absl::InvalidArgumentError( - "Unable to parse the key in a key-value pair"); - } - QUICHE_RETURN_IF_ERROR(SafeIncrementByWithStatus(type, type_diff)); - if (type % 2 == 1) { - absl::string_view bytes; - if (!reader.ReadStringPieceMoqVarInt(&bytes)) { - return absl::InvalidArgumentError( - "Unable to read the string value in a key-value pair"); - } - list.insert(type, bytes); - continue; - } - uint64_t value; - if (!reader.ReadMoqVarInt(&value)) { - return absl::InvalidArgumentError( - "Unable to read the integer value in a key-value pair"); - } - list.insert(type, value); - } - return absl::OkStatus(); -} - -absl::Status ParseKeyValuePairListWithNoPrefix(quic::QuicDataReader& reader, - KeyValuePairList& list) { - list.clear(); - uint64_t type = 0; - while (reader.BytesRemaining() > 0) { - uint64_t type_diff; - if (!reader.ReadMoqVarInt(&type_diff)) { - return absl::InvalidArgumentError( - "Unable to parse the key in a key-value pair"); - } - QUICHE_RETURN_IF_ERROR(SafeIncrementByWithStatus(type, type_diff)); - if (type % 2 == 1) { - absl::string_view bytes; - if (!reader.ReadStringPieceMoqVarInt(&bytes)) { - return absl::InvalidArgumentError( - "Unable to read the string value in a key-value pair"); - } - list.insert(type, bytes); - continue; - } - uint64_t value; - if (!reader.ReadMoqVarInt(&value)) { - return absl::InvalidArgumentError( - "Unable to read the integer value in a key-value pair"); - } - list.insert(type, value); - } - return absl::OkStatus(); -} - -bool ParseAuthTokenOption(absl::string_view field, - std::vector<AuthToken>& out) { +bool ParseAuthToken(absl::string_view field, std::vector<AuthToken>& out) { quic::QuicDataReader reader(field); AuthTokenAliasType alias_type; uint64_t alias; @@ -226,12 +160,6 @@ return true; } -bool ParseLocation(absl::string_view field, Location& out) { - quic::QuicDataReader reader(field); - return reader.ReadMoqVarInt(&out.group) && - reader.ReadMoqVarInt(&out.object) && reader.IsDoneReading(); -} - absl::Status ParseSubscriptionFilter(absl::string_view field, std::optional<SubscriptionFilter>& out) { quic::QuicDataReader reader(field); @@ -265,6 +193,194 @@ return absl::OkStatus(); } +absl::StatusOr<MessageParameters> ParseMessageParameters( + quic::QuicDataReader& reader) { + MessageParameters params; + uint64_t num_params; + if (!reader.ReadMoqVarInt(&num_params)) { + return absl::InvalidArgumentError( + "Unable to parse key-value pair list element count"); + } + uint64_t type = 0; + uint8_t value8; + uint64_t value64; + absl::string_view value_str; + for (uint64_t i = 0; i < num_params; ++i) { + uint64_t type_diff; + if (!reader.ReadMoqVarInt(&type_diff)) { + return absl::InvalidArgumentError("Unable to parse the parameter type"); + } + QUICHE_RETURN_IF_ERROR(SafeIncrementByWithStatus(type, type_diff)); + switch (static_cast<MessageParameter>(type)) { + case MessageParameter::kObjectDeliveryTimeout: + if (params.object_delivery_timeout.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&value64)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + if (value64 == 0) { + params.object_delivery_timeout = quic::QuicTimeDelta::Infinite(); + break; + } + params.object_delivery_timeout = + quic::QuicTimeDelta::TryFromMilliseconds(value64).value_or( + quic::QuicTimeDelta::Infinite()); + break; + case MessageParameter::kAuthorizationToken: + if (!reader.ReadStringPieceMoqVarInt(&value_str)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + if (!ParseAuthToken(value_str, params.authorization_tokens)) { + return absl::InvalidArgumentError("Malformed auth token parameter"); + } + break; + case MessageParameter::kSubgroupDeliveryTimeout: + if (params.subgroup_delivery_timeout.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&value64)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + if (value64 == 0) { + params.subgroup_delivery_timeout = quic::QuicTimeDelta::Infinite(); + break; + } + params.subgroup_delivery_timeout = + quic::QuicTimeDelta::TryFromMilliseconds(value64).value_or( + quic::QuicTimeDelta::Infinite()); + break; + case MessageParameter::kExpires: + if (params.expires.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&value64)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + params.expires = + quic::QuicTimeDelta::TryFromMilliseconds(value64).value_or( + quic::QuicTimeDelta::Infinite()); + if (params.expires->IsZero()) { + params.expires = quic::QuicTimeDelta::Infinite(); + } + break; + case MessageParameter::kLargestObject: { + uint64_t group, object; + if (params.largest_object.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&group) || !reader.ReadMoqVarInt(&object)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + params.largest_object = Location(group, object); + break; + } + case MessageParameter::kForward: + if (params.forward_has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadUInt8(&value8)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + if (value8 > 1) { + return absl::InvalidArgumentError("FORWARD must be 0 or 1"); + } + params.set_forward(value8 != 0); + break; + case MessageParameter::kSubscriberPriority: + if (params.subscriber_priority.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadUInt8(&value8)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + params.subscriber_priority = static_cast<MoqtPriority>(value8); + break; + case MessageParameter::kSubscriptionFilter: + if (params.subscription_filter.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadStringPieceMoqVarInt(&value_str)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + QUICHE_RETURN_IF_ERROR( + ParseSubscriptionFilter(value_str, params.subscription_filter)); + break; + case MessageParameter::kGroupOrder: + if (params.group_order.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadUInt8(&value8)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + if (value8 > kMaxMoqtDeliveryOrder || value8 < kMinMoqtDeliveryOrder) { + return absl::InvalidArgumentError( + "GROUP_ORDER is outside the valid range"); + } + params.group_order = static_cast<MoqtDeliveryOrder>(value8); + break; + case MessageParameter::kNewGroupRequest: + if (params.new_group_request.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&value64)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + params.new_group_request = value64; + break; + case MessageParameter::kOackWindowSize: + if (params.oack_window_size.has_value()) { + return absl::InvalidArgumentError("Duplicate Message Parameter"); + } + if (!reader.ReadMoqVarInt(&value64)) { + return absl::InvalidArgumentError("Parameter parsing error"); + } + // Since QuicTimeDelta stores an int64_t value in microseconds, it is + // possible for the value to be out of range. + params.oack_window_size = + value64 > std::numeric_limits<int64_t>::max() + ? quic::QuicTimeDelta::Infinite() + : quic::QuicTimeDelta::FromMicroseconds(value64); + break; + default: + // Unknown MessageParameters not allowed! + return absl::InvalidArgumentError( + absl::StrCat("Unknown message parameter 0x", absl::Hex(type))); + } + } + return params; +} + +absl::Status ParseKeyValuePairList(quic::QuicDataReader& reader, + KeyValuePairList& list) { + list.clear(); + uint64_t type = 0; + while (reader.BytesRemaining() > 0) { + uint64_t type_diff; + if (!reader.ReadMoqVarInt(&type_diff)) { + return absl::InvalidArgumentError( + "Unable to parse the key in a key-value pair"); + } + QUICHE_RETURN_IF_ERROR(SafeIncrementByWithStatus(type, type_diff)); + if (type % 2 == 1) { + absl::string_view bytes; + if (!reader.ReadStringPieceMoqVarInt(&bytes)) { + return absl::InvalidArgumentError( + "Unable to read the string value in a key-value pair"); + } + list.insert(type, bytes); + continue; + } + uint64_t value; + if (!reader.ReadMoqVarInt(&value)) { + return absl::InvalidArgumentError( + "Unable to read the integer value in a key-value pair"); + } + list.insert(type, value); + } + return absl::OkStatus(); +} + } // namespace absl::Status SetupOptions::FromKeyValuePairList(const KeyValuePairList& list) { @@ -296,8 +412,8 @@ path = std::get<absl::string_view>(value); break; case SetupOption::kAuthorizationToken: - if (!ParseAuthTokenOption(std::get<absl::string_view>(value), - authorization_tokens)) { + if (!ParseAuthToken(std::get<absl::string_view>(value), + authorization_tokens)) { status = KeyValueFormatError("Malformed auth token option"); return false; } @@ -345,154 +461,6 @@ return status; } -absl::Status MessageParameters::FromKeyValuePairList( - const KeyValuePairList& list) { - absl::Status status = absl::OkStatus(); - uint64_t last_key; - bool result = list.ForEach([&](uint64_t key, - std::variant<uint64_t, absl::string_view> - value) { - last_key = key; - switch (static_cast<MessageParameter>(key)) { - case MessageParameter::kObjectDeliveryTimeout: - if (object_delivery_timeout.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - if (std::get<uint64_t>(value) == 0) { - object_delivery_timeout = quic::QuicTimeDelta::Infinite(); - break; - } - object_delivery_timeout = - quic::QuicTimeDelta::TryFromMilliseconds(std::get<uint64_t>(value)) - .value_or(quic::QuicTimeDelta::Infinite()); - break; - case MessageParameter::kAuthorizationToken: - if (!ParseAuthTokenOption(std::get<absl::string_view>(value), - authorization_tokens)) { - status = KeyValueFormatError("Malformed auth token parameter"); - return false; - } - break; - case MessageParameter::kSubgroupDeliveryTimeout: - if (subgroup_delivery_timeout.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - if (std::get<uint64_t>(value) == 0) { - subgroup_delivery_timeout = quic::QuicTimeDelta::Infinite(); - break; - } - subgroup_delivery_timeout = - quic::QuicTimeDelta::TryFromMilliseconds(std::get<uint64_t>(value)) - .value_or(quic::QuicTimeDelta::Infinite()); - break; - case MessageParameter::kExpires: - if (expires.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - expires = - quic::QuicTimeDelta::TryFromMilliseconds(std::get<uint64_t>(value)) - .value_or(quic::QuicTimeDelta::Infinite()); - if (expires->IsZero()) { - expires = quic::QuicTimeDelta::Infinite(); - } - break; - case MessageParameter::kLargestObject: - if (largest_object.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - largest_object = Location(); - if (!ParseLocation(std::get<absl::string_view>(value), - *largest_object)) { - status = KeyValueFormatError( - "Failed to parse location of the largest object"); - return false; - } - break; - case MessageParameter::kForward: - if (forward_has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - if (std::get<uint64_t>(value) > 1) { - status = absl::InvalidArgumentError("FORWARD must be 0 or 1"); - return false; - } - set_forward(std::get<uint64_t>(value) != 0); - break; - case MessageParameter::kSubscriberPriority: - if (subscriber_priority.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - if (std::get<uint64_t>(value) > kMaxPriority) { - status = - absl::InvalidArgumentError("Subscriber priority exceeds maximum"); - return false; - } - subscriber_priority = - static_cast<MoqtPriority>(std::get<uint64_t>(value)); - break; - case MessageParameter::kSubscriptionFilter: - if (subscription_filter.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - // TODO(martinduke): Support multiple subscription filters. - return false; - } - status = ParseSubscriptionFilter(std::get<absl::string_view>(value), - subscription_filter); - if (!status.ok()) { - return false; - } - break; - case MessageParameter::kGroupOrder: - if (group_order.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - if (std::get<uint64_t>(value) > kMaxMoqtDeliveryOrder || - std::get<uint64_t>(value) < kMinMoqtDeliveryOrder) { - status = absl::InvalidArgumentError( - "GROUP_ORDER is outside the valid range"); - return false; - } - group_order = static_cast<MoqtDeliveryOrder>(std::get<uint64_t>(value)); - break; - case MessageParameter::kNewGroupRequest: - if (new_group_request.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - new_group_request = std::get<uint64_t>(value); - break; - case MessageParameter::kOackWindowSize: - if (oack_window_size.has_value()) { - status = absl::InvalidArgumentError("Duplicate Message Parameter"); - return false; - } - oack_window_size = - quic::QuicTimeDelta::FromMicroseconds(std::get<uint64_t>(value)); - break; - default: - // Unknown MessageParameters not allowed! - status = absl::InvalidArgumentError( - absl::StrCat("Unknown message parameter 0x", - absl::Hex(static_cast<uint64_t>(key)))); - return false; - } - return true; - }); - if (!result && status.ok()) { - return absl::InvalidArgumentError(absl::StrCat( - "Failed to parse the value for the message parameter key 0x", - absl::Hex(static_cast<uint64_t>(last_key)))); - } - return status; -} - MoqtStreamTypeParser::MoqtStreamTypeParser( MoqtStreamTypeParser&& other) noexcept : stream_(other.stream_), type_(other.type_), status_(other.status_) { @@ -666,8 +634,7 @@ return absl::InvalidArgumentError("Failed to read request ID"); } QUICHE_RETURN_IF_ERROR(ReadFullTrackName(reader, subscribe.full_track_name)); - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, subscribe.parameters)); + QUICHE_ASSIGN_OR_RETURN(subscribe.parameters, ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return subscribe; } @@ -679,11 +646,10 @@ if (!reader.ReadMoqVarInt(&subscribe_ok.track_alias)) { return absl::InvalidArgumentError("Failed to read the track alias"); } - KeyValuePairList pairs; - QUICHE_RETURN_IF_ERROR(ParseKeyValuePairList(reader, pairs)); - QUICHE_RETURN_IF_ERROR(subscribe_ok.parameters.FromKeyValuePairList(pairs)); + QUICHE_ASSIGN_OR_RETURN(subscribe_ok.parameters, + ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR( - ParseKeyValuePairListWithNoPrefix(reader, subscribe_ok.properties)); + ParseKeyValuePairList(reader, subscribe_ok.properties)); if (!subscribe_ok.properties.Validate()) { return absl::InvalidArgumentError("Invalid SUBSCRIBE_OK track properties"); } @@ -736,8 +702,8 @@ !reader.ReadMoqVarInt(&request_update.existing_request_id)) { return absl::InvalidArgumentError("Message missing request IDs"); } - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, request_update.parameters)); + QUICHE_ASSIGN_OR_RETURN(request_update.parameters, + ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return request_update; } @@ -752,8 +718,8 @@ } QUICHE_RETURN_IF_ERROR( ReadTrackNamespace(reader, publish_namespace.track_namespace)); - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, publish_namespace.parameters)); + QUICHE_ASSIGN_OR_RETURN(publish_namespace.parameters, + ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return publish_namespace; } @@ -791,10 +757,9 @@ absl::string_view data) const { quic::QuicDataReader reader(data); MoqtRequestOk request_ok; - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, request_ok.parameters)); - QUICHE_RETURN_IF_ERROR( - ParseKeyValuePairListWithNoPrefix(reader, request_ok.properties)); + QUICHE_ASSIGN_OR_RETURN(request_ok.parameters, + ParseMessageParameters(reader)); + QUICHE_RETURN_IF_ERROR(ParseKeyValuePairList(reader, request_ok.properties)); if (!request_ok.properties.Validate()) { return absl::InvalidArgumentError("Invalid REQUEST_OK track properties"); } @@ -849,8 +814,8 @@ } QUICHE_RETURN_IF_ERROR( ReadTrackNamespace(reader, subscribe_namespace.track_namespace_prefix)); - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, subscribe_namespace.parameters)); + QUICHE_ASSIGN_OR_RETURN(subscribe_namespace.parameters, + ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return subscribe_namespace; } @@ -864,8 +829,8 @@ } QUICHE_RETURN_IF_ERROR( ReadTrackNamespace(reader, subscribe_tracks.track_namespace_prefix)); - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, subscribe_tracks.parameters)); + QUICHE_ASSIGN_OR_RETURN(subscribe_tracks.parameters, + ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return subscribe_tracks; } @@ -929,8 +894,7 @@ default: return absl::InvalidArgumentError("Invalid FETCH type"); } - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, fetch.parameters)); + QUICHE_ASSIGN_OR_RETURN(fetch.parameters, ParseMessageParameters(reader)); QUICHE_RETURN_IF_ERROR(CheckForTrailingData(reader)); return fetch; } @@ -954,10 +918,8 @@ --fetch_ok.end_location.object; } fetch_ok.end_of_track = end_of_track == 1; - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, fetch_ok.parameters)); - QUICHE_RETURN_IF_ERROR( - ParseKeyValuePairListWithNoPrefix(reader, fetch_ok.properties)); + QUICHE_ASSIGN_OR_RETURN(fetch_ok.parameters, ParseMessageParameters(reader)); + QUICHE_RETURN_IF_ERROR(ParseKeyValuePairList(reader, fetch_ok.properties)); if (!fetch_ok.properties.Validate()) { return absl::InvalidArgumentError("Invalid FETCH_OK track properties"); } @@ -977,10 +939,8 @@ if (!reader.ReadMoqVarInt(&publish.track_alias)) { return absl::InvalidArgumentError("Track alias missing"); } - QUICHE_RETURN_IF_ERROR( - FillAndValidateMessageParameters(reader, publish.parameters)); - QUICHE_RETURN_IF_ERROR( - ParseKeyValuePairListWithNoPrefix(reader, publish.properties)); + QUICHE_ASSIGN_OR_RETURN(publish.parameters, ParseMessageParameters(reader)); + QUICHE_RETURN_IF_ERROR(ParseKeyValuePairList(reader, publish.properties)); if (!publish.properties.Validate()) { return absl::InvalidArgumentError("Invalid PUBLISH track properties"); } @@ -1058,15 +1018,6 @@ return absl::OkStatus(); } -absl::Status MoqtControlMessageParser::FillAndValidateMessageParameters( - quic::QuicDataReader& reader, MessageParameters& out) const { - KeyValuePairList pairs; - QUICHE_RETURN_IF_ERROR(ParseKeyValuePairList(reader, pairs)); - // All parameter types are allowed in all messages. - QUICHE_RETURN_IF_ERROR(out.FromKeyValuePairList(pairs)); - return absl::OkStatus(); -} - MoqtDataParser::MoqtDataParser(MoqtStreamTypeParser type_parser, MoqtDataParserVisitor* visitor) : stream_(*type_parser.stream()), visitor_(*visitor) {
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h index cda423a..2acb81b 100644 --- a/quiche/quic/moqt/moqt_parser.h +++ b/quiche/quic/moqt/moqt_parser.h
@@ -250,11 +250,6 @@ FullTrackName& full_track_name) const; absl::Status FillAndValidateSetupOptions(const KeyValuePairList& in, SetupOptions& out) const; - // |reader| points to the beginning of a KeyValuePairList. Returns false if - // there is any sort of error. (The function calls ParseError(), so the - // caller has no need to do so.) - absl::Status FillAndValidateMessageParameters(quic::QuicDataReader& reader, - MessageParameters& out) const; bool uses_web_transport_; const quic::Perspective perspective_;
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc index 78e7252..69a0210 100644 --- a/quiche/quic/moqt/moqt_parser_test.cc +++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -618,11 +618,9 @@ TEST_F(MoqtMessageSpecificTest, ClientSetupMaxAuthTokenCacheSizeAppearsTwice) { char setup[] = { - 0xaf, 0x00, 0x00, 0x0a, - 0x03, // 3 options - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x03, 0x32, // max_auth_token_cache_size = 50 - 0x00, 0x32, // max_auth_token_cache_size = 50 + 0xaf, 0x00, 0x00, 0x09, 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x03, 0x32, // max_auth_token_cache_size = 50 + 0x00, 0x32, // max_auth_token_cache_size = 50 }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages(absl::string_view(setup, sizeof(setup))); @@ -632,9 +630,8 @@ TEST_F(MoqtMessageSpecificTest, ServerSetupAuthorizationTokenTagRegister) { char setup[] = { - 0xaf, 0x00, 0x00, 0x09, - 0x01, // 1 option - 0x03, 0x06, 0x01, 0x10, 0x00, 0x62, 0x61, 0x72, // REGISTER 0x01 + 0xaf, 0x00, 0x00, 0x08, 0x03, 0x06, + 0x01, 0x10, 0x00, 0x62, 0x61, 0x72, // REGISTER 0x01 }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages(absl::string_view(setup, sizeof(setup)), @@ -645,9 +642,7 @@ TEST_F(MoqtMessageSpecificTest, SetupPathFromServer) { char setup[] = { - 0xaf, 0x00, 0x00, 0x06, - 0x01, // 1 option - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0xaf, 0x00, 0x00, 0x05, 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages(absl::string_view(setup, sizeof(setup)), @@ -661,9 +656,8 @@ TEST_F(MoqtMessageSpecificTest, SetupAuthorityFromServer) { char setup[] = { - 0xaf, 0x00, 0x00, 0x06, - 0x01, // 1 option - 0x05, 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" + 0xaf, 0x00, 0x00, 0x05, 0x05, + 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages(absl::string_view(setup, sizeof(setup)), @@ -674,10 +668,8 @@ TEST_F(MoqtMessageSpecificTest, SetupPathAppearsTwice) { char setup[] = { - 0xaf, 0x00, 0x00, 0x0b, - 0x02, // 2 options - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x00, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0xaf, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x00, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kRawQuic); @@ -687,9 +679,7 @@ TEST_F(MoqtMessageSpecificTest, SetupPathOverWebtrans) { char setup[] = { - 0xaf, 0x00, 0x00, 0x06, - 0x01, // 1 option - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0xaf, 0x00, 0x00, 0x05, 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kWebTrans); @@ -699,9 +689,8 @@ TEST_F(MoqtMessageSpecificTest, SetupAuthorityOverWebtrans) { char setup[] = { - 0xaf, 0x00, 0x00, 0x06, - 0x01, // 1 option - 0x05, 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" + 0xaf, 0x00, 0x00, 0x05, 0x05, + 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kWebTrans); @@ -711,8 +700,10 @@ TEST_F(MoqtMessageSpecificTest, SetupPathMissing) { char setup[] = { - 0xaf, 0x00, 0x00, 0x01, - 0x00, // no options + 0xaf, + 0x00, + 0x00, + 0x00, // Empty SETUP message. }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kRawQuic); @@ -722,9 +713,7 @@ TEST_F(MoqtMessageSpecificTest, ClientSetupMalformedPath) { char setup[] = { - 0xaf, 0x00, 0x00, 0x06, - 0x01, // 1 option - 0x01, 0x03, 0x66, 0x5c, 0x6f, // path = "f\o" + 0xaf, 0x00, 0x00, 0x05, 0x01, 0x03, 0x66, 0x5c, 0x6f, // path = "f\o" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kRawQuic); @@ -734,9 +723,7 @@ TEST_F(MoqtMessageSpecificTest, ClientSetupMalformedAuthority) { char setup[] = { - 0xaf, 0x00, 0x00, 0x0b, - 0x02, // 2 options - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0xaf, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" 0x04, 0x03, 0x66, 0x5c, 0x6f, // authority = "f\o" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( @@ -747,10 +734,8 @@ TEST_F(MoqtMessageSpecificTest, ServerSetupUnknownOptionIsOk) { char setup[] = { - 0xaf, 0x00, 0x00, 0x0b, - 0x02, // 2 options - 0x1f, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" - 0x00, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" + 0xaf, 0x00, 0x00, 0x0a, 0x1f, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" + 0x00, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" }; absl::StatusOr<std::vector<AnyMoqtControlMessage>> parsed = ParseAllMessages( absl::string_view(setup, sizeof(setup)), kDefaultMoqtVersion, kRawQuic, @@ -828,7 +813,7 @@ ParseAllMessages(absl::string_view(subscribe, sizeof(subscribe)), kDefaultMoqtVersion, kRawQuic); EXPECT_EQ(ExtractMoqtErrorForStatus(parsed.status()), - MoqtError::kKeyValueFormattingError); + MoqtError::kProtocolViolation); } TEST_F(MoqtMessageSpecificTest, @@ -844,7 +829,7 @@ ParseAllMessages(absl::string_view(subscribe, sizeof(subscribe)), kDefaultMoqtVersion, kRawQuic); EXPECT_EQ(ExtractMoqtErrorForStatus(parsed.status()), - MoqtError::kKeyValueFormattingError); + MoqtError::kProtocolViolation); } TEST_F(MoqtMessageSpecificTest, SubscribeInvalidForward) { @@ -1549,8 +1534,7 @@ TEST_F(MoqtMessageSpecificTest, ParseKeyValuePairListIntegerOverflow) { char setup[] = { - 0xaf, 0x00, 0x00, 0x0c, // kSetup, length = 12 - 0x02, // num_params + 0xaf, 0x00, 0x00, 0x0b, // kSetup, length = 11 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // type_diff = max 0x00, // string length = 0 0x01, // type_diff = 1 (overflows)
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h index 5325c92..5a60089 100644 --- a/quiche/quic/moqt/test_tools/moqt_test_message.h +++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -608,12 +608,11 @@ // Should not send PATH or AUTHORITY. client_setup_.options.path = std::nullopt; client_setup_.options.authority = std::nullopt; - raw_packet_[3] -= 17; // adjust payload length - raw_packet_[4] = 0x01; // only one option + raw_packet_[3] -= 17; // adjust payload length // Move MoqtImplementation up in the packet. - memmove(raw_packet_ + 5, raw_packet_ + 22, + memmove(raw_packet_ + 4, raw_packet_ + 21, kTestImplementationString.length() + 2); - raw_packet_[5] = 0x07; // Diff from 0. + raw_packet_[4] = 0x07; // Diff from 0. SetWireImage(raw_packet_, sizeof(raw_packet_) - 17); } else { SetWireImage(raw_packet_, sizeof(raw_packet_)); @@ -631,9 +630,9 @@ void ExpandVarints() override { if (client_setup_.options.path.has_value()) { - ExpandVarintsImpl("vvv----vv---------vv---------------------------"); + ExpandVarintsImpl("vv----vv---------vv---------------------------"); } else { - ExpandVarintsImpl("vvv---------------------------"); + ExpandVarintsImpl("vv---------------------------"); } } @@ -646,9 +645,8 @@ // string options in order. Unfortunately, this means that // kMoqtImplementation goes last even though it is always present, while // kPath and KAuthority aren't. - uint8_t raw_packet_[52] = { - 0xaf, 0x00, 0x00, 0x30, // type, length - 0x03, // 3 options + uint8_t raw_packet_[51] = { + 0xaf, 0x00, 0x00, 0x2f, // type, length 0x01, 0x04, 0x70, 0x61, 0x74, 0x68, // path = "path" 0x04, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, // authority = "authority" @@ -677,16 +675,15 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvv"); } + void ExpandVarints() override { ExpandVarintsImpl("vv"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(server_setup_); } private: - uint8_t raw_packet_[35] = {0xaf, 0x00, 0x00, 0x1f, // type, length - 0x01, // one option - // moqt_implementation: + uint8_t raw_packet_[34] = {0xaf, 0x00, 0x00, 0x1e, // type, length + // moqt_implementation: 0x07, 0x1c, 0x4d, 0x6f, 0x71, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, @@ -719,7 +716,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvv---v----vv--vv-----vvvvvv---vv"); + ExpandVarintsImpl("vvv---v----vv--vv-----v-v-vv---v-"); } MessageStructuredData structured_data() const override { @@ -773,14 +770,14 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvvvv--v--v--vv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvvvvvv--v--vv"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(subscribe_ok_); } void SetInvalidDeliveryOrder() { - raw_packet_[18] = 0x10; + raw_packet_[17] = 0x10; SetWireImage(raw_packet_, sizeof(raw_packet_)); } @@ -801,10 +798,10 @@ }; private: - uint8_t raw_packet_[19] = { - 0x04, 0x00, 0x10, 0x02, 0x02, // alias, 2 params + uint8_t raw_packet_[18] = { + 0x04, 0x00, 0x0f, 0x02, 0x02, // alias, 2 params 0x08, 0x03, // expires = 3 - 0x01, 0x02, 0x0c, 0x14, // largest_location = (12, 20) + 0x01, 0x0c, 0x14, // largest_location = (12, 20) // Properties 0x02, 0xa7, 0x10, // object_delivery_timeout = 10000 0x02, 0xa7, 0x10, // max_cache_duration = 10000 @@ -935,19 +932,19 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvv--vvv--vv----"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvv--v-v-vv----"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(request_update_); } private: - uint8_t raw_packet_[20] = { - 0x02, 0x00, 0x11, 0x02, 0x00, // request IDs 2 and 0 + uint8_t raw_packet_[19] = { + 0x02, 0x00, 0x10, 0x02, 0x00, // request IDs 2 and 0 0x04, // Four parameters 0x02, 0xa7, 0x10, // object_delivery_timeout = 10000 0x0e, 0x01, // forward = true - 0x10, 0x80, 0xaa, // subscriber_priority = 0xaa + 0x10, 0xaa, // subscriber_priority = 0xaa 0x01, 0x04, 0x04, 0x03, 0x01, 0x02, // Absolute Range: (3, 1) to 5 }; @@ -1121,17 +1118,17 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvv--v--v--vv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvvv--v--v-"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(request_ok_); } private: - uint8_t raw_packet_[16] = { - 0x07, 0x00, 0x0d, - 0x01, // 1 parameter - 0x09, 0x02, 0x05, 0x01, // Largest Object = (5, 1) + uint8_t raw_packet_[15] = { + 0x07, 0x00, 0x0c, + 0x01, // 1 parameter + 0x09, 0x05, 0x01, // Largest Object = (5, 1) // Properties 0x02, 0xa7, 0x10, // object_delivery_timeout = 10000 0x02, 0xa7, 0x10, // max_cache_duration = 10000 @@ -1245,7 +1242,7 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----vv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----v-"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(subscribe_namespace_); @@ -1294,7 +1291,7 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----vv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvv---vvv-----v-"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(subscribe_tracks_); @@ -1343,7 +1340,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvvv---v---vvvvvvv-----vvvv"); + ExpandVarintsImpl("vvvv---v---vvvvvvv-----v-v-"); } MessageStructuredData structured_data() const override { @@ -1421,7 +1418,7 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvvvvv-----vvvv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvvvvv-----v-v-"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(fetch_); @@ -1479,7 +1476,7 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvvvvv-----vvvv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvvvvv-----v-v-"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(fetch_); @@ -1597,7 +1594,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvv---v---vvvv-----vv--vvvv"); + ExpandVarintsImpl("vvv---v---vvvv-----vvvv-v-"); } MessageStructuredData structured_data() const override { @@ -1605,15 +1602,15 @@ } private: - uint8_t raw_packet_[30] = { - 0x1d, 0x00, 0x1b, + uint8_t raw_packet_[29] = { + 0x1d, 0x00, 0x1a, 0x01, // request_id = 1 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x03, 0x62, 0x61, 0x72, // track_name = "bar" 0x04, // track_alias = 4 0x03, // 3 parameters 0x03, 0x05, 0x03, 0x00, 0x62, 0x61, 0x7a, // token = "baz" - 0x06, 0x02, 0x0a, 0x01, // largest_object = 10, 1 + 0x06, 0x0a, 0x01, // largest_object = 10, 1 0x07, 0x01, // forward = 1 0x22, 0x02, // group_order = kAscending };