| // 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 "net/third_party/quiche/src/common/platform/api/quiche_export.h" |
| #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_logging.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, |
| 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. |
| 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. |
| 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; |
| |
| // 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". |
| 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 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 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) { |
| 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(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) { |
| 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) { |
| 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 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) { |
| 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 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) { |
| 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 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::unique_ptr<std::string> encoding) { |
| encoding_ = std::move(encoding); |
| } |
| size_t size() const override; |
| |
| private: |
| std::unique_ptr<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_; |
| }; |
| |
| // 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_; } |
| |
| // 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 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 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_ |