// Copyright 2016 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 NET_HTTP2_HPACK_DECODER_HPACK_DECODER_TABLES_H_ #define NET_HTTP2_HPACK_DECODER_HPACK_DECODER_TABLES_H_ // Static and dynamic tables for the HPACK decoder. See: // http://httpwg.org/specs/rfc7541.html#indexing.tables // Note that the Lookup methods return nullptr if the requested index was not // found. This should be treated as a COMPRESSION error according to the HTTP/2 // spec, which is a connection level protocol error (i.e. the connection must // be terminated). See these sections in the two RFCs: // http://httpwg.org/specs/rfc7541.html#indexed.header.representation // http://httpwg.org/specs/rfc7541.html#index.address.space // http://httpwg.org/specs/rfc7540.html#HeaderBlock #include #include #include "base/containers/circular_deque.h" #include "base/macros.h" #include "net/http2/hpack/hpack_string.h" #include "net/http2/http2_constants.h" #include "net/http2/platform/api/http2_export.h" namespace net { namespace test { class HpackDecoderTablesPeer; } // namespace test // TODO(jamessynge): Move to hpack_constants.h const size_t kFirstDynamicTableIndex = 62; // HpackDecoderTablesDebugListener supports a QUIC experiment, enabling // the gathering of information about the time-line of use of HPACK // dynamic table entries. class HTTP2_EXPORT_PRIVATE HpackDecoderTablesDebugListener { public: HpackDecoderTablesDebugListener(); virtual ~HpackDecoderTablesDebugListener(); // The entry has been inserted into the dynamic table. insert_count starts at // 62 because 61 is the last index in the static table; insert_count increases // by 1 with each insert into the dynamic table; it is not incremented when // when a entry is too large to fit into the dynamic table at all (which has // the effect of emptying the dynamic table). // Returns a value that can be used as time_added in OnUseEntry. virtual int64_t OnEntryInserted(const HpackStringPair& entry, size_t insert_count) = 0; // The entry has been used, either for the name or for the name and value. // insert_count is the same as passed to OnEntryInserted when entry was // inserted to the dynamic table, and time_added is the value that was // returned by OnEntryInserted. virtual void OnUseEntry(const HpackStringPair& entry, size_t insert_count, int64_t time_added) = 0; private: DISALLOW_COPY_AND_ASSIGN(HpackDecoderTablesDebugListener); }; // See http://httpwg.org/specs/rfc7541.html#static.table.definition for the // contents, and http://httpwg.org/specs/rfc7541.html#index.address.space for // info about accessing the static table. class HTTP2_EXPORT_PRIVATE HpackDecoderStaticTable { public: explicit HpackDecoderStaticTable(const std::vector* table); // Uses a global table shared by all threads. HpackDecoderStaticTable(); // If index is valid, returns a pointer to the entry, otherwise returns // nullptr. const HpackStringPair* Lookup(size_t index) const; private: friend class test::HpackDecoderTablesPeer; const std::vector* const table_; }; // HpackDecoderDynamicTable implements HPACK compression feature "indexed // headers"; previously sent headers may be referenced later by their index // in the dynamic table. See these sections of the RFC: // http://httpwg.org/specs/rfc7541.html#dynamic.table // http://httpwg.org/specs/rfc7541.html#dynamic.table.management class HTTP2_EXPORT_PRIVATE HpackDecoderDynamicTable { public: HpackDecoderDynamicTable(); ~HpackDecoderDynamicTable(); // Set the listener to be notified of insertions into this table, and later // uses of those entries. Added for evaluation of changes to QUIC's use // of HPACK. void set_debug_listener(HpackDecoderTablesDebugListener* debug_listener) { debug_listener_ = debug_listener; } // Sets a new size limit, received from the peer; performs evictions if // necessary to ensure that the current size does not exceed the new limit. // The caller needs to have validated that size_limit does not // exceed the acknowledged value of SETTINGS_HEADER_TABLE_SIZE. void DynamicTableSizeUpdate(size_t size_limit); // Returns true if inserted, false if too large (at which point the // dynamic table will be empty.) bool Insert(const HpackString& name, const HpackString& value); // If index is valid, returns a pointer to the entry, otherwise returns // nullptr. const HpackStringPair* Lookup(size_t index) const; size_t size_limit() const { return size_limit_; } size_t current_size() const { return current_size_; } private: friend class test::HpackDecoderTablesPeer; struct HpackDecoderTableEntry : public HpackStringPair { HpackDecoderTableEntry(const HpackString& name, const HpackString& value); int64_t time_added; }; // Drop older entries to ensure the size is not greater than limit. void EnsureSizeNoMoreThan(size_t limit); // Removes the oldest dynamic table entry. void RemoveLastEntry(); base::circular_deque table_; // The last received DynamicTableSizeUpdate value, initialized to // SETTINGS_HEADER_TABLE_SIZE. size_t size_limit_ = Http2SettingsInfo::DefaultHeaderTableSize(); size_t current_size_ = 0; // insert_count_ and debug_listener_ are used by a QUIC experiment; remove // when the experiment is done. size_t insert_count_; HpackDecoderTablesDebugListener* debug_listener_; DISALLOW_COPY_AND_ASSIGN(HpackDecoderDynamicTable); }; class HTTP2_EXPORT_PRIVATE HpackDecoderTables { public: HpackDecoderTables(); ~HpackDecoderTables(); // Set the listener to be notified of insertions into the dynamic table, and // later uses of those entries. Added for evaluation of changes to QUIC's use // of HPACK. void set_debug_listener(HpackDecoderTablesDebugListener* debug_listener); // Sets a new size limit, received from the peer; performs evictions if // necessary to ensure that the current size does not exceed the new limit. // The caller needs to have validated that size_limit does not // exceed the acknowledged value of SETTINGS_HEADER_TABLE_SIZE. void DynamicTableSizeUpdate(size_t size_limit) { dynamic_table_.DynamicTableSizeUpdate(size_limit); } // Returns true if inserted, false if too large (at which point the // dynamic table will be empty.) // TODO(jamessynge): Add methods for moving the string(s) into the table, // or for otherwise avoiding unnecessary copies. bool Insert(const HpackString& name, const HpackString& value) { return dynamic_table_.Insert(name, value); } // If index is valid, returns a pointer to the entry, otherwise returns // nullptr. const HpackStringPair* Lookup(size_t index) const; // The size limit that the peer (the HPACK encoder) has told the decoder it is // currently operating with. Defaults to SETTINGS_HEADER_TABLE_SIZE, 4096. size_t header_table_size_limit() const { return dynamic_table_.size_limit(); } // Sum of the sizes of the dynamic table entries. size_t current_header_table_size() const { return dynamic_table_.current_size(); } private: friend class test::HpackDecoderTablesPeer; HpackDecoderStaticTable static_table_; HpackDecoderDynamicTable dynamic_table_; DISALLOW_COPY_AND_ASSIGN(HpackDecoderTables); }; } // namespace net #endif // NET_HTTP2_HPACK_DECODER_HPACK_DECODER_TABLES_H_