| // 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_data_reader.h" |
| #include "quic/core/quic_types.h" |
| #include "common/platform/api/quiche_logging.h" |
| #include "common/quiche_buffer_allocator.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 quiche::QuicheBuffer SerializeCapsule( |
| const Capsule& capsule, quiche::QuicheBufferAllocator* allocator); |
| |
| } // namespace quic |
| |
| #endif // QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ |