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_HTTP2_STRUCTURE_DECODER_H_ 6 #define QUICHE_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_ 7 8 // Http2StructureDecoder is a class for decoding the fixed size structures in 9 // the HTTP/2 spec, defined in quiche/http2/http2_structures.h. This class 10 // is in aid of deciding whether to keep the SlowDecode methods which I 11 // (jamessynge) now think may not be worth their complexity. In particular, 12 // if most transport buffers are large, so it is rare that a structure is 13 // split across buffer boundaries, than the cost of buffering upon 14 // those rare occurrences is small, which then simplifies the callers. 15 16 #include <cstdint> 17 18 #include "quiche/http2/decoder/decode_buffer.h" 19 #include "quiche/http2/decoder/decode_http2_structures.h" 20 #include "quiche/http2/decoder/decode_status.h" 21 #include "quiche/http2/http2_structures.h" 22 #include "quiche/common/platform/api/quiche_export.h" 23 #include "quiche/common/platform/api/quiche_logging.h" 24 25 namespace http2 { 26 namespace test { 27 class Http2StructureDecoderPeer; 28 } // namespace test 29 30 class QUICHE_EXPORT Http2StructureDecoder { 31 public: 32 // The caller needs to keep track of whether to call Start or Resume. 33 // 34 // Start has an optimization for the case where the DecodeBuffer holds the 35 // entire encoded structure; in that case it decodes into *out and returns 36 // true, and does NOT touch the data members of the Http2StructureDecoder 37 // instance because the caller won't be calling Resume later. 38 // 39 // However, if the DecodeBuffer is too small to hold the entire encoded 40 // structure, Start copies the available bytes into the Http2StructureDecoder 41 // instance, and returns false to indicate that it has not been able to 42 // complete the decoding. 43 // 44 template <class S> Start(S * out,DecodeBuffer * db)45 bool Start(S* out, DecodeBuffer* db) { 46 static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small"); 47 QUICHE_DVLOG(2) << __func__ << "@" << this 48 << ": db->Remaining=" << db->Remaining() 49 << "; EncodedSize=" << S::EncodedSize(); 50 if (db->Remaining() >= S::EncodedSize()) { 51 DoDecode(out, db); 52 return true; 53 } 54 IncompleteStart(db, S::EncodedSize()); 55 return false; 56 } 57 58 template <class S> Resume(S * out,DecodeBuffer * db)59 bool Resume(S* out, DecodeBuffer* db) { 60 QUICHE_DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_ 61 << "; db->Remaining=" << db->Remaining(); 62 if (ResumeFillingBuffer(db, S::EncodedSize())) { 63 // We have the whole thing now. 64 QUICHE_DVLOG(2) << __func__ << "@" << this << " offset_=" << offset_ 65 << " Ready to decode from buffer_."; 66 DecodeBuffer buffer_db(buffer_, S::EncodedSize()); 67 DoDecode(out, &buffer_db); 68 return true; 69 } 70 QUICHE_DCHECK_LT(offset_, S::EncodedSize()); 71 return false; 72 } 73 74 // A second pair of Start and Resume, where the caller has a variable, 75 // |remaining_payload| that is both tested for sufficiency and updated 76 // during decoding. Note that the decode buffer may extend beyond the 77 // remaining payload because the buffer may include padding. 78 template <class S> Start(S * out,DecodeBuffer * db,uint32_t * remaining_payload)79 DecodeStatus Start(S* out, DecodeBuffer* db, uint32_t* remaining_payload) { 80 static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small"); 81 QUICHE_DVLOG(2) << __func__ << "@" << this 82 << ": *remaining_payload=" << *remaining_payload 83 << "; db->Remaining=" << db->Remaining() 84 << "; EncodedSize=" << S::EncodedSize(); 85 if (db->MinLengthRemaining(*remaining_payload) >= S::EncodedSize()) { 86 DoDecode(out, db); 87 *remaining_payload -= S::EncodedSize(); 88 return DecodeStatus::kDecodeDone; 89 } 90 return IncompleteStart(db, remaining_payload, S::EncodedSize()); 91 } 92 93 template <class S> Resume(S * out,DecodeBuffer * db,uint32_t * remaining_payload)94 bool Resume(S* out, DecodeBuffer* db, uint32_t* remaining_payload) { 95 QUICHE_DVLOG(3) << __func__ << "@" << this << ": offset_=" << offset_ 96 << "; *remaining_payload=" << *remaining_payload 97 << "; db->Remaining=" << db->Remaining() 98 << "; EncodedSize=" << S::EncodedSize(); 99 if (ResumeFillingBuffer(db, remaining_payload, S::EncodedSize())) { 100 // We have the whole thing now. 101 QUICHE_DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_ 102 << "; Ready to decode from buffer_."; 103 DecodeBuffer buffer_db(buffer_, S::EncodedSize()); 104 DoDecode(out, &buffer_db); 105 return true; 106 } 107 QUICHE_DCHECK_LT(offset_, S::EncodedSize()); 108 return false; 109 } 110 offset()111 uint32_t offset() const { return offset_; } 112 113 private: 114 friend class test::Http2StructureDecoderPeer; 115 116 uint32_t IncompleteStart(DecodeBuffer* db, uint32_t target_size); 117 DecodeStatus IncompleteStart(DecodeBuffer* db, uint32_t* remaining_payload, 118 uint32_t target_size); 119 120 bool ResumeFillingBuffer(DecodeBuffer* db, uint32_t target_size); 121 bool ResumeFillingBuffer(DecodeBuffer* db, uint32_t* remaining_payload, 122 uint32_t target_size); 123 124 uint32_t offset_; 125 char buffer_[Http2FrameHeader::EncodedSize()]; 126 }; 127 128 } // namespace http2 129 130 #endif // QUICHE_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_ 131