blob: 9bb16889060fb7bd7c69eb5be044d07017a4ec1c [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_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
#define QUICHE_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
// Http2FrameDecoderListener is the interface which the HTTP/2 decoder uses
// to report the decoded frames to a listener.
//
// The general design is to assume that the listener will copy the data it needs
// (e.g. frame headers) and will keep track of the implicit state of the
// decoding process (i.e. the decoder maintains just the information it needs in
// order to perform the decoding). Therefore, the parameters are just those with
// (potentially) new data, not previously provided info about the current frame.
//
// The calls are described as if they are made in quick succession, i.e. one
// after another, but of course the decoder needs input to decode, and the
// decoder will only call the listener once the necessary input has been
// provided. For example: OnDataStart can only be called once the 9 bytes of
// of an HTTP/2 common frame header have been received. The decoder will call
// the listener methods as soon as possible to avoid almost all buffering.
//
// The listener interface is designed so that it is possible to exactly
// reconstruct the serialized frames, with the exception of reserved bits,
// including in the frame header's flags and stream_id fields, which will have
// been cleared before the methods below are called.
#include <stddef.h>
#include <cstdint>
#include <type_traits>
#include "http2/http2_constants.h"
#include "http2/http2_structures.h"
namespace http2 {
// TODO(jamessynge): Consider sorting the methods by frequency of call, if that
// helps at all.
class Http2FrameDecoderListener {
public:
Http2FrameDecoderListener() {}
virtual ~Http2FrameDecoderListener() {}
// Called once the common frame header has been decoded for any frame, and
// before any of the methods below, which will also be called. This method is
// included in this interface only for the purpose of supporting SpdyFramer
// semantics via an adapter. This is the only method that has a non-void
// return type, and this is just so that Http2FrameDecoderAdapter (called
// from SpdyFramer) can more readily pass existing tests that expect decoding
// to stop if the headers alone indicate an error. Return false to stop
// decoding just after decoding the header, else return true to continue
// decoding.
// TODO(jamessynge): Remove OnFrameHeader once done with supporting
// SpdyFramer's exact states.
virtual bool OnFrameHeader(const Http2FrameHeader& header) = 0;
//////////////////////////////////////////////////////////////////////////////
// Called once the common frame header has been decoded for a DATA frame,
// before examining the frame's payload, after which:
// OnPadLength will be called if header.IsPadded() is true, i.e. if the
// PADDED flag is set;
// OnDataPayload will be called as the non-padding portion of the payload
// is available until all of it has been provided;
// OnPadding will be called if the frame is padded AND the Pad Length field
// is greater than zero;
// OnDataEnd will be called last. If the frame is unpadded and has no
// payload, then this will be called immediately after OnDataStart.
virtual void OnDataStart(const Http2FrameHeader& header) = 0;
// Called when the next non-padding portion of a DATA frame's payload is
// received.
// |data| The start of |len| bytes of data.
// |len| The length of the data buffer. Maybe zero in some cases, which does
// not mean anything special.
virtual void OnDataPayload(const char* data, size_t len) = 0;
// Called after an entire DATA frame has been received.
// If header.IsEndStream() == true, this is the last data for the stream.
virtual void OnDataEnd() = 0;
// Called once the common frame header has been decoded for a HEADERS frame,
// before examining the frame's payload, after which:
// OnPadLength will be called if header.IsPadded() is true, i.e. if the
// PADDED flag is set;
// OnHeadersPriority will be called if header.HasPriority() is true, i.e. if
// the frame has the PRIORITY flag;
// OnHpackFragment as the remainder of the non-padding payload is available
// until all if has been provided;
// OnPadding will be called if the frame is padded AND the Pad Length field
// is greater than zero;
// OnHeadersEnd will be called last; If the frame is unpadded and has no
// payload, then this will be called immediately after OnHeadersStart;
// OnHeadersEnd indicates the end of the HPACK block only if the frame
// header had the END_HEADERS flag set, else the END_HEADERS should be
// looked for on a subsequent CONTINUATION frame.
virtual void OnHeadersStart(const Http2FrameHeader& header) = 0;
// Called when a HEADERS frame is received with the PRIORITY flag set and
// the priority fields have been decoded.
virtual void OnHeadersPriority(
const Http2PriorityFields& priority_fields) = 0;
// Called when a fragment (i.e. some or all of an HPACK Block) is received;
// this may be part of a HEADERS, PUSH_PROMISE or CONTINUATION frame.
// |data| The start of |len| bytes of data.
// |len| The length of the data buffer. Maybe zero in some cases, which does
// not mean anything special, except that it simplified the decoder.
virtual void OnHpackFragment(const char* data, size_t len) = 0;
// Called after an entire HEADERS frame has been received. The frame is the
// end of the HEADERS if the END_HEADERS flag is set; else there should be
// CONTINUATION frames after this frame.
virtual void OnHeadersEnd() = 0;
// Called when an entire PRIORITY frame has been decoded.
virtual void OnPriorityFrame(const Http2FrameHeader& header,
const Http2PriorityFields& priority_fields) = 0;
// Called once the common frame header has been decoded for a CONTINUATION
// frame, before examining the frame's payload, after which:
// OnHpackFragment as the frame's payload is available until all of it
// has been provided;
// OnContinuationEnd will be called last; If the frame has no payload,
// then this will be called immediately after OnContinuationStart;
// the HPACK block is at an end if and only if the frame header passed
// to OnContinuationStart had the END_HEADERS flag set.
virtual void OnContinuationStart(const Http2FrameHeader& header) = 0;
// Called after an entire CONTINUATION frame has been received. The frame is
// the end of the HEADERS if the END_HEADERS flag is set.
virtual void OnContinuationEnd() = 0;
// Called when Pad Length field has been read. Applies to DATA and HEADERS
// frames. For PUSH_PROMISE frames, the Pad Length + 1 is provided in the
// OnPushPromiseStart call as total_padding_length.
virtual void OnPadLength(size_t pad_length) = 0;
// Called when padding is skipped over.
virtual void OnPadding(const char* padding, size_t skipped_length) = 0;
// Called when an entire RST_STREAM frame has been decoded.
// This is the only callback for RST_STREAM frames.
virtual void OnRstStream(const Http2FrameHeader& header,
Http2ErrorCode error_code) = 0;
// Called once the common frame header has been decoded for a SETTINGS frame
// without the ACK flag, before examining the frame's payload, after which:
// OnSetting will be called in turn for each pair of settings parameter and
// value found in the payload;
// OnSettingsEnd will be called last; If the frame has no payload,
// then this will be called immediately after OnSettingsStart.
// The frame header is passed so that the caller can check the stream_id,
// which should be zero, but that hasn't been checked by the decoder.
virtual void OnSettingsStart(const Http2FrameHeader& header) = 0;
// Called for each setting parameter and value within a SETTINGS frame.
virtual void OnSetting(const Http2SettingFields& setting_fields) = 0;
// Called after parsing the complete payload of SETTINGS frame (non-ACK).
virtual void OnSettingsEnd() = 0;
// Called when an entire SETTINGS frame, with the ACK flag, has been decoded.
virtual void OnSettingsAck(const Http2FrameHeader& header) = 0;
// Called just before starting to process the HPACK block of a PUSH_PROMISE
// frame. The Pad Length field has already been decoded at this point, so
// OnPadLength will not be called; note that total_padding_length is Pad
// Length + 1. After OnPushPromiseStart:
// OnHpackFragment as the remainder of the non-padding payload is available
// until all if has been provided;
// OnPadding will be called if the frame is padded AND the Pad Length field
// is greater than zero (i.e. total_padding_length > 1);
// OnPushPromiseEnd will be called last; If the frame is unpadded and has no
// payload, then this will be called immediately after OnPushPromiseStart.
virtual void OnPushPromiseStart(const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) = 0;
// Called after all of the HPACK block fragment and padding of a PUSH_PROMISE
// has been decoded and delivered to the listener. This call indicates the end
// of the HPACK block if and only if the frame header had the END_HEADERS flag
// set (i.e. header.IsEndHeaders() is true); otherwise the next block must be
// a CONTINUATION frame with the same stream id (not the same promised stream
// id).
virtual void OnPushPromiseEnd() = 0;
// Called when an entire PING frame, without the ACK flag, has been decoded.
virtual void OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) = 0;
// Called when an entire PING frame, with the ACK flag, has been decoded.
virtual void OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) = 0;
// Called after parsing a GOAWAY frame's header and fixed size fields, after
// which:
// OnGoAwayOpaqueData will be called as opaque data of the payload becomes
// available to the decoder, until all of it has been provided to the
// listener;
// OnGoAwayEnd will be called last, after all the opaque data has been
// provided to the listener; if there is no opaque data, then OnGoAwayEnd
// will be called immediately after OnGoAwayStart.
virtual void OnGoAwayStart(const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) = 0;
// Called when the next portion of a GOAWAY frame's payload is received.
// |data| The start of |len| bytes of opaque data.
// |len| The length of the opaque data buffer. Maybe zero in some cases,
// which does not mean anything special.
virtual void OnGoAwayOpaqueData(const char* data, size_t len) = 0;
// Called after finishing decoding all of a GOAWAY frame.
virtual void OnGoAwayEnd() = 0;
// Called when an entire WINDOW_UPDATE frame has been decoded. The
// window_size_increment is required to be non-zero, but that has not been
// checked. If header.stream_id==0, the connection's flow control window is
// being increased, else the specified stream's flow control is being
// increased.
virtual void OnWindowUpdate(const Http2FrameHeader& header,
uint32_t window_size_increment) = 0;
// Called when an ALTSVC frame header and origin length have been parsed.
// Either or both lengths may be zero. After OnAltSvcStart:
// OnAltSvcOriginData will be called until all of the (optional) Origin
// has been provided;
// OnAltSvcValueData will be called until all of the Alt-Svc-Field-Value
// has been provided;
// OnAltSvcEnd will called last, after all of the origin and
// Alt-Svc-Field-Value have been delivered to the listener.
virtual void OnAltSvcStart(const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) = 0;
// Called when decoding the (optional) origin of an ALTSVC;
// the field is uninterpreted.
virtual void OnAltSvcOriginData(const char* data, size_t len) = 0;
// Called when decoding the Alt-Svc-Field-Value of an ALTSVC;
// the field is uninterpreted.
virtual void OnAltSvcValueData(const char* data, size_t len) = 0;
// Called after decoding all of a ALTSVC frame and providing to the listener
// via the above methods.
virtual void OnAltSvcEnd() = 0;
// Called when an PRIORITY_UPDATE frame header and Prioritized Stream ID have
// been parsed. Afterwards:
// OnPriorityUpdatePayload will be called each time a portion of the
// Priority Field Value field is available until all of it has been
// provided;
// OnPriorityUpdateEnd will be called last. If the frame has an empty
// Priority Field Value, then this will be called immediately after
// OnPriorityUpdateStart.
virtual void OnPriorityUpdateStart(
const Http2FrameHeader& header,
const Http2PriorityUpdateFields& priority_update) = 0;
// Called when the next portion of a PRIORITY_UPDATE frame's Priority Field
// Value field is received.
// |data| The start of |len| bytes of data.
// |len| The length of the data buffer. May be zero in some cases, which does
// not mean anything special.
virtual void OnPriorityUpdatePayload(const char* data, size_t len) = 0;
// Called after an entire PRIORITY_UPDATE frame has been received.
virtual void OnPriorityUpdateEnd() = 0;
// Called when the common frame header has been decoded, but the frame type
// is unknown, after which:
// OnUnknownPayload is called as the payload of the frame is provided to the
// decoder, until all of the payload has been decoded;
// OnUnknownEnd will called last, after the entire frame of the unknown type
// has been decoded and provided to the listener.
virtual void OnUnknownStart(const Http2FrameHeader& header) = 0;
// Called when the payload of an unknown frame type is received.
// |data| A buffer containing the data received.
// |len| The length of the data buffer.
virtual void OnUnknownPayload(const char* data, size_t len) = 0;
// Called after decoding all of the payload of an unknown frame type.
virtual void OnUnknownEnd() = 0;
//////////////////////////////////////////////////////////////////////////////
// Below here are events indicating a problem has been detected during
// decoding (i.e. the received frames are malformed in some way).
// Padding field (uint8) has a value that is too large (i.e. the amount of
// padding is greater than the remainder of the payload that isn't required).
// From RFC Section 6.1, DATA:
// If the length of the padding is the length of the frame payload or
// greater, the recipient MUST treat this as a connection error
// (Section 5.4.1) of type PROTOCOL_ERROR.
// The same is true for HEADERS and PUSH_PROMISE.
virtual void OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) = 0;
// Frame size error. Depending upon the effected frame, this may or may not
// require terminating the connection, though that is probably the best thing
// to do.
// From RFC Section 4.2, Frame Size:
// An endpoint MUST send an error code of FRAME_SIZE_ERROR if a frame
// exceeds the size defined in SETTINGS_MAX_FRAME_SIZE, exceeds any limit
// defined for the frame type, or is too small to contain mandatory frame
// data. A frame size error in a frame that could alter the state of the
// the entire connection MUST be treated as a connection error
// (Section 5.4.1); this includes any frame carrying a header block
// (Section 4.3) (that is, HEADERS, PUSH_PROMISE, and CONTINUATION),
// SETTINGS, and any frame with a stream identifier of 0.
virtual void OnFrameSizeError(const Http2FrameHeader& header) = 0;
};
// Do nothing for each call. Useful for ignoring a frame that is invalid.
class Http2FrameDecoderNoOpListener : public Http2FrameDecoderListener {
public:
Http2FrameDecoderNoOpListener() {}
~Http2FrameDecoderNoOpListener() override {}
// TODO(jamessynge): Remove OnFrameHeader once done with supporting
// SpdyFramer's exact states.
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 /*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 {}
};
static_assert(!std::is_abstract<Http2FrameDecoderNoOpListener>(),
"Http2FrameDecoderNoOpListener ought to be concrete.");
} // namespace http2
#endif // QUICHE_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_