// 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_
