| // 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 <utility> | |
| #include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_bitmasks.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.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. | |
| SPDY_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, | |
| MAX_FRAME_TYPE = ALTSVC, | |
| // The specific value of EXTENSION is meaningless; it is a placeholder used | |
| // within SpdyFramer's state machine when handling unknown frames via an | |
| // extension API. | |
| // TODO(birenroy): Remove the fake EXTENSION value from the SpdyFrameType | |
| // enum. | |
| EXTENSION = 0xff | |
| }; | |
| // 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://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00. | |
| SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8, | |
| SETTINGS_MAX = SETTINGS_ENABLE_CONNECT_PROTOCOL, | |
| // 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. | |
| SPDY_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. | |
| SPDY_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 | |
| }; | |
| // 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]. | |
| SPDY_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]. | |
| SPDY_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. | |
| SPDY_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). | |
| SPDY_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. | |
| SPDY_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. | |
| SPDY_EXPORT_PRIVATE SpdyFrameType ParseFrameType(uint8_t frame_type_field); | |
| // Serializes a frame type to the on-the-wire value. | |
| SPDY_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. | |
| SPDY_EXPORT_PRIVATE bool IsValidHTTP2FrameStreamId( | |
| SpdyStreamId current_frame_stream_id, | |
| SpdyFrameType frame_type_field); | |
| // Serialize |frame_type| to string for logging/debugging. | |
| 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. | |
| SPDY_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(). | |
| SPDY_EXPORT_PRIVATE SpdyString SettingsIdToString(SpdySettingsId id); | |
| // Parse |wire_error_code| to a SpdyErrorCode. | |
| // Treat unrecognized error codes as INTERNAL_ERROR | |
| // as recommended by the HTTP/2 specification. | |
| SPDY_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); | |
| // 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; | |
| // 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); | |
| // Per-header overhead for block size accounting in bytes. | |
| const size_t kPerHeaderOverhead = 32; | |
| // 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". | |
| 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. | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2AuthorityHeader; | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2MethodHeader; | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2PathHeader; | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2SchemeHeader; | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2ProtocolHeader; | |
| // Name of pseudo-header defined for HTTP/2 responses. | |
| SPDY_EXPORT_PRIVATE extern const char* const kHttp2StatusHeader; | |
| SPDY_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 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 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 SPDY_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 SPDY_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 SPDY_EXPORT_PRIVATE SpdyFrameWithHeaderBlockIR | |
| : public SpdyFrameWithFinIR { | |
| public: | |
| ~SpdyFrameWithHeaderBlockIR() override; | |
| const SpdyHeaderBlock& header_block() const { return header_block_; } | |
| void set_header_block(SpdyHeaderBlock header_block) { | |
| // Deep copy. | |
| header_block_ = std::move(header_block); | |
| } | |
| void SetHeader(SpdyStringPiece name, SpdyStringPiece value) { | |
| header_block_[name] = value; | |
| } | |
| protected: | |
| SpdyFrameWithHeaderBlockIR(SpdyStreamId stream_id, | |
| SpdyHeaderBlock header_block); | |
| SpdyFrameWithHeaderBlockIR(const SpdyFrameWithHeaderBlockIR&) = delete; | |
| SpdyFrameWithHeaderBlockIR& operator=(const SpdyFrameWithHeaderBlockIR&) = | |
| delete; | |
| private: | |
| SpdyHeaderBlock header_block_; | |
| }; | |
| class SPDY_EXPORT_PRIVATE SpdyDataIR : public SpdyFrameWithFinIR { | |
| public: | |
| // Performs a deep copy on data. | |
| SpdyDataIR(SpdyStreamId stream_id, SpdyStringPiece 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, SpdyString 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) { | |
| DCHECK_GT(padding_len, 0); | |
| 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(SpdyStringPiece data) { | |
| data_store_ = SpdyMakeUnique<SpdyString>(data.data(), data.size()); | |
| data_ = data_store_->data(); | |
| data_len_ = data.size(); | |
| } | |
| // Shallow-copy of data (do not keep private copy). | |
| void SetDataShallow(SpdyStringPiece 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<SpdyString> 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 SPDY_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 SPDY_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 SPDY_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 SPDY_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, | |
| SpdyStringPiece 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, | |
| SpdyString 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) { | |
| 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 SpdyStringPiece& 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 SpdyString description_store_; | |
| const SpdyStringPiece description_; | |
| }; | |
| class SPDY_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithHeaderBlockIR { | |
| public: | |
| explicit SpdyHeadersIR(SpdyStreamId stream_id) | |
| : SpdyHeadersIR(stream_id, SpdyHeaderBlock()) {} | |
| SpdyHeadersIR(SpdyStreamId stream_id, SpdyHeaderBlock 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) { | |
| DCHECK_GT(padding_len, 0); | |
| 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 SPDY_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) { | |
| DCHECK_LE(0, delta); | |
| 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 SPDY_EXPORT_PRIVATE SpdyPushPromiseIR | |
| : public SpdyFrameWithHeaderBlockIR { | |
| public: | |
| SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) | |
| : SpdyPushPromiseIR(stream_id, promised_stream_id, SpdyHeaderBlock()) {} | |
| SpdyPushPromiseIR(SpdyStreamId stream_id, | |
| SpdyStreamId promised_stream_id, | |
| SpdyHeaderBlock 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) { | |
| DCHECK_GT(padding_len, 0); | |
| 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 SPDY_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 SpdyString& encoding() const { return *encoding_; } | |
| void take_encoding(std::unique_ptr<SpdyString> encoding) { | |
| encoding_ = std::move(encoding); | |
| } | |
| size_t size() const override; | |
| private: | |
| std::unique_ptr<SpdyString> encoding_; | |
| bool end_headers_; | |
| }; | |
| class SPDY_EXPORT_PRIVATE SpdyAltSvcIR : public SpdyFrameIR { | |
| public: | |
| explicit SpdyAltSvcIR(SpdyStreamId stream_id); | |
| SpdyAltSvcIR(const SpdyAltSvcIR&) = delete; | |
| SpdyAltSvcIR& operator=(const SpdyAltSvcIR&) = delete; | |
| ~SpdyAltSvcIR() override; | |
| SpdyString origin() const { return origin_; } | |
| const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector() const { | |
| return altsvc_vector_; | |
| } | |
| void set_origin(SpdyString 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: | |
| SpdyString origin_; | |
| SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_; | |
| }; | |
| class SPDY_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_; | |
| }; | |
| // Represents a frame of unrecognized type. | |
| class SPDY_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR { | |
| public: | |
| SpdyUnknownIR(SpdyStreamId stream_id, | |
| uint8_t type, | |
| uint8_t flags, | |
| SpdyString 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 SpdyString& 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 SpdyString payload_; | |
| }; | |
| class SPDY_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_; } | |
| // 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; | |
| } | |
| // Returns the estimate of dynamically allocated memory in bytes. | |
| size_t EstimateMemoryUsage() const { return owns_buffer_ ? size_ : 0; } | |
| 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 SPDY_EXPORT_PRIVATE SpdyFrameVisitor { | |
| public: | |
| 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 VisitUnknown(const SpdyUnknownIR& unknown) { | |
| // TODO(birenroy): make abstract. | |
| } | |
| protected: | |
| SpdyFrameVisitor() {} | |
| SpdyFrameVisitor(const SpdyFrameVisitor&) = delete; | |
| SpdyFrameVisitor& operator=(const SpdyFrameVisitor&) = delete; | |
| virtual ~SpdyFrameVisitor() {} | |
| }; | |
| // 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 SPDY_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_ |