| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_ |
| #define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_ |
| |
| // Decodes HPACK blocks, calls an HpackDecoderListener with the decoded header |
| // entries. Also notifies the listener of errors and of the boundaries of the |
| // HPACK blocks. |
| |
| // TODO(jamessynge): Add feature allowing an HpackEntryDecoderListener |
| // sub-class (and possibly others) to be passed in for counting events, |
| // so that deciding whether to count is not done by having lots of if |
| // statements, but instead by inserting an indirection only when needed. |
| |
| // TODO(jamessynge): Consider whether to return false from methods below |
| // when an error has been previously detected. It protects calling code |
| // from its failure to pay attention to previous errors, but should we |
| // spend time to do that? |
| |
| #include <stddef.h> |
| |
| #include <cstdint> |
| |
| #include "http2/decoder/decode_buffer.h" |
| #include "http2/hpack/decoder/hpack_block_decoder.h" |
| #include "http2/hpack/decoder/hpack_decoder_listener.h" |
| #include "http2/hpack/decoder/hpack_decoder_state.h" |
| #include "http2/hpack/decoder/hpack_decoder_tables.h" |
| #include "http2/hpack/decoder/hpack_decoding_error.h" |
| #include "http2/hpack/decoder/hpack_whole_entry_buffer.h" |
| #include "common/platform/api/quiche_export.h" |
| |
| namespace http2 { |
| namespace test { |
| class HpackDecoderPeer; |
| } // namespace test |
| |
| class QUICHE_EXPORT_PRIVATE HpackDecoder { |
| public: |
| HpackDecoder(HpackDecoderListener* listener, size_t max_string_size); |
| virtual ~HpackDecoder(); |
| |
| HpackDecoder(const HpackDecoder&) = delete; |
| HpackDecoder& operator=(const HpackDecoder&) = delete; |
| |
| // max_string_size specifies the maximum size of an on-the-wire string (name |
| // or value, plain or Huffman encoded) that will be accepted. See sections |
| // 5.1 and 5.2 of RFC 7541. This is a defense against OOM attacks; HTTP/2 |
| // allows a decoder to enforce any limit of the size of the header lists |
| // that it is willing to decode, including less than the MAX_HEADER_LIST_SIZE |
| // setting, a setting that is initially unlimited. For example, we might |
| // choose to send a MAX_HEADER_LIST_SIZE of 64KB, and to use that same value |
| // as the upper bound for individual strings. |
| void set_max_string_size_bytes(size_t max_string_size_bytes); |
| |
| // ApplyHeaderTableSizeSetting notifies this object that this endpoint has |
| // received a SETTINGS ACK frame acknowledging an earlier SETTINGS frame from |
| // this endpoint specifying a new value for SETTINGS_HEADER_TABLE_SIZE (the |
| // maximum size of the dynamic table that this endpoint will use to decode |
| // HPACK blocks). |
| // Because a SETTINGS frame can contain SETTINGS_HEADER_TABLE_SIZE values, |
| // the caller must keep track of those multiple changes, and make |
| // corresponding calls to this method. In particular, a call must be made |
| // with the lowest value acknowledged by the peer, and a call must be made |
| // with the final value acknowledged, in that order; additional calls may |
| // be made if additional values were sent. These calls must be made between |
| // decoding the SETTINGS ACK, and before the next HPACK block is decoded. |
| void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size); |
| |
| // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE. |
| size_t GetCurrentHeaderTableSizeSetting() const { |
| return decoder_state_.GetCurrentHeaderTableSizeSetting(); |
| } |
| |
| // Prepares the decoder for decoding a new HPACK block, and announces this to |
| // its listener. Returns true if OK to continue with decoding, false if an |
| // error has been detected, which for StartDecodingBlock means the error was |
| // detected while decoding a previous HPACK block. |
| bool StartDecodingBlock(); |
| |
| // Decodes a fragment (some or all of the remainder) of an HPACK block, |
| // reporting header entries (name & value pairs) that it completely decodes |
| // in the process to the listener. Returns true successfully decoded, false if |
| // an error has been detected, either during decoding of the fragment, or |
| // prior to this call. |
| bool DecodeFragment(DecodeBuffer* db); |
| |
| // Completes the process of decoding an HPACK block: if the HPACK block was |
| // properly terminated, announces the end of the header list to the listener |
| // and returns true; else returns false. |
| bool EndDecodingBlock(); |
| |
| // If no error has been detected so far, query |decoder_state_| for errors and |
| // set |error_| if necessary. Returns true if an error has ever been |
| // detected. |
| bool DetectError(); |
| |
| size_t GetDynamicTableSize() const { |
| return decoder_state_.GetDynamicTableSize(); |
| } |
| |
| // Error code if an error has occurred, HpackDecodingError::kOk otherwise. |
| HpackDecodingError error() const { return error_; } |
| |
| std::string detailed_error() const { return detailed_error_; } |
| |
| private: |
| friend class test::HpackDecoderPeer; |
| |
| // Reports an error to the listener IF this is the first error detected. |
| void ReportError(HpackDecodingError error, std::string detailed_error); |
| |
| // The decompressor state, as defined by HPACK (i.e. the static and dynamic |
| // tables). |
| HpackDecoderState decoder_state_; |
| |
| // Assembles the various parts of a header entry into whole entries. |
| HpackWholeEntryBuffer entry_buffer_; |
| |
| // The decoder of HPACK blocks into entry parts, passed to entry_buffer_. |
| HpackBlockDecoder block_decoder_; |
| |
| // Error code if an error has occurred, HpackDecodingError::kOk otherwise. |
| HpackDecodingError error_; |
| std::string detailed_error_; |
| }; |
| |
| } // namespace http2 |
| |
| #endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_ |