1 // Copyright 2016 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 #include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
6
7 #include "absl/strings/str_cat.h"
8 #include "quiche/http2/hpack/http2_hpack_constants.h"
9 #include "quiche/common/platform/api/quiche_logging.h"
10
11 namespace http2 {
12 namespace {
13
MakeStaticTable()14 std::vector<HpackStringPair>* MakeStaticTable() {
15 auto* ptr = new std::vector<HpackStringPair>();
16 ptr->reserve(kFirstDynamicTableIndex);
17 ptr->emplace_back("", "");
18
19 #define STATIC_TABLE_ENTRY(name, value, index) \
20 QUICHE_DCHECK_EQ(ptr->size(), static_cast<size_t>(index)); \
21 ptr->emplace_back(name, value)
22
23 #include "quiche/http2/hpack/hpack_static_table_entries.inc"
24
25 #undef STATIC_TABLE_ENTRY
26
27 return ptr;
28 }
29
GetStaticTable()30 const std::vector<HpackStringPair>* GetStaticTable() {
31 static const std::vector<HpackStringPair>* const g_static_table =
32 MakeStaticTable();
33 return g_static_table;
34 }
35
36 } // namespace
37
HpackStringPair(std::string name,std::string value)38 HpackStringPair::HpackStringPair(std::string name, std::string value)
39 : name(std::move(name)), value(std::move(value)) {
40 QUICHE_DVLOG(3) << DebugString() << " ctor";
41 }
42
~HpackStringPair()43 HpackStringPair::~HpackStringPair() {
44 QUICHE_DVLOG(3) << DebugString() << " dtor";
45 }
46
DebugString() const47 std::string HpackStringPair::DebugString() const {
48 return absl::StrCat("HpackStringPair(name=", name, ", value=", value, ")");
49 }
50
operator <<(std::ostream & os,const HpackStringPair & p)51 std::ostream& operator<<(std::ostream& os, const HpackStringPair& p) {
52 os << p.DebugString();
53 return os;
54 }
55
HpackDecoderStaticTable(const std::vector<HpackStringPair> * table)56 HpackDecoderStaticTable::HpackDecoderStaticTable(
57 const std::vector<HpackStringPair>* table)
58 : table_(table) {}
59
HpackDecoderStaticTable()60 HpackDecoderStaticTable::HpackDecoderStaticTable() : table_(GetStaticTable()) {}
61
Lookup(size_t index) const62 const HpackStringPair* HpackDecoderStaticTable::Lookup(size_t index) const {
63 if (0 < index && index < kFirstDynamicTableIndex) {
64 return &((*table_)[index]);
65 }
66 return nullptr;
67 }
68
HpackDecoderDynamicTable()69 HpackDecoderDynamicTable::HpackDecoderDynamicTable()
70 : insert_count_(kFirstDynamicTableIndex - 1) {}
71 HpackDecoderDynamicTable::~HpackDecoderDynamicTable() = default;
72
DynamicTableSizeUpdate(size_t size_limit)73 void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) {
74 QUICHE_DVLOG(3) << "HpackDecoderDynamicTable::DynamicTableSizeUpdate "
75 << size_limit;
76 EnsureSizeNoMoreThan(size_limit);
77 QUICHE_DCHECK_LE(current_size_, size_limit);
78 size_limit_ = size_limit;
79 }
80
81 // TODO(jamessynge): Check somewhere before here that names received from the
82 // peer are valid (e.g. are lower-case, no whitespace, etc.).
Insert(std::string name,std::string value)83 void HpackDecoderDynamicTable::Insert(std::string name, std::string value) {
84 HpackStringPair entry(std::move(name), std::move(value));
85 size_t entry_size = entry.size();
86 QUICHE_DVLOG(2) << "InsertEntry of size=" << entry_size
87 << "\n name: " << entry.name
88 << "\n value: " << entry.value;
89 if (entry_size > size_limit_) {
90 QUICHE_DVLOG(2) << "InsertEntry: entry larger than table, removing "
91 << table_.size() << " entries, of total size "
92 << current_size_ << " bytes.";
93 table_.clear();
94 current_size_ = 0;
95 return;
96 }
97 ++insert_count_;
98 size_t insert_limit = size_limit_ - entry_size;
99 EnsureSizeNoMoreThan(insert_limit);
100 table_.push_front(entry);
101 current_size_ += entry_size;
102 QUICHE_DVLOG(2) << "InsertEntry: current_size_=" << current_size_;
103 QUICHE_DCHECK_GE(current_size_, entry_size);
104 QUICHE_DCHECK_LE(current_size_, size_limit_);
105 }
106
Lookup(size_t index) const107 const HpackStringPair* HpackDecoderDynamicTable::Lookup(size_t index) const {
108 if (index < table_.size()) {
109 return &table_[index];
110 }
111 return nullptr;
112 }
113
EnsureSizeNoMoreThan(size_t limit)114 void HpackDecoderDynamicTable::EnsureSizeNoMoreThan(size_t limit) {
115 QUICHE_DVLOG(2) << "EnsureSizeNoMoreThan limit=" << limit
116 << ", current_size_=" << current_size_;
117 // Not the most efficient choice, but any easy way to start.
118 while (current_size_ > limit) {
119 RemoveLastEntry();
120 }
121 QUICHE_DCHECK_LE(current_size_, limit);
122 }
123
RemoveLastEntry()124 void HpackDecoderDynamicTable::RemoveLastEntry() {
125 QUICHE_DCHECK(!table_.empty());
126 if (!table_.empty()) {
127 QUICHE_DVLOG(2) << "RemoveLastEntry current_size_=" << current_size_
128 << ", last entry size=" << table_.back().size();
129 QUICHE_DCHECK_GE(current_size_, table_.back().size());
130 current_size_ -= table_.back().size();
131 table_.pop_back();
132 // Empty IFF current_size_ == 0.
133 QUICHE_DCHECK_EQ(table_.empty(), current_size_ == 0);
134 }
135 }
136
137 HpackDecoderTables::HpackDecoderTables() = default;
138 HpackDecoderTables::~HpackDecoderTables() = default;
139
Lookup(size_t index) const140 const HpackStringPair* HpackDecoderTables::Lookup(size_t index) const {
141 if (index < kFirstDynamicTableIndex) {
142 return static_table_.Lookup(index);
143 } else {
144 return dynamic_table_.Lookup(index - kFirstDynamicTableIndex);
145 }
146 }
147
148 } // namespace http2
149