blob: 42190bba5bf54b1eb2535c18d41871bfb1614cff [file] [log] [blame]
// 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_