blob: 9e4b68bc927d7e4a5fd621ff02650656cab5b7a6 [file] [log] [blame]
// 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 "quiche/http2/decoder/decode_buffer.h"
#include "quiche/http2/hpack/decoder/hpack_block_decoder.h"
#include "quiche/http2/hpack/decoder/hpack_decoder_listener.h"
#include "quiche/http2/hpack/decoder/hpack_decoder_state.h"
#include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
#include "quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h"
#include "quiche/common/platform/api/quiche_export.h"
namespace http2 {
namespace test {
class HpackDecoderPeer;
} // namespace test
class QUICHE_EXPORT 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_