HTTP/2 Decoder Implementation Notes

The general philosophy used here is to do as little as possible. In particular, this means that member variables aren't read or written if not absolutely necessary, especially in optimized builds.

If we can reasonably expect the listener to double check our work (i.e. looking up a stream id, and therefore noticing if it is supposed to be non-zero but isn‘t), then we’ll skip that check.

FrameDecoderState

This class provides common state and behaviors needed by all of the payload decoders, including:

  • Providing the common frame header (Http2FrameHeader) for the frame being decoded.

  • Tracking the amount of payload remaining to be decoded (often tracked only if the payload is split across decode buffers);

  • Decoding fixed size structures in the payload (e.g. Http2PriorityFields), updating the amount of payload remaining to be decoded as it does so, or reporting a Frame Size Error if a fixed size structure is truncated.

For DATA, HEADERS and PUSH_PROMISE frames, which support padding, FrameDecoderState also supports:

  • Reading the (optional) Pad Length field and reporting its value to the listener.

  • Tracking the amount of trailing padding that remains to be skipped.

  • Skipping the trailing padding, reporting to the listener as it does so.

Http2FrameDecoderListener

Defines the interface which HTTP/2 decoder clients must implement in order to receive callbacks from the decoder as it decodes each frame.

Simple frames that contain a single fixed size payload have correspondingly simple callback methods. For example:

  • WINDOW_UPDATE frames are reported via OnWindowUpdate().
  • PING frames are reported via OnPing() or OnPingAck(), depending on whether the ACK flag is set.

However a frame type X with a variable length payload will be reported via OnXStart() and OnXEnd() methods, which bracket calls to other methods that report on the payload. For example:

  • DATA frames are reported via OnDataStart(), OnPadLength() (if the PADDED flag is set), OnDataPayload() (more than once if necessary), OnPadding() (if OnPadLength() reports a non-zero amount of trailing padding), and OnDataEnd() once all the payload and padding has been reported to the listener.
  • GOAWAY frames are reported via OnGoAwayStart(), OnGoAwayOpaqueData() (more than once if necessary), and OnGoAwayEnd() once all the opaque data has been reported to the listener.

In addition there are two error callbacks:

  • OnPaddingTooLong(): There isn‘t enough room in the frame’s payload for all of the padding, let alone anything else.
  • OnFrameSizeError(): The frame size isn‘t correct, except for errors reported by OnPaddingTooLong(). For example, a PRIORITY frame isn’t exactly 5 bytes long, or a SETTINGS frame payload isn't a multiple of 6 bytes long.

FailingHttp2FrameDecoderListener

This is a test-only sub-class of Http2FrameDecoderListener that treats any call as a test failure. Its purpose is to make it easy to detect when a payload decoder has called the wrong method or a test listener has failed to implement all necessary methods.

FrameParts

This is a test-only sub-class of Http2FrameDecoderListener that implements ALL of the Http2FrameDecoderListener methods. Its purpose is to record all the provided info during the decoding of a single frame, and also to provide some validation of the callbacks as they are received (e.g. it generates a test failure if OnPadLength() is called for a frame that doesn't have padding).

FrameParts instances are also directly created by tests for comparing against those instances that are created while decoding. See FrameParts::VerifyEquals().

FramePartsCollector

This is a test-only sub-class of FailingHttp2FrameDecoderListener, which implements NONE of the Http2FrameDecoderListener methods, but instead serves as a base class for test listeners for each payload decoder. It provides these protected methods:

MethodPurpose
StartFrame()For use when implementing the OnXStart() method for
: : frame type X. Creates a new FrameParts instance, :
: : and making it available to CurrentFrame(), :
: : EndFrame() and FrameError() (see below). :
CurrentFrame()For use when implementing the listener methods for
: : variable length and optional payload components (e.g. :
: : OnDataPayload(), OnHeadersPriority(), or :
: : OnPadding()). StartFrame() must have already been :
: : called for the frame. :
EndFrame()For use when implementing the OnXEnd() method for
: : frame type X. Pushes the frame that CurrentFrame() :
: : would report onto the list of completely decoded :
: : frames that it has recorded. StartFrame() must have :
: : already been called for the frame. :
StartAndEndFrame()For use when implementing the OnX() method for
: : frame type X that has a fixed size payload (e.g. :
: : OnRstStream()), and hence is not reported via :
: : bracketed OnXStart() and OnXEnd() methods. :
FrameError()For use in handling OnPaddingTooLong() and
: : OnFrameSizeError(), where an earlier OnXStart() :
: : method may or may not have already been called. :

For consideration: If FramePartsCollector implemented ALL of the Http2FrameDecoderListener methods, then we could eliminate the Listener classes in payload decoder tests. Instead we could add a FramePartsCollector constructor overload which takes the expected Http2FrameType. If the frame type has been provided, then FramePartsCollector will validate it, but otherwise will forward all calls to the current FrameParts instance, using the methods described above just as a Listener might today.