1 // Copyright 2017 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_HPACK_DECODER_HPACK_DECODER_H_ 6 #define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_ 7 8 // Decodes HPACK blocks, calls an HpackDecoderListener with the decoded header 9 // entries. Also notifies the listener of errors and of the boundaries of the 10 // HPACK blocks. 11 12 // TODO(jamessynge): Add feature allowing an HpackEntryDecoderListener 13 // sub-class (and possibly others) to be passed in for counting events, 14 // so that deciding whether to count is not done by having lots of if 15 // statements, but instead by inserting an indirection only when needed. 16 17 // TODO(jamessynge): Consider whether to return false from methods below 18 // when an error has been previously detected. It protects calling code 19 // from its failure to pay attention to previous errors, but should we 20 // spend time to do that? 21 22 #include <stddef.h> 23 24 #include <cstdint> 25 26 #include "quiche/http2/decoder/decode_buffer.h" 27 #include "quiche/http2/hpack/decoder/hpack_block_decoder.h" 28 #include "quiche/http2/hpack/decoder/hpack_decoder_listener.h" 29 #include "quiche/http2/hpack/decoder/hpack_decoder_state.h" 30 #include "quiche/http2/hpack/decoder/hpack_decoder_tables.h" 31 #include "quiche/http2/hpack/decoder/hpack_decoding_error.h" 32 #include "quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h" 33 #include "quiche/common/platform/api/quiche_export.h" 34 35 namespace http2 { 36 namespace test { 37 class HpackDecoderPeer; 38 } // namespace test 39 40 class QUICHE_EXPORT HpackDecoder { 41 public: 42 HpackDecoder(HpackDecoderListener* listener, size_t max_string_size); 43 virtual ~HpackDecoder(); 44 45 HpackDecoder(const HpackDecoder&) = delete; 46 HpackDecoder& operator=(const HpackDecoder&) = delete; 47 48 // max_string_size specifies the maximum size of an on-the-wire string (name 49 // or value, plain or Huffman encoded) that will be accepted. See sections 50 // 5.1 and 5.2 of RFC 7541. This is a defense against OOM attacks; HTTP/2 51 // allows a decoder to enforce any limit of the size of the header lists 52 // that it is willing to decode, including less than the MAX_HEADER_LIST_SIZE 53 // setting, a setting that is initially unlimited. For example, we might 54 // choose to send a MAX_HEADER_LIST_SIZE of 64KB, and to use that same value 55 // as the upper bound for individual strings. 56 void set_max_string_size_bytes(size_t max_string_size_bytes); 57 58 // ApplyHeaderTableSizeSetting notifies this object that this endpoint has 59 // received a SETTINGS ACK frame acknowledging an earlier SETTINGS frame from 60 // this endpoint specifying a new value for SETTINGS_HEADER_TABLE_SIZE (the 61 // maximum size of the dynamic table that this endpoint will use to decode 62 // HPACK blocks). 63 // Because a SETTINGS frame can contain SETTINGS_HEADER_TABLE_SIZE values, 64 // the caller must keep track of those multiple changes, and make 65 // corresponding calls to this method. In particular, a call must be made 66 // with the lowest value acknowledged by the peer, and a call must be made 67 // with the final value acknowledged, in that order; additional calls may 68 // be made if additional values were sent. These calls must be made between 69 // decoding the SETTINGS ACK, and before the next HPACK block is decoded. 70 void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size); 71 72 // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE. GetCurrentHeaderTableSizeSetting()73 size_t GetCurrentHeaderTableSizeSetting() const { 74 return decoder_state_.GetCurrentHeaderTableSizeSetting(); 75 } 76 77 // Prepares the decoder for decoding a new HPACK block, and announces this to 78 // its listener. Returns true if OK to continue with decoding, false if an 79 // error has been detected, which for StartDecodingBlock means the error was 80 // detected while decoding a previous HPACK block. 81 bool StartDecodingBlock(); 82 83 // Decodes a fragment (some or all of the remainder) of an HPACK block, 84 // reporting header entries (name & value pairs) that it completely decodes 85 // in the process to the listener. Returns true successfully decoded, false if 86 // an error has been detected, either during decoding of the fragment, or 87 // prior to this call. 88 bool DecodeFragment(DecodeBuffer* db); 89 90 // Completes the process of decoding an HPACK block: if the HPACK block was 91 // properly terminated, announces the end of the header list to the listener 92 // and returns true; else returns false. 93 bool EndDecodingBlock(); 94 95 // If no error has been detected so far, query |decoder_state_| for errors and 96 // set |error_| if necessary. Returns true if an error has ever been 97 // detected. 98 bool DetectError(); 99 GetDynamicTableSize()100 size_t GetDynamicTableSize() const { 101 return decoder_state_.GetDynamicTableSize(); 102 } 103 104 // Error code if an error has occurred, HpackDecodingError::kOk otherwise. error()105 HpackDecodingError error() const { return error_; } 106 107 private: 108 friend class test::HpackDecoderPeer; 109 110 // Reports an error to the listener IF this is the first error detected. 111 void ReportError(HpackDecodingError error); 112 113 // The decompressor state, as defined by HPACK (i.e. the static and dynamic 114 // tables). 115 HpackDecoderState decoder_state_; 116 117 // Assembles the various parts of a header entry into whole entries. 118 HpackWholeEntryBuffer entry_buffer_; 119 120 // The decoder of HPACK blocks into entry parts, passed to entry_buffer_. 121 HpackBlockDecoder block_decoder_; 122 123 // Error code if an error has occurred, HpackDecodingError::kOk otherwise. 124 HpackDecodingError error_; 125 }; 126 127 } // namespace http2 128 129 #endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_ 130