Simplify capsule.h to reduce the amount of code per individual capsule. This code switches an enum to absl::variant, and simplifies the parsing logic by using StatusOr instead of an error callback. PiperOrigin-RevId: 506389241
diff --git a/quiche/common/capsule.cc b/quiche/common/capsule.cc index bb1ab7e..22b8883 100644 --- a/quiche/common/capsule.cc +++ b/quiche/common/capsule.cc
@@ -12,6 +12,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" +#include "absl/types/variant.h" #include "quiche/common/platform/api/quiche_bug_tracker.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_buffer_allocator.h" @@ -48,208 +49,48 @@ return os; } -Capsule::Capsule(CapsuleType capsule_type) : capsule_type_(capsule_type) { - switch (capsule_type) { - case CapsuleType::DATAGRAM: - static_assert(std::is_standard_layout<DatagramCapsule>::value && - std::is_trivially_destructible<DatagramCapsule>::value, - "All inline capsule structs must have these properties"); - datagram_capsule_ = DatagramCapsule(); - break; - case CapsuleType::LEGACY_DATAGRAM: - static_assert( - std::is_standard_layout<LegacyDatagramCapsule>::value && - std::is_trivially_destructible<LegacyDatagramCapsule>::value, - "All inline capsule structs must have these properties"); - legacy_datagram_capsule_ = LegacyDatagramCapsule(); - break; - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - static_assert( - std::is_standard_layout<LegacyDatagramWithoutContextCapsule>::value && - std::is_trivially_destructible< - LegacyDatagramWithoutContextCapsule>::value, - "All inline capsule structs must have these properties"); - legacy_datagram_without_context_capsule_ = - LegacyDatagramWithoutContextCapsule(); - break; - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - static_assert( - std::is_standard_layout<CloseWebTransportSessionCapsule>::value && - std::is_trivially_destructible< - CloseWebTransportSessionCapsule>::value, - "All inline capsule structs must have these properties"); - close_web_transport_session_capsule_ = CloseWebTransportSessionCapsule(); - break; - case CapsuleType::ADDRESS_REQUEST: - address_request_capsule_ = new AddressRequestCapsule(); - break; - case CapsuleType::ADDRESS_ASSIGN: - address_assign_capsule_ = new AddressAssignCapsule(); - break; - case CapsuleType::ROUTE_ADVERTISEMENT: - route_advertisement_capsule_ = new RouteAdvertisementCapsule(); - break; - default: - unknown_capsule_data_ = absl::string_view(); - break; - } -} - -void Capsule::Free() { - switch (capsule_type_) { - // Inlined capsule types. - case CapsuleType::DATAGRAM: - case CapsuleType::LEGACY_DATAGRAM: - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - // Do nothing, these are guaranteed to be trivially destructible. - break; - // Out-of-line capsule types. - case CapsuleType::ADDRESS_REQUEST: - delete address_request_capsule_; - break; - case CapsuleType::ADDRESS_ASSIGN: - delete address_assign_capsule_; - break; - case CapsuleType::ROUTE_ADVERTISEMENT: - delete route_advertisement_capsule_; - break; - } - capsule_type_ = static_cast<CapsuleType>(0x17); // Reserved unknown value. - unknown_capsule_data_ = absl::string_view(); -} -Capsule::~Capsule() { Free(); } - // static Capsule Capsule::Datagram(absl::string_view http_datagram_payload) { - Capsule capsule(CapsuleType::DATAGRAM); - capsule.datagram_capsule().http_datagram_payload = http_datagram_payload; - return capsule; + return Capsule(DatagramCapsule{http_datagram_payload}); } // static Capsule Capsule::LegacyDatagram(absl::string_view http_datagram_payload) { - Capsule capsule(CapsuleType::LEGACY_DATAGRAM); - capsule.legacy_datagram_capsule().http_datagram_payload = - http_datagram_payload; - return capsule; + return Capsule(LegacyDatagramCapsule{http_datagram_payload}); } // static Capsule Capsule::LegacyDatagramWithoutContext( absl::string_view http_datagram_payload) { - Capsule capsule(CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT); - capsule.legacy_datagram_without_context_capsule().http_datagram_payload = - http_datagram_payload; - return capsule; + return Capsule(LegacyDatagramWithoutContextCapsule{http_datagram_payload}); } // static Capsule Capsule::CloseWebTransportSession( webtransport::SessionErrorCode error_code, absl::string_view error_message) { - Capsule capsule(CapsuleType::CLOSE_WEBTRANSPORT_SESSION); - capsule.close_web_transport_session_capsule().error_code = error_code; - capsule.close_web_transport_session_capsule().error_message = error_message; - return capsule; + return Capsule(CloseWebTransportSessionCapsule({error_code, error_message})); } // static -Capsule Capsule::AddressRequest() { - return Capsule(CapsuleType::ADDRESS_REQUEST); -} +Capsule Capsule::AddressRequest() { return Capsule(AddressRequestCapsule()); } // static -Capsule Capsule::AddressAssign() { - return Capsule(CapsuleType::ADDRESS_ASSIGN); -} +Capsule Capsule::AddressAssign() { return Capsule(AddressAssignCapsule()); } // static Capsule Capsule::RouteAdvertisement() { - return Capsule(CapsuleType::ROUTE_ADVERTISEMENT); + return Capsule(RouteAdvertisementCapsule()); } // static Capsule Capsule::Unknown(uint64_t capsule_type, absl::string_view unknown_capsule_data) { - Capsule capsule(static_cast<CapsuleType>(capsule_type)); - capsule.unknown_capsule_data() = unknown_capsule_data; - return capsule; -} - -Capsule& Capsule::operator=(const Capsule& other) { - Free(); - capsule_type_ = other.capsule_type_; - switch (capsule_type_) { - case CapsuleType::DATAGRAM: - datagram_capsule_ = other.datagram_capsule_; - break; - case CapsuleType::LEGACY_DATAGRAM: - legacy_datagram_capsule_ = other.legacy_datagram_capsule_; - break; - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - legacy_datagram_without_context_capsule_ = - other.legacy_datagram_without_context_capsule_; - break; - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - close_web_transport_session_capsule_ = - other.close_web_transport_session_capsule_; - break; - case CapsuleType::ADDRESS_ASSIGN: - address_assign_capsule_ = new AddressAssignCapsule(); - *address_assign_capsule_ = *other.address_assign_capsule_; - break; - case CapsuleType::ADDRESS_REQUEST: - address_request_capsule_ = new AddressRequestCapsule(); - *address_request_capsule_ = *other.address_request_capsule_; - break; - case CapsuleType::ROUTE_ADVERTISEMENT: - route_advertisement_capsule_ = new RouteAdvertisementCapsule(); - *route_advertisement_capsule_ = *other.route_advertisement_capsule_; - break; - default: - unknown_capsule_data_ = other.unknown_capsule_data_; - break; - } - return *this; -} - -Capsule::Capsule(const Capsule& other) : Capsule(other.capsule_type_) { - *this = other; + return Capsule(UnknownCapsule{capsule_type, unknown_capsule_data}); } bool Capsule::operator==(const Capsule& other) const { - if (capsule_type_ != other.capsule_type_) { - return false; - } - switch (capsule_type_) { - case CapsuleType::DATAGRAM: - return datagram_capsule_.http_datagram_payload == - other.datagram_capsule_.http_datagram_payload; - case CapsuleType::LEGACY_DATAGRAM: - return legacy_datagram_capsule_.http_datagram_payload == - other.legacy_datagram_capsule_.http_datagram_payload; - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - return legacy_datagram_without_context_capsule_.http_datagram_payload == - other.legacy_datagram_without_context_capsule_ - .http_datagram_payload; - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - return close_web_transport_session_capsule_.error_code == - other.close_web_transport_session_capsule_.error_code && - close_web_transport_session_capsule_.error_message == - other.close_web_transport_session_capsule_.error_message; - case CapsuleType::ADDRESS_REQUEST: - return address_request_capsule_->requested_addresses == - other.address_request_capsule_->requested_addresses; - case CapsuleType::ADDRESS_ASSIGN: - return address_assign_capsule_->assigned_addresses == - other.address_assign_capsule_->assigned_addresses; - case CapsuleType::ROUTE_ADVERTISEMENT: - return route_advertisement_capsule_->ip_address_ranges == - other.route_advertisement_capsule_->ip_address_ranges; - default: - return unknown_capsule_data_ == other.unknown_capsule_data_; - } + return capsule_ == other.capsule_; } std::string DatagramCapsule::ToString() const { @@ -303,26 +144,14 @@ return rv; } +std::string UnknownCapsule::ToString() const { + return absl::StrCat("Unknown(", type, ") [", absl::BytesToHexString(payload), + "]"); +} + std::string Capsule::ToString() const { - switch (capsule_type_) { - case CapsuleType::DATAGRAM: - return datagram_capsule_.ToString(); - case CapsuleType::LEGACY_DATAGRAM: - return legacy_datagram_capsule_.ToString(); - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - return legacy_datagram_without_context_capsule_.ToString(); - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - return close_web_transport_session_capsule_.ToString(); - case CapsuleType::ADDRESS_REQUEST: - return address_request_capsule_->ToString(); - case CapsuleType::ADDRESS_ASSIGN: - return address_assign_capsule_->ToString(); - case CapsuleType::ROUTE_ADVERTISEMENT: - return route_advertisement_capsule_->ToString(); - default: - return absl::StrCat(CapsuleTypeToString(capsule_type_), "[", - absl::BytesToHexString(unknown_capsule_data_), "]"); - } + return absl::visit([](const auto& capsule) { return capsule.ToString(); }, + capsule_); } std::ostream& operator<<(std::ostream& os, const Capsule& capsule) { @@ -438,8 +267,9 @@ WireSpan<WireIpAddressRange>(absl::MakeConstSpan( capsule.route_advertisement_capsule().ip_address_ranges))); default: - return SerializeCapsuleFields(capsule.capsule_type(), allocator, - WireBytes(capsule.unknown_capsule_data())); + return SerializeCapsuleFields( + capsule.capsule_type(), allocator, + WireBytes(capsule.unknown_capsule().payload)); } } @@ -462,16 +292,16 @@ } absl::StrAppend(&buffered_data_, capsule_fragment); while (true) { - const size_t buffered_data_read = AttemptParseCapsule(); - if (parsing_error_occurred_) { - QUICHE_DCHECK_EQ(buffered_data_read, 0u); + const absl::StatusOr<size_t> buffered_data_read = AttemptParseCapsule(); + if (!buffered_data_read.ok()) { + ReportParseFailure(buffered_data_read.status().message()); buffered_data_.clear(); return false; } - if (buffered_data_read == 0) { + if (*buffered_data_read == 0) { break; } - buffered_data_.erase(0, buffered_data_read); + buffered_data_.erase(0, *buffered_data_read); } static constexpr size_t kMaxCapsuleBufferSize = 1024 * 1024; if (buffered_data_.size() > kMaxCapsuleBufferSize) { @@ -482,7 +312,166 @@ return true; } -size_t CapsuleParser::AttemptParseCapsule() { +static absl::StatusOr<Capsule> ParseCapsulePayload(QuicheDataReader& reader, + CapsuleType type) { + switch (type) { + case CapsuleType::DATAGRAM: + return Capsule::Datagram(reader.ReadRemainingPayload()); + case CapsuleType::LEGACY_DATAGRAM: + return Capsule::LegacyDatagram(reader.ReadRemainingPayload()); + case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: + return Capsule::LegacyDatagramWithoutContext( + reader.ReadRemainingPayload()); + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: { + CloseWebTransportSessionCapsule capsule; + if (!reader.ReadUInt32(&capsule.error_code)) { + return absl::InvalidArgumentError( + "Unable to parse capsule CLOSE_WEBTRANSPORT_SESSION error code"); + } + capsule.error_message = reader.ReadRemainingPayload(); + return Capsule(std::move(capsule)); + } + case CapsuleType::ADDRESS_REQUEST: { + AddressRequestCapsule capsule; + while (!reader.IsDoneReading()) { + PrefixWithId requested_address; + if (!reader.ReadVarInt62(&requested_address.request_id)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_REQUEST request ID"); + } + uint8_t address_family; + if (!reader.ReadUInt8(&address_family)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_REQUEST family"); + } + if (address_family != 4 && address_family != 6) { + return absl::InvalidArgumentError("Bad ADDRESS_REQUEST family"); + } + absl::string_view ip_address_bytes; + if (!reader.ReadStringPiece(&ip_address_bytes, + address_family == 4 + ? QuicheIpAddress::kIPv4AddressSize + : QuicheIpAddress::kIPv6AddressSize)) { + return absl::InvalidArgumentError( + "Unable to read capsule ADDRESS_REQUEST address"); + } + quiche::QuicheIpAddress ip_address; + if (!ip_address.FromPackedString(ip_address_bytes.data(), + ip_address_bytes.size())) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_REQUEST address"); + } + uint8_t ip_prefix_length; + if (!reader.ReadUInt8(&ip_prefix_length)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_REQUEST IP prefix length"); + } + if (ip_prefix_length > QuicheIpPrefix(ip_address).prefix_length()) { + return absl::InvalidArgumentError("Invalid IP prefix length"); + } + requested_address.ip_prefix = + QuicheIpPrefix(ip_address, ip_prefix_length); + capsule.requested_addresses.push_back(requested_address); + } + return Capsule(std::move(capsule)); + } + case CapsuleType::ADDRESS_ASSIGN: { + AddressAssignCapsule capsule; + while (!reader.IsDoneReading()) { + PrefixWithId assigned_address; + if (!reader.ReadVarInt62(&assigned_address.request_id)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_ASSIGN request ID"); + } + uint8_t address_family; + if (!reader.ReadUInt8(&address_family)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_ASSIGN family"); + } + if (address_family != 4 && address_family != 6) { + return absl::InvalidArgumentError("Bad ADDRESS_ASSIGN family"); + } + absl::string_view ip_address_bytes; + if (!reader.ReadStringPiece(&ip_address_bytes, + address_family == 4 + ? QuicheIpAddress::kIPv4AddressSize + : QuicheIpAddress::kIPv6AddressSize)) { + return absl::InvalidArgumentError( + "Unable to read capsule ADDRESS_ASSIGN address"); + } + quiche::QuicheIpAddress ip_address; + if (!ip_address.FromPackedString(ip_address_bytes.data(), + ip_address_bytes.size())) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_ASSIGN address"); + } + uint8_t ip_prefix_length; + if (!reader.ReadUInt8(&ip_prefix_length)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ADDRESS_ASSIGN IP prefix length"); + } + if (ip_prefix_length > QuicheIpPrefix(ip_address).prefix_length()) { + return absl::InvalidArgumentError("Invalid IP prefix length"); + } + assigned_address.ip_prefix = + QuicheIpPrefix(ip_address, ip_prefix_length); + capsule.assigned_addresses.push_back(assigned_address); + } + return Capsule(std::move(capsule)); + } + case CapsuleType::ROUTE_ADVERTISEMENT: { + RouteAdvertisementCapsule capsule; + while (!reader.IsDoneReading()) { + uint8_t address_family; + if (!reader.ReadUInt8(&address_family)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ROUTE_ADVERTISEMENT family"); + } + if (address_family != 4 && address_family != 6) { + return absl::InvalidArgumentError("Bad ROUTE_ADVERTISEMENT family"); + } + IpAddressRange ip_address_range; + absl::string_view start_ip_address_bytes; + if (!reader.ReadStringPiece(&start_ip_address_bytes, + address_family == 4 + ? QuicheIpAddress::kIPv4AddressSize + : QuicheIpAddress::kIPv6AddressSize)) { + return absl::InvalidArgumentError( + "Unable to read capsule ROUTE_ADVERTISEMENT start address"); + } + if (!ip_address_range.start_ip_address.FromPackedString( + start_ip_address_bytes.data(), start_ip_address_bytes.size())) { + return absl::InvalidArgumentError( + "Unable to parse capsule ROUTE_ADVERTISEMENT start address"); + } + absl::string_view end_ip_address_bytes; + if (!reader.ReadStringPiece(&end_ip_address_bytes, + address_family == 4 + ? QuicheIpAddress::kIPv4AddressSize + : QuicheIpAddress::kIPv6AddressSize)) { + return absl::InvalidArgumentError( + "Unable to read capsule ROUTE_ADVERTISEMENT end address"); + } + if (!ip_address_range.end_ip_address.FromPackedString( + end_ip_address_bytes.data(), end_ip_address_bytes.size())) { + return absl::InvalidArgumentError( + "Unable to parse capsule ROUTE_ADVERTISEMENT end address"); + } + if (!reader.ReadUInt8(&ip_address_range.ip_protocol)) { + return absl::InvalidArgumentError( + "Unable to parse capsule ROUTE_ADVERTISEMENT IP protocol"); + } + capsule.ip_address_ranges.push_back(ip_address_range); + } + return Capsule(std::move(capsule)); + } + default: + return Capsule(UnknownCapsule{static_cast<uint64_t>(type), + reader.ReadRemainingPayload()}); + } +} + +absl::StatusOr<size_t> CapsuleParser::AttemptParseCapsule() { QUICHE_DCHECK(!parsing_error_occurred_); if (buffered_data_.empty()) { return 0; @@ -501,190 +490,16 @@ return 0; } QuicheDataReader capsule_data_reader(capsule_data); - Capsule capsule(static_cast<CapsuleType>(capsule_type64)); - switch (capsule.capsule_type()) { - case CapsuleType::DATAGRAM: - capsule.datagram_capsule().http_datagram_payload = - capsule_data_reader.ReadRemainingPayload(); - break; - case CapsuleType::LEGACY_DATAGRAM: - capsule.legacy_datagram_capsule().http_datagram_payload = - capsule_data_reader.ReadRemainingPayload(); - break; - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: - capsule.legacy_datagram_without_context_capsule().http_datagram_payload = - capsule_data_reader.ReadRemainingPayload(); - break; - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: - if (!capsule_data_reader.ReadUInt32( - &capsule.close_web_transport_session_capsule().error_code)) { - ReportParseFailure( - "Unable to parse capsule CLOSE_WEBTRANSPORT_SESSION error code"); - return 0; - } - capsule.close_web_transport_session_capsule().error_message = - capsule_data_reader.ReadRemainingPayload(); - break; - case CapsuleType::ADDRESS_REQUEST: { - while (!capsule_data_reader.IsDoneReading()) { - PrefixWithId requested_address; - if (!capsule_data_reader.ReadVarInt62(&requested_address.request_id)) { - ReportParseFailure( - "Unable to parse capsule ADDRESS_REQUEST request ID"); - return 0; - } - uint8_t address_family; - if (!capsule_data_reader.ReadUInt8(&address_family)) { - ReportParseFailure("Unable to parse capsule ADDRESS_REQUEST family"); - return 0; - } - if (address_family != 4 && address_family != 6) { - ReportParseFailure("Bad ADDRESS_REQUEST family"); - return 0; - } - absl::string_view ip_address_bytes; - if (!capsule_data_reader.ReadStringPiece( - &ip_address_bytes, address_family == 4 - ? QuicheIpAddress::kIPv4AddressSize - : QuicheIpAddress::kIPv6AddressSize)) { - ReportParseFailure("Unable to read capsule ADDRESS_REQUEST address"); - return 0; - } - quiche::QuicheIpAddress ip_address; - if (!ip_address.FromPackedString(ip_address_bytes.data(), - ip_address_bytes.size())) { - ReportParseFailure("Unable to parse capsule ADDRESS_REQUEST address"); - return 0; - } - uint8_t ip_prefix_length; - if (!capsule_data_reader.ReadUInt8(&ip_prefix_length)) { - ReportParseFailure( - "Unable to parse capsule ADDRESS_REQUEST IP prefix length"); - return 0; - } - if (ip_prefix_length > - quiche::QuicheIpPrefix(ip_address).prefix_length()) { - ReportParseFailure("Invalid IP prefix length"); - return 0; - } - requested_address.ip_prefix = - quiche::QuicheIpPrefix(ip_address, ip_prefix_length); - capsule.address_request_capsule().requested_addresses.push_back( - requested_address); - } - } break; - case CapsuleType::ADDRESS_ASSIGN: { - while (!capsule_data_reader.IsDoneReading()) { - PrefixWithId assigned_address; - if (!capsule_data_reader.ReadVarInt62(&assigned_address.request_id)) { - ReportParseFailure( - "Unable to parse capsule ADDRESS_ASSIGN request ID"); - return 0; - } - uint8_t address_family; - if (!capsule_data_reader.ReadUInt8(&address_family)) { - ReportParseFailure("Unable to parse capsule ADDRESS_ASSIGN family"); - return 0; - } - if (address_family != 4 && address_family != 6) { - ReportParseFailure("Bad ADDRESS_ASSIGN family"); - return 0; - } - absl::string_view ip_address_bytes; - if (!capsule_data_reader.ReadStringPiece( - &ip_address_bytes, address_family == 4 - ? QuicheIpAddress::kIPv4AddressSize - : QuicheIpAddress::kIPv6AddressSize)) { - ReportParseFailure("Unable to read capsule ADDRESS_ASSIGN address"); - return 0; - } - quiche::QuicheIpAddress ip_address; - if (!ip_address.FromPackedString(ip_address_bytes.data(), - ip_address_bytes.size())) { - ReportParseFailure("Unable to parse capsule ADDRESS_ASSIGN address"); - return 0; - } - uint8_t ip_prefix_length; - if (!capsule_data_reader.ReadUInt8(&ip_prefix_length)) { - ReportParseFailure( - "Unable to parse capsule ADDRESS_ASSIGN IP prefix length"); - return 0; - } - if (ip_prefix_length > - quiche::QuicheIpPrefix(ip_address).prefix_length()) { - ReportParseFailure("Invalid IP prefix length"); - return 0; - } - assigned_address.ip_prefix = - quiche::QuicheIpPrefix(ip_address, ip_prefix_length); - capsule.address_assign_capsule().assigned_addresses.push_back( - assigned_address); - } - } break; - case CapsuleType::ROUTE_ADVERTISEMENT: { - while (!capsule_data_reader.IsDoneReading()) { - uint8_t address_family; - if (!capsule_data_reader.ReadUInt8(&address_family)) { - ReportParseFailure( - "Unable to parse capsule ROUTE_ADVERTISEMENT family"); - return 0; - } - if (address_family != 4 && address_family != 6) { - ReportParseFailure("Bad ROUTE_ADVERTISEMENT family"); - return 0; - } - IpAddressRange ip_address_range; - absl::string_view start_ip_address_bytes; - if (!capsule_data_reader.ReadStringPiece( - &start_ip_address_bytes, - address_family == 4 ? QuicheIpAddress::kIPv4AddressSize - : QuicheIpAddress::kIPv6AddressSize)) { - ReportParseFailure( - "Unable to read capsule ROUTE_ADVERTISEMENT start address"); - return 0; - } - if (!ip_address_range.start_ip_address.FromPackedString( - start_ip_address_bytes.data(), start_ip_address_bytes.size())) { - ReportParseFailure( - "Unable to parse capsule ROUTE_ADVERTISEMENT start address"); - return 0; - } - absl::string_view end_ip_address_bytes; - if (!capsule_data_reader.ReadStringPiece( - &end_ip_address_bytes, - address_family == 4 ? QuicheIpAddress::kIPv4AddressSize - : QuicheIpAddress::kIPv6AddressSize)) { - ReportParseFailure( - "Unable to read capsule ROUTE_ADVERTISEMENT end address"); - return 0; - } - if (!ip_address_range.end_ip_address.FromPackedString( - end_ip_address_bytes.data(), end_ip_address_bytes.size())) { - ReportParseFailure( - "Unable to parse capsule ROUTE_ADVERTISEMENT end address"); - return 0; - } - if (!capsule_data_reader.ReadUInt8(&ip_address_range.ip_protocol)) { - ReportParseFailure( - "Unable to parse capsule ROUTE_ADVERTISEMENT IP protocol"); - return 0; - } - capsule.route_advertisement_capsule().ip_address_ranges.push_back( - ip_address_range); - } - } break; - default: - capsule.unknown_capsule_data() = - capsule_data_reader.ReadRemainingPayload(); - } - if (!visitor_->OnCapsule(capsule)) { - ReportParseFailure("Visitor failed to process capsule"); - return 0; + absl::StatusOr<Capsule> capsule = ParseCapsulePayload( + capsule_data_reader, static_cast<CapsuleType>(capsule_type64)); + QUICHE_RETURN_IF_ERROR(capsule.status()); + if (!visitor_->OnCapsule(*capsule)) { + return absl::AbortedError("Visitor failed to process capsule"); } return capsule_fragment_reader.PreviouslyReadPayload().length(); } -void CapsuleParser::ReportParseFailure(const std::string& error_message) { +void CapsuleParser::ReportParseFailure(absl::string_view error_message) { if (parsing_error_occurred_) { QUICHE_BUG(multiple parse errors) << "Experienced multiple parse failures"; return;
diff --git a/quiche/common/capsule.h b/quiche/common/capsule.h index 85292c4..52c7ef5 100644 --- a/quiche/common/capsule.h +++ b/quiche/common/capsule.h
@@ -7,10 +7,13 @@ #include <cstdint> #include <string> +#include <variant> #include <vector> +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/variant.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_buffer_allocator.h" #include "quiche/common/quiche_ip_address.h" @@ -37,21 +40,50 @@ struct QUICHE_EXPORT DatagramCapsule { absl::string_view http_datagram_payload; + std::string ToString() const; + CapsuleType capsule_type() const { return CapsuleType::DATAGRAM; } + bool operator==(const DatagramCapsule& other) const { + return http_datagram_payload == other.http_datagram_payload; + } }; + struct QUICHE_EXPORT LegacyDatagramCapsule { absl::string_view http_datagram_payload; + std::string ToString() const; + CapsuleType capsule_type() const { return CapsuleType::LEGACY_DATAGRAM; } + bool operator==(const LegacyDatagramCapsule& other) const { + return http_datagram_payload == other.http_datagram_payload; + } }; + struct QUICHE_EXPORT LegacyDatagramWithoutContextCapsule { absl::string_view http_datagram_payload; + std::string ToString() const; + CapsuleType capsule_type() const { + return CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT; + } + bool operator==(const LegacyDatagramWithoutContextCapsule& other) const { + return http_datagram_payload == other.http_datagram_payload; + } }; + struct QUICHE_EXPORT CloseWebTransportSessionCapsule { webtransport::SessionErrorCode error_code; absl::string_view error_message; + std::string ToString() const; + CapsuleType capsule_type() const { + return CapsuleType::CLOSE_WEBTRANSPORT_SESSION; + } + bool operator==(const CloseWebTransportSessionCapsule& other) const { + return error_code == other.error_code && + error_message == other.error_message; + } }; + struct QUICHE_EXPORT PrefixWithId { uint64_t request_id; quiche::QuicheIpPrefix ip_prefix; @@ -63,20 +95,34 @@ uint8_t ip_protocol; bool operator==(const IpAddressRange& other) const; }; + struct QUICHE_EXPORT AddressAssignCapsule { std::vector<PrefixWithId> assigned_addresses; bool operator==(const AddressAssignCapsule& other) const; std::string ToString() const; + CapsuleType capsule_type() const { return CapsuleType::ADDRESS_ASSIGN; } }; struct QUICHE_EXPORT AddressRequestCapsule { std::vector<PrefixWithId> requested_addresses; bool operator==(const AddressRequestCapsule& other) const; std::string ToString() const; + CapsuleType capsule_type() const { return CapsuleType::ADDRESS_REQUEST; } }; struct QUICHE_EXPORT RouteAdvertisementCapsule { std::vector<IpAddressRange> ip_address_ranges; bool operator==(const RouteAdvertisementCapsule& other) const; std::string ToString() const; + CapsuleType capsule_type() const { return CapsuleType::ROUTE_ADVERTISEMENT; } +}; +struct QUICHE_EXPORT UnknownCapsule { + uint64_t type; + absl::string_view payload; + + std::string ToString() const; + CapsuleType capsule_type() const { return static_cast<CapsuleType>(type); } + bool operator==(const UnknownCapsule& other) const { + return type == other.type && payload == other.payload; + } }; // Capsule from RFC 9297. @@ -102,10 +148,8 @@ uint64_t capsule_type, absl::string_view unknown_capsule_data = absl::string_view()); - explicit Capsule(CapsuleType capsule_type); - ~Capsule(); - Capsule(const Capsule& other); - Capsule& operator=(const Capsule& other); + template <typename CapsuleStruct> + explicit Capsule(CapsuleStruct capsule) : capsule_(std::move(capsule)) {} bool operator==(const Capsule& other) const; // Human-readable information string for debugging purposes. @@ -113,107 +157,68 @@ friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os, const Capsule& capsule); - CapsuleType capsule_type() const { return capsule_type_; } + CapsuleType capsule_type() const { + return absl::visit( + [](const auto& capsule) { return capsule.capsule_type(); }, capsule_); + } DatagramCapsule& datagram_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM); - return datagram_capsule_; + return absl::get<DatagramCapsule>(capsule_); } const DatagramCapsule& datagram_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM); - return datagram_capsule_; + return absl::get<DatagramCapsule>(capsule_); } LegacyDatagramCapsule& legacy_datagram_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::LEGACY_DATAGRAM); - return legacy_datagram_capsule_; + return absl::get<LegacyDatagramCapsule>(capsule_); } const LegacyDatagramCapsule& legacy_datagram_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::LEGACY_DATAGRAM); - return legacy_datagram_capsule_; + return absl::get<LegacyDatagramCapsule>(capsule_); } LegacyDatagramWithoutContextCapsule& legacy_datagram_without_context_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, - CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT); - return legacy_datagram_without_context_capsule_; + return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_); } const LegacyDatagramWithoutContextCapsule& legacy_datagram_without_context_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, - CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT); - return legacy_datagram_without_context_capsule_; + return absl::get<LegacyDatagramWithoutContextCapsule>(capsule_); } CloseWebTransportSessionCapsule& close_web_transport_session_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); - return close_web_transport_session_capsule_; + return absl::get<CloseWebTransportSessionCapsule>(capsule_); } const CloseWebTransportSessionCapsule& close_web_transport_session_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); - return close_web_transport_session_capsule_; + return absl::get<CloseWebTransportSessionCapsule>(capsule_); } AddressRequestCapsule& address_request_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ADDRESS_REQUEST); - return *address_request_capsule_; + return absl::get<AddressRequestCapsule>(capsule_); } const AddressRequestCapsule& address_request_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ADDRESS_REQUEST); - return *address_request_capsule_; + return absl::get<AddressRequestCapsule>(capsule_); } AddressAssignCapsule& address_assign_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ADDRESS_ASSIGN); - return *address_assign_capsule_; + return absl::get<AddressAssignCapsule>(capsule_); } const AddressAssignCapsule& address_assign_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ADDRESS_ASSIGN); - return *address_assign_capsule_; + return absl::get<AddressAssignCapsule>(capsule_); } RouteAdvertisementCapsule& route_advertisement_capsule() { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ROUTE_ADVERTISEMENT); - return *route_advertisement_capsule_; + return absl::get<RouteAdvertisementCapsule>(capsule_); } const RouteAdvertisementCapsule& route_advertisement_capsule() const { - QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::ROUTE_ADVERTISEMENT); - return *route_advertisement_capsule_; + return absl::get<RouteAdvertisementCapsule>(capsule_); } - absl::string_view& unknown_capsule_data() { - QUICHE_DCHECK(capsule_type_ != CapsuleType::DATAGRAM && - capsule_type_ != CapsuleType::LEGACY_DATAGRAM && - capsule_type_ != - CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT && - capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION && - capsule_type_ != CapsuleType::ADDRESS_REQUEST && - capsule_type_ != CapsuleType::ADDRESS_ASSIGN && - capsule_type_ != CapsuleType::ROUTE_ADVERTISEMENT) - << capsule_type_; - return unknown_capsule_data_; + UnknownCapsule& unknown_capsule() { + return absl::get<UnknownCapsule>(capsule_); } - const absl::string_view& unknown_capsule_data() const { - QUICHE_DCHECK(capsule_type_ != CapsuleType::DATAGRAM && - capsule_type_ != CapsuleType::LEGACY_DATAGRAM && - capsule_type_ != - CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT && - capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION && - capsule_type_ != CapsuleType::ADDRESS_REQUEST && - capsule_type_ != CapsuleType::ADDRESS_ASSIGN && - capsule_type_ != CapsuleType::ROUTE_ADVERTISEMENT) - << capsule_type_; - return unknown_capsule_data_; + const UnknownCapsule& unknown_capsule() const { + return absl::get<UnknownCapsule>(capsule_); } private: - void Free(); - CapsuleType capsule_type_; - union { - DatagramCapsule datagram_capsule_; - LegacyDatagramCapsule legacy_datagram_capsule_; - LegacyDatagramWithoutContextCapsule - legacy_datagram_without_context_capsule_; - CloseWebTransportSessionCapsule close_web_transport_session_capsule_; - AddressRequestCapsule* address_request_capsule_; - AddressAssignCapsule* address_assign_capsule_; - RouteAdvertisementCapsule* route_advertisement_capsule_; - absl::string_view unknown_capsule_data_; - }; + absl::variant<DatagramCapsule, LegacyDatagramCapsule, + LegacyDatagramWithoutContextCapsule, + CloseWebTransportSessionCapsule, AddressRequestCapsule, + AddressAssignCapsule, RouteAdvertisementCapsule, UnknownCapsule> + capsule_; }; namespace test { @@ -235,7 +240,7 @@ // MUST make a deep copy before this returns. virtual bool OnCapsule(const Capsule& capsule) = 0; - virtual void OnCapsuleParseFailure(const std::string& error_message) = 0; + virtual void OnCapsuleParseFailure(absl::string_view error_message) = 0; }; // |visitor| must be non-null, and must outlive CapsuleParser. @@ -252,10 +257,10 @@ private: // Attempts to parse a single capsule from |buffered_data_|. If a full capsule - // is not available, returns 0. If a parsing error occurs, returns 0. + // is not available, returns 0. If a parsing error occurs, returns an error. // Otherwise, returns the number of bytes in the parsed capsule. - size_t AttemptParseCapsule(); - void ReportParseFailure(const std::string& error_message); + absl::StatusOr<size_t> AttemptParseCapsule(); + void ReportParseFailure(absl::string_view error_message); // Whether a parsing error has occurred. bool parsing_error_occurred_ = false;
diff --git a/quiche/common/capsule_test.cc b/quiche/common/capsule_test.cc index 23d7533..dde2ea7 100644 --- a/quiche/common/capsule_test.cc +++ b/quiche/common/capsule_test.cc
@@ -39,7 +39,7 @@ } ~MockCapsuleParserVisitor() override = default; MOCK_METHOD(bool, OnCapsule, (const Capsule& capsule), (override)); - MOCK_METHOD(void, OnCapsuleParseFailure, (const std::string& error_message), + MOCK_METHOD(void, OnCapsuleParseFailure, (absl::string_view error_message), (override)); };
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc index 90e5341..fae3ce8 100644 --- a/quiche/quic/core/http/quic_spdy_stream.cc +++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -1393,7 +1393,7 @@ return true; } -void QuicSpdyStream::OnCapsuleParseFailure(const std::string& error_message) { +void QuicSpdyStream::OnCapsuleParseFailure(absl::string_view error_message) { QUIC_DLOG(ERROR) << ENDPOINT << "Capsule parse failure: " << error_message; Reset(QUIC_BAD_APPLICATION_PAYLOAD); }
diff --git a/quiche/quic/core/http/quic_spdy_stream.h b/quiche/quic/core/http/quic_spdy_stream.h index 887facb..e59afd5 100644 --- a/quiche/quic/core/http/quic_spdy_stream.h +++ b/quiche/quic/core/http/quic_spdy_stream.h
@@ -257,7 +257,7 @@ // From CapsuleParser::Visitor. bool OnCapsule(const quiche::Capsule& capsule) override; - void OnCapsuleParseFailure(const std::string& error_message) override; + void OnCapsuleParseFailure(absl::string_view error_message) override; // Sends an HTTP/3 datagram. The stream ID is not part of |payload|. Virtual // to allow mocking in tests.