No public description PiperOrigin-RevId: 981880016
diff --git a/quiche/http2/core/spdy_frame_builder.h b/quiche/http2/core/spdy_frame_builder.h index 5628753..ee879a4 100644 --- a/quiche/http2/core/spdy_frame_builder.h +++ b/quiche/http2/core/spdy_frame_builder.h
@@ -135,335 +135,6 @@ size_t offset_; // Position at which the latest frame begins. }; -// Packs the exclusive dependency bit (bit 31) and the 31-bit parent stream ID -// into a single 32-bit unsigned integer in network wire format for PRIORITY and -// HEADERS frames. -inline uint32_t PackStreamDependencyValues(bool exclusive, - SpdyStreamId parent_stream_id) { - uint32_t parent = parent_stream_id & 0x7fffffff; - uint32_t e_bit = exclusive ? 0x80000000 : 0; - return parent | e_bit; -} - -inline bool SerializeDataFrame(const DataFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.flags; - if (frame.fin()) { - flags |= DATA_FLAG_FIN; - } - if (frame.padded()) { - flags |= DATA_FLAG_PADDED; - } - size_t payload_len = frame.data.size() + - (frame.padded() ? (1 + frame.padding_payload_len) : 0); - if (!builder.BeginNewFrame(SpdyFrameType::DATA, flags, frame.stream_id, - payload_len)) { - return false; - } - if (frame.padded()) { - if (!builder.WriteUInt8(frame.padding_payload_len)) { - return false; - } - } - if (!builder.WriteBytes(frame.data.data(), frame.data.size())) { - return false; - } - if (frame.padded() && frame.padding_payload_len > 0) { - std::string padding(frame.padding_payload_len, 0); - if (!builder.WriteBytes(padding.data(), padding.length())) { - return false; - } - } - return true; -} - -inline bool SerializeHeadersFrame(const HeadersFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.flags; - if (frame.fin()) { - flags |= CONTROL_FLAG_FIN; - } - if (frame.end_headers()) { - flags |= HEADERS_FLAG_END_HEADERS; - } - if (frame.padded()) { - flags |= HEADERS_FLAG_PADDED; - } - if (frame.has_priority) { - flags |= HEADERS_FLAG_PRIORITY; - } - size_t payload_len = frame.hpack_block.size() + - (frame.padded() ? (1 + frame.padding_payload_len) : 0) + - (frame.has_priority ? 5 : 0); - if (!builder.BeginNewFrame(SpdyFrameType::HEADERS, flags, frame.stream_id, - payload_len)) { - return false; - } - if (frame.padded()) { - if (!builder.WriteUInt8(frame.padding_payload_len)) { - return false; - } - } - if (frame.has_priority) { - int weight = ClampHttp2Weight(frame.priority.weight); - if (!builder.WriteUInt32(PackStreamDependencyValues( - frame.priority.exclusive, frame.priority.parent_stream_id))) { - return false; - } - if (!builder.WriteUInt8(static_cast<uint8_t>(weight - 1))) { - return false; - } - } - if (!builder.WriteBytes(frame.hpack_block.data(), frame.hpack_block.size())) { - return false; - } - if (frame.padded() && frame.padding_payload_len > 0) { - std::string padding(frame.padding_payload_len, 0); - if (!builder.WriteBytes(padding.data(), padding.length())) { - return false; - } - } - return true; -} - -inline bool SerializePriorityFrame(const PriorityFrame& frame, - SpdyFrameBuilder& builder) { - if (!builder.BeginNewFrame(SpdyFrameType::PRIORITY, 0, frame.stream_id, 5)) { - return false; - } - int weight = ClampHttp2Weight(frame.priority.weight); - if (!builder.WriteUInt32(PackStreamDependencyValues( - frame.priority.exclusive, frame.priority.parent_stream_id))) { - return false; - } - return builder.WriteUInt8(static_cast<uint8_t>(weight - 1)); -} - -inline bool SerializeRstStreamFrame(const RstStreamFrame& frame, - SpdyFrameBuilder& builder) { - if (!builder.BeginNewFrame(SpdyFrameType::RST_STREAM, 0, frame.stream_id, - 4)) { - return false; - } - return builder.WriteUInt32(static_cast<uint32_t>(frame.error_code)); -} - -inline bool SerializeSettingsFrame(const SettingsFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.is_ack ? SETTINGS_FLAG_ACK : 0; - size_t payload_len = - frame.is_ack ? 0 : (frame.values.size() * kSettingsOneSettingSize); - if (!builder.BeginNewFrame(SpdyFrameType::SETTINGS, flags, 0, payload_len)) { - return false; - } - if (!frame.is_ack) { - for (const auto& param : frame.values) { - if (!builder.WriteUInt16(param.id) || !builder.WriteUInt32(param.value)) { - return false; - } - } - } - return true; -} - -inline bool SerializePushPromiseFrame(const PushPromiseFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.flags; - if (frame.end_headers()) { - flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; - } - if (frame.padded()) { - flags |= PUSH_PROMISE_FLAG_PADDED; - } - size_t payload_len = 4 + frame.hpack_block.size() + - (frame.padded() ? (1 + frame.padding_payload_len) : 0); - if (!builder.BeginNewFrame(SpdyFrameType::PUSH_PROMISE, flags, - frame.stream_id, payload_len)) { - return false; - } - if (frame.padded()) { - if (!builder.WriteUInt8(frame.padding_payload_len)) { - return false; - } - } - if (!builder.WriteUInt32(frame.promised_stream_id & 0x7fffffff)) { - return false; - } - if (!builder.WriteBytes(frame.hpack_block.data(), frame.hpack_block.size())) { - return false; - } - if (frame.padded() && frame.padding_payload_len > 0) { - std::string padding(frame.padding_payload_len, 0); - if (!builder.WriteBytes(padding.data(), padding.length())) { - return false; - } - } - return true; -} - -inline bool SerializePingFrame(const PingFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.is_ack ? PING_FLAG_ACK : 0; - if (!builder.BeginNewFrame(SpdyFrameType::PING, flags, 0, 8)) { - return false; - } - return builder.WriteUInt64(frame.opaque_data); -} - -inline bool SerializeGoAwayFrame(const GoAwayFrame& frame, - SpdyFrameBuilder& builder) { - size_t payload_len = 8 + frame.debug_data.size(); - if (!builder.BeginNewFrame(SpdyFrameType::GOAWAY, 0, 0, payload_len)) { - return false; - } - if (!builder.WriteUInt32(frame.last_good_stream_id & 0x7fffffff)) { - return false; - } - if (!builder.WriteUInt32(static_cast<uint32_t>(frame.error_code))) { - return false; - } - if (!frame.debug_data.empty()) { - if (!builder.WriteBytes(frame.debug_data.data(), frame.debug_data.size())) { - return false; - } - } - return true; -} - -inline bool SerializeWindowUpdateFrame(const WindowUpdateFrame& frame, - SpdyFrameBuilder& builder) { - if (!builder.BeginNewFrame(SpdyFrameType::WINDOW_UPDATE, 0, frame.stream_id, - 4)) { - return false; - } - return builder.WriteUInt32(frame.delta & 0x7fffffff); -} - -inline bool SerializeContinuationFrame(const ContinuationFrame& frame, - SpdyFrameBuilder& builder) { - uint8_t flags = frame.flags; - if (frame.end_headers()) { - flags |= HEADERS_FLAG_END_HEADERS; - } - if (!builder.BeginNewFrame(SpdyFrameType::CONTINUATION, flags, - frame.stream_id, frame.hpack_block.size())) { - return false; - } - return builder.WriteBytes(frame.hpack_block.data(), frame.hpack_block.size()); -} - -inline bool SerializeAltSvcFrame(const AltSvcFrame& frame, - SpdyFrameBuilder& builder) { - size_t payload_len = 2 + frame.origin.size() + frame.value.size(); - if (!builder.BeginNewFrame(SpdyFrameType::ALTSVC, 0, frame.stream_id, - payload_len)) { - return false; - } - if (!builder.WriteUInt16(frame.origin.size())) { - return false; - } - if (!builder.WriteBytes(frame.origin.data(), frame.origin.size())) { - return false; - } - return builder.WriteBytes(frame.value.data(), frame.value.size()); -} - -inline bool SerializePriorityUpdateFrame(const PriorityUpdateFrame& frame, - SpdyFrameBuilder& builder) { - size_t payload_len = 4 + frame.priority_field_value.size(); - if (!builder.BeginNewFrame(SpdyFrameType::PRIORITY_UPDATE, 0, 0, - payload_len)) { - return false; - } - if (!builder.WriteUInt32(frame.prioritized_stream_id & 0x7fffffff)) { - return false; - } - return builder.WriteBytes(frame.priority_field_value.data(), - frame.priority_field_value.size()); -} - -inline bool SerializeAcceptChFrame(const AcceptChFrame& frame, - SpdyFrameBuilder& builder) { - size_t payload_len = 0; - for (size_t i = 0; i < frame.num_entries && i < frame.entries.size(); ++i) { - payload_len += - 4 + frame.entries[i].origin.size() + frame.entries[i].value.size(); - } - if (!builder.BeginNewFrame(SpdyFrameType::ACCEPT_CH, 0, 0, payload_len)) { - return false; - } - for (size_t i = 0; i < frame.num_entries && i < frame.entries.size(); ++i) { - if (!builder.WriteUInt16(frame.entries[i].origin.size()) || - !builder.WriteBytes(frame.entries[i].origin.data(), - frame.entries[i].origin.size()) || - !builder.WriteUInt16(frame.entries[i].value.size()) || - !builder.WriteBytes(frame.entries[i].value.data(), - frame.entries[i].value.size())) { - return false; - } - } - return true; -} - -inline bool SerializeUnknownFrame(const UnknownFrame& frame, - SpdyFrameBuilder& builder) { - // Handles other unknown frame types, where the payload is opaque. - if (!builder.BeginNewUncheckedFrame(frame.type, frame.flags, frame.stream_id, - frame.payload.size())) { - return false; - } - return builder.WriteBytes(frame.payload.data(), frame.payload.size()); -} - -// Serializes a strongly-typed HTTP/2 frame representation directly into the -// provided `SpdyFrameBuilder`. -// -// This generic template writes HTTP/2 wire-format frames (RFC 9113) without -// allocating intermediate heap objects. Returns true on success, or false if -// the buffer cannot accommodate the frame or serialization fails. -template <Http2FrameConcept T> -bool SerializeFrame(const T& frame, SpdyFrameBuilder& builder) { - if constexpr (std::is_same_v<T, DataFrame>) { - return SerializeDataFrame(frame, builder); - } else if constexpr (std::is_same_v<T, HeadersFrame>) { - return SerializeHeadersFrame(frame, builder); - } else if constexpr (std::is_same_v<T, PriorityFrame>) { - return SerializePriorityFrame(frame, builder); - } else if constexpr (std::is_same_v<T, RstStreamFrame>) { - return SerializeRstStreamFrame(frame, builder); - } else if constexpr (std::is_same_v<T, SettingsFrame>) { - return SerializeSettingsFrame(frame, builder); - } else if constexpr (std::is_same_v<T, PushPromiseFrame>) { - return SerializePushPromiseFrame(frame, builder); - } else if constexpr (std::is_same_v<T, PingFrame>) { - return SerializePingFrame(frame, builder); - } else if constexpr (std::is_same_v<T, GoAwayFrame>) { - return SerializeGoAwayFrame(frame, builder); - } else if constexpr (std::is_same_v<T, WindowUpdateFrame>) { - return SerializeWindowUpdateFrame(frame, builder); - } else if constexpr (std::is_same_v<T, ContinuationFrame>) { - return SerializeContinuationFrame(frame, builder); - } else if constexpr (std::is_same_v<T, AltSvcFrame>) { - return SerializeAltSvcFrame(frame, builder); - } else if constexpr (std::is_same_v<T, PriorityUpdateFrame>) { - return SerializePriorityUpdateFrame(frame, builder); - } else if constexpr (std::is_same_v<T, AcceptChFrame>) { - return SerializeAcceptChFrame(frame, builder); - } else if constexpr (std::is_same_v<T, UnknownFrame>) { - return SerializeUnknownFrame(frame, builder); - } - return false; -} - -// Serializes a unified `SpdyFrame` variant (containing any standard-layout -// HTTP/2 frame type) into the provided `SpdyFrameBuilder` using `std::visit`. -// Returns true on success, or false if serialization fails. -inline bool SerializeSpdyFrame(const SpdyFrame& frame, - SpdyFrameBuilder& builder) { - return std::visit( - [&builder](const auto& f) { return SerializeFrame(f, builder); }, frame); -} - } // namespace spdy #endif // QUICHE_HTTP2_CORE_SPDY_FRAME_BUILDER_H_
diff --git a/quiche/http2/core/spdy_framer.cc b/quiche/http2/core/spdy_framer.cc index 699eb96..9f400c4 100644 --- a/quiche/http2/core/spdy_framer.cc +++ b/quiche/http2/core/spdy_framer.cc
@@ -27,6 +27,17 @@ namespace { +// Pack parent stream ID and exclusive flag into the format used by HTTP/2 +// headers and priority frames. +uint32_t PackStreamDependencyValues(bool exclusive, + SpdyStreamId parent_stream_id) { + // Make sure the highest-order bit in the parent stream id is zeroed out. + uint32_t parent = parent_stream_id & 0x7fffffff; + // Set the one-bit exclusivity flag. + uint32_t e_bit = exclusive ? 0x80000000 : 0; + return parent | e_bit; +} + // Used to indicate no flags in a HTTP2 flags field. const uint8_t kNoFlags = 0;
diff --git a/quiche/http2/core/spdy_protocol.h b/quiche/http2/core/spdy_protocol.h index 8ee1af2..a5ec174 100644 --- a/quiche/http2/core/spdy_protocol.h +++ b/quiche/http2/core/spdy_protocol.h
@@ -9,8 +9,6 @@ #ifndef QUICHE_HTTP2_CORE_SPDY_PROTOCOL_H_ #define QUICHE_HTTP2_CORE_SPDY_PROTOCOL_H_ -#include <array> -#include <concepts> #include <cstddef> #include <cstdint> #include <cstring> @@ -19,12 +17,10 @@ #include <memory> #include <optional> #include <string> -#include <type_traits> #include <utility> #include <variant> #include <vector> -#include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" #include "quiche/http2/core/spdy_alt_svc_wire_format.h" #include "quiche/http2/core/spdy_bitmasks.h" @@ -451,367 +447,6 @@ typedef StreamPrecedence<SpdyStreamId> SpdyStreamPrecedence; -// ============================================================================= -// Value-Semantic POD Frame Structs -// ============================================================================= - -// Contains the fields specific to any priority-conveying frames. -struct QUICHE_EXPORT PriorityFields { - SpdyStreamId parent_stream_id = 0; - uint16_t weight = kHttp2DefaultStreamWeight; - bool exclusive = false; - constexpr bool operator==(const PriorityFields&) const = default; -}; - -// A container for a single SETTINGS field. -struct QUICHE_EXPORT SettingParameter { - SpdySettingsId id = 0; - uint32_t value = 0; - constexpr bool operator==(const SettingParameter&) const = default; -}; - -// A container for a single ACCEPT_CH entry. -struct QUICHE_EXPORT AcceptChEntryView { - absl::string_view origin; - absl::string_view value; - constexpr bool operator==(const AcceptChEntryView&) const = default; -}; - -struct QUICHE_EXPORT DataFrame { - SpdyStreamId stream_id = 0; - uint8_t flags = 0; - uint8_t padding_payload_len = 0; - absl::string_view data; - - constexpr bool HasFin() const { return (flags & DATA_FLAG_FIN) != 0; } - ABSL_DEPRECATED("Use HasFin() instead.") constexpr bool fin() const { - return HasFin(); - } - constexpr bool IsPadded() const { - return (flags & DATA_FLAG_PADDED) != 0 || padding_payload_len > 0; - } - ABSL_DEPRECATED("Use IsPadded() instead.") constexpr bool padded() const { - return IsPadded(); - } - constexpr bool operator==(const DataFrame&) const = default; -}; -static_assert(sizeof(DataFrame) == (sizeof(void*) == 8 ? 24 : 16)); - -struct QUICHE_EXPORT HeadersFrame { - SpdyStreamId stream_id = 0; - uint8_t flags = 0; - uint8_t padding_payload_len = 0; - bool has_priority = false; - PriorityFields priority{}; - absl::string_view hpack_block; - - constexpr bool HasFin() const { return (flags & CONTROL_FLAG_FIN) != 0; } - ABSL_DEPRECATED("Use HasFin() instead.") constexpr bool fin() const { - return HasFin(); - } - constexpr bool HasEndHeaders() const { - return (flags & HEADERS_FLAG_END_HEADERS) != 0; - } - ABSL_DEPRECATED("Use HasEndHeaders() instead.") - constexpr bool end_headers() const { return HasEndHeaders(); } - constexpr bool IsPadded() const { - return (flags & HEADERS_FLAG_PADDED) != 0 || padding_payload_len > 0; - } - ABSL_DEPRECATED("Use IsPadded() instead.") constexpr bool padded() const { - return IsPadded(); - } - constexpr bool operator==(const HeadersFrame&) const = default; -}; -static_assert(sizeof(HeadersFrame) == (sizeof(void*) == 8 ? 32 : 24)); - -struct QUICHE_EXPORT PriorityFrame { - SpdyStreamId stream_id = 0; - PriorityFields priority{}; - constexpr bool operator==(const PriorityFrame&) const = default; -}; -static_assert(sizeof(PriorityFrame) == 12); - -struct QUICHE_EXPORT RstStreamFrame { - SpdyStreamId stream_id = 0; - SpdyErrorCode error_code = ERROR_CODE_NO_ERROR; - constexpr bool operator==(const RstStreamFrame&) const = default; -}; -static_assert(sizeof(RstStreamFrame) == 8); - -struct QUICHE_EXPORT SettingsFrame { - static constexpr size_t kMaxInlineSettings = 5; - bool is_ack = false; - absl::InlinedVector<SettingParameter, kMaxInlineSettings> values; - bool operator==(const SettingsFrame&) const = default; -}; -static_assert(sizeof(SettingsFrame) == (sizeof(void*) == 8 ? 56 : 48)); - -struct QUICHE_EXPORT PushPromiseFrame { - SpdyStreamId stream_id = 0; - SpdyStreamId promised_stream_id = 0; - uint8_t flags = 0; - uint8_t padding_payload_len = 0; - absl::string_view hpack_block; - - constexpr bool HasEndHeaders() const { - return (flags & PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0; - } - ABSL_DEPRECATED("Use HasEndHeaders() instead.") - constexpr bool end_headers() const { return HasEndHeaders(); } - constexpr bool IsPadded() const { - return (flags & PUSH_PROMISE_FLAG_PADDED) != 0 || padding_payload_len > 0; - } - ABSL_DEPRECATED("Use IsPadded() instead.") constexpr bool padded() const { - return IsPadded(); - } - constexpr bool operator==(const PushPromiseFrame&) const = default; -}; -static_assert(sizeof(PushPromiseFrame) == (sizeof(void*) == 8 ? 32 : 20)); - -struct QUICHE_EXPORT PingFrame { - uint64_t opaque_data = 0; - bool is_ack = false; - constexpr bool operator==(const PingFrame&) const = default; -}; -static_assert(sizeof(PingFrame) == 16); - -struct QUICHE_EXPORT GoAwayFrame { - SpdyStreamId last_good_stream_id = 0; - SpdyErrorCode error_code = ERROR_CODE_NO_ERROR; - absl::string_view debug_data; - constexpr bool operator==(const GoAwayFrame&) const = default; -}; -static_assert(sizeof(GoAwayFrame) == (sizeof(void*) == 8 ? 24 : 16)); - -struct QUICHE_EXPORT WindowUpdateFrame { - SpdyStreamId stream_id = 0; - uint32_t delta = 0; - constexpr bool operator==(const WindowUpdateFrame&) const = default; -}; -static_assert(sizeof(WindowUpdateFrame) == 8); - -struct QUICHE_EXPORT ContinuationFrame { - SpdyStreamId stream_id = 0; - uint8_t flags = 0; - absl::string_view hpack_block; - - constexpr bool HasEndHeaders() const { - return (flags & HEADERS_FLAG_END_HEADERS) != 0; - } - ABSL_DEPRECATED("Use HasEndHeaders() instead.") - constexpr bool end_headers() const { return HasEndHeaders(); } - constexpr bool operator==(const ContinuationFrame&) const = default; -}; -static_assert(sizeof(ContinuationFrame) == (sizeof(void*) == 8 ? 24 : 16)); - -struct QUICHE_EXPORT AltSvcFrame { - SpdyStreamId stream_id = 0; - absl::string_view origin; - absl::string_view value; - constexpr bool operator==(const AltSvcFrame&) const = default; -}; -static_assert(sizeof(AltSvcFrame) == (sizeof(void*) == 8 ? 40 : 20)); - -struct QUICHE_EXPORT PriorityUpdateFrame { - SpdyStreamId prioritized_stream_id = 0; - absl::string_view priority_field_value; - constexpr bool operator==(const PriorityUpdateFrame&) const = default; -}; -static_assert(sizeof(PriorityUpdateFrame) == (sizeof(void*) == 8 ? 24 : 12)); - -struct QUICHE_EXPORT AcceptChFrame { - static constexpr size_t kMaxInlineEntries = 1; - uint8_t num_entries = 0; - std::array<AcceptChEntryView, kMaxInlineEntries> entries{}; - constexpr bool operator==(const AcceptChFrame&) const = default; -}; -static_assert(sizeof(AcceptChFrame) == (sizeof(void*) == 8 ? 40 : 20)); - -struct QUICHE_EXPORT UnknownFrame { - SpdyStreamId stream_id = 0; - uint8_t type = 0; - uint8_t flags = 0; - absl::string_view payload; - constexpr bool operator==(const UnknownFrame&) const = default; -}; -static_assert(sizeof(UnknownFrame) == (sizeof(void*) == 8 ? 24 : 16)); - -// Compile-Time Type Traits -template <typename T> -struct FrameTraits; - -#define REGISTER_FRAME_TRAITS(Type, EnumVal, HasStreamId, HasFin, HasPadding, \ - IsFixed, FixedSize, ConsumesFC) \ - template <> \ - struct FrameTraits<Type> { \ - static constexpr SpdyFrameType frame_type = EnumVal; \ - static constexpr bool has_stream_id = HasStreamId; \ - static constexpr bool has_fin = HasFin; \ - static constexpr bool has_padding = HasPadding; \ - static constexpr bool is_fixed_size = IsFixed; \ - static constexpr size_t fixed_payload_size = FixedSize; \ - static constexpr bool consumes_flow_control = ConsumesFC; \ - }; - -REGISTER_FRAME_TRAITS(DataFrame, SpdyFrameType::DATA, true, true, true, false, - 0, true) -REGISTER_FRAME_TRAITS(HeadersFrame, SpdyFrameType::HEADERS, true, true, true, - false, 0, false) -REGISTER_FRAME_TRAITS(PriorityFrame, SpdyFrameType::PRIORITY, true, false, - false, true, 5, false) -REGISTER_FRAME_TRAITS(RstStreamFrame, SpdyFrameType::RST_STREAM, true, false, - false, true, 4, false) -REGISTER_FRAME_TRAITS(SettingsFrame, SpdyFrameType::SETTINGS, false, false, - false, false, 0, false) -REGISTER_FRAME_TRAITS(PushPromiseFrame, SpdyFrameType::PUSH_PROMISE, true, - false, true, false, 0, false) -REGISTER_FRAME_TRAITS(PingFrame, SpdyFrameType::PING, false, false, false, true, - 8, false) -REGISTER_FRAME_TRAITS(GoAwayFrame, SpdyFrameType::GOAWAY, false, false, false, - false, 0, false) -REGISTER_FRAME_TRAITS(WindowUpdateFrame, SpdyFrameType::WINDOW_UPDATE, true, - false, false, true, 4, false) -REGISTER_FRAME_TRAITS(ContinuationFrame, SpdyFrameType::CONTINUATION, true, - false, false, false, 0, false) -REGISTER_FRAME_TRAITS(AltSvcFrame, SpdyFrameType::ALTSVC, true, false, false, - false, 0, false) -REGISTER_FRAME_TRAITS(PriorityUpdateFrame, SpdyFrameType::PRIORITY_UPDATE, - false, false, false, false, 0, false) -REGISTER_FRAME_TRAITS(AcceptChFrame, SpdyFrameType::ACCEPT_CH, false, false, - false, false, 0, false) -REGISTER_FRAME_TRAITS(UnknownFrame, SpdyFrameType::DATA, true, false, false, - false, 0, false) - -#undef REGISTER_FRAME_TRAITS - -template <typename T> -inline constexpr SpdyFrameType frame_type_v = FrameTraits<T>::frame_type; - -template <typename T> -inline constexpr bool is_fixed_size_v = FrameTraits<T>::is_fixed_size; - -template <typename T> -inline constexpr bool has_stream_id_v = FrameTraits<T>::has_stream_id; - -template <typename T> -inline constexpr bool has_fin_v = FrameTraits<T>::has_fin; - -template <typename T> -inline constexpr bool has_padding_v = FrameTraits<T>::has_padding; - -template <typename T> -inline constexpr bool consumes_flow_control_v = - FrameTraits<T>::consumes_flow_control; - -template <typename T> -concept Http2FrameConcept = requires { - { FrameTraits<T>::frame_type } -> std::convertible_to<SpdyFrameType>; - { FrameTraits<T>::is_fixed_size } -> std::convertible_to<bool>; - { FrameTraits<T>::has_stream_id } -> std::convertible_to<bool>; - { FrameTraits<T>::has_fin } -> std::convertible_to<bool>; - { FrameTraits<T>::has_padding } -> std::convertible_to<bool>; - { FrameTraits<T>::consumes_flow_control } -> std::convertible_to<bool>; -}; - -// Generic Size Calculation -template <Http2FrameConcept T> -constexpr size_t FrameSize(const T& frame) { - if constexpr (is_fixed_size_v<T>) { - return kFrameHeaderSize + FrameTraits<T>::fixed_payload_size; - } else if constexpr (std::is_same_v<T, DataFrame>) { - return kDataFrameMinimumSize + frame.data.size() + - (frame.padded() ? (1 + frame.padding_payload_len) : 0); - } else if constexpr (std::is_same_v<T, HeadersFrame>) { - size_t size = kHeadersFrameMinimumSize + frame.hpack_block.size(); - if (frame.padded()) { - size += 1 + frame.padding_payload_len; - } - if (frame.has_priority) { - size += 5; - } - return size; - } else if constexpr (std::is_same_v<T, SettingsFrame>) { - if (frame.is_ack) { - return kSettingsFrameMinimumSize; - } - return kSettingsFrameMinimumSize + - (frame.values.size() * kSettingsOneSettingSize); - } else if constexpr (std::is_same_v<T, PushPromiseFrame>) { - size_t size = kPushPromiseFrameMinimumSize + frame.hpack_block.size(); - if (frame.padded()) { - size += 1 + frame.padding_payload_len; - } - return size; - } else if constexpr (std::is_same_v<T, GoAwayFrame>) { - return kGoawayFrameMinimumSize + frame.debug_data.size(); - } else if constexpr (std::is_same_v<T, ContinuationFrame>) { - return kContinuationFrameMinimumSize + frame.hpack_block.size(); - } else if constexpr (std::is_same_v<T, AltSvcFrame>) { - return kGetAltSvcFrameMinimumSize + frame.origin.size() + - frame.value.size(); - } else if constexpr (std::is_same_v<T, PriorityUpdateFrame>) { - return kPriorityUpdateFrameMinimumSize + frame.priority_field_value.size(); - } else if constexpr (std::is_same_v<T, AcceptChFrame>) { - size_t total_size = kAcceptChFrameMinimumSize; - for (size_t i = 0; i < frame.num_entries && i < frame.entries.size(); ++i) { - total_size += frame.entries[i].origin.size() + - frame.entries[i].value.size() + - kAcceptChFramePerEntryOverhead; - } - return total_size; - } else if constexpr (std::is_same_v<T, UnknownFrame>) { - return kFrameHeaderSize + frame.payload.size(); - } else { - return kFrameHeaderSize; - } -} - -// Forward declares SpdyFrameBuilder for SerializeFrame. -class SpdyFrameBuilder; - -template <Http2FrameConcept T> -bool SerializeFrame(const T& frame, SpdyFrameBuilder& builder); - -// Unified variant. -using SpdyFrame = - std::variant<DataFrame, HeadersFrame, PriorityFrame, RstStreamFrame, - SettingsFrame, PushPromiseFrame, PingFrame, GoAwayFrame, - WindowUpdateFrame, ContinuationFrame, AltSvcFrame, - PriorityUpdateFrame, AcceptChFrame, UnknownFrame>; - -static_assert(sizeof(SpdyFrame) <= 64, - "SpdyFrame must fit within a 64-byte cache line"); - -inline SpdyStreamId GetFrameStreamId(const SpdyFrame& frame) { - return std::visit( - [](const auto& f) -> SpdyStreamId { - using T = std::decay_t<decltype(f)>; - if constexpr (has_stream_id_v<T>) { - return f.stream_id; - } - return 0; - }, - frame); -} - -inline SpdyFrameType GetFrameType(const SpdyFrame& frame) { - return std::visit( - [](const auto& f) -> SpdyFrameType { - using T = std::decay_t<decltype(f)>; - if constexpr (std::is_same_v<T, UnknownFrame>) { - return static_cast<SpdyFrameType>(f.type); - } - return frame_type_v<T>; - }, - frame); -} - -inline size_t GetFrameSize(const SpdyFrame& frame) { - return std::visit([](const auto& f) { return FrameSize(f); }, frame); -} - -bool SerializeSpdyFrame(const SpdyFrame& frame, SpdyFrameBuilder& builder); - class SpdyFrameVisitor; // Intermediate representation for HTTP2 frames.
diff --git a/quiche/http2/core/spdy_protocol_test.cc b/quiche/http2/core/spdy_protocol_test.cc index 82100e3..1adef29 100644 --- a/quiche/http2/core/spdy_protocol_test.cc +++ b/quiche/http2/core/spdy_protocol_test.cc
@@ -6,12 +6,9 @@ #include <iostream> #include <string> -#include <type_traits> #include <utility> #include "absl/strings/string_view.h" -#include "quiche/http2/core/spdy_frame_builder.h" -#include "quiche/http2/core/spdy_framer.h" #include "quiche/common/platform/api/quiche_expect_bug.h" #include "quiche/common/platform/api/quiche_test.h" @@ -285,408 +282,5 @@ EXPECT_EQ(frame.end(), frame.data() + frame.size()); } -// ============================================================================= -// Modern HTTP/2 Frame Representations Tests -// ============================================================================= - -TEST(ModernFrameTest, StandardLayoutAndTriviallyCopyable) { - static_assert(std::is_standard_layout_v<PriorityFields>); - static_assert(std::is_trivially_copyable_v<PriorityFields>); - - static_assert(std::is_standard_layout_v<SettingParameter>); - static_assert(std::is_trivially_copyable_v<SettingParameter>); - - static_assert(std::is_standard_layout_v<AcceptChEntryView>); - static_assert(std::is_trivially_copyable_v<AcceptChEntryView>); - - static_assert(std::is_standard_layout_v<DataFrame>); - static_assert(std::is_trivially_copyable_v<DataFrame>); - - static_assert(std::is_standard_layout_v<HeadersFrame>); - static_assert(std::is_trivially_copyable_v<HeadersFrame>); - - static_assert(std::is_standard_layout_v<PriorityFrame>); - static_assert(std::is_trivially_copyable_v<PriorityFrame>); - - static_assert(std::is_standard_layout_v<RstStreamFrame>); - static_assert(std::is_trivially_copyable_v<RstStreamFrame>); - - static_assert(std::is_standard_layout_v<SettingsFrame>); - - static_assert(std::is_standard_layout_v<PushPromiseFrame>); - static_assert(std::is_trivially_copyable_v<PushPromiseFrame>); - - static_assert(std::is_standard_layout_v<PingFrame>); - static_assert(std::is_trivially_copyable_v<PingFrame>); - - static_assert(std::is_standard_layout_v<GoAwayFrame>); - static_assert(std::is_trivially_copyable_v<GoAwayFrame>); - - static_assert(std::is_standard_layout_v<WindowUpdateFrame>); - static_assert(std::is_trivially_copyable_v<WindowUpdateFrame>); - - static_assert(std::is_standard_layout_v<ContinuationFrame>); - static_assert(std::is_trivially_copyable_v<ContinuationFrame>); - - static_assert(std::is_standard_layout_v<AltSvcFrame>); - static_assert(std::is_trivially_copyable_v<AltSvcFrame>); - - static_assert(std::is_standard_layout_v<PriorityUpdateFrame>); - static_assert(std::is_trivially_copyable_v<PriorityUpdateFrame>); - - static_assert(std::is_standard_layout_v<AcceptChFrame>); - static_assert(std::is_trivially_copyable_v<AcceptChFrame>); - - static_assert(std::is_standard_layout_v<UnknownFrame>); - static_assert(std::is_trivially_copyable_v<UnknownFrame>); - - static_assert(sizeof(SpdyFrame) <= 64, - "SpdyFrame must fit in a 64-byte cache line"); -} - -TEST(ModernFrameTest, Http2FrameConcept) { - static_assert(Http2FrameConcept<DataFrame>); - static_assert(Http2FrameConcept<HeadersFrame>); - static_assert(Http2FrameConcept<PriorityFrame>); - static_assert(Http2FrameConcept<RstStreamFrame>); - static_assert(Http2FrameConcept<SettingsFrame>); - static_assert(Http2FrameConcept<PushPromiseFrame>); - static_assert(Http2FrameConcept<PingFrame>); - static_assert(Http2FrameConcept<GoAwayFrame>); - static_assert(Http2FrameConcept<WindowUpdateFrame>); - static_assert(Http2FrameConcept<ContinuationFrame>); - static_assert(Http2FrameConcept<AltSvcFrame>); - static_assert(Http2FrameConcept<PriorityUpdateFrame>); - static_assert(Http2FrameConcept<AcceptChFrame>); - static_assert(Http2FrameConcept<UnknownFrame>); - - // Non-frame types should not satisfy the concept. - static_assert(!Http2FrameConcept<int>); - static_assert(!Http2FrameConcept<std::string>); - static_assert(!Http2FrameConcept<PriorityFields>); - static_assert(!Http2FrameConcept<SpdyDataIR>); -} - -TEST(ModernFrameTest, FrameTraits) { - EXPECT_EQ(SpdyFrameType::DATA, frame_type_v<DataFrame>); - EXPECT_EQ(SpdyFrameType::HEADERS, frame_type_v<HeadersFrame>); - EXPECT_EQ(SpdyFrameType::PRIORITY, frame_type_v<PriorityFrame>); - EXPECT_EQ(SpdyFrameType::RST_STREAM, frame_type_v<RstStreamFrame>); - EXPECT_EQ(SpdyFrameType::SETTINGS, frame_type_v<SettingsFrame>); - EXPECT_EQ(SpdyFrameType::PUSH_PROMISE, frame_type_v<PushPromiseFrame>); - EXPECT_EQ(SpdyFrameType::PING, frame_type_v<PingFrame>); - EXPECT_EQ(SpdyFrameType::GOAWAY, frame_type_v<GoAwayFrame>); - EXPECT_EQ(SpdyFrameType::WINDOW_UPDATE, frame_type_v<WindowUpdateFrame>); - EXPECT_EQ(SpdyFrameType::CONTINUATION, frame_type_v<ContinuationFrame>); - EXPECT_EQ(SpdyFrameType::ALTSVC, frame_type_v<AltSvcFrame>); - EXPECT_EQ(SpdyFrameType::PRIORITY_UPDATE, frame_type_v<PriorityUpdateFrame>); - EXPECT_EQ(SpdyFrameType::ACCEPT_CH, frame_type_v<AcceptChFrame>); - - // Fixed size checks - EXPECT_TRUE(is_fixed_size_v<PriorityFrame>); - EXPECT_TRUE(is_fixed_size_v<RstStreamFrame>); - EXPECT_TRUE(is_fixed_size_v<PingFrame>); - EXPECT_TRUE(is_fixed_size_v<WindowUpdateFrame>); - EXPECT_FALSE(is_fixed_size_v<DataFrame>); - EXPECT_FALSE(is_fixed_size_v<HeadersFrame>); - EXPECT_FALSE(is_fixed_size_v<SettingsFrame>); - - // Stream ID presence - EXPECT_TRUE(has_stream_id_v<DataFrame>); - EXPECT_TRUE(has_stream_id_v<HeadersFrame>); - EXPECT_TRUE(has_stream_id_v<PriorityFrame>); - EXPECT_TRUE(has_stream_id_v<RstStreamFrame>); - EXPECT_TRUE(has_stream_id_v<PushPromiseFrame>); - EXPECT_TRUE(has_stream_id_v<WindowUpdateFrame>); - EXPECT_FALSE(has_stream_id_v<SettingsFrame>); - EXPECT_FALSE(has_stream_id_v<PingFrame>); - EXPECT_FALSE(has_stream_id_v<GoAwayFrame>); - - // Fin flag presence - EXPECT_TRUE(has_fin_v<DataFrame>); - EXPECT_TRUE(has_fin_v<HeadersFrame>); - EXPECT_FALSE(has_fin_v<RstStreamFrame>); - EXPECT_FALSE(has_fin_v<SettingsFrame>); - - // Padding presence - EXPECT_TRUE(has_padding_v<DataFrame>); - EXPECT_TRUE(has_padding_v<HeadersFrame>); - EXPECT_TRUE(has_padding_v<PushPromiseFrame>); - EXPECT_FALSE(has_padding_v<PingFrame>); - - // Flow control consumption - EXPECT_TRUE(consumes_flow_control_v<DataFrame>); - EXPECT_FALSE(consumes_flow_control_v<HeadersFrame>); - EXPECT_FALSE(consumes_flow_control_v<SettingsFrame>); -} - -TEST(ModernFrameTest, FrameSizeCalculation) { - DataFrame data{.stream_id = 1, .data = "hello"}; - EXPECT_EQ(14u, FrameSize(data)); - data.flags = DATA_FLAG_PADDED; - data.padding_payload_len = 3; - EXPECT_EQ(18u, FrameSize(data)); - - HeadersFrame headers{.stream_id = 1, .hpack_block = "0123456789"}; - EXPECT_EQ(19u, FrameSize(headers)); - headers.has_priority = true; - EXPECT_EQ(24u, FrameSize(headers)); - - PriorityFrame priority{.stream_id = 1}; - EXPECT_EQ(14u, FrameSize(priority)); - - RstStreamFrame rst{.stream_id = 1}; - EXPECT_EQ(13u, FrameSize(rst)); - - SettingsFrame settings_ack{.is_ack = true, .values = {}}; - EXPECT_EQ(9u, FrameSize(settings_ack)); - SettingsFrame settings_data{ - .is_ack = false, - .values = {{SETTINGS_HEADER_TABLE_SIZE, 4096}, - {SETTINGS_MAX_CONCURRENT_STREAMS, 100}}}; - EXPECT_EQ(21u, FrameSize(settings_data)); - - PushPromiseFrame push{ - .stream_id = 1, .promised_stream_id = 2, .hpack_block = "12345678"}; - EXPECT_EQ(21u, FrameSize(push)); - - PingFrame ping{}; - EXPECT_EQ(17u, FrameSize(ping)); - - GoAwayFrame goaway{.debug_data = "abcd"}; - EXPECT_EQ(21u, FrameSize(goaway)); - - WindowUpdateFrame win{.stream_id = 1, .delta = 100}; - EXPECT_EQ(13u, FrameSize(win)); - - ContinuationFrame cont{.stream_id = 1, .hpack_block = "abcdef"}; - EXPECT_EQ(15u, FrameSize(cont)); - - AltSvcFrame altsvc{.stream_id = 1, .origin = "foo", .value = "h2=\":443\""}; - EXPECT_EQ(9u + 2u + 3u + 9u, FrameSize(altsvc)); - - PriorityUpdateFrame prio_up{.prioritized_stream_id = 3, - .priority_field_value = "u=3,i"}; - EXPECT_EQ(9u + 4u + 5u, FrameSize(prio_up)); - - AcceptChFrame accept_ch{.num_entries = 1, - .entries = {{{.origin = "foo", .value = "bar"}}}}; - EXPECT_EQ(9u + 4u + 3u + 3u, FrameSize(accept_ch)); - - UnknownFrame unknown{.type = 0x99, .payload = "payload"}; - EXPECT_EQ(16u, FrameSize(unknown)); -} - -TEST(ModernFrameTest, SerializeDataFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - DataFrame df{.stream_id = 3, .flags = DATA_FLAG_FIN, .data = "hello world"}; - SpdyFrameBuilder builder(FrameSize(df)); - EXPECT_TRUE(SerializeFrame(df, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyDataIR data_ir(3, "hello world"); - data_ir.set_fin(true); - SpdySerializedFrame expected = framer.SerializeData(data_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializePriorityFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - PriorityFrame pf{ - .stream_id = 5, - .priority = {.parent_stream_id = 1, .weight = 32, .exclusive = true}}; - SpdyFrameBuilder builder(FrameSize(pf)); - EXPECT_TRUE(SerializeFrame(pf, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyPriorityIR prio_ir(5, 1, 32, true); - SpdySerializedFrame expected = framer.SerializePriority(prio_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeRstStreamFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - // 3. RST_STREAM frame - RstStreamFrame rf{.stream_id = 7, .error_code = ERROR_CODE_CANCEL}; - SpdyFrameBuilder builder(FrameSize(rf)); - EXPECT_TRUE(SerializeFrame(rf, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyRstStreamIR rst_ir(7, ERROR_CODE_CANCEL); - SpdySerializedFrame expected = framer.SerializeRstStream(rst_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeSettingsFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - // SETTINGS ack. - { - SettingsFrame sf_ack{.is_ack = true, .values = {}}; - SpdyFrameBuilder builder(FrameSize(sf_ack)); - EXPECT_TRUE(SerializeFrame(sf_ack, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdySettingsIR settings_ack_ir; - settings_ack_ir.set_is_ack(true); - SpdySerializedFrame expected = framer.SerializeSettings(settings_ack_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); - } - // SETTINGS with values. - { - SettingsFrame sf_vals{.is_ack = false, - .values = {{SETTINGS_HEADER_TABLE_SIZE, 4096}, - {SETTINGS_MAX_CONCURRENT_STREAMS, 100}}}; - SpdyFrameBuilder builder(FrameSize(sf_vals)); - EXPECT_TRUE(SerializeFrame(sf_vals, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdySettingsIR settings_ir; - settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 4096); - settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100); - SpdySerializedFrame expected = framer.SerializeSettings(settings_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); - } -} - -TEST(ModernFrameTest, SerializePingFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - PingFrame ping{.opaque_data = 0x0102030405060708ULL, .is_ack = true}; - SpdyFrameBuilder builder(FrameSize(ping)); - EXPECT_TRUE(SerializeFrame(ping, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyPingIR ping_ir(0x0102030405060708ULL); - ping_ir.set_is_ack(true); - SpdySerializedFrame expected = framer.SerializePing(ping_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeGoAwayFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - GoAwayFrame goaway{.last_good_stream_id = 9, - .error_code = ERROR_CODE_PROTOCOL_ERROR, - .debug_data = "protocol error occurred"}; - SpdyFrameBuilder builder(FrameSize(goaway)); - EXPECT_TRUE(SerializeFrame(goaway, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyGoAwayIR goaway_ir(9, ERROR_CODE_PROTOCOL_ERROR, - "protocol error occurred"); - SpdySerializedFrame expected = framer.SerializeGoAway(goaway_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeWindowUpdateFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - WindowUpdateFrame win{.stream_id = 11, .delta = 65535}; - SpdyFrameBuilder builder(FrameSize(win)); - EXPECT_TRUE(SerializeFrame(win, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyWindowUpdateIR win_ir(11, 65535); - SpdySerializedFrame expected = framer.SerializeWindowUpdate(win_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeContinuationFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - ContinuationFrame cont{.stream_id = 13, - .flags = HEADERS_FLAG_END_HEADERS, - .hpack_block = "continuation_block"}; - SpdyFrameBuilder builder(FrameSize(cont)); - EXPECT_TRUE(SerializeFrame(cont, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyContinuationIR cont_ir(13); - cont_ir.set_end_headers(true); - cont_ir.take_encoding("continuation_block"); - SpdySerializedFrame expected = framer.SerializeContinuation(cont_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializePriorityUpdateFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - PriorityUpdateFrame prio_up{.prioritized_stream_id = 5, - .priority_field_value = "u=1,i"}; - SpdyFrameBuilder builder(FrameSize(prio_up)); - EXPECT_TRUE(SerializeFrame(prio_up, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyPriorityUpdateIR prio_up_ir(0, 5, "u=1,i"); - SpdySerializedFrame expected = framer.SerializePriorityUpdate(prio_up_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeAcceptChFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - AcceptChFrame accept_ch{ - .num_entries = 1, - .entries = {{{.origin = "example.com", .value = "sec-ch-ua"}}}}; - SpdyFrameBuilder builder(FrameSize(accept_ch)); - EXPECT_TRUE(SerializeFrame(accept_ch, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyAcceptChIR accept_ch_ir({{"example.com", "sec-ch-ua"}}); - SpdySerializedFrame expected = framer.SerializeAcceptCh(accept_ch_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); -} - -TEST(ModernFrameTest, SerializeUnknownFrame) { - SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); - - // 11. UNKNOWN frame - { - UnknownFrame unknown{.stream_id = 1, - .type = 0xfe, - .flags = 0x05, - .payload = "custom_payload"}; - SpdyFrameBuilder builder(FrameSize(unknown)); - EXPECT_TRUE(SerializeFrame(unknown, builder)); - SpdySerializedFrame serialized = builder.take(); - - SpdyUnknownIR unknown_ir(1, 0xfe, 0x05, "custom_payload"); - SpdySerializedFrame expected = framer.SerializeUnknown(unknown_ir); - - EXPECT_EQ(absl::string_view(expected), absl::string_view(serialized)); - } -} - -TEST(ModernFrameTest, SpdyFrameVariant) { - SpdyFrame frame = DataFrame{.stream_id = 42, .data = "payload"}; - EXPECT_EQ(42u, GetFrameStreamId(frame)); - EXPECT_EQ(SpdyFrameType::DATA, GetFrameType(frame)); - EXPECT_EQ(16u, GetFrameSize(frame)); - - frame = PingFrame{.opaque_data = 123}; - EXPECT_EQ(0u, GetFrameStreamId(frame)); - EXPECT_EQ(SpdyFrameType::PING, GetFrameType(frame)); - EXPECT_EQ(17u, GetFrameSize(frame)); - - SpdyFrameBuilder builder(GetFrameSize(frame)); - EXPECT_TRUE(SerializeSpdyFrame(frame, builder)); - EXPECT_EQ(17u, builder.length()); -} - } // namespace test } // namespace spdy