QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_ |
| 6 | #define QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_ |
| 7 | |
| 8 | // FrameDecoderState provides state and behaviors in support of decoding |
| 9 | // the common frame header and the payload of all frame types. |
| 10 | // It is an input to all of the payload decoders. |
| 11 | |
| 12 | // TODO(jamessynge): Since FrameDecoderState has far more than state in it, |
| 13 | // rename to FrameDecoderHelper, or similar. |
| 14 | |
| 15 | #include <stddef.h> |
| 16 | |
| 17 | #include <cstdint> |
| 18 | |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 19 | #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" |
| 20 | #include "net/third_party/quiche/src/http2/decoder/decode_status.h" |
| 21 | #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h" |
| 22 | #include "net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h" |
| 23 | #include "net/third_party/quiche/src/http2/http2_constants.h" |
| 24 | #include "net/third_party/quiche/src/http2/http2_structures.h" |
| 25 | #include "net/third_party/quiche/src/http2/platform/api/http2_export.h" |
QUICHE team | 61940b4 | 2019-03-07 23:32:27 -0500 | [diff] [blame] | 26 | #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 27 | |
| 28 | namespace http2 { |
| 29 | namespace test { |
| 30 | class FrameDecoderStatePeer; |
| 31 | } // namespace test |
| 32 | |
| 33 | class HTTP2_EXPORT_PRIVATE FrameDecoderState { |
| 34 | public: |
| 35 | FrameDecoderState() {} |
| 36 | |
| 37 | // Sets the listener which the decoders should call as they decode HTTP/2 |
| 38 | // frames. The listener can be changed at any time, which allows for replacing |
| 39 | // it with a no-op listener when an error is detected, either by the payload |
| 40 | // decoder (OnPaddingTooLong or OnFrameSizeError) or by the "real" listener. |
| 41 | // That in turn allows us to define Http2FrameDecoderListener such that all |
| 42 | // methods have return type void, with no direct way to indicate whether the |
| 43 | // decoder should stop, and to eliminate from the decoder all checks of the |
| 44 | // return value. Instead the listener/caller can simply replace the current |
| 45 | // listener with a no-op listener implementation. |
| 46 | // TODO(jamessynge): Make set_listener private as only Http2FrameDecoder |
| 47 | // and tests need to set it, so it doesn't need to be public. |
| 48 | void set_listener(Http2FrameDecoderListener* listener) { |
| 49 | listener_ = listener; |
| 50 | } |
| 51 | Http2FrameDecoderListener* listener() const { return listener_; } |
| 52 | |
| 53 | // The most recently decoded frame header. |
| 54 | const Http2FrameHeader& frame_header() const { return frame_header_; } |
| 55 | |
| 56 | // Decode a structure in the payload, adjusting remaining_payload_ to account |
| 57 | // for the consumed portion of the payload. Returns kDecodeDone when fully |
| 58 | // decoded, kDecodeError if it ran out of payload before decoding completed, |
| 59 | // and kDecodeInProgress if the decode buffer didn't have enough of the |
| 60 | // remaining payload. |
| 61 | template <class S> |
| 62 | DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) { |
QUICHE team | 61940b4 | 2019-03-07 23:32:27 -0500 | [diff] [blame] | 63 | HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining() |
| 64 | << "\n\tremaining_payload_=" << remaining_payload_ |
| 65 | << "\n\tneed=" << S::EncodedSize(); |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 66 | DecodeStatus status = |
| 67 | structure_decoder_.Start(out, db, &remaining_payload_); |
| 68 | if (status != DecodeStatus::kDecodeError) { |
| 69 | return status; |
| 70 | } |
QUICHE team | 61940b4 | 2019-03-07 23:32:27 -0500 | [diff] [blame] | 71 | HTTP2_DVLOG(2) |
| 72 | << "StartDecodingStructureInPayload: detected frame size error"; |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 73 | return ReportFrameSizeError(); |
| 74 | } |
| 75 | |
| 76 | // Resume decoding of a structure that has been split across buffers, |
| 77 | // adjusting remaining_payload_ to account for the consumed portion of |
| 78 | // the payload. Returns values are as for StartDecodingStructureInPayload. |
| 79 | template <class S> |
| 80 | DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) { |
QUICHE team | 61940b4 | 2019-03-07 23:32:27 -0500 | [diff] [blame] | 81 | HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining() |
| 82 | << "\n\tremaining_payload_=" << remaining_payload_; |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 83 | if (structure_decoder_.Resume(out, db, &remaining_payload_)) { |
| 84 | return DecodeStatus::kDecodeDone; |
| 85 | } else if (remaining_payload_ > 0) { |
| 86 | return DecodeStatus::kDecodeInProgress; |
| 87 | } else { |
QUICHE team | 61940b4 | 2019-03-07 23:32:27 -0500 | [diff] [blame] | 88 | HTTP2_DVLOG(2) |
| 89 | << "ResumeDecodingStructureInPayload: detected frame size error"; |
QUICHE team | fd50a40 | 2018-12-07 22:54:05 -0500 | [diff] [blame] | 90 | return ReportFrameSizeError(); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Initializes the two remaining* fields, which is needed if the frame's |
| 95 | // payload is split across buffers, or the decoder calls ReadPadLength or |
| 96 | // StartDecodingStructureInPayload, and of course the methods below which |
| 97 | // read those fields, as their names imply. |
| 98 | void InitializeRemainders() { |
| 99 | remaining_payload_ = frame_header().payload_length; |
| 100 | // Note that remaining_total_payload() relies on remaining_padding_ being |
| 101 | // zero for frames that have no padding. |
| 102 | remaining_padding_ = 0; |
| 103 | } |
| 104 | |
| 105 | // Returns the number of bytes of the frame's payload that remain to be |
| 106 | // decoded, including any trailing padding. This method must only be called |
| 107 | // after the variables have been initialized, which in practice means once a |
| 108 | // payload decoder has called InitializeRemainders and/or ReadPadLength. |
| 109 | size_t remaining_total_payload() const { |
| 110 | DCHECK(IsPaddable() || remaining_padding_ == 0) << frame_header(); |
| 111 | return remaining_payload_ + remaining_padding_; |
| 112 | } |
| 113 | |
| 114 | // Returns the number of bytes of the frame's payload that remain to be |
| 115 | // decoded, excluding any trailing padding. This method must only be called |
| 116 | // after the variable has been initialized, which in practice means once a |
| 117 | // payload decoder has called InitializeRemainders; ReadPadLength will deduct |
| 118 | // the total number of padding bytes from remaining_payload_, including the |
| 119 | // size of the Pad Length field itself (1 byte). |
| 120 | size_t remaining_payload() const { return remaining_payload_; } |
| 121 | |
| 122 | // Returns the number of bytes of the frame's payload that remain to be |
| 123 | // decoded, including any trailing padding. This method must only be called if |
| 124 | // the frame type allows padding, and after the variable has been initialized, |
| 125 | // which in practice means once a payload decoder has called |
| 126 | // InitializeRemainders and/or ReadPadLength. |
| 127 | size_t remaining_payload_and_padding() const { |
| 128 | DCHECK(IsPaddable()) << frame_header(); |
| 129 | return remaining_payload_ + remaining_padding_; |
| 130 | } |
| 131 | |
| 132 | // Returns the number of bytes of trailing padding after the payload that |
| 133 | // remain to be decoded. This method must only be called if the frame type |
| 134 | // allows padding, and after the variable has been initialized, which in |
| 135 | // practice means once a payload decoder has called InitializeRemainders, |
| 136 | // and isn't set to a non-zero value until ReadPadLength has been called. |
| 137 | uint32_t remaining_padding() const { |
| 138 | DCHECK(IsPaddable()) << frame_header(); |
| 139 | return remaining_padding_; |
| 140 | } |
| 141 | |
| 142 | // How many bytes of the remaining payload are in db? |
| 143 | size_t AvailablePayload(DecodeBuffer* db) const { |
| 144 | return db->MinLengthRemaining(remaining_payload_); |
| 145 | } |
| 146 | |
| 147 | // How many bytes of the remaining payload and padding are in db? |
| 148 | // Call only for frames whose type is paddable. |
| 149 | size_t AvailablePayloadAndPadding(DecodeBuffer* db) const { |
| 150 | DCHECK(IsPaddable()) << frame_header(); |
| 151 | return db->MinLengthRemaining(remaining_payload_ + remaining_padding_); |
| 152 | } |
| 153 | |
| 154 | // How many bytes of the padding that have not yet been skipped are in db? |
| 155 | // Call only after remaining_padding_ has been set (for padded frames), or |
| 156 | // been cleared (for unpadded frames); and after all of the non-padding |
| 157 | // payload has been decoded. |
| 158 | size_t AvailablePadding(DecodeBuffer* db) const { |
| 159 | DCHECK(IsPaddable()) << frame_header(); |
| 160 | DCHECK_EQ(remaining_payload_, 0u); |
| 161 | return db->MinLengthRemaining(remaining_padding_); |
| 162 | } |
| 163 | |
| 164 | // Reduces remaining_payload_ by amount. To be called by a payload decoder |
| 165 | // after it has passed a variable length portion of the payload to the |
| 166 | // listener; remaining_payload_ will be automatically reduced when fixed |
| 167 | // size structures and padding, including the Pad Length field, are decoded. |
| 168 | void ConsumePayload(size_t amount) { |
| 169 | DCHECK_LE(amount, remaining_payload_); |
| 170 | remaining_payload_ -= amount; |
| 171 | } |
| 172 | |
| 173 | // Reads the Pad Length field into remaining_padding_, and appropriately sets |
| 174 | // remaining_payload_. When present, the Pad Length field is always the first |
| 175 | // field in the payload, which this method relies on so that the caller need |
| 176 | // not set remaining_payload_ before calling this method. |
| 177 | // If report_pad_length is true, calls the listener's OnPadLength method when |
| 178 | // it decodes the Pad Length field. |
| 179 | // Returns kDecodeDone if the decode buffer was not empty (i.e. because the |
| 180 | // field is only a single byte long, it can always be decoded if the buffer is |
| 181 | // not empty). |
| 182 | // Returns kDecodeError if the buffer is empty because the frame has no |
| 183 | // payload (i.e. payload_length() == 0). |
| 184 | // Returns kDecodeInProgress if the buffer is empty but the frame has a |
| 185 | // payload. |
| 186 | DecodeStatus ReadPadLength(DecodeBuffer* db, bool report_pad_length); |
| 187 | |
| 188 | // Skip the trailing padding bytes; only call once remaining_payload_==0. |
| 189 | // Returns true when the padding has been skipped. |
| 190 | // Does NOT check that the padding is all zeroes. |
| 191 | bool SkipPadding(DecodeBuffer* db); |
| 192 | |
| 193 | // Calls the listener's OnFrameSizeError method and returns kDecodeError. |
| 194 | DecodeStatus ReportFrameSizeError(); |
| 195 | |
| 196 | private: |
| 197 | friend class Http2FrameDecoder; |
| 198 | friend class test::FrameDecoderStatePeer; |
| 199 | |
| 200 | // Starts the decoding of a common frame header. Returns true if completed the |
| 201 | // decoding, false if the decode buffer didn't have enough data in it, in |
| 202 | // which case the decode buffer will have been drained and the caller should |
| 203 | // call ResumeDecodingFrameHeader when more data is available. This is called |
| 204 | // from Http2FrameDecoder, a friend class. |
| 205 | bool StartDecodingFrameHeader(DecodeBuffer* db) { |
| 206 | return structure_decoder_.Start(&frame_header_, db); |
| 207 | } |
| 208 | |
| 209 | // Resumes decoding the common frame header after the preceding call to |
| 210 | // StartDecodingFrameHeader returned false, as did any subsequent calls to |
| 211 | // ResumeDecodingFrameHeader. This is called from Http2FrameDecoder, |
| 212 | // a friend class. |
| 213 | bool ResumeDecodingFrameHeader(DecodeBuffer* db) { |
| 214 | return structure_decoder_.Resume(&frame_header_, db); |
| 215 | } |
| 216 | |
| 217 | // Clear any of the flags in the frame header that aren't set in valid_flags. |
| 218 | void RetainFlags(uint8_t valid_flags) { |
| 219 | frame_header_.RetainFlags(valid_flags); |
| 220 | } |
| 221 | |
| 222 | // Clear all of the flags in the frame header; for use with frame types that |
| 223 | // don't define any flags, such as WINDOW_UPDATE. |
| 224 | void ClearFlags() { frame_header_.flags = Http2FrameFlag(); } |
| 225 | |
| 226 | // Returns true if the type of frame being decoded can have padding. |
| 227 | bool IsPaddable() const { |
| 228 | return frame_header().type == Http2FrameType::DATA || |
| 229 | frame_header().type == Http2FrameType::HEADERS || |
| 230 | frame_header().type == Http2FrameType::PUSH_PROMISE; |
| 231 | } |
| 232 | |
| 233 | Http2FrameDecoderListener* listener_ = nullptr; |
| 234 | Http2FrameHeader frame_header_; |
| 235 | |
| 236 | // Number of bytes remaining to be decoded, if set; does not include the |
| 237 | // trailing padding once the length of padding has been determined. |
| 238 | // See ReadPadLength. |
| 239 | uint32_t remaining_payload_; |
| 240 | |
| 241 | // The amount of trailing padding after the payload that remains to be |
| 242 | // decoded. See ReadPadLength. |
| 243 | uint32_t remaining_padding_; |
| 244 | |
| 245 | // Generic decoder of structures, which takes care of buffering the needed |
| 246 | // bytes if the encoded structure is split across decode buffers. |
| 247 | Http2StructureDecoder structure_decoder_; |
| 248 | }; |
| 249 | |
| 250 | } // namespace http2 |
| 251 | |
| 252 | #endif // QUICHE_HTTP2_DECODER_FRAME_DECODER_STATE_H_ |