| # 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: |
| |
| | Method | Purpose | |
| | -------------------- | ----------------------------------------------------- | |
| | `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.* |