Project import generated by Copybara.
PiperOrigin-RevId: 229942388
Change-Id: Ib5a23c152c95ed4294cece9f902227c21ce531ef
diff --git a/spdy/core/http2_frame_decoder_adapter.h b/spdy/core/http2_frame_decoder_adapter.h
new file mode 100644
index 0000000..02deafd
--- /dev/null
+++ b/spdy/core/http2_frame_decoder_adapter.h
@@ -0,0 +1,514 @@
+// 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_