|  | // Copyright (c) 2021 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ | 
|  | #define QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  |  | 
|  | #include "absl/strings/str_cat.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "absl/types/optional.h" | 
|  | #include "quic/core/quic_buffer_allocator.h" | 
|  | #include "quic/core/quic_data_reader.h" | 
|  | #include "quic/core/quic_types.h" | 
|  | #include "common/platform/api/quiche_logging.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | enum class CapsuleType : uint64_t { | 
|  | // Casing in this enum matches the IETF specification. | 
|  | LEGACY_DATAGRAM = 0xff37a0,  // draft-ietf-masque-h3-datagram-04 | 
|  | REGISTER_DATAGRAM_CONTEXT = 0xff37a1, | 
|  | REGISTER_DATAGRAM_NO_CONTEXT = 0xff37a2, | 
|  | CLOSE_DATAGRAM_CONTEXT = 0xff37a3, | 
|  | DATAGRAM_WITH_CONTEXT = 0xff37a4, | 
|  | DATAGRAM_WITHOUT_CONTEXT = 0xff37a5, | 
|  | CLOSE_WEBTRANSPORT_SESSION = 0x2843, | 
|  | }; | 
|  |  | 
|  | QUIC_EXPORT_PRIVATE std::string CapsuleTypeToString(CapsuleType capsule_type); | 
|  | QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, | 
|  | const CapsuleType& capsule_type); | 
|  |  | 
|  | enum class DatagramFormatType : uint64_t { | 
|  | // Casing in this enum matches the IETF specification. | 
|  | UDP_PAYLOAD = 0xff6f00, | 
|  | WEBTRANSPORT = 0xff7c00, | 
|  | }; | 
|  |  | 
|  | QUIC_EXPORT_PRIVATE std::string DatagramFormatTypeToString( | 
|  | DatagramFormatType datagram_format_type); | 
|  | QUIC_EXPORT_PRIVATE std::ostream& operator<<( | 
|  | std::ostream& os, const DatagramFormatType& datagram_format_type); | 
|  |  | 
|  | enum class ContextCloseCode : uint64_t { | 
|  | // Casing in this enum matches the IETF specification. | 
|  | CLOSE_NO_ERROR = 0xff78a0,  // NO_ERROR already exists in winerror.h. | 
|  | UNKNOWN_FORMAT = 0xff78a1, | 
|  | DENIED = 0xff78a2, | 
|  | RESOURCE_LIMIT = 0xff78a3, | 
|  | }; | 
|  |  | 
|  | QUIC_EXPORT_PRIVATE std::string ContextCloseCodeToString( | 
|  | ContextCloseCode context_close_code); | 
|  | QUIC_EXPORT_PRIVATE std::ostream& operator<<( | 
|  | std::ostream& os, const ContextCloseCode& context_close_code); | 
|  |  | 
|  | struct QUIC_EXPORT_PRIVATE LegacyDatagramCapsule { | 
|  | absl::optional<QuicDatagramContextId> context_id; | 
|  | absl::string_view http_datagram_payload; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE DatagramWithContextCapsule { | 
|  | QuicDatagramContextId context_id; | 
|  | absl::string_view http_datagram_payload; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE DatagramWithoutContextCapsule { | 
|  | absl::string_view http_datagram_payload; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE RegisterDatagramContextCapsule { | 
|  | QuicDatagramContextId context_id; | 
|  | DatagramFormatType format_type; | 
|  | absl::string_view format_additional_data; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE RegisterDatagramNoContextCapsule { | 
|  | DatagramFormatType format_type; | 
|  | absl::string_view format_additional_data; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE CloseDatagramContextCapsule { | 
|  | QuicDatagramContextId context_id; | 
|  | ContextCloseCode close_code; | 
|  | absl::string_view close_details; | 
|  | }; | 
|  | struct QUIC_EXPORT_PRIVATE CloseWebTransportSessionCapsule { | 
|  | WebTransportSessionError error_code; | 
|  | absl::string_view error_message; | 
|  | }; | 
|  |  | 
|  | // Capsule from draft-ietf-masque-h3-datagram. | 
|  | // IMPORTANT NOTE: Capsule does not own any of the absl::string_view memory it | 
|  | // points to. Strings saved into a capsule must outlive the capsule object. Any | 
|  | // code that sees a capsule in a callback needs to either process it immediately | 
|  | // or perform its own deep copy. | 
|  | class QUIC_EXPORT_PRIVATE Capsule { | 
|  | public: | 
|  | static Capsule LegacyDatagram( | 
|  | absl::optional<QuicDatagramContextId> context_id = absl::nullopt, | 
|  | absl::string_view http_datagram_payload = absl::string_view()); | 
|  | static Capsule DatagramWithContext( | 
|  | QuicDatagramContextId context_id, | 
|  | absl::string_view http_datagram_payload = absl::string_view()); | 
|  | static Capsule DatagramWithoutContext( | 
|  | absl::string_view http_datagram_payload = absl::string_view()); | 
|  | static Capsule RegisterDatagramContext( | 
|  | QuicDatagramContextId context_id, DatagramFormatType format_type, | 
|  | absl::string_view format_additional_data = absl::string_view()); | 
|  | static Capsule RegisterDatagramNoContext( | 
|  | DatagramFormatType format_type, | 
|  | absl::string_view format_additional_data = absl::string_view()); | 
|  | static Capsule CloseDatagramContext( | 
|  | QuicDatagramContextId context_id, | 
|  | ContextCloseCode close_code = ContextCloseCode::CLOSE_NO_ERROR, | 
|  | absl::string_view close_details = absl::string_view()); | 
|  | static Capsule CloseWebTransportSession( | 
|  | WebTransportSessionError error_code = 0, | 
|  | absl::string_view error_message = ""); | 
|  | static Capsule Unknown( | 
|  | uint64_t capsule_type, | 
|  | absl::string_view unknown_capsule_data = absl::string_view()); | 
|  |  | 
|  | explicit Capsule(CapsuleType capsule_type); | 
|  | Capsule(const Capsule& other); | 
|  | Capsule& operator=(const Capsule& other); | 
|  | bool operator==(const Capsule& other) const; | 
|  |  | 
|  | // Human-readable information string for debugging purposes. | 
|  | std::string ToString() const; | 
|  | friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, | 
|  | const Capsule& capsule); | 
|  |  | 
|  | CapsuleType capsule_type() const { return capsule_type_; } | 
|  | LegacyDatagramCapsule& legacy_datagram_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::LEGACY_DATAGRAM); | 
|  | return legacy_datagram_capsule_; | 
|  | } | 
|  | const LegacyDatagramCapsule& legacy_datagram_capsule() const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::LEGACY_DATAGRAM); | 
|  | return legacy_datagram_capsule_; | 
|  | } | 
|  | DatagramWithContextCapsule& datagram_with_context_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM_WITH_CONTEXT); | 
|  | return datagram_with_context_capsule_; | 
|  | } | 
|  | const DatagramWithContextCapsule& datagram_with_context_capsule() const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM_WITH_CONTEXT); | 
|  | return datagram_with_context_capsule_; | 
|  | } | 
|  | DatagramWithoutContextCapsule& datagram_without_context_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM_WITHOUT_CONTEXT); | 
|  | return datagram_without_context_capsule_; | 
|  | } | 
|  | const DatagramWithoutContextCapsule& datagram_without_context_capsule() | 
|  | const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM_WITHOUT_CONTEXT); | 
|  | return datagram_without_context_capsule_; | 
|  | } | 
|  | RegisterDatagramContextCapsule& register_datagram_context_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_CONTEXT); | 
|  | return register_datagram_context_capsule_; | 
|  | } | 
|  | const RegisterDatagramContextCapsule& register_datagram_context_capsule() | 
|  | const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_CONTEXT); | 
|  | return register_datagram_context_capsule_; | 
|  | } | 
|  | RegisterDatagramNoContextCapsule& register_datagram_no_context_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT); | 
|  | return register_datagram_no_context_capsule_; | 
|  | } | 
|  | const RegisterDatagramNoContextCapsule& register_datagram_no_context_capsule() | 
|  | const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT); | 
|  | return register_datagram_no_context_capsule_; | 
|  | } | 
|  | CloseDatagramContextCapsule& close_datagram_context_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_DATAGRAM_CONTEXT); | 
|  | return close_datagram_context_capsule_; | 
|  | } | 
|  | const CloseDatagramContextCapsule& close_datagram_context_capsule() const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_DATAGRAM_CONTEXT); | 
|  | return close_datagram_context_capsule_; | 
|  | } | 
|  | CloseWebTransportSessionCapsule& close_web_transport_session_capsule() { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); | 
|  | return close_web_transport_session_capsule_; | 
|  | } | 
|  | const CloseWebTransportSessionCapsule& close_web_transport_session_capsule() | 
|  | const { | 
|  | QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); | 
|  | return close_web_transport_session_capsule_; | 
|  | } | 
|  | absl::string_view& unknown_capsule_data() { | 
|  | QUICHE_DCHECK(capsule_type_ != CapsuleType::LEGACY_DATAGRAM && | 
|  | capsule_type_ != CapsuleType::DATAGRAM_WITH_CONTEXT && | 
|  | capsule_type_ != CapsuleType::DATAGRAM_WITHOUT_CONTEXT && | 
|  | capsule_type_ != CapsuleType::REGISTER_DATAGRAM_CONTEXT && | 
|  | capsule_type_ != CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT && | 
|  | capsule_type_ != CapsuleType::CLOSE_DATAGRAM_CONTEXT && | 
|  | capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION) | 
|  | << capsule_type_; | 
|  | return unknown_capsule_data_; | 
|  | } | 
|  | const absl::string_view& unknown_capsule_data() const { | 
|  | QUICHE_DCHECK(capsule_type_ != CapsuleType::LEGACY_DATAGRAM && | 
|  | capsule_type_ != CapsuleType::DATAGRAM_WITH_CONTEXT && | 
|  | capsule_type_ != CapsuleType::DATAGRAM_WITHOUT_CONTEXT && | 
|  | capsule_type_ != CapsuleType::REGISTER_DATAGRAM_CONTEXT && | 
|  | capsule_type_ != CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT && | 
|  | capsule_type_ != CapsuleType::CLOSE_DATAGRAM_CONTEXT && | 
|  | capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION) | 
|  | << capsule_type_; | 
|  | return unknown_capsule_data_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | CapsuleType capsule_type_; | 
|  | union { | 
|  | LegacyDatagramCapsule legacy_datagram_capsule_; | 
|  | DatagramWithContextCapsule datagram_with_context_capsule_; | 
|  | DatagramWithoutContextCapsule datagram_without_context_capsule_; | 
|  | RegisterDatagramContextCapsule register_datagram_context_capsule_; | 
|  | RegisterDatagramNoContextCapsule register_datagram_no_context_capsule_; | 
|  | CloseDatagramContextCapsule close_datagram_context_capsule_; | 
|  | CloseWebTransportSessionCapsule close_web_transport_session_capsule_; | 
|  | absl::string_view unknown_capsule_data_; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | namespace test { | 
|  | class CapsuleParserPeer; | 
|  | }  // namespace test | 
|  |  | 
|  | class QUIC_EXPORT_PRIVATE CapsuleParser { | 
|  | public: | 
|  | class QUIC_EXPORT_PRIVATE Visitor { | 
|  | public: | 
|  | virtual ~Visitor() {} | 
|  |  | 
|  | // Called when a capsule has been successfully parsed. The return value | 
|  | // indicates whether the contents of the capsule are valid: if false is | 
|  | // returned, the parse operation will be considered failed and | 
|  | // OnCapsuleParseFailure will be called. Note that since Capsule does not | 
|  | // own the memory backing its string_views, that memory is only valid until | 
|  | // this callback returns. Visitors that wish to access the capsule later | 
|  | // MUST make a deep copy before this returns. | 
|  | virtual bool OnCapsule(const Capsule& capsule) = 0; | 
|  |  | 
|  | virtual void OnCapsuleParseFailure(const std::string& error_message) = 0; | 
|  | }; | 
|  |  | 
|  | // |visitor| must be non-null, and must outlive CapsuleParser. | 
|  | explicit CapsuleParser(Visitor* visitor); | 
|  |  | 
|  | void set_datagram_context_id_present(bool datagram_context_id_present) { | 
|  | datagram_context_id_present_ = datagram_context_id_present; | 
|  | } | 
|  |  | 
|  | // Ingests a capsule fragment (any fragment of bytes from the capsule data | 
|  | // stream) and parses and complete capsules it encounters. Returns false if a | 
|  | // parsing error occurred. | 
|  | bool IngestCapsuleFragment(absl::string_view capsule_fragment); | 
|  |  | 
|  | void ErrorIfThereIsRemainingBufferedData(); | 
|  |  | 
|  | friend class test::CapsuleParserPeer; | 
|  |  | 
|  | 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. | 
|  | // Otherwise, returns the number of bytes in the parsed capsule. | 
|  | size_t AttemptParseCapsule(); | 
|  | void ReportParseFailure(const std::string& error_message); | 
|  |  | 
|  | // Whether HTTP Datagram Context IDs are present. | 
|  | bool datagram_context_id_present_ = false; | 
|  | // Whether a parsing error has occurred. | 
|  | bool parsing_error_occurred_ = false; | 
|  | // Visitor which will receive callbacks, unowned. | 
|  | Visitor* visitor_; | 
|  |  | 
|  | std::string buffered_data_; | 
|  | }; | 
|  |  | 
|  | // Serializes |capsule| into a newly allocated buffer. | 
|  | QUIC_EXPORT_PRIVATE QuicBuffer SerializeCapsule(const Capsule& capsule, | 
|  | QuicBufferAllocator* allocator); | 
|  |  | 
|  | }  // namespace quic | 
|  |  | 
|  | #endif  // QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ |