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.