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 19 #include "quiche/http2/decoder/decode_buffer.h" 20 #include "quiche/http2/decoder/decode_status.h" 21 #include "quiche/http2/decoder/http2_frame_decoder_listener.h" 22 #include "quiche/http2/decoder/http2_structure_decoder.h" 23 #include "quiche/http2/http2_constants.h" 24 #include "quiche/http2/http2_structures.h" 25 #include "quiche/common/platform/api/quiche_export.h" 26 #include "quiche/common/platform/api/quiche_logging.h" 27 28 namespace http2 { 29 namespace test { 30 class FrameDecoderStatePeer; 31 } // namespace test 32 33 class QUICHE_EXPORT FrameDecoderState { 34 public: FrameDecoderState()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. set_listener(Http2FrameDecoderListener * listener)48 void set_listener(Http2FrameDecoderListener* listener) { 49 listener_ = listener; 50 } listener()51 Http2FrameDecoderListener* listener() const { return listener_; } 52 53 // The most recently decoded frame header. 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> StartDecodingStructureInPayload(S * out,DecodeBuffer * db)62 DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) { 63 QUICHE_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining() 64 << "\n\tremaining_payload_=" << remaining_payload_ 65 << "\n\tneed=" << S::EncodedSize(); 66 DecodeStatus status = 67 structure_decoder_.Start(out, db, &remaining_payload_); 68 if (status != DecodeStatus::kDecodeError) { 69 return status; 70 } 71 QUICHE_DVLOG(2) 72 << "StartDecodingStructureInPayload: detected frame size error"; 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> ResumeDecodingStructureInPayload(S * out,DecodeBuffer * db)80 DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) { 81 QUICHE_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining() 82 << "\n\tremaining_payload_=" << remaining_payload_; 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 { 88 QUICHE_DVLOG(2) 89 << "ResumeDecodingStructureInPayload: detected frame size error"; 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. InitializeRemainders()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. remaining_total_payload()109 size_t remaining_total_payload() const { 110 QUICHE_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). remaining_payload()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. remaining_payload_and_padding()127 size_t remaining_payload_and_padding() const { 128 QUICHE_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. remaining_padding()137 uint32_t remaining_padding() const { 138 QUICHE_DCHECK(IsPaddable()) << frame_header(); 139 return remaining_padding_; 140 } 141 142 // How many bytes of the remaining payload are in db? AvailablePayload(DecodeBuffer * 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. AvailablePayloadAndPadding(DecodeBuffer * db)149 size_t AvailablePayloadAndPadding(DecodeBuffer* db) const { 150 QUICHE_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. AvailablePadding(DecodeBuffer * db)158 size_t AvailablePadding(DecodeBuffer* db) const { 159 QUICHE_DCHECK(IsPaddable()) << frame_header(); 160 QUICHE_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. ConsumePayload(size_t amount)168 void ConsumePayload(size_t amount) { 169 QUICHE_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. StartDecodingFrameHeader(DecodeBuffer * db)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. ResumeDecodingFrameHeader(DecodeBuffer * db)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. RetainFlags(uint8_t 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. ClearFlags()224 void ClearFlags() { frame_header_.flags = Http2FrameFlag(); } 225 226 // Returns true if the type of frame being decoded can have padding. IsPaddable()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_ 253