| // Copyright 2016 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_SPDY_CORE_HTTP2_FRAME_DECODER_ADAPTER_H_ | |
| #define QUICHE_SPDY_CORE_HTTP2_FRAME_DECODER_ADAPTER_H_ | |
| #include <stddef.h> | |
| #include <cstdint> | |
| #include <memory> | |
| #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h" | |
| #include "net/third_party/quiche/src/http2/platform/api/http2_optional.h" | |
| #include "net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h" | |
| #include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_headers_handler_interface.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" | |
| #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h" | |
| namespace spdy { | |
| class SpdyFramerVisitorInterface; | |
| class ExtensionVisitorInterface; | |
| } // namespace spdy | |
| // TODO(dahollings): Perform various renames/moves suggested in cl/164660364. | |
| namespace http2 { | |
| // Adapts SpdyFramer interface to use Http2FrameDecoder. | |
| class SPDY_EXPORT_PRIVATE Http2DecoderAdapter | |
| : public http2::Http2FrameDecoderListener { | |
| public: | |
| // HTTP2 states. | |
| enum SpdyState { | |
| SPDY_ERROR, | |
| SPDY_READY_FOR_FRAME, // Framer is ready for reading the next frame. | |
| SPDY_FRAME_COMPLETE, // Framer has finished reading a frame, need to reset. | |
| SPDY_READING_COMMON_HEADER, | |
| SPDY_CONTROL_FRAME_PAYLOAD, | |
| SPDY_READ_DATA_FRAME_PADDING_LENGTH, | |
| SPDY_CONSUME_PADDING, | |
| SPDY_IGNORE_REMAINING_PAYLOAD, | |
| SPDY_FORWARD_STREAM_FRAME, | |
| SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, | |
| SPDY_CONTROL_FRAME_HEADER_BLOCK, | |
| SPDY_GOAWAY_FRAME_PAYLOAD, | |
| SPDY_SETTINGS_FRAME_HEADER, | |
| SPDY_SETTINGS_FRAME_PAYLOAD, | |
| SPDY_ALTSVC_FRAME_PAYLOAD, | |
| SPDY_EXTENSION_FRAME_PAYLOAD, | |
| }; | |
| // Framer error codes. | |
| enum SpdyFramerError { | |
| SPDY_NO_ERROR, | |
| SPDY_INVALID_STREAM_ID, // Stream ID is invalid | |
| SPDY_INVALID_CONTROL_FRAME, // Control frame is mal-formatted. | |
| SPDY_CONTROL_PAYLOAD_TOO_LARGE, // Control frame payload was too large. | |
| SPDY_ZLIB_INIT_FAILURE, // The Zlib library could not initialize. | |
| SPDY_UNSUPPORTED_VERSION, // Control frame has unsupported version. | |
| SPDY_DECOMPRESS_FAILURE, // There was an error decompressing. | |
| SPDY_COMPRESS_FAILURE, // There was an error compressing. | |
| SPDY_GOAWAY_FRAME_CORRUPT, // GOAWAY frame could not be parsed. | |
| SPDY_RST_STREAM_FRAME_CORRUPT, // RST_STREAM frame could not be parsed. | |
| SPDY_INVALID_PADDING, // HEADERS or DATA frame padding invalid | |
| SPDY_INVALID_DATA_FRAME_FLAGS, // Data frame has invalid flags. | |
| SPDY_INVALID_CONTROL_FRAME_FLAGS, // Control frame has invalid flags. | |
| SPDY_UNEXPECTED_FRAME, // Frame received out of order. | |
| SPDY_INTERNAL_FRAMER_ERROR, // SpdyFramer was used incorrectly. | |
| SPDY_INVALID_CONTROL_FRAME_SIZE, // Control frame not sized to spec | |
| SPDY_OVERSIZED_PAYLOAD, // Payload size was too large | |
| LAST_ERROR, // Must be the last entry in the enum. | |
| }; | |
| // For debugging. | |
| static const char* StateToString(int state); | |
| static const char* SpdyFramerErrorToString(SpdyFramerError spdy_framer_error); | |
| Http2DecoderAdapter(); | |
| ~Http2DecoderAdapter() override; | |
| // Set callbacks to be called from the framer. A visitor must be set, or | |
| // else the framer will likely crash. It is acceptable for the visitor | |
| // to do nothing. If this is called multiple times, only the last visitor | |
| // will be used. | |
| void set_visitor(spdy::SpdyFramerVisitorInterface* visitor); | |
| spdy::SpdyFramerVisitorInterface* visitor() const { return visitor_; } | |
| // Set extension callbacks to be called from the framer or decoder. Optional. | |
| // If called multiple times, only the last visitor will be used. | |
| void set_extension_visitor(spdy::ExtensionVisitorInterface* visitor); | |
| // Set debug callbacks to be called from the framer. The debug visitor is | |
| // completely optional and need not be set in order for normal operation. | |
| // If this is called multiple times, only the last visitor will be used. | |
| void set_debug_visitor(spdy::SpdyFramerDebugVisitorInterface* debug_visitor); | |
| spdy::SpdyFramerDebugVisitorInterface* debug_visitor() const { | |
| return debug_visitor_; | |
| } | |
| // Set debug callbacks to be called from the HPACK decoder. | |
| void SetDecoderHeaderTableDebugVisitor( | |
| std::unique_ptr<spdy::HpackHeaderTable::DebugVisitorInterface> visitor); | |
| // Sets whether or not ProcessInput returns after finishing a frame, or | |
| // continues processing additional frames. Normally ProcessInput processes | |
| // all input, but this method enables the caller (and visitor) to work with | |
| // a single frame at a time (or that portion of the frame which is provided | |
| // as input). Reset() does not change the value of this flag. | |
| void set_process_single_input_frame(bool v); | |
| bool process_single_input_frame() const { | |
| return process_single_input_frame_; | |
| } | |
| // Decode the |len| bytes of encoded HTTP/2 starting at |*data|. Returns | |
| // the number of bytes consumed. It is safe to pass more bytes in than | |
| // may be consumed. Should process (or otherwise buffer) as much as | |
| // available, unless process_single_input_frame is true. | |
| size_t ProcessInput(const char* data, size_t len); | |
| // Reset the decoder (used just for tests at this time). | |
| void Reset(); | |
| // Current state of the decoder. | |
| SpdyState state() const; | |
| // Current error code (NO_ERROR if state != ERROR). | |
| SpdyFramerError spdy_framer_error() const; | |
| // Has any frame header looked like the start of an HTTP/1.1 (or earlier) | |
| // response? Used to detect if a backend/server that we sent a request to | |
| // has responded with an HTTP/1.1 (or earlier) response. | |
| bool probable_http_response() const; | |
| // Returns the estimate of dynamically allocated memory in bytes. | |
| size_t EstimateMemoryUsage() const; | |
| spdy::HpackDecoderAdapter* GetHpackDecoder(); | |
| bool HasError() const; | |
| private: | |
| bool OnFrameHeader(const Http2FrameHeader& header) override; | |
| void OnDataStart(const Http2FrameHeader& header) override; | |
| void OnDataPayload(const char* data, size_t len) override; | |
| void OnDataEnd() override; | |
| void OnHeadersStart(const Http2FrameHeader& header) override; | |
| void OnHeadersPriority(const Http2PriorityFields& priority) override; | |
| void OnHpackFragment(const char* data, size_t len) override; | |
| void OnHeadersEnd() override; | |
| void OnPriorityFrame(const Http2FrameHeader& header, | |
| const Http2PriorityFields& priority) override; | |
| void OnContinuationStart(const Http2FrameHeader& header) override; | |
| void OnContinuationEnd() override; | |
| void OnPadLength(size_t trailing_length) override; | |
| void OnPadding(const char* padding, size_t skipped_length) override; | |
| void OnRstStream(const Http2FrameHeader& header, | |
| Http2ErrorCode http2_error_code) override; | |
| void OnSettingsStart(const Http2FrameHeader& header) override; | |
| void OnSetting(const Http2SettingFields& setting_fields) override; | |
| void OnSettingsEnd() override; | |
| void OnSettingsAck(const Http2FrameHeader& header) override; | |
| void OnPushPromiseStart(const Http2FrameHeader& header, | |
| const Http2PushPromiseFields& promise, | |
| size_t total_padding_length) override; | |
| void OnPushPromiseEnd() override; | |
| void OnPing(const Http2FrameHeader& header, | |
| const Http2PingFields& ping) override; | |
| void OnPingAck(const Http2FrameHeader& header, | |
| const Http2PingFields& ping) override; | |
| void OnGoAwayStart(const Http2FrameHeader& header, | |
| const Http2GoAwayFields& goaway) override; | |
| void OnGoAwayOpaqueData(const char* data, size_t len) override; | |
| void OnGoAwayEnd() override; | |
| void OnWindowUpdate(const Http2FrameHeader& header, | |
| uint32_t increment) override; | |
| void OnAltSvcStart(const Http2FrameHeader& header, | |
| size_t origin_length, | |
| size_t value_length) override; | |
| void OnAltSvcOriginData(const char* data, size_t len) override; | |
| void OnAltSvcValueData(const char* data, size_t len) override; | |
| void OnAltSvcEnd() override; | |
| void OnUnknownStart(const Http2FrameHeader& header) override; | |
| void OnUnknownPayload(const char* data, size_t len) override; | |
| void OnUnknownEnd() override; | |
| void OnPaddingTooLong(const Http2FrameHeader& header, | |
| size_t missing_length) override; | |
| void OnFrameSizeError(const Http2FrameHeader& header) override; | |
| size_t ProcessInputFrame(const char* data, size_t len); | |
| void DetermineSpdyState(DecodeStatus status); | |
| void ResetBetweenFrames(); | |
| // ResetInternal is called from the constructor, and during tests, but not | |
| // otherwise (i.e. not between every frame). | |
| void ResetInternal(); | |
| void set_spdy_state(SpdyState v); | |
| void SetSpdyErrorAndNotify(SpdyFramerError error); | |
| const Http2FrameHeader& frame_header() const; | |
| uint32_t stream_id() const; | |
| Http2FrameType frame_type() const; | |
| size_t remaining_total_payload() const; | |
| bool IsReadingPaddingLength(); | |
| bool IsSkippingPadding(); | |
| bool IsDiscardingPayload(); | |
| // Called from OnXyz or OnXyzStart methods to decide whether it is OK to | |
| // handle the callback. | |
| bool IsOkToStartFrame(const Http2FrameHeader& header); | |
| bool HasRequiredStreamId(uint32_t stream_id); | |
| bool HasRequiredStreamId(const Http2FrameHeader& header); | |
| bool HasRequiredStreamIdZero(uint32_t stream_id); | |
| bool HasRequiredStreamIdZero(const Http2FrameHeader& header); | |
| void ReportReceiveCompressedFrame(const Http2FrameHeader& header); | |
| void CommonStartHpackBlock(); | |
| // SpdyFramer calls HandleControlFrameHeadersData even if there are zero | |
| // fragment bytes in the first frame, so do the same. | |
| void MaybeAnnounceEmptyFirstHpackFragment(); | |
| void CommonHpackFragmentEnd(); | |
| // The most recently decoded frame header; invalid after we reached the end | |
| // of that frame. | |
| Http2FrameHeader frame_header_; | |
| // If decoding an HPACK block that is split across multiple frames, this holds | |
| // the frame header of the HEADERS or PUSH_PROMISE that started the block. | |
| Http2FrameHeader hpack_first_frame_header_; | |
| // Amount of trailing padding. Currently used just as an indicator of whether | |
| // OnPadLength has been called. | |
| Http2Optional<size_t> opt_pad_length_; | |
| // Temporary buffers for the AltSvc fields. | |
| Http2String alt_svc_origin_; | |
| Http2String alt_svc_value_; | |
| // Listener used if we transition to an error state; the listener ignores all | |
| // the callbacks. | |
| Http2FrameDecoderNoOpListener no_op_listener_; | |
| spdy::SpdyFramerVisitorInterface* visitor_ = nullptr; | |
| spdy::SpdyFramerDebugVisitorInterface* debug_visitor_ = nullptr; | |
| // If non-null, unknown frames and settings are passed to the extension. | |
| spdy::ExtensionVisitorInterface* extension_ = nullptr; | |
| // The HPACK decoder to be used for this adapter. User is responsible for | |
| // clearing if the adapter is to be used for another connection. | |
| std::unique_ptr<spdy::HpackDecoderAdapter> hpack_decoder_ = nullptr; | |
| // The HTTP/2 frame decoder. Accessed via a unique_ptr to allow replacement | |
| // (e.g. in tests) when Reset() is called. | |
| std::unique_ptr<Http2FrameDecoder> frame_decoder_; | |
| // Next frame type expected. Currently only used for CONTINUATION frames, | |
| // but could be used for detecting whether the first frame is a SETTINGS | |
| // frame. | |
| // TODO(jamessyng): Provide means to indicate that decoder should require | |
| // SETTINGS frame as the first frame. | |
| Http2FrameType expected_frame_type_; | |
| // Attempt to duplicate the SpdyState and SpdyFramerError values that | |
| // SpdyFramer sets. Values determined by getting tests to pass. | |
| SpdyState spdy_state_; | |
| SpdyFramerError spdy_framer_error_; | |
| // The limit on the size of received HTTP/2 payloads as specified in the | |
| // SETTINGS_MAX_FRAME_SIZE advertised to peer. | |
| size_t recv_frame_size_limit_ = spdy::kHttp2DefaultFramePayloadLimit; | |
| // Has OnFrameHeader been called? | |
| bool decoded_frame_header_ = false; | |
| // Have we recorded an Http2FrameHeader for the current frame? | |
| // We only do so if the decoder will make multiple callbacks for | |
| // the frame; for example, for PING frames we don't make record | |
| // the frame header, but for ALTSVC we do. | |
| bool has_frame_header_ = false; | |
| // Have we recorded an Http2FrameHeader for the current HPACK block? | |
| // True only for multi-frame HPACK blocks. | |
| bool has_hpack_first_frame_header_ = false; | |
| // Has OnHeaders() already been called for current HEADERS block? Only | |
| // meaningful between OnHeadersStart and OnHeadersPriority. | |
| bool on_headers_called_; | |
| // Has OnHpackFragment() already been called for current HPACK block? | |
| // SpdyFramer will pass an empty buffer to the HPACK decoder if a HEADERS | |
| // or PUSH_PROMISE has no HPACK data in it (e.g. a HEADERS frame with only | |
| // padding). Detect that condition and replicate the behavior using this | |
| // field. | |
| bool on_hpack_fragment_called_; | |
| // Have we seen a frame header that appears to be an HTTP/1 response? | |
| bool latched_probable_http_response_ = false; | |
| // Is expected_frame_type_ set? | |
| bool has_expected_frame_type_ = false; | |
| // Is the current frame payload destined for |extension_|? | |
| bool handling_extension_payload_ = false; | |
| bool process_single_input_frame_ = false; | |
| }; | |
| } // namespace http2 | |
| namespace spdy { | |
| // Http2DecoderAdapter will use the given visitor implementing this | |
| // interface to deliver event callbacks as frames are decoded. | |
| // | |
| // Control frames that contain HTTP2 header blocks (HEADER, and PUSH_PROMISE) | |
| // are processed in fashion that allows the decompressed header block to be | |
| // delivered in chunks to the visitor. | |
| // The following steps are followed: | |
| // 1. OnHeaders, or OnPushPromise is called. | |
| // 2. OnHeaderFrameStart is called; visitor is expected to return an instance | |
| // of SpdyHeadersHandlerInterface that will receive the header key-value | |
| // pairs. | |
| // 3. OnHeaderFrameEnd is called, indicating that the full header block has | |
| // been delivered for the control frame. | |
| // During step 2, if the visitor is not interested in accepting the header data, | |
| // it should return a no-op implementation of SpdyHeadersHandlerInterface. | |
| class SPDY_EXPORT_PRIVATE SpdyFramerVisitorInterface { | |
| public: | |
| virtual ~SpdyFramerVisitorInterface() {} | |
| // Called if an error is detected in the SpdyFrame protocol. | |
| virtual void OnError(http2::Http2DecoderAdapter::SpdyFramerError error) = 0; | |
| // Called when the common header for a frame is received. Validating the | |
| // common header occurs in later processing. | |
| virtual void OnCommonHeader(SpdyStreamId /*stream_id*/, | |
| size_t /*length*/, | |
| uint8_t /*type*/, | |
| uint8_t /*flags*/) {} | |
| // Called when a data frame header is received. The frame's data | |
| // payload will be provided via subsequent calls to | |
| // OnStreamFrameData(). | |
| virtual void OnDataFrameHeader(SpdyStreamId stream_id, | |
| size_t length, | |
| bool fin) = 0; | |
| // Called when data is received. | |
| // |stream_id| The stream receiving data. | |
| // |data| A buffer containing the data received. | |
| // |len| The length of the data buffer. | |
| virtual void OnStreamFrameData(SpdyStreamId stream_id, | |
| const char* data, | |
| size_t len) = 0; | |
| // Called when the other side has finished sending data on this stream. | |
| // |stream_id| The stream that was receiving data. | |
| virtual void OnStreamEnd(SpdyStreamId stream_id) = 0; | |
| // Called when padding length field is received on a DATA frame. | |
| // |stream_id| The stream receiving data. | |
| // |value| The value of the padding length field. | |
| virtual void OnStreamPadLength(SpdyStreamId stream_id, size_t value) {} | |
| // Called when padding is received (the trailing octets, not pad_len field) on | |
| // a DATA frame. | |
| // |stream_id| The stream receiving data. | |
| // |len| The number of padding octets. | |
| virtual void OnStreamPadding(SpdyStreamId stream_id, size_t len) = 0; | |
| // Called just before processing the payload of a frame containing header | |
| // data. Should return an implementation of SpdyHeadersHandlerInterface that | |
| // will receive headers for stream |stream_id|. The caller will not take | |
| // ownership of the headers handler. The same instance should remain live | |
| // and be returned for all header frames comprising a logical header block | |
| // (i.e. until OnHeaderFrameEnd() is called). | |
| virtual SpdyHeadersHandlerInterface* OnHeaderFrameStart( | |
| SpdyStreamId stream_id) = 0; | |
| // Called after processing the payload of a frame containing header data. | |
| virtual void OnHeaderFrameEnd(SpdyStreamId stream_id) = 0; | |
| // Called when a RST_STREAM frame has been parsed. | |
| virtual void OnRstStream(SpdyStreamId stream_id, | |
| SpdyErrorCode error_code) = 0; | |
| // Called when a SETTINGS frame is received. | |
| virtual void OnSettings() {} | |
| // Called when a complete setting within a SETTINGS frame has been parsed. | |
| // Note that |id| may or may not be a SETTINGS ID defined in the HTTP/2 spec. | |
| virtual void OnSetting(SpdySettingsId id, uint32_t value) = 0; | |
| // Called when a SETTINGS frame is received with the ACK flag set. | |
| virtual void OnSettingsAck() {} | |
| // Called before and after parsing SETTINGS id and value tuples. | |
| virtual void OnSettingsEnd() = 0; | |
| // Called when a PING frame has been parsed. | |
| virtual void OnPing(SpdyPingId unique_id, bool is_ack) = 0; | |
| // Called when a GOAWAY frame has been parsed. | |
| virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
| SpdyErrorCode error_code) = 0; | |
| // Called when a HEADERS frame is received. | |
| // Note that header block data is not included. See OnHeaderFrameStart(). | |
| // |stream_id| The stream receiving the header. | |
| // |has_priority| Whether or not the headers frame included a priority value, | |
| // and stream dependency info. | |
| // |weight| If |has_priority| is true, then weight (in the range [1, 256]) | |
| // for the receiving stream, otherwise 0. | |
| // |parent_stream_id| If |has_priority| is true the parent stream of the | |
| // receiving stream, else 0. | |
| // |exclusive| If |has_priority| is true the exclusivity of dependence on the | |
| // parent stream, else false. | |
| // |fin| Whether FIN flag is set in frame headers. | |
| // |end| False if HEADERs frame is to be followed by a CONTINUATION frame, | |
| // or true if not. | |
| virtual void OnHeaders(SpdyStreamId stream_id, | |
| bool has_priority, | |
| int weight, | |
| SpdyStreamId parent_stream_id, | |
| bool exclusive, | |
| bool fin, | |
| bool end) = 0; | |
| // Called when a WINDOW_UPDATE frame has been parsed. | |
| virtual void OnWindowUpdate(SpdyStreamId stream_id, | |
| int delta_window_size) = 0; | |
| // Called when a goaway frame opaque data is available. | |
| // |goaway_data| A buffer containing the opaque GOAWAY data chunk received. | |
| // |len| The length of the header data buffer. A length of zero indicates | |
| // that the header data block has been completely sent. | |
| // When this function returns true the visitor indicates that it accepted | |
| // all of the data. Returning false indicates that that an error has | |
| // occurred while processing the data. Default implementation returns true. | |
| virtual bool OnGoAwayFrameData(const char* goaway_data, size_t len); | |
| // Called when a PUSH_PROMISE frame is received. | |
| // Note that header block data is not included. See OnHeaderFrameStart(). | |
| virtual void OnPushPromise(SpdyStreamId stream_id, | |
| SpdyStreamId promised_stream_id, | |
| bool end) = 0; | |
| // Called when a CONTINUATION frame is received. | |
| // Note that header block data is not included. See OnHeaderFrameStart(). | |
| virtual void OnContinuation(SpdyStreamId stream_id, bool end) = 0; | |
| // Called when an ALTSVC frame has been parsed. | |
| virtual void OnAltSvc( | |
| SpdyStreamId /*stream_id*/, | |
| SpdyStringPiece /*origin*/, | |
| const SpdyAltSvcWireFormat::AlternativeServiceVector& /*altsvc_vector*/) { | |
| } | |
| // Called when a PRIORITY frame is received. | |
| // |stream_id| The stream to update the priority of. | |
| // |parent_stream_id| The parent stream of |stream_id|. | |
| // |weight| Stream weight, in the range [1, 256]. | |
| // |exclusive| Whether |stream_id| should be an only child of | |
| // |parent_stream_id|. | |
| virtual void OnPriority(SpdyStreamId stream_id, | |
| SpdyStreamId parent_stream_id, | |
| int weight, | |
| bool exclusive) = 0; | |
| // Called when a frame type we don't recognize is received. | |
| // Return true if this appears to be a valid extension frame, false otherwise. | |
| // We distinguish between extension frames and nonsense by checking | |
| // whether the stream id is valid. | |
| virtual bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) = 0; | |
| }; | |
| class SPDY_EXPORT_PRIVATE ExtensionVisitorInterface { | |
| public: | |
| virtual ~ExtensionVisitorInterface() {} | |
| // Called when SETTINGS are received, including non-standard SETTINGS. | |
| virtual void OnSetting(SpdySettingsId id, uint32_t value) = 0; | |
| // Called when non-standard frames are received. | |
| virtual bool OnFrameHeader(SpdyStreamId stream_id, | |
| size_t length, | |
| uint8_t type, | |
| uint8_t flags) = 0; | |
| // The payload for a single frame may be delivered as multiple calls to | |
| // OnFramePayload. Since the length field is passed in OnFrameHeader, there is | |
| // no explicit indication of the end of the frame payload. | |
| virtual void OnFramePayload(const char* data, size_t len) = 0; | |
| }; | |
| } // namespace spdy | |
| #endif // QUICHE_SPDY_CORE_HTTP2_FRAME_DECODER_ADAPTER_H_ |