Commit d4ec6008 authored by bnc's avatar bnc Committed by Commit bot

Make HPACK static table static and immutable.

Create HpackStaticTable singleton class, so that every HPACK context can share
a constant instance of the static table.

This lands server change 74862865 by bnc.

BUG=400336

Review URL: https://codereview.chromium.org/549583003

Cr-Commit-Position: refs/heads/master@{#293710}
parent cb6d57d6
......@@ -1029,6 +1029,8 @@
'spdy/hpack_input_stream.h',
'spdy/hpack_output_stream.cc',
'spdy/hpack_output_stream.h',
'spdy/hpack_static_table.cc',
'spdy/hpack_static_table.h',
'spdy/hpack_string_util.cc',
'spdy/hpack_string_util.h',
'spdy/spdy_bitmasks.h',
......@@ -1598,6 +1600,7 @@
'spdy/hpack_input_stream_test.cc',
'spdy/hpack_output_stream_test.cc',
'spdy/hpack_round_trip_test.cc',
'spdy/hpack_static_table_test.cc',
'spdy/hpack_string_util_test.cc',
'spdy/mock_spdy_framer_visitor.cc',
'spdy/mock_spdy_framer_visitor.h',
......
......@@ -4,10 +4,13 @@
#include "net/spdy/hpack_constants.h"
#include <vector>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "net/spdy/hpack_huffman_table.h"
#include "net/spdy/hpack_static_table.h"
namespace net {
......@@ -33,6 +36,25 @@ struct SharedHpackHuffmanTable {
scoped_ptr<const HpackHuffmanTable> table;
};
// SharedHpackStaticTable is a Singleton wrapping a HpackStaticTable
// instance initialized with |kHpackStaticTable|.
struct SharedHpackStaticTable {
public:
SharedHpackStaticTable() {
std::vector<HpackStaticEntry> static_table = HpackStaticTableVector();
scoped_ptr<HpackStaticTable> mutable_table(new HpackStaticTable());
mutable_table->Initialize(&static_table[0], static_table.size());
CHECK(mutable_table->IsInitialized());
table.reset(mutable_table.release());
}
static SharedHpackStaticTable* GetInstance() {
return Singleton<SharedHpackStaticTable>::get();
}
scoped_ptr<const HpackStaticTable> table;
};
} // namespace
// Produced by applying the python program [1] with tables
......@@ -309,8 +331,88 @@ std::vector<HpackHuffmanSymbol> HpackHuffmanCode() {
kHpackHuffmanCode + arraysize(kHpackHuffmanCode));
}
// The "constructor" for a HpackStaticEntry that computes the lengths at
// compile time.
#define STATIC_ENTRY(name, value) \
{ name, arraysize(name) - 1, value, arraysize(value) - 1 }
std::vector<HpackStaticEntry> HpackStaticTableVector() {
static const HpackStaticEntry kHpackStaticTable[] = {
STATIC_ENTRY(":authority", ""), // 1
STATIC_ENTRY(":method", "GET"), // 2
STATIC_ENTRY(":method", "POST"), // 3
STATIC_ENTRY(":path", "/"), // 4
STATIC_ENTRY(":path", "/index.html"), // 5
STATIC_ENTRY(":scheme", "http"), // 6
STATIC_ENTRY(":scheme", "https"), // 7
STATIC_ENTRY(":status", "200"), // 8
STATIC_ENTRY(":status", "204"), // 9
STATIC_ENTRY(":status", "206"), // 10
STATIC_ENTRY(":status", "304"), // 11
STATIC_ENTRY(":status", "400"), // 12
STATIC_ENTRY(":status", "404"), // 13
STATIC_ENTRY(":status", "500"), // 14
STATIC_ENTRY("accept-charset", ""), // 15
STATIC_ENTRY("accept-encoding", "gzip, deflate"), // 16
STATIC_ENTRY("accept-language", ""), // 17
STATIC_ENTRY("accept-ranges", ""), // 18
STATIC_ENTRY("accept", ""), // 19
STATIC_ENTRY("access-control-allow-origin", ""), // 20
STATIC_ENTRY("age", ""), // 21
STATIC_ENTRY("allow", ""), // 22
STATIC_ENTRY("authorization", ""), // 23
STATIC_ENTRY("cache-control", ""), // 24
STATIC_ENTRY("content-disposition", ""), // 25
STATIC_ENTRY("content-encoding", ""), // 26
STATIC_ENTRY("content-language", ""), // 27
STATIC_ENTRY("content-length", ""), // 28
STATIC_ENTRY("content-location", ""), // 29
STATIC_ENTRY("content-range", ""), // 30
STATIC_ENTRY("content-type", ""), // 31
STATIC_ENTRY("cookie", ""), // 32
STATIC_ENTRY("date", ""), // 33
STATIC_ENTRY("etag", ""), // 34
STATIC_ENTRY("expect", ""), // 35
STATIC_ENTRY("expires", ""), // 36
STATIC_ENTRY("from", ""), // 37
STATIC_ENTRY("host", ""), // 38
STATIC_ENTRY("if-match", ""), // 39
STATIC_ENTRY("if-modified-since", ""), // 40
STATIC_ENTRY("if-none-match", ""), // 41
STATIC_ENTRY("if-range", ""), // 42
STATIC_ENTRY("if-unmodified-since", ""), // 43
STATIC_ENTRY("last-modified", ""), // 44
STATIC_ENTRY("link", ""), // 45
STATIC_ENTRY("location", ""), // 46
STATIC_ENTRY("max-forwards", ""), // 47
STATIC_ENTRY("proxy-authenticate", ""), // 48
STATIC_ENTRY("proxy-authorization", ""), // 49
STATIC_ENTRY("range", ""), // 50
STATIC_ENTRY("referer", ""), // 51
STATIC_ENTRY("refresh", ""), // 52
STATIC_ENTRY("retry-after", ""), // 53
STATIC_ENTRY("server", ""), // 54
STATIC_ENTRY("set-cookie", ""), // 55
STATIC_ENTRY("strict-transport-security", ""), // 56
STATIC_ENTRY("transfer-encoding", ""), // 57
STATIC_ENTRY("user-agent", ""), // 58
STATIC_ENTRY("vary", ""), // 59
STATIC_ENTRY("via", ""), // 60
STATIC_ENTRY("www-authenticate", ""), // 61
};
return std::vector<HpackStaticEntry>(
kHpackStaticTable,
kHpackStaticTable + arraysize(kHpackStaticTable));
}
#undef STATIC_ENTRY
const HpackHuffmanTable& ObtainHpackHuffmanTable() {
return *SharedHpackHuffmanTable::GetInstance()->table;
}
const HpackStaticTable& ObtainHpackStaticTable() {
return *SharedHpackStaticTable::GetInstance()->table;
}
} // namespace net
......@@ -29,7 +29,17 @@ struct HpackHuffmanSymbol {
uint16 id;
};
// An entry in the static table. Must be a POD in order to avoid static
// initializers, i.e. no user-defined constructors or destructors.
struct HpackStaticEntry {
const char* const name;
const size_t name_len;
const char* const value;
const size_t value_len;
};
class HpackHuffmanTable;
class HpackStaticTable;
const uint32 kDefaultHeaderTableSizeSetting = 4096;
......@@ -65,14 +75,22 @@ const HpackPrefix kLiteralNeverIndexOpcode = { 0x1, 4 };
// table size with a 5-bit prefix.
const HpackPrefix kHeaderTableSizeUpdateOpcode = { 0x1, 3 };
// Returns symbol code table from "Appendix C. Huffman Codes".
// Returns symbol code table from "Appendix C. Huffman Code".
NET_EXPORT_PRIVATE std::vector<HpackHuffmanSymbol> HpackHuffmanCode();
// Returns static table from "Appendix B. Static Table Definition".
NET_EXPORT_PRIVATE std::vector<HpackStaticEntry> HpackStaticTableVector();
// Returns a HpackHuffmanTable instance initialized with |kHpackHuffmanCode|.
// The instance is read-only, has static lifetime, and is safe to share amoung
// threads. This function is thread-safe.
NET_EXPORT_PRIVATE const HpackHuffmanTable& ObtainHpackHuffmanTable();
// Returns a HpackStaticTable instance initialized with |kHpackStaticTable|.
// The instance is read-only, has static lifetime, and is safe to share amoung
// threads. This function is thread-safe.
NET_EXPORT_PRIVATE const HpackStaticTable& ObtainHpackStaticTable();
// Pseudo-headers start with a colon. (HTTP2 8.1.2.1., HPACK 3.1.)
const char kPseudoHeaderPrefix = ':';
......
......@@ -139,7 +139,7 @@ bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
if (!input_stream->DecodeNextUint32(&index))
return false;
HpackEntry* entry = header_table_.GetByIndex(index);
const HpackEntry* entry = header_table_.GetByIndex(index);
if (entry == NULL)
return false;
......
......@@ -92,7 +92,7 @@ class HpackDecoderTest : public ::testing::Test {
void expectEntry(size_t index, size_t size, const string& name,
const string& value) {
HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
EXPECT_EQ(name, entry->name()) << "index " << index;
EXPECT_EQ(value, entry->value());
EXPECT_EQ(size, entry->Size());
......
......@@ -46,7 +46,8 @@ bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
// Encode pseudo-headers.
for (Representations::const_iterator it = pseudo_headers.begin();
it != pseudo_headers.end(); ++it) {
HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
const HpackEntry* entry =
header_table_.GetByNameAndValue(it->first, it->second);
if (entry != NULL) {
EmitIndex(entry);
} else {
......@@ -69,7 +70,8 @@ bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
Representations literal_headers;
for (Representations::const_iterator it = regular_headers.begin();
it != regular_headers.end(); ++it) {
HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
const HpackEntry* entry =
header_table_.GetByNameAndValue(it->first, it->second);
if (entry != NULL) {
EmitIndex(entry);
} else {
......@@ -103,7 +105,7 @@ bool HpackEncoder::EncodeHeaderSetWithoutCompression(
return true;
}
void HpackEncoder::EmitIndex(HpackEntry* entry) {
void HpackEncoder::EmitIndex(const HpackEntry* entry) {
output_stream_.AppendPrefix(kIndexedOpcode);
output_stream_.AppendUint32(header_table_.IndexOf(entry));
}
......
......@@ -67,7 +67,7 @@ class NET_EXPORT_PRIVATE HpackEncoder {
typedef std::vector<Representation> Representations;
// Emits a static/dynamic indexed representation (Section 7.1).
void EmitIndex(HpackEntry* entry);
void EmitIndex(const HpackEntry* entry);
// Emits a literal representation (Section 7.2).
void EmitIndexedLiteral(const Representation& representation);
......
......@@ -98,10 +98,10 @@ class HpackEncoderTest : public ::testing::Test {
HpackEncoderTest()
: encoder_(ObtainHpackHuffmanTable()),
peer_(&encoder_) {}
peer_(&encoder_),
static_(peer_.table()->GetByIndex(1)) {}
virtual void SetUp() {
static_ = peer_.table()->GetByIndex(1);
// Populate dynamic entries into the table fixture. For simplicity each
// entry has name.size() + value.size() == 10.
key_1_ = peer_.table()->TryAddEntry("key1", "value1");
......@@ -120,7 +120,7 @@ class HpackEncoderTest : public ::testing::Test {
expected_.AppendPrefix(kIndexedOpcode);
expected_.AppendUint32(index);
}
void ExpectIndexedLiteral(HpackEntry* key_entry, StringPiece value) {
void ExpectIndexedLiteral(const HpackEntry* key_entry, StringPiece value) {
expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
expected_.AppendUint32(IndexOf(key_entry));
expected_.AppendPrefix(kStringLiteralIdentityEncoded);
......@@ -156,15 +156,18 @@ class HpackEncoderTest : public ::testing::Test {
size_t IndexOf(HpackEntry* entry) {
return peer_.table()->IndexOf(entry);
}
size_t IndexOf(const HpackEntry* entry) {
return peer_.table()->IndexOf(entry);
}
HpackEncoder encoder_;
test::HpackEncoderPeer peer_;
HpackEntry* static_;
HpackEntry* key_1_;
HpackEntry* key_2_;
HpackEntry* cookie_a_;
HpackEntry* cookie_c_;
const HpackEntry* static_;
const HpackEntry* key_1_;
const HpackEntry* key_2_;
const HpackEntry* cookie_a_;
const HpackEntry* cookie_c_;
HpackOutputStream expected_;
};
......
This diff is collapsed.
......@@ -19,6 +19,8 @@
namespace net {
using base::StringPiece;
namespace test {
class HpackHeaderTablePeer;
} // namespace test
......@@ -31,7 +33,7 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
// HpackHeaderTable takes advantage of the deque property that references
// remain valid, so long as insertions & deletions are at the head & tail.
// If this changes (eg we start to drop entries from the middle of the table),
// this needs to be a std::list, in which case |index_| can be trivially
// this needs to be a std::list, in which case |*_index_| can be trivially
// extended to map to list iterators.
typedef std::deque<HpackEntry> EntryTable;
......@@ -41,12 +43,7 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
// composed with the 'lookup' HpackEntry constructor to allow for efficient
// lower-bounding of matching entries.
struct NET_EXPORT_PRIVATE EntryComparator {
explicit EntryComparator(HpackHeaderTable* table) : table_(table) {}
bool operator() (const HpackEntry* lhs, const HpackEntry* rhs) const;
private:
HpackHeaderTable* table_;
};
typedef std::set<HpackEntry*, EntryComparator> OrderedEntrySet;
......@@ -63,14 +60,13 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
size_t max_size() const { return max_size_; }
// Returns the entry matching the index, or NULL.
HpackEntry* GetByIndex(size_t index);
const HpackEntry* GetByIndex(size_t index);
// Returns the lowest-value entry having |name|, or NULL.
HpackEntry* GetByName(base::StringPiece name);
const HpackEntry* GetByName(StringPiece name);
// Returns the lowest-index matching entry, or NULL.
HpackEntry* GetByNameAndValue(base::StringPiece name,
base::StringPiece value);
const HpackEntry* GetByNameAndValue(StringPiece name, StringPiece value);
// Returns the index of an entry within this header table.
size_t IndexOf(const HpackEntry* entry) const;
......@@ -86,7 +82,8 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
// Determine the set of entries which would be evicted by the insertion
// of |name| & |value| into the table, as per section 3.3.3. No eviction
// actually occurs. The set is returned via range [begin_out, end_out).
void EvictionSet(base::StringPiece name, base::StringPiece value,
void EvictionSet(StringPiece name,
StringPiece value,
EntryTable::iterator* begin_out,
EntryTable::iterator* end_out);
......@@ -94,14 +91,13 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
// and |value| must not be owned by an entry which could be evicted. The
// added HpackEntry is returned, or NULL is returned if all entries were
// evicted and the empty table is of insufficent size for the representation.
HpackEntry* TryAddEntry(base::StringPiece name, base::StringPiece value);
const HpackEntry* TryAddEntry(StringPiece name, StringPiece value);
void DebugLogTableState() const;
private:
// Returns number of evictions required to enter |name| & |value|.
size_t EvictionCountForEntry(base::StringPiece name,
base::StringPiece value) const;
size_t EvictionCountForEntry(StringPiece name, StringPiece value) const;
// Returns number of evictions required to reclaim |reclaim_size| table size.
size_t EvictionCountToReclaim(size_t reclaim_size) const;
......@@ -109,11 +105,13 @@ class NET_EXPORT_PRIVATE HpackHeaderTable {
// Evicts |count| oldest entries from the table.
void Evict(size_t count);
// |static_entries_| and |static_index_| are owned by HpackStaticTable
// singleton.
const EntryTable& static_entries_;
EntryTable dynamic_entries_;
EntryTable static_entries_;
// Full table index, over |dynamic_entries_| and |static_entries_|.
OrderedEntrySet index_;
const OrderedEntrySet& static_index_;
OrderedEntrySet dynamic_index_;
// Last acknowledged value for SETTINGS_HEADER_TABLE_SIZE.
size_t settings_size_bound_;
......
This diff is collapsed.
// Copyright 2014 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.
#include "net/spdy/hpack_static_table.h"
#include "base/logging.h"
#include "net/spdy/hpack_constants.h"
#include "net/spdy/hpack_entry.h"
namespace net {
HpackStaticTable::HpackStaticTable() {}
HpackStaticTable::~HpackStaticTable() {}
void HpackStaticTable::Initialize(const HpackStaticEntry* static_entry_table,
size_t static_entry_count) {
CHECK(!IsInitialized());
int total_insertions = 0;
for (const HpackStaticEntry* it = static_entry_table;
it != static_entry_table + static_entry_count; ++it) {
static_entries_.push_back(HpackEntry(StringPiece(it->name, it->name_len),
StringPiece(it->value, it->value_len),
true, // is_static
total_insertions));
CHECK(static_index_.insert(&static_entries_.back()).second);
++total_insertions;
}
}
bool HpackStaticTable::IsInitialized() const {
return !static_entries_.empty();
}
} // namespace net
// Copyright 2014 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_SPDY_HPACK_STATIC_TABLE_H_
#define NET_SPDY_HPACK_STATIC_TABLE_H_
#include "net/spdy/hpack_header_table.h"
namespace net {
struct HpackStaticEntry;
// HpackStaticTable provides |static_entries_| and |static_index_| for HPACK
// encoding and decoding contexts. Once initialized, an instance is read only
// and may be accessed only through its const interface. Such an instance may
// be shared accross multiple HPACK contexts.
class NET_EXPORT_PRIVATE HpackStaticTable {
public:
HpackStaticTable();
~HpackStaticTable();
// Prepares HpackStaticTable by filling up static_entries_ and static_index_
// from an array of struct HpackStaticEntry. Must be called exactly once.
void Initialize(const HpackStaticEntry* static_entry_table,
size_t static_entry_count);
// Returns whether Initialize() has been called.
bool IsInitialized() const;
// Accessors.
const HpackHeaderTable::EntryTable& GetStaticEntries() const {
return static_entries_;
}
const HpackHeaderTable::OrderedEntrySet& GetStaticIndex() const {
return static_index_;
}
private:
HpackHeaderTable::EntryTable static_entries_;
HpackHeaderTable::OrderedEntrySet static_index_;
};
} // namespace net
#endif // NET_SPDY_HPACK_STATIC_TABLE_H_
// Copyright 2014 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.
#include "net/spdy/hpack_static_table.h"
#include <vector>
#include "net/base/net_export.h"
#include "net/spdy/hpack_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
namespace {
class HpackStaticTableTest : public ::testing::Test {
protected:
HpackStaticTableTest() : table_() {}
HpackStaticTable table_;
};
// Check that an initialized instance has the right number of entries.
TEST_F(HpackStaticTableTest, Initialize) {
EXPECT_FALSE(table_.IsInitialized());
std::vector<HpackStaticEntry> static_table = HpackStaticTableVector();
table_.Initialize(&static_table[0], static_table.size());
EXPECT_TRUE(table_.IsInitialized());
HpackHeaderTable::EntryTable static_entries = table_.GetStaticEntries();
EXPECT_EQ(static_table.size(), static_entries.size());
HpackHeaderTable::OrderedEntrySet static_index = table_.GetStaticIndex();
EXPECT_EQ(static_table.size(), static_index.size());
}
// Test that ObtainHpackStaticTable returns the same instance every time.
TEST_F(HpackStaticTableTest, IsSingleton) {
const HpackStaticTable* static_table_one = &ObtainHpackStaticTable();
const HpackStaticTable* static_table_two = &ObtainHpackStaticTable();
EXPECT_EQ(static_table_one, static_table_two);
}
} // namespace
} // namespace test
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment