| // Copyright (c) 2012 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. | 
 |  | 
 | // This file contains some protocol structures for use with SPDY 3 and HTTP 2 | 
 | // The SPDY 3 spec can be found at: | 
 | // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3 | 
 |  | 
 | #ifndef QUICHE_SPDY_CORE_SPDY_PROTOCOL_H_ | 
 | #define QUICHE_SPDY_CORE_SPDY_PROTOCOL_H_ | 
 |  | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <iosfwd> | 
 | #include <limits> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <new> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "common/platform/api/quiche_export.h" | 
 | #include "common/platform/api/quiche_logging.h" | 
 | #include "spdy/core/spdy_alt_svc_wire_format.h" | 
 | #include "spdy/core/spdy_bitmasks.h" | 
 | #include "spdy/core/spdy_header_block.h" | 
 |  | 
 | namespace spdy { | 
 |  | 
 | // A stream ID is a 31-bit entity. | 
 | using SpdyStreamId = uint32_t; | 
 |  | 
 | // A SETTINGS ID is a 16-bit entity. | 
 | using SpdySettingsId = uint16_t; | 
 |  | 
 | // Specifies the stream ID used to denote the current session (for | 
 | // flow control). | 
 | const SpdyStreamId kSessionFlowControlStreamId = 0; | 
 |  | 
 | // 0 is not a valid stream ID for any other purpose than flow control. | 
 | const SpdyStreamId kInvalidStreamId = 0; | 
 |  | 
 | // Max stream id. | 
 | const SpdyStreamId kMaxStreamId = 0x7fffffff; | 
 |  | 
 | // The maximum possible frame payload size allowed by the spec. | 
 | const uint32_t kSpdyMaxFrameSizeLimit = (1 << 24) - 1; | 
 |  | 
 | // The initial value for the maximum frame payload size as per the spec. This is | 
 | // the maximum control frame size we accept. | 
 | const uint32_t kHttp2DefaultFramePayloadLimit = 1 << 14; | 
 |  | 
 | // The maximum size of the control frames that we send, including the size of | 
 | // the header. This limit is arbitrary. We can enforce it here or at the | 
 | // application layer. We chose the framing layer, but this can be changed (or | 
 | // removed) if necessary later down the line. | 
 | const size_t kHttp2MaxControlFrameSendSize = kHttp2DefaultFramePayloadLimit - 1; | 
 |  | 
 | // Number of octets in the frame header. | 
 | const size_t kFrameHeaderSize = 9; | 
 |  | 
 | // The initial value for the maximum frame payload size as per the spec. This is | 
 | // the maximum control frame size we accept. | 
 | const uint32_t kHttp2DefaultFrameSizeLimit = | 
 |     kHttp2DefaultFramePayloadLimit + kFrameHeaderSize; | 
 |  | 
 | // The initial value for the maximum size of the header list, "unlimited" (max | 
 | // unsigned 32-bit int) as per the spec. | 
 | const uint32_t kSpdyInitialHeaderListSizeLimit = 0xFFFFFFFF; | 
 |  | 
 | // Maximum window size for a Spdy stream or session. | 
 | const int32_t kSpdyMaximumWindowSize = 0x7FFFFFFF;  // Max signed 32bit int | 
 |  | 
 | // Maximum padding size in octets for one DATA or HEADERS or PUSH_PROMISE frame. | 
 | const int32_t kPaddingSizePerFrame = 256; | 
 |  | 
 | // The HTTP/2 connection preface, which must be the first bytes sent by the | 
 | // client upon starting an HTTP/2 connection, and which must be followed by a | 
 | // SETTINGS frame.  Note that even though |kHttp2ConnectionHeaderPrefix| is | 
 | // defined as a string literal with a null terminator, the actual connection | 
 | // preface is only the first |kHttp2ConnectionHeaderPrefixSize| bytes, which | 
 | // excludes the null terminator. | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2ConnectionHeaderPrefix; | 
 | const int kHttp2ConnectionHeaderPrefixSize = 24; | 
 |  | 
 | // Wire values for HTTP2 frame types. | 
 | enum class SpdyFrameType : uint8_t { | 
 |   DATA = 0x00, | 
 |   HEADERS = 0x01, | 
 |   PRIORITY = 0x02, | 
 |   RST_STREAM = 0x03, | 
 |   SETTINGS = 0x04, | 
 |   PUSH_PROMISE = 0x05, | 
 |   PING = 0x06, | 
 |   GOAWAY = 0x07, | 
 |   WINDOW_UPDATE = 0x08, | 
 |   CONTINUATION = 0x09, | 
 |   // ALTSVC is a public extension. | 
 |   ALTSVC = 0x0a, | 
 |   PRIORITY_UPDATE = 0x10, | 
 |   ACCEPT_CH = 0x89, | 
 | }; | 
 |  | 
 | // Flags on data packets. | 
 | enum SpdyDataFlags { | 
 |   DATA_FLAG_NONE = 0x00, | 
 |   DATA_FLAG_FIN = 0x01, | 
 |   DATA_FLAG_PADDED = 0x08, | 
 | }; | 
 |  | 
 | // Flags on control packets | 
 | enum SpdyControlFlags { | 
 |   CONTROL_FLAG_NONE = 0x00, | 
 |   CONTROL_FLAG_FIN = 0x01, | 
 | }; | 
 |  | 
 | enum SpdyPingFlags { | 
 |   PING_FLAG_ACK = 0x01, | 
 | }; | 
 |  | 
 | // Used by HEADERS, PUSH_PROMISE, and CONTINUATION. | 
 | enum SpdyHeadersFlags { | 
 |   HEADERS_FLAG_END_HEADERS = 0x04, | 
 |   HEADERS_FLAG_PADDED = 0x08, | 
 |   HEADERS_FLAG_PRIORITY = 0x20, | 
 | }; | 
 |  | 
 | enum SpdyPushPromiseFlags { | 
 |   PUSH_PROMISE_FLAG_END_PUSH_PROMISE = 0x04, | 
 |   PUSH_PROMISE_FLAG_PADDED = 0x08, | 
 | }; | 
 |  | 
 | enum Http2SettingsControlFlags { | 
 |   SETTINGS_FLAG_ACK = 0x01, | 
 | }; | 
 |  | 
 | // Wire values of HTTP/2 setting identifiers. | 
 | enum SpdyKnownSettingsId : SpdySettingsId { | 
 |   // HPACK header table maximum size. | 
 |   SETTINGS_HEADER_TABLE_SIZE = 0x1, | 
 |   SETTINGS_MIN = SETTINGS_HEADER_TABLE_SIZE, | 
 |   // Whether or not server push (PUSH_PROMISE) is enabled. | 
 |   SETTINGS_ENABLE_PUSH = 0x2, | 
 |   // The maximum number of simultaneous live streams in each direction. | 
 |   SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, | 
 |   // Initial window size in bytes | 
 |   SETTINGS_INITIAL_WINDOW_SIZE = 0x4, | 
 |   // The size of the largest frame payload that a receiver is willing to accept. | 
 |   SETTINGS_MAX_FRAME_SIZE = 0x5, | 
 |   // The maximum size of header list that the sender is prepared to accept. | 
 |   SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, | 
 |   // Enable Websockets over HTTP/2, see | 
 |   // https://httpwg.org/specs/rfc8441.html | 
 |   SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8, | 
 |   // Disable HTTP/2 priorities, see | 
 |   // https://tools.ietf.org/html/draft-ietf-httpbis-priority-02. | 
 |   SETTINGS_DEPRECATE_HTTP2_PRIORITIES = 0x9, | 
 |   SETTINGS_MAX = SETTINGS_DEPRECATE_HTTP2_PRIORITIES, | 
 |   // Experimental setting used to configure an alternative write scheduler. | 
 |   SETTINGS_EXPERIMENT_SCHEDULER = 0xFF45, | 
 | }; | 
 |  | 
 | // This explicit operator is needed, otherwise compiler finds | 
 | // overloaded operator to be ambiguous. | 
 | QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, | 
 |                                                SpdyKnownSettingsId id); | 
 |  | 
 | // This operator is needed, because SpdyFrameType is an enum class, | 
 | // therefore implicit conversion to underlying integer type is not allowed. | 
 | QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, | 
 |                                                SpdyFrameType frame_type); | 
 |  | 
 | using SettingsMap = std::map<SpdySettingsId, uint32_t>; | 
 |  | 
 | // HTTP/2 error codes, RFC 7540 Section 7. | 
 | enum SpdyErrorCode : uint32_t { | 
 |   ERROR_CODE_NO_ERROR = 0x0, | 
 |   ERROR_CODE_PROTOCOL_ERROR = 0x1, | 
 |   ERROR_CODE_INTERNAL_ERROR = 0x2, | 
 |   ERROR_CODE_FLOW_CONTROL_ERROR = 0x3, | 
 |   ERROR_CODE_SETTINGS_TIMEOUT = 0x4, | 
 |   ERROR_CODE_STREAM_CLOSED = 0x5, | 
 |   ERROR_CODE_FRAME_SIZE_ERROR = 0x6, | 
 |   ERROR_CODE_REFUSED_STREAM = 0x7, | 
 |   ERROR_CODE_CANCEL = 0x8, | 
 |   ERROR_CODE_COMPRESSION_ERROR = 0x9, | 
 |   ERROR_CODE_CONNECT_ERROR = 0xa, | 
 |   ERROR_CODE_ENHANCE_YOUR_CALM = 0xb, | 
 |   ERROR_CODE_INADEQUATE_SECURITY = 0xc, | 
 |   ERROR_CODE_HTTP_1_1_REQUIRED = 0xd, | 
 |   ERROR_CODE_MAX = ERROR_CODE_HTTP_1_1_REQUIRED | 
 | }; | 
 |  | 
 | // Type of priority write scheduler. | 
 | enum class WriteSchedulerType { | 
 |   LIFO,  // Last added stream has the highest priority. | 
 |   SPDY,  // Uses SPDY priorities described in | 
 |          // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-Stream-priority. | 
 |   HTTP2,  // Uses HTTP2 (tree-style) priority described in | 
 |           // https://tools.ietf.org/html/rfc7540#section-5.3. | 
 |   FIFO,   // Stream with the smallest stream ID has the highest priority. | 
 | }; | 
 |  | 
 | // A SPDY priority is a number between 0 and 7 (inclusive). | 
 | typedef uint8_t SpdyPriority; | 
 |  | 
 | // Lowest and Highest here refer to SPDY priorities as described in | 
 | // https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-2.3.3-Stream-priority | 
 | const SpdyPriority kV3HighestPriority = 0; | 
 | const SpdyPriority kV3LowestPriority = 7; | 
 |  | 
 | // Returns SPDY 3.x priority value clamped to the valid range of [0, 7]. | 
 | QUICHE_EXPORT_PRIVATE SpdyPriority ClampSpdy3Priority(SpdyPriority priority); | 
 |  | 
 | // HTTP/2 stream weights are integers in range [1, 256], as specified in RFC | 
 | // 7540 section 5.3.2. Default stream weight is defined in section 5.3.5. | 
 | const int kHttp2MinStreamWeight = 1; | 
 | const int kHttp2MaxStreamWeight = 256; | 
 | const int kHttp2DefaultStreamWeight = 16; | 
 |  | 
 | // Returns HTTP/2 weight clamped to the valid range of [1, 256]. | 
 | QUICHE_EXPORT_PRIVATE int ClampHttp2Weight(int weight); | 
 |  | 
 | // Maps SPDY 3.x priority value in range [0, 7] to HTTP/2 weight value in range | 
 | // [1, 256], where priority 0 (i.e. highest precedence) corresponds to maximum | 
 | // weight 256 and priority 7 (lowest precedence) corresponds to minimum weight | 
 | // 1. | 
 | QUICHE_EXPORT_PRIVATE int Spdy3PriorityToHttp2Weight(SpdyPriority priority); | 
 |  | 
 | // Maps HTTP/2 weight value in range [1, 256] to SPDY 3.x priority value in | 
 | // range [0, 7], where minimum weight 1 corresponds to priority 7 (lowest | 
 | // precedence) and maximum weight 256 corresponds to priority 0 (highest | 
 | // precedence). | 
 | QUICHE_EXPORT_PRIVATE SpdyPriority Http2WeightToSpdy3Priority(int weight); | 
 |  | 
 | // Reserved ID for root stream of HTTP/2 stream dependency tree, as specified | 
 | // in RFC 7540 section 5.3.1. | 
 | const unsigned int kHttp2RootStreamId = 0; | 
 |  | 
 | typedef uint64_t SpdyPingId; | 
 |  | 
 | // Returns true if a given on-the-wire enumeration of a frame type is defined | 
 | // in a standardized HTTP/2 specification, false otherwise. | 
 | QUICHE_EXPORT_PRIVATE bool IsDefinedFrameType(uint8_t frame_type_field); | 
 |  | 
 | // Parses a frame type from an on-the-wire enumeration. | 
 | // Behavior is undefined for invalid frame type fields; consumers should first | 
 | // use IsValidFrameType() to verify validity of frame type fields. | 
 | QUICHE_EXPORT_PRIVATE SpdyFrameType ParseFrameType(uint8_t frame_type_field); | 
 |  | 
 | // Serializes a frame type to the on-the-wire value. | 
 | QUICHE_EXPORT_PRIVATE uint8_t SerializeFrameType(SpdyFrameType frame_type); | 
 |  | 
 | // (HTTP/2) All standard frame types except WINDOW_UPDATE are | 
 | // (stream-specific xor connection-level). Returns false iff we know | 
 | // the given frame type does not align with the given streamID. | 
 | QUICHE_EXPORT_PRIVATE bool IsValidHTTP2FrameStreamId( | 
 |     SpdyStreamId current_frame_stream_id, | 
 |     SpdyFrameType frame_type_field); | 
 |  | 
 | // Serialize |frame_type| to string for logging/debugging. | 
 | QUICHE_EXPORT_PRIVATE const char* FrameTypeToString(SpdyFrameType frame_type); | 
 |  | 
 | // If |wire_setting_id| is the on-the-wire representation of a defined SETTINGS | 
 | // parameter, parse it to |*setting_id| and return true. | 
 | QUICHE_EXPORT_PRIVATE bool ParseSettingsId(SpdySettingsId wire_setting_id, | 
 |                                            SpdyKnownSettingsId* setting_id); | 
 |  | 
 | // Returns a string representation of the |id| for logging/debugging. Returns | 
 | // the |id| prefixed with "SETTINGS_UNKNOWN_" for unknown SETTINGS IDs. To parse | 
 | // the |id| into a SpdyKnownSettingsId (if applicable), use ParseSettingsId(). | 
 | QUICHE_EXPORT_PRIVATE std::string SettingsIdToString(SpdySettingsId id); | 
 |  | 
 | // Parse |wire_error_code| to a SpdyErrorCode. | 
 | // Treat unrecognized error codes as INTERNAL_ERROR | 
 | // as recommended by the HTTP/2 specification. | 
 | QUICHE_EXPORT_PRIVATE SpdyErrorCode ParseErrorCode(uint32_t wire_error_code); | 
 |  | 
 | // Serialize RST_STREAM or GOAWAY frame error code to string | 
 | // for logging/debugging. | 
 | const char* ErrorCodeToString(SpdyErrorCode error_code); | 
 |  | 
 | // Serialize |type| to string for logging/debugging. | 
 | QUICHE_EXPORT_PRIVATE const char* WriteSchedulerTypeToString( | 
 |     WriteSchedulerType type); | 
 |  | 
 | // Minimum size of a frame, in octets. | 
 | const size_t kFrameMinimumSize = kFrameHeaderSize; | 
 |  | 
 | // Minimum frame size for variable size frame types (includes mandatory fields), | 
 | // frame size for fixed size frames, in octets. | 
 |  | 
 | const size_t kDataFrameMinimumSize = kFrameHeaderSize; | 
 | const size_t kHeadersFrameMinimumSize = kFrameHeaderSize; | 
 | // PRIORITY frame has stream_dependency (4 octets) and weight (1 octet) fields. | 
 | const size_t kPriorityFrameSize = kFrameHeaderSize + 5; | 
 | // RST_STREAM frame has error_code (4 octets) field. | 
 | const size_t kRstStreamFrameSize = kFrameHeaderSize + 4; | 
 | const size_t kSettingsFrameMinimumSize = kFrameHeaderSize; | 
 | const size_t kSettingsOneSettingSize = | 
 |     sizeof(uint32_t) + sizeof(SpdySettingsId); | 
 | // PUSH_PROMISE frame has promised_stream_id (4 octet) field. | 
 | const size_t kPushPromiseFrameMinimumSize = kFrameHeaderSize + 4; | 
 | // PING frame has opaque_bytes (8 octet) field. | 
 | const size_t kPingFrameSize = kFrameHeaderSize + 8; | 
 | // GOAWAY frame has last_stream_id (4 octet) and error_code (4 octet) fields. | 
 | const size_t kGoawayFrameMinimumSize = kFrameHeaderSize + 8; | 
 | // WINDOW_UPDATE frame has window_size_increment (4 octet) field. | 
 | const size_t kWindowUpdateFrameSize = kFrameHeaderSize + 4; | 
 | const size_t kContinuationFrameMinimumSize = kFrameHeaderSize; | 
 | // ALTSVC frame has origin_len (2 octets) field. | 
 | const size_t kGetAltSvcFrameMinimumSize = kFrameHeaderSize + 2; | 
 | // PRIORITY_UPDATE frame has prioritized_stream_id (4 octets) field. | 
 | const size_t kPriorityUpdateFrameMinimumSize = kFrameHeaderSize + 4; | 
 | // ACCEPT_CH frame may have empty payload. | 
 | const size_t kAcceptChFrameMinimumSize = kFrameHeaderSize; | 
 | // Each ACCEPT_CH frame entry has a 16-bit origin length and a 16-bit value | 
 | // length. | 
 | const size_t kAcceptChFramePerEntryOverhead = 4; | 
 |  | 
 | // Maximum possible configurable size of a frame in octets. | 
 | const size_t kMaxFrameSizeLimit = kSpdyMaxFrameSizeLimit + kFrameHeaderSize; | 
 | // Size of a header block size field. | 
 | const size_t kSizeOfSizeField = sizeof(uint32_t); | 
 | // Initial window size for a stream in bytes. | 
 | const int32_t kInitialStreamWindowSize = 64 * 1024 - 1; | 
 | // Initial window size for a session in bytes. | 
 | const int32_t kInitialSessionWindowSize = 64 * 1024 - 1; | 
 | // The NPN string for HTTP2, "h2". | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2Npn; | 
 | // An estimate size of the HPACK overhead for each header field. 1 bytes for | 
 | // indexed literal, 1 bytes for key literal and length encoding, and 2 bytes for | 
 | // value literal and length encoding. | 
 | const size_t kPerHeaderHpackOverhead = 4; | 
 |  | 
 | // Names of pseudo-headers defined for HTTP/2 requests. | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2AuthorityHeader; | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2MethodHeader; | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2PathHeader; | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2SchemeHeader; | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2ProtocolHeader; | 
 |  | 
 | // Name of pseudo-header defined for HTTP/2 responses. | 
 | QUICHE_EXPORT_PRIVATE extern const char* const kHttp2StatusHeader; | 
 |  | 
 | QUICHE_EXPORT_PRIVATE size_t GetNumberRequiredContinuationFrames(size_t size); | 
 |  | 
 | // Variant type (i.e. tagged union) that is either a SPDY 3.x priority value, | 
 | // or else an HTTP/2 stream dependency tuple {parent stream ID, weight, | 
 | // exclusive bit}. Templated to allow for use by QUIC code; SPDY and HTTP/2 | 
 | // code should use the concrete type instantiation SpdyStreamPrecedence. | 
 | template <typename StreamIdType> | 
 | class QUICHE_EXPORT_PRIVATE StreamPrecedence { | 
 |  public: | 
 |   // Constructs instance that is a SPDY 3.x priority. Clamps priority value to | 
 |   // the valid range [0, 7]. | 
 |   explicit StreamPrecedence(SpdyPriority priority) | 
 |       : is_spdy3_priority_(true), | 
 |         spdy3_priority_(ClampSpdy3Priority(priority)) {} | 
 |  | 
 |   // Constructs instance that is an HTTP/2 stream weight, parent stream ID, and | 
 |   // exclusive bit. Clamps stream weight to the valid range [1, 256]. | 
 |   StreamPrecedence(StreamIdType parent_id, int weight, bool is_exclusive) | 
 |       : is_spdy3_priority_(false), | 
 |         http2_stream_dependency_{parent_id, ClampHttp2Weight(weight), | 
 |                                  is_exclusive} {} | 
 |  | 
 |   // Intentionally copyable, to support pass by value. | 
 |   StreamPrecedence(const StreamPrecedence& other) = default; | 
 |   StreamPrecedence& operator=(const StreamPrecedence& other) = default; | 
 |  | 
 |   // Returns true if this instance is a SPDY 3.x priority, or false if this | 
 |   // instance is an HTTP/2 stream dependency. | 
 |   bool is_spdy3_priority() const { return is_spdy3_priority_; } | 
 |  | 
 |   // Returns SPDY 3.x priority value. If |is_spdy3_priority()| is true, this is | 
 |   // the value provided at construction, clamped to the legal priority | 
 |   // range. Otherwise, it is the HTTP/2 stream weight mapped to a SPDY 3.x | 
 |   // priority value, where minimum weight 1 corresponds to priority 7 (lowest | 
 |   // precedence) and maximum weight 256 corresponds to priority 0 (highest | 
 |   // precedence). | 
 |   SpdyPriority spdy3_priority() const { | 
 |     return is_spdy3_priority_ | 
 |                ? spdy3_priority_ | 
 |                : Http2WeightToSpdy3Priority(http2_stream_dependency_.weight); | 
 |   } | 
 |  | 
 |   // Returns HTTP/2 parent stream ID. If |is_spdy3_priority()| is false, this is | 
 |   // the value provided at construction, otherwise it is |kHttp2RootStreamId|. | 
 |   StreamIdType parent_id() const { | 
 |     return is_spdy3_priority_ ? kHttp2RootStreamId | 
 |                               : http2_stream_dependency_.parent_id; | 
 |   } | 
 |  | 
 |   // Returns HTTP/2 stream weight. If |is_spdy3_priority()| is false, this is | 
 |   // the value provided at construction, clamped to the legal weight | 
 |   // range. Otherwise, it is the SPDY 3.x priority value mapped to an HTTP/2 | 
 |   // stream weight, where priority 0 (i.e. highest precedence) corresponds to | 
 |   // maximum weight 256 and priority 7 (lowest precedence) corresponds to | 
 |   // minimum weight 1. | 
 |   int weight() const { | 
 |     return is_spdy3_priority_ ? Spdy3PriorityToHttp2Weight(spdy3_priority_) | 
 |                               : http2_stream_dependency_.weight; | 
 |   } | 
 |  | 
 |   // Returns HTTP/2 parent stream exclusivity. If |is_spdy3_priority()| is | 
 |   // false, this is the value provided at construction, otherwise it is false. | 
 |   bool is_exclusive() const { | 
 |     return !is_spdy3_priority_ && http2_stream_dependency_.is_exclusive; | 
 |   } | 
 |  | 
 |   // Facilitates test assertions. | 
 |   bool operator==(const StreamPrecedence& other) const { | 
 |     if (is_spdy3_priority()) { | 
 |       return other.is_spdy3_priority() && | 
 |              (spdy3_priority() == other.spdy3_priority()); | 
 |     } else { | 
 |       return !other.is_spdy3_priority() && (parent_id() == other.parent_id()) && | 
 |              (weight() == other.weight()) && | 
 |              (is_exclusive() == other.is_exclusive()); | 
 |     } | 
 |   } | 
 |  | 
 |   bool operator!=(const StreamPrecedence& other) const { | 
 |     return !(*this == other); | 
 |   } | 
 |  | 
 |  private: | 
 |   struct QUICHE_EXPORT_PRIVATE Http2StreamDependency { | 
 |     StreamIdType parent_id; | 
 |     int weight; | 
 |     bool is_exclusive; | 
 |   }; | 
 |  | 
 |   bool is_spdy3_priority_; | 
 |   union { | 
 |     SpdyPriority spdy3_priority_; | 
 |     Http2StreamDependency http2_stream_dependency_; | 
 |   }; | 
 | }; | 
 |  | 
 | typedef StreamPrecedence<SpdyStreamId> SpdyStreamPrecedence; | 
 |  | 
 | class SpdyFrameVisitor; | 
 |  | 
 | // Intermediate representation for HTTP2 frames. | 
 | class QUICHE_EXPORT_PRIVATE SpdyFrameIR { | 
 |  public: | 
 |   virtual ~SpdyFrameIR() {} | 
 |  | 
 |   virtual void Visit(SpdyFrameVisitor* visitor) const = 0; | 
 |   virtual SpdyFrameType frame_type() const = 0; | 
 |   SpdyStreamId stream_id() const { return stream_id_; } | 
 |   virtual bool fin() const; | 
 |   // Returns an estimate of the size of the serialized frame, without applying | 
 |   // compression. May not be exact. | 
 |   virtual size_t size() const = 0; | 
 |  | 
 |   // Returns the number of bytes of flow control window that would be consumed | 
 |   // by this frame if written to the wire. | 
 |   virtual int flow_control_window_consumed() const; | 
 |  | 
 |  protected: | 
 |   SpdyFrameIR() : stream_id_(0) {} | 
 |   explicit SpdyFrameIR(SpdyStreamId stream_id) : stream_id_(stream_id) {} | 
 |   SpdyFrameIR(const SpdyFrameIR&) = delete; | 
 |   SpdyFrameIR& operator=(const SpdyFrameIR&) = delete; | 
 |  | 
 |  private: | 
 |   SpdyStreamId stream_id_; | 
 | }; | 
 |  | 
 | // Abstract class intended to be inherited by IRs that have the option of a FIN | 
 | // flag. | 
 | class QUICHE_EXPORT_PRIVATE SpdyFrameWithFinIR : public SpdyFrameIR { | 
 |  public: | 
 |   ~SpdyFrameWithFinIR() override {} | 
 |   bool fin() const override; | 
 |   void set_fin(bool fin) { fin_ = fin; } | 
 |  | 
 |  protected: | 
 |   explicit SpdyFrameWithFinIR(SpdyStreamId stream_id) | 
 |       : SpdyFrameIR(stream_id), fin_(false) {} | 
 |   SpdyFrameWithFinIR(const SpdyFrameWithFinIR&) = delete; | 
 |   SpdyFrameWithFinIR& operator=(const SpdyFrameWithFinIR&) = delete; | 
 |  | 
 |  private: | 
 |   bool fin_; | 
 | }; | 
 |  | 
 | // Abstract class intended to be inherited by IRs that contain a header | 
 | // block. Implies SpdyFrameWithFinIR. | 
 | class QUICHE_EXPORT_PRIVATE SpdyFrameWithHeaderBlockIR | 
 |     : public SpdyFrameWithFinIR { | 
 |  public: | 
 |   ~SpdyFrameWithHeaderBlockIR() override; | 
 |  | 
 |   const Http2HeaderBlock& header_block() const { return header_block_; } | 
 |   void set_header_block(Http2HeaderBlock header_block) { | 
 |     // Deep copy. | 
 |     header_block_ = std::move(header_block); | 
 |   } | 
 |   void SetHeader(absl::string_view name, absl::string_view value) { | 
 |     header_block_[name] = value; | 
 |   } | 
 |  | 
 |  protected: | 
 |   SpdyFrameWithHeaderBlockIR(SpdyStreamId stream_id, | 
 |                              Http2HeaderBlock header_block); | 
 |   SpdyFrameWithHeaderBlockIR(const SpdyFrameWithHeaderBlockIR&) = delete; | 
 |   SpdyFrameWithHeaderBlockIR& operator=(const SpdyFrameWithHeaderBlockIR&) = | 
 |       delete; | 
 |  | 
 |  private: | 
 |   Http2HeaderBlock header_block_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyDataIR : public SpdyFrameWithFinIR { | 
 |  public: | 
 |   // Performs a deep copy on data. | 
 |   SpdyDataIR(SpdyStreamId stream_id, absl::string_view data); | 
 |  | 
 |   // Performs a deep copy on data. | 
 |   SpdyDataIR(SpdyStreamId stream_id, const char* data); | 
 |  | 
 |   // Moves data into data_store_. Makes a copy if passed a non-movable string. | 
 |   SpdyDataIR(SpdyStreamId stream_id, std::string data); | 
 |  | 
 |   // Use in conjunction with SetDataShallow() for shallow-copy on data. | 
 |   explicit SpdyDataIR(SpdyStreamId stream_id); | 
 |   SpdyDataIR(const SpdyDataIR&) = delete; | 
 |   SpdyDataIR& operator=(const SpdyDataIR&) = delete; | 
 |  | 
 |   ~SpdyDataIR() override; | 
 |  | 
 |   const char* data() const { return data_; } | 
 |   size_t data_len() const { return data_len_; } | 
 |  | 
 |   bool padded() const { return padded_; } | 
 |  | 
 |   int padding_payload_len() const { return padding_payload_len_; } | 
 |  | 
 |   void set_padding_len(int padding_len) { | 
 |     QUICHE_DCHECK_GT(padding_len, 0); | 
 |     QUICHE_DCHECK_LE(padding_len, kPaddingSizePerFrame); | 
 |     padded_ = true; | 
 |     // The pad field takes one octet on the wire. | 
 |     padding_payload_len_ = padding_len - 1; | 
 |   } | 
 |  | 
 |   // Deep-copy of data (keep private copy). | 
 |   void SetDataDeep(absl::string_view data) { | 
 |     data_store_ = std::make_unique<std::string>(data.data(), data.size()); | 
 |     data_ = data_store_->data(); | 
 |     data_len_ = data.size(); | 
 |   } | 
 |  | 
 |   // Shallow-copy of data (do not keep private copy). | 
 |   void SetDataShallow(absl::string_view data) { | 
 |     data_store_.reset(); | 
 |     data_ = data.data(); | 
 |     data_len_ = data.size(); | 
 |   } | 
 |  | 
 |   // Use this method if we don't have a contiguous buffer and only | 
 |   // need a length. | 
 |   void SetDataShallow(size_t len) { | 
 |     data_store_.reset(); | 
 |     data_ = nullptr; | 
 |     data_len_ = len; | 
 |   } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   int flow_control_window_consumed() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   // Used to store data that this SpdyDataIR should own. | 
 |   std::unique_ptr<std::string> data_store_; | 
 |   const char* data_; | 
 |   size_t data_len_; | 
 |  | 
 |   bool padded_; | 
 |   // padding_payload_len_ = desired padding length - len(padding length field). | 
 |   int padding_payload_len_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyRstStreamIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyRstStreamIR(SpdyStreamId stream_id, SpdyErrorCode error_code); | 
 |   SpdyRstStreamIR(const SpdyRstStreamIR&) = delete; | 
 |   SpdyRstStreamIR& operator=(const SpdyRstStreamIR&) = delete; | 
 |  | 
 |   ~SpdyRstStreamIR() override; | 
 |  | 
 |   SpdyErrorCode error_code() const { return error_code_; } | 
 |   void set_error_code(SpdyErrorCode error_code) { error_code_ = error_code; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SpdyErrorCode error_code_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdySettingsIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdySettingsIR(); | 
 |   SpdySettingsIR(const SpdySettingsIR&) = delete; | 
 |   SpdySettingsIR& operator=(const SpdySettingsIR&) = delete; | 
 |   ~SpdySettingsIR() override; | 
 |  | 
 |   // Overwrites as appropriate. | 
 |   const SettingsMap& values() const { return values_; } | 
 |   void AddSetting(SpdySettingsId id, int32_t value) { values_[id] = value; } | 
 |  | 
 |   bool is_ack() const { return is_ack_; } | 
 |   void set_is_ack(bool is_ack) { is_ack_ = is_ack; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SettingsMap values_; | 
 |   bool is_ack_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyPingIR : public SpdyFrameIR { | 
 |  public: | 
 |   explicit SpdyPingIR(SpdyPingId id) : id_(id), is_ack_(false) {} | 
 |   SpdyPingIR(const SpdyPingIR&) = delete; | 
 |   SpdyPingIR& operator=(const SpdyPingIR&) = delete; | 
 |   SpdyPingId id() const { return id_; } | 
 |  | 
 |   bool is_ack() const { return is_ack_; } | 
 |   void set_is_ack(bool is_ack) { is_ack_ = is_ack; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SpdyPingId id_; | 
 |   bool is_ack_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR { | 
 |  public: | 
 |   // References description, doesn't copy it, so description must outlast | 
 |   // this SpdyGoAwayIR. | 
 |   SpdyGoAwayIR(SpdyStreamId last_good_stream_id, | 
 |                SpdyErrorCode error_code, | 
 |                absl::string_view description); | 
 |  | 
 |   // References description, doesn't copy it, so description must outlast | 
 |   // this SpdyGoAwayIR. | 
 |   SpdyGoAwayIR(SpdyStreamId last_good_stream_id, | 
 |                SpdyErrorCode error_code, | 
 |                const char* description); | 
 |  | 
 |   // Moves description into description_store_, so caller doesn't need to | 
 |   // keep description live after constructing this SpdyGoAwayIR. | 
 |   SpdyGoAwayIR(SpdyStreamId last_good_stream_id, | 
 |                SpdyErrorCode error_code, | 
 |                std::string description); | 
 |   SpdyGoAwayIR(const SpdyGoAwayIR&) = delete; | 
 |   SpdyGoAwayIR& operator=(const SpdyGoAwayIR&) = delete; | 
 |  | 
 |   ~SpdyGoAwayIR() override; | 
 |  | 
 |   SpdyStreamId last_good_stream_id() const { return last_good_stream_id_; } | 
 |   void set_last_good_stream_id(SpdyStreamId last_good_stream_id) { | 
 |     QUICHE_DCHECK_EQ(0u, last_good_stream_id & ~kStreamIdMask); | 
 |     last_good_stream_id_ = last_good_stream_id; | 
 |   } | 
 |   SpdyErrorCode error_code() const { return error_code_; } | 
 |   void set_error_code(SpdyErrorCode error_code) { | 
 |     // TODO(hkhalil): Check valid ranges of error_code? | 
 |     error_code_ = error_code; | 
 |   } | 
 |  | 
 |   const absl::string_view& description() const { return description_; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SpdyStreamId last_good_stream_id_; | 
 |   SpdyErrorCode error_code_; | 
 |   const std::string description_store_; | 
 |   const absl::string_view description_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithHeaderBlockIR { | 
 |  public: | 
 |   explicit SpdyHeadersIR(SpdyStreamId stream_id) | 
 |       : SpdyHeadersIR(stream_id, Http2HeaderBlock()) {} | 
 |   SpdyHeadersIR(SpdyStreamId stream_id, Http2HeaderBlock header_block) | 
 |       : SpdyFrameWithHeaderBlockIR(stream_id, std::move(header_block)) {} | 
 |   SpdyHeadersIR(const SpdyHeadersIR&) = delete; | 
 |   SpdyHeadersIR& operator=(const SpdyHeadersIR&) = delete; | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |   bool has_priority() const { return has_priority_; } | 
 |   void set_has_priority(bool has_priority) { has_priority_ = has_priority; } | 
 |   int weight() const { return weight_; } | 
 |   void set_weight(int weight) { weight_ = weight; } | 
 |   SpdyStreamId parent_stream_id() const { return parent_stream_id_; } | 
 |   void set_parent_stream_id(SpdyStreamId id) { parent_stream_id_ = id; } | 
 |   bool exclusive() const { return exclusive_; } | 
 |   void set_exclusive(bool exclusive) { exclusive_ = exclusive; } | 
 |   bool padded() const { return padded_; } | 
 |   int padding_payload_len() const { return padding_payload_len_; } | 
 |   void set_padding_len(int padding_len) { | 
 |     QUICHE_DCHECK_GT(padding_len, 0); | 
 |     QUICHE_DCHECK_LE(padding_len, kPaddingSizePerFrame); | 
 |     padded_ = true; | 
 |     // The pad field takes one octet on the wire. | 
 |     padding_payload_len_ = padding_len - 1; | 
 |   } | 
 |  | 
 |  private: | 
 |   bool has_priority_ = false; | 
 |   int weight_ = kHttp2DefaultStreamWeight; | 
 |   SpdyStreamId parent_stream_id_ = 0; | 
 |   bool exclusive_ = false; | 
 |   bool padded_ = false; | 
 |   int padding_payload_len_ = 0; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyWindowUpdateIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyWindowUpdateIR(SpdyStreamId stream_id, int32_t delta) | 
 |       : SpdyFrameIR(stream_id) { | 
 |     set_delta(delta); | 
 |   } | 
 |   SpdyWindowUpdateIR(const SpdyWindowUpdateIR&) = delete; | 
 |   SpdyWindowUpdateIR& operator=(const SpdyWindowUpdateIR&) = delete; | 
 |  | 
 |   int32_t delta() const { return delta_; } | 
 |   void set_delta(int32_t delta) { | 
 |     QUICHE_DCHECK_LE(0, delta); | 
 |     QUICHE_DCHECK_LE(delta, kSpdyMaximumWindowSize); | 
 |     delta_ = delta; | 
 |   } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   int32_t delta_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyPushPromiseIR | 
 |     : public SpdyFrameWithHeaderBlockIR { | 
 |  public: | 
 |   SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) | 
 |       : SpdyPushPromiseIR(stream_id, promised_stream_id, Http2HeaderBlock()) {} | 
 |   SpdyPushPromiseIR(SpdyStreamId stream_id, | 
 |                     SpdyStreamId promised_stream_id, | 
 |                     Http2HeaderBlock header_block) | 
 |       : SpdyFrameWithHeaderBlockIR(stream_id, std::move(header_block)), | 
 |         promised_stream_id_(promised_stream_id), | 
 |         padded_(false), | 
 |         padding_payload_len_(0) {} | 
 |   SpdyPushPromiseIR(const SpdyPushPromiseIR&) = delete; | 
 |   SpdyPushPromiseIR& operator=(const SpdyPushPromiseIR&) = delete; | 
 |   SpdyStreamId promised_stream_id() const { return promised_stream_id_; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |   bool padded() const { return padded_; } | 
 |   int padding_payload_len() const { return padding_payload_len_; } | 
 |   void set_padding_len(int padding_len) { | 
 |     QUICHE_DCHECK_GT(padding_len, 0); | 
 |     QUICHE_DCHECK_LE(padding_len, kPaddingSizePerFrame); | 
 |     padded_ = true; | 
 |     // The pad field takes one octet on the wire. | 
 |     padding_payload_len_ = padding_len - 1; | 
 |   } | 
 |  | 
 |  private: | 
 |   SpdyStreamId promised_stream_id_; | 
 |  | 
 |   bool padded_; | 
 |   int padding_payload_len_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyContinuationIR : public SpdyFrameIR { | 
 |  public: | 
 |   explicit SpdyContinuationIR(SpdyStreamId stream_id); | 
 |   SpdyContinuationIR(const SpdyContinuationIR&) = delete; | 
 |   SpdyContinuationIR& operator=(const SpdyContinuationIR&) = delete; | 
 |   ~SpdyContinuationIR() override; | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   bool end_headers() const { return end_headers_; } | 
 |   void set_end_headers(bool end_headers) { end_headers_ = end_headers; } | 
 |   const std::string& encoding() const { return encoding_; } | 
 |   void take_encoding(std::string encoding) { encoding_ = std::move(encoding); } | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   std::string encoding_; | 
 |   bool end_headers_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyAltSvcIR : public SpdyFrameIR { | 
 |  public: | 
 |   explicit SpdyAltSvcIR(SpdyStreamId stream_id); | 
 |   SpdyAltSvcIR(const SpdyAltSvcIR&) = delete; | 
 |   SpdyAltSvcIR& operator=(const SpdyAltSvcIR&) = delete; | 
 |   ~SpdyAltSvcIR() override; | 
 |  | 
 |   std::string origin() const { return origin_; } | 
 |   const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector() const { | 
 |     return altsvc_vector_; | 
 |   } | 
 |  | 
 |   void set_origin(std::string origin) { origin_ = std::move(origin); } | 
 |   void add_altsvc(const SpdyAltSvcWireFormat::AlternativeService& altsvc) { | 
 |     altsvc_vector_.push_back(altsvc); | 
 |   } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   std::string origin_; | 
 |   SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyPriorityIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyPriorityIR(SpdyStreamId stream_id, | 
 |                  SpdyStreamId parent_stream_id, | 
 |                  int weight, | 
 |                  bool exclusive) | 
 |       : SpdyFrameIR(stream_id), | 
 |         parent_stream_id_(parent_stream_id), | 
 |         weight_(weight), | 
 |         exclusive_(exclusive) {} | 
 |   SpdyPriorityIR(const SpdyPriorityIR&) = delete; | 
 |   SpdyPriorityIR& operator=(const SpdyPriorityIR&) = delete; | 
 |   SpdyStreamId parent_stream_id() const { return parent_stream_id_; } | 
 |   int weight() const { return weight_; } | 
 |   bool exclusive() const { return exclusive_; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SpdyStreamId parent_stream_id_; | 
 |   int weight_; | 
 |   bool exclusive_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyPriorityUpdateIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyPriorityUpdateIR(SpdyStreamId stream_id, | 
 |                        SpdyStreamId prioritized_stream_id, | 
 |                        std::string priority_field_value) | 
 |       : SpdyFrameIR(stream_id), | 
 |         prioritized_stream_id_(prioritized_stream_id), | 
 |         priority_field_value_(std::move(priority_field_value)) {} | 
 |   SpdyPriorityUpdateIR(const SpdyPriorityUpdateIR&) = delete; | 
 |   SpdyPriorityUpdateIR& operator=(const SpdyPriorityUpdateIR&) = delete; | 
 |   SpdyStreamId prioritized_stream_id() const { return prioritized_stream_id_; } | 
 |   const std::string& priority_field_value() const { | 
 |     return priority_field_value_; | 
 |   } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  private: | 
 |   SpdyStreamId prioritized_stream_id_; | 
 |   std::string priority_field_value_; | 
 | }; | 
 |  | 
 | struct QUICHE_EXPORT_PRIVATE AcceptChOriginValuePair { | 
 |   std::string origin; | 
 |   std::string value; | 
 |   bool operator==(const AcceptChOriginValuePair& rhs) const { | 
 |     return origin == rhs.origin && value == rhs.value; | 
 |   } | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdyAcceptChIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyAcceptChIR(std::vector<AcceptChOriginValuePair> entries) | 
 |       : entries_(std::move(entries)) {} | 
 |   SpdyAcceptChIR(const SpdyAcceptChIR&) = delete; | 
 |   SpdyAcceptChIR& operator=(const SpdyAcceptChIR&) = delete; | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |   const std::vector<AcceptChOriginValuePair>& entries() const { | 
 |     return entries_; | 
 |   } | 
 |  | 
 |  private: | 
 |   std::vector<AcceptChOriginValuePair> entries_; | 
 | }; | 
 |  | 
 | // Represents a frame of unrecognized type. | 
 | class QUICHE_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR { | 
 |  public: | 
 |   SpdyUnknownIR(SpdyStreamId stream_id, | 
 |                 uint8_t type, | 
 |                 uint8_t flags, | 
 |                 std::string payload) | 
 |       : SpdyFrameIR(stream_id), | 
 |         type_(type), | 
 |         flags_(flags), | 
 |         length_(payload.size()), | 
 |         payload_(std::move(payload)) {} | 
 |   SpdyUnknownIR(const SpdyUnknownIR&) = delete; | 
 |   SpdyUnknownIR& operator=(const SpdyUnknownIR&) = delete; | 
 |   uint8_t type() const { return type_; } | 
 |   uint8_t flags() const { return flags_; } | 
 |   size_t length() const { return length_; } | 
 |   const std::string& payload() const { return payload_; } | 
 |  | 
 |   void Visit(SpdyFrameVisitor* visitor) const override; | 
 |  | 
 |   SpdyFrameType frame_type() const override; | 
 |  | 
 |   int flow_control_window_consumed() const override; | 
 |  | 
 |   size_t size() const override; | 
 |  | 
 |  protected: | 
 |   // Allows subclasses to overwrite the default payload length. | 
 |   void set_length(size_t length) { length_ = length; } | 
 |  | 
 |  private: | 
 |   uint8_t type_; | 
 |   uint8_t flags_; | 
 |   size_t length_; | 
 |   const std::string payload_; | 
 | }; | 
 |  | 
 | class QUICHE_EXPORT_PRIVATE SpdySerializedFrame { | 
 |  public: | 
 |   SpdySerializedFrame() | 
 |       : frame_(const_cast<char*>("")), size_(0), owns_buffer_(false) {} | 
 |  | 
 |   // Create a valid SpdySerializedFrame using a pre-created buffer. | 
 |   // If |owns_buffer| is true, this class takes ownership of the buffer and will | 
 |   // delete it on cleanup.  The buffer must have been created using new char[]. | 
 |   // If |owns_buffer| is false, the caller retains ownership of the buffer and | 
 |   // is responsible for making sure the buffer outlives this frame.  In other | 
 |   // words, this class does NOT create a copy of the buffer. | 
 |   SpdySerializedFrame(char* data, size_t size, bool owns_buffer) | 
 |       : frame_(data), size_(size), owns_buffer_(owns_buffer) {} | 
 |  | 
 |   SpdySerializedFrame(SpdySerializedFrame&& other) | 
 |       : frame_(other.frame_), | 
 |         size_(other.size_), | 
 |         owns_buffer_(other.owns_buffer_) { | 
 |     // |other| is no longer responsible for the buffer. | 
 |     other.owns_buffer_ = false; | 
 |   } | 
 |   SpdySerializedFrame(const SpdySerializedFrame&) = delete; | 
 |   SpdySerializedFrame& operator=(const SpdySerializedFrame&) = delete; | 
 |  | 
 |   SpdySerializedFrame& operator=(SpdySerializedFrame&& other) { | 
 |     // Free buffer if necessary. | 
 |     if (owns_buffer_) { | 
 |       delete[] frame_; | 
 |     } | 
 |     // Take over |other|. | 
 |     frame_ = other.frame_; | 
 |     size_ = other.size_; | 
 |     owns_buffer_ = other.owns_buffer_; | 
 |     // |other| is no longer responsible for the buffer. | 
 |     other.owns_buffer_ = false; | 
 |     return *this; | 
 |   } | 
 |  | 
 |   ~SpdySerializedFrame() { | 
 |     if (owns_buffer_) { | 
 |       delete[] frame_; | 
 |     } | 
 |   } | 
 |  | 
 |   // Provides access to the frame bytes, which is a buffer containing the frame | 
 |   // packed as expected for sending over the wire. | 
 |   char* data() const { return frame_; } | 
 |  | 
 |   // Returns the actual size of the underlying buffer. | 
 |   size_t size() const { return size_; } | 
 |  | 
 |   operator absl::string_view() const { | 
 |     return absl::string_view{frame_, size_}; | 
 |   } | 
 |  | 
 |   operator std::string() const { return std::string{frame_, size_}; } | 
 |  | 
 |   // Returns a buffer containing the contents of the frame, of which the caller | 
 |   // takes ownership, and clears this SpdySerializedFrame. | 
 |   char* ReleaseBuffer() { | 
 |     char* buffer; | 
 |     if (owns_buffer_) { | 
 |       // If the buffer is owned, relinquish ownership to the caller. | 
 |       buffer = frame_; | 
 |       owns_buffer_ = false; | 
 |     } else { | 
 |       // Otherwise, we need to make a copy to give to the caller. | 
 |       buffer = new char[size_]; | 
 |       memcpy(buffer, frame_, size_); | 
 |     } | 
 |     *this = SpdySerializedFrame(); | 
 |     return buffer; | 
 |   } | 
 |  | 
 |  protected: | 
 |   char* frame_; | 
 |  | 
 |  private: | 
 |   size_t size_; | 
 |   bool owns_buffer_; | 
 | }; | 
 |  | 
 | // This interface is for classes that want to process SpdyFrameIRs without | 
 | // having to know what type they are.  An instance of this interface can be | 
 | // passed to a SpdyFrameIR's Visit method, and the appropriate type-specific | 
 | // method of this class will be called. | 
 | class QUICHE_EXPORT_PRIVATE SpdyFrameVisitor { | 
 |  public: | 
 |   SpdyFrameVisitor() {} | 
 |   SpdyFrameVisitor(const SpdyFrameVisitor&) = delete; | 
 |   SpdyFrameVisitor& operator=(const SpdyFrameVisitor&) = delete; | 
 |   virtual ~SpdyFrameVisitor() {} | 
 |  | 
 |   virtual void VisitRstStream(const SpdyRstStreamIR& rst_stream) = 0; | 
 |   virtual void VisitSettings(const SpdySettingsIR& settings) = 0; | 
 |   virtual void VisitPing(const SpdyPingIR& ping) = 0; | 
 |   virtual void VisitGoAway(const SpdyGoAwayIR& goaway) = 0; | 
 |   virtual void VisitHeaders(const SpdyHeadersIR& headers) = 0; | 
 |   virtual void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) = 0; | 
 |   virtual void VisitPushPromise(const SpdyPushPromiseIR& push_promise) = 0; | 
 |   virtual void VisitContinuation(const SpdyContinuationIR& continuation) = 0; | 
 |   virtual void VisitAltSvc(const SpdyAltSvcIR& altsvc) = 0; | 
 |   virtual void VisitPriority(const SpdyPriorityIR& priority) = 0; | 
 |   virtual void VisitData(const SpdyDataIR& data) = 0; | 
 |   virtual void VisitPriorityUpdate( | 
 |       const SpdyPriorityUpdateIR& priority_update) = 0; | 
 |   virtual void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) = 0; | 
 |   virtual void VisitUnknown(const SpdyUnknownIR& /*unknown*/) { | 
 |     // TODO(birenroy): make abstract. | 
 |   } | 
 | }; | 
 |  | 
 | // Optionally, and in addition to SpdyFramerVisitorInterface, a class supporting | 
 | // SpdyFramerDebugVisitorInterface may be used in conjunction with SpdyFramer in | 
 | // order to extract debug/internal information about the SpdyFramer as it | 
 | // operates. | 
 | // | 
 | // Most HTTP2 implementations need not bother with this interface at all. | 
 | class QUICHE_EXPORT_PRIVATE SpdyFramerDebugVisitorInterface { | 
 |  public: | 
 |   virtual ~SpdyFramerDebugVisitorInterface() {} | 
 |  | 
 |   // Called after compressing a frame with a payload of | 
 |   // a list of name-value pairs. | 
 |   // |payload_len| is the uncompressed payload size. | 
 |   // |frame_len| is the compressed frame size. | 
 |   virtual void OnSendCompressedFrame(SpdyStreamId /*stream_id*/, | 
 |                                      SpdyFrameType /*type*/, | 
 |                                      size_t /*payload_len*/, | 
 |                                      size_t /*frame_len*/) {} | 
 |  | 
 |   // Called when a frame containing a compressed payload of | 
 |   // name-value pairs is received. | 
 |   // |frame_len| is the compressed frame size. | 
 |   virtual void OnReceiveCompressedFrame(SpdyStreamId /*stream_id*/, | 
 |                                         SpdyFrameType /*type*/, | 
 |                                         size_t /*frame_len*/) {} | 
 | }; | 
 |  | 
 | // Calculates the number of bytes required to serialize a SpdyHeadersIR, not | 
 | // including the bytes to be used for the encoded header set. | 
 | size_t GetHeaderFrameSizeSansBlock(const SpdyHeadersIR& header_ir); | 
 |  | 
 | // Calculates the number of bytes required to serialize a SpdyPushPromiseIR, | 
 | // not including the bytes to be used for the encoded header set. | 
 | size_t GetPushPromiseFrameSizeSansBlock( | 
 |     const SpdyPushPromiseIR& push_promise_ir); | 
 |  | 
 | }  // namespace spdy | 
 |  | 
 | #endif  // QUICHE_SPDY_CORE_SPDY_PROTOCOL_H_ |