blob: fab2669805ea9e935ad0900a98671daf090c496b [file] [log] [blame]
// 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 <optional>
#include <string>
#include "absl/strings/string_view.h"
#include "quiche/http2/decoder/decode_status.h"
#include "quiche/http2/decoder/http2_frame_decoder.h"
#include "quiche/http2/decoder/http2_frame_decoder_listener.h"
#include "quiche/http2/http2_constants.h"
#include "quiche/http2/http2_structures.h"
#include "quiche/common/platform/api/quiche_export.h"
#include "quiche/spdy/core/hpack/hpack_decoder_adapter.h"
#include "quiche/spdy/core/spdy_alt_svc_wire_format.h"
#include "quiche/spdy/core/spdy_headers_handler_interface.h"
#include "quiche/spdy/core/spdy_protocol.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 QUICHE_EXPORT 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_DECOMPRESS_FAILURE, // There was an error decompressing.
SPDY_INVALID_PADDING, // HEADERS or DATA frame padding invalid
SPDY_INVALID_DATA_FRAME_FLAGS, // Data 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
// HttpDecoder or HttpDecoderAdapter error.
// See HpackDecodingError for description of each error code.
SPDY_HPACK_INDEX_VARINT_ERROR,
SPDY_HPACK_NAME_LENGTH_VARINT_ERROR,
SPDY_HPACK_VALUE_LENGTH_VARINT_ERROR,
SPDY_HPACK_NAME_TOO_LONG,
SPDY_HPACK_VALUE_TOO_LONG,
SPDY_HPACK_NAME_HUFFMAN_ERROR,
SPDY_HPACK_VALUE_HUFFMAN_ERROR,
SPDY_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE,
SPDY_HPACK_INVALID_INDEX,
SPDY_HPACK_INVALID_NAME_INDEX,
SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED,
SPDY_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK,
SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING,
SPDY_HPACK_TRUNCATED_BLOCK,
SPDY_HPACK_FRAGMENT_TOO_LONG,
SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT,
// Set if the visitor no longer wishes to receive events for this
// connection.
SPDY_STOP_PROCESSING,
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;
Http2DecoderAdapter(const Http2DecoderAdapter&) = delete;
Http2DecoderAdapter& operator=(const Http2DecoderAdapter&) = delete;
// 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);
spdy::ExtensionVisitorInterface* extension_visitor() const {
return extension_;
}
// 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_;
}
// 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.
//
// If the input contains the entirety of a DATA frame payload, GOAWAY frame
// Additional Debug Data field, or unknown frame payload, then the
// corresponding SpdyFramerVisitorInterface::OnStreamFrameData(),
// OnGoAwayFrameData(), or ExtensionVisitorInterface::OnFramePayload() method
// is guaranteed to be called exactly once, with the entire payload or field.
size_t ProcessInput(const char* data, size_t len);
// 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;
spdy::HpackDecoderAdapter& GetHpackDecoder() { return hpack_decoder_; }
const spdy::HpackDecoderAdapter& GetHpackDecoder() const {
return hpack_decoder_;
}
bool HasError() const;
// A visitor may call this method to indicate it no longer wishes to receive
// events for this connection.
void StopProcessing();
// Sets the limit on the size of received HTTP/2 frame payloads. Corresponds
// to SETTINGS_MAX_FRAME_SIZE as advertised to the peer.
void SetMaxFrameSize(size_t max_frame_size);
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 OnPriorityUpdateStart(
const Http2FrameHeader& header,
const Http2PriorityUpdateFields& priority_update) override;
void OnPriorityUpdatePayload(const char* data, size_t len) override;
void OnPriorityUpdateEnd() 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();
void set_spdy_state(SpdyState v);
void SetSpdyErrorAndNotify(SpdyFramerError error, std::string detailed_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.
std::optional<size_t> opt_pad_length_;
// Temporary buffers for the AltSvc fields.
std::string alt_svc_origin_;
std::string alt_svc_value_;
// Temporary buffers for PRIORITY_UPDATE fields.
uint32_t prioritized_stream_id_ = 0;
std::string priority_field_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.
spdy::HpackDecoderAdapter hpack_decoder_;
// The HTTP/2 frame decoder.
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(jamessynge): 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_ = SpdyState::SPDY_READY_FOR_FRAME;
SpdyFramerError spdy_framer_error_ = SpdyFramerError::SPDY_NO_ERROR;
// The limit on the size of received HTTP/2 payloads as specified in the
// SETTINGS_MAX_FRAME_SIZE advertised to peer.
size_t max_frame_size_ = 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_ = false;
// 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_ = false;
// 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;
};
} // 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 QUICHE_EXPORT SpdyFramerVisitorInterface {
public:
virtual ~SpdyFramerVisitorInterface() {}
// Called if an error is detected in the SpdyFrame protocol.
virtual void OnError(http2::Http2DecoderAdapter::SpdyFramerError error,
std::string detailed_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().
// |stream_id| The stream receiving data.
// |length| The length of the payload in this DATA frame. Includes the length
// of the data itself and potential padding.
// |fin| Whether the END_STREAM flag is set in the frame header.
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.
// |payload_length| The length of the payload in this HEADERS frame. Includes
// the length of the encoded header block and potential padding.
// |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 the END_STREAM flag is set in the frame header.
// |end| False if HEADERs frame is to be followed by a CONTINUATION frame,
// or true if not.
virtual void OnHeaders(SpdyStreamId stream_id, size_t payload_length,
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().
// |stream_id| The stream receiving the CONTINUATION.
// |payload_length| The length of the payload in this CONTINUATION frame.
// |end| True if this CONTINUATION frame will not be followed by another
// CONTINUATION frame.
virtual void OnContinuation(SpdyStreamId stream_id, size_t payload_length,
bool end) = 0;
// Called when an ALTSVC frame has been parsed.
virtual void OnAltSvc(
SpdyStreamId /*stream_id*/, absl::string_view /*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 PRIORITY_UPDATE frame is received on stream 0.
// |prioritized_stream_id| is the Prioritized Stream ID and
// |priority_field_value| is the Priority Field Value
// parsed from the frame payload.
virtual void OnPriorityUpdate(SpdyStreamId prioritized_stream_id,
absl::string_view priority_field_value) = 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.
// TODO(b/239060116): Remove this callback altogether.
virtual bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) = 0;
// Called when the common header for a non-standard frame is received. If the
// `length` is nonzero, the frame's payload will be provided via subsequent
// calls to OnUnknownFramePayload().
// |stream_id| The stream receiving the non-standard frame.
// |length| The length of the payload of the frame.
// |type| The type of the frame. This type is non-standard.
// |flags| The flags of the frame.
virtual void OnUnknownFrameStart(SpdyStreamId stream_id, size_t length,
uint8_t type, uint8_t flags) = 0;
// Called when a non-empty payload chunk for a non-standard frame is received.
// The payload for a single frame may be delivered as multiple calls to
// OnUnknownFramePayload(). Since the length field is passed in
// OnUnknownFrameStart(), there is no explicit indication of the end of the
// frame payload.
// |stream_id| The stream receiving the non-standard frame.
// |payload| The payload chunk, which will be non-empty.
virtual void OnUnknownFramePayload(SpdyStreamId stream_id,
absl::string_view payload) = 0;
};
class QUICHE_EXPORT ExtensionVisitorInterface {
public:
virtual ~ExtensionVisitorInterface() {}
// Called when non-standard SETTINGS are received.
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_