blob: d7e3faa9ba0b939b00afc03c2280b74e87c50232 [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// Copyright (c) 2012 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 "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
6
7#include <string.h>
8
9#include <algorithm>
10#include <utility>
11
QUICHE team82dee2f2019-01-18 12:35:12 -050012#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
QUICHE teamded03512019-03-07 14:45:11 -080013#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050014#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050015
16namespace spdy {
17namespace {
18
19// By default, linked_hash_map's internal map allocates space for 100 map
20// buckets on construction, which is larger than necessary. Standard library
21// unordered map implementations use a list of prime numbers to set the bucket
22// count for a particular capacity. |kInitialMapBuckets| is chosen to reduce
23// memory usage for small header blocks, at the cost of having to rehash for
24// large header blocks.
25const size_t kInitialMapBuckets = 11;
26
QUICHE team82dee2f2019-01-18 12:35:12 -050027const char kCookieKey[] = "cookie";
28const char kNullSeparator = 0;
29
bnc7f82d042020-01-03 12:18:53 -080030quiche::QuicheStringPiece SeparatorForKey(quiche::QuicheStringPiece key) {
QUICHE team82dee2f2019-01-18 12:35:12 -050031 if (key == kCookieKey) {
bnc7f82d042020-01-03 12:18:53 -080032 static quiche::QuicheStringPiece cookie_separator = "; ";
QUICHE team82dee2f2019-01-18 12:35:12 -050033 return cookie_separator;
34 } else {
bnc7f82d042020-01-03 12:18:53 -080035 return quiche::QuicheStringPiece(&kNullSeparator, 1);
QUICHE team82dee2f2019-01-18 12:35:12 -050036 }
37}
38
39} // namespace
40
bnc7f82d042020-01-03 12:18:53 -080041SpdyHeaderBlock::HeaderValue::HeaderValue(
42 SpdyHeaderStorage* storage,
43 quiche::QuicheStringPiece key,
44 quiche::QuicheStringPiece initial_value)
QUICHE team82dee2f2019-01-18 12:35:12 -050045 : storage_(storage),
46 fragments_({initial_value}),
47 pair_({key, {}}),
48 size_(initial_value.size()),
49 separator_size_(SeparatorForKey(key).size()) {}
50
51SpdyHeaderBlock::HeaderValue::HeaderValue(HeaderValue&& other)
52 : storage_(other.storage_),
53 fragments_(std::move(other.fragments_)),
54 pair_(std::move(other.pair_)),
55 size_(other.size_),
56 separator_size_(other.separator_size_) {}
57
58SpdyHeaderBlock::HeaderValue& SpdyHeaderBlock::HeaderValue::operator=(
59 HeaderValue&& other) {
60 storage_ = other.storage_;
61 fragments_ = std::move(other.fragments_);
62 pair_ = std::move(other.pair_);
63 size_ = other.size_;
64 separator_size_ = other.separator_size_;
65 return *this;
66}
67
QUICHE team0b745502019-12-10 11:02:08 -080068void SpdyHeaderBlock::HeaderValue::set_storage(SpdyHeaderStorage* storage) {
69 storage_ = storage;
70}
71
QUICHE team82dee2f2019-01-18 12:35:12 -050072SpdyHeaderBlock::HeaderValue::~HeaderValue() = default;
73
bnc7f82d042020-01-03 12:18:53 -080074quiche::QuicheStringPiece SpdyHeaderBlock::HeaderValue::ConsolidatedValue()
75 const {
QUICHE team82dee2f2019-01-18 12:35:12 -050076 if (fragments_.empty()) {
bnc7f82d042020-01-03 12:18:53 -080077 return quiche::QuicheStringPiece();
QUICHE team82dee2f2019-01-18 12:35:12 -050078 }
79 if (fragments_.size() > 1) {
80 fragments_ = {
81 storage_->WriteFragments(fragments_, SeparatorForKey(pair_.first))};
82 }
83 return fragments_[0];
84}
85
bnc7f82d042020-01-03 12:18:53 -080086void SpdyHeaderBlock::HeaderValue::Append(quiche::QuicheStringPiece fragment) {
QUICHE team82dee2f2019-01-18 12:35:12 -050087 size_ += (fragment.size() + separator_size_);
88 fragments_.push_back(fragment);
89}
90
bnc7f82d042020-01-03 12:18:53 -080091const std::pair<quiche::QuicheStringPiece, quiche::QuicheStringPiece>&
QUICHE team82dee2f2019-01-18 12:35:12 -050092SpdyHeaderBlock::HeaderValue::as_pair() const {
93 pair_.second = ConsolidatedValue();
94 return pair_;
95}
96
97SpdyHeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
98
99SpdyHeaderBlock::iterator::iterator(const iterator& other) = default;
100
101SpdyHeaderBlock::iterator::~iterator() = default;
102
103SpdyHeaderBlock::ValueProxy::ValueProxy(
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800104 SpdyHeaderBlock* block,
QUICHE team82dee2f2019-01-18 12:35:12 -0500105 SpdyHeaderBlock::MapType::iterator lookup_result,
bnc7f82d042020-01-03 12:18:53 -0800106 const quiche::QuicheStringPiece key,
QUICHE team82dee2f2019-01-18 12:35:12 -0500107 size_t* spdy_header_block_value_size)
108 : block_(block),
QUICHE team82dee2f2019-01-18 12:35:12 -0500109 lookup_result_(lookup_result),
110 key_(key),
111 spdy_header_block_value_size_(spdy_header_block_value_size),
112 valid_(true) {}
113
114SpdyHeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other)
115 : block_(other.block_),
QUICHE team82dee2f2019-01-18 12:35:12 -0500116 lookup_result_(other.lookup_result_),
117 key_(other.key_),
118 spdy_header_block_value_size_(other.spdy_header_block_value_size_),
119 valid_(true) {
120 other.valid_ = false;
121}
122
123SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
124 SpdyHeaderBlock::ValueProxy&& other) {
125 block_ = other.block_;
QUICHE team82dee2f2019-01-18 12:35:12 -0500126 lookup_result_ = other.lookup_result_;
127 key_ = other.key_;
128 valid_ = true;
129 other.valid_ = false;
130 spdy_header_block_value_size_ = other.spdy_header_block_value_size_;
131 return *this;
132}
133
134SpdyHeaderBlock::ValueProxy::~ValueProxy() {
135 // If the ValueProxy is destroyed while lookup_result_ == block_->end(),
QUICHE team6b297002019-12-09 09:37:56 -0800136 // the assignment operator was never used, and the block's SpdyHeaderStorage
137 // can reclaim the memory used by the key. This makes lookup-only access to
QUICHE team82dee2f2019-01-18 12:35:12 -0500138 // SpdyHeaderBlock through operator[] memory-neutral.
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800139 if (valid_ && lookup_result_ == block_->map_.end()) {
QUICHE team0b745502019-12-10 11:02:08 -0800140 block_->storage_.Rewind(key_);
QUICHE team82dee2f2019-01-18 12:35:12 -0500141 }
142}
143
144SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
bnc7f82d042020-01-03 12:18:53 -0800145 quiche::QuicheStringPiece value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500146 *spdy_header_block_value_size_ += value.size();
QUICHE team0b745502019-12-10 11:02:08 -0800147 SpdyHeaderStorage* storage = &block_->storage_;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800148 if (lookup_result_ == block_->map_.end()) {
QUICHE teamded03512019-03-07 14:45:11 -0800149 SPDY_DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500150 lookup_result_ =
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800151 block_->map_
152 .emplace(std::make_pair(
153 key_, HeaderValue(storage, key_, storage->Write(value))))
QUICHE team82dee2f2019-01-18 12:35:12 -0500154 .first;
155 } else {
QUICHE teamded03512019-03-07 14:45:11 -0800156 SPDY_DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
QUICHE team82dee2f2019-01-18 12:35:12 -0500157 *spdy_header_block_value_size_ -= lookup_result_->second.SizeEstimate();
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800158 lookup_result_->second = HeaderValue(storage, key_, storage->Write(value));
QUICHE team82dee2f2019-01-18 12:35:12 -0500159 }
160 return *this;
161}
162
bnc7f82d042020-01-03 12:18:53 -0800163bool SpdyHeaderBlock::ValueProxy::operator==(
164 quiche::QuicheStringPiece value) const {
QUICHE team3f57c752019-12-16 14:09:53 -0800165 if (lookup_result_ == block_->map_.end()) {
166 return false;
167 } else {
168 return value == lookup_result_->second.value();
169 }
170}
171
bnc44712912019-08-15 18:58:14 -0700172std::string SpdyHeaderBlock::ValueProxy::as_string() const {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800173 if (lookup_result_ == block_->map_.end()) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500174 return "";
175 } else {
bnc44712912019-08-15 18:58:14 -0700176 return std::string(lookup_result_->second.value());
QUICHE team82dee2f2019-01-18 12:35:12 -0500177 }
178}
179
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800180SpdyHeaderBlock::SpdyHeaderBlock() : map_(kInitialMapBuckets) {}
QUICHE team82dee2f2019-01-18 12:35:12 -0500181
182SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other)
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800183 : map_(kInitialMapBuckets) {
184 map_.swap(other.map_);
QUICHE team0b745502019-12-10 11:02:08 -0800185 storage_ = std::move(other.storage_);
186 for (auto& p : map_) {
187 p.second.set_storage(&storage_);
188 }
QUICHE team82dee2f2019-01-18 12:35:12 -0500189 key_size_ = other.key_size_;
190 value_size_ = other.value_size_;
191}
192
193SpdyHeaderBlock::~SpdyHeaderBlock() = default;
194
195SpdyHeaderBlock& SpdyHeaderBlock::operator=(SpdyHeaderBlock&& other) {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800196 map_.swap(other.map_);
QUICHE team0b745502019-12-10 11:02:08 -0800197 storage_ = std::move(other.storage_);
198 for (auto& p : map_) {
199 p.second.set_storage(&storage_);
200 }
QUICHE team82dee2f2019-01-18 12:35:12 -0500201 key_size_ = other.key_size_;
202 value_size_ = other.value_size_;
203 return *this;
204}
205
206SpdyHeaderBlock SpdyHeaderBlock::Clone() const {
207 SpdyHeaderBlock copy;
208 for (const auto& p : *this) {
209 copy.AppendHeader(p.first, p.second);
210 }
211 return copy;
212}
213
214bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const {
215 return size() == other.size() && std::equal(begin(), end(), other.begin());
216}
217
218bool SpdyHeaderBlock::operator!=(const SpdyHeaderBlock& other) const {
219 return !(operator==(other));
220}
221
bnc44712912019-08-15 18:58:14 -0700222std::string SpdyHeaderBlock::DebugString() const {
QUICHE team82dee2f2019-01-18 12:35:12 -0500223 if (empty()) {
224 return "{}";
225 }
226
bnc44712912019-08-15 18:58:14 -0700227 std::string output = "\n{\n";
QUICHE team82dee2f2019-01-18 12:35:12 -0500228 for (auto it = begin(); it != end(); ++it) {
229 SpdyStrAppend(&output, " ", it->first, " ", it->second, "\n");
230 }
231 SpdyStrAppend(&output, "}\n");
232 return output;
233}
234
bnc7f82d042020-01-03 12:18:53 -0800235void SpdyHeaderBlock::erase(quiche::QuicheStringPiece key) {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800236 auto iter = map_.find(key);
237 if (iter != map_.end()) {
QUICHE teamded03512019-03-07 14:45:11 -0800238 SPDY_DVLOG(1) << "Erasing header with name: " << key;
QUICHE team82dee2f2019-01-18 12:35:12 -0500239 key_size_ -= key.size();
240 value_size_ -= iter->second.SizeEstimate();
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800241 map_.erase(iter);
QUICHE team82dee2f2019-01-18 12:35:12 -0500242 }
243}
244
245void SpdyHeaderBlock::clear() {
246 key_size_ = 0;
247 value_size_ = 0;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800248 map_.clear();
QUICHE team0b745502019-12-10 11:02:08 -0800249 storage_.Clear();
QUICHE team82dee2f2019-01-18 12:35:12 -0500250}
251
252void SpdyHeaderBlock::insert(const SpdyHeaderBlock::value_type& value) {
253 // TODO(birenroy): Write new value in place of old value, if it fits.
254 value_size_ += value.second.size();
255
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800256 auto iter = map_.find(value.first);
257 if (iter == map_.end()) {
QUICHE teamded03512019-03-07 14:45:11 -0800258 SPDY_DVLOG(1) << "Inserting: (" << value.first << ", " << value.second
259 << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500260 AppendHeader(value.first, value.second);
261 } else {
QUICHE teamded03512019-03-07 14:45:11 -0800262 SPDY_DVLOG(1) << "Updating key: " << iter->first
263 << " with value: " << value.second;
QUICHE team82dee2f2019-01-18 12:35:12 -0500264 value_size_ -= iter->second.SizeEstimate();
QUICHE team82dee2f2019-01-18 12:35:12 -0500265 iter->second =
QUICHE team0b745502019-12-10 11:02:08 -0800266 HeaderValue(&storage_, iter->first, storage_.Write(value.second));
QUICHE team82dee2f2019-01-18 12:35:12 -0500267 }
268}
269
270SpdyHeaderBlock::ValueProxy SpdyHeaderBlock::operator[](
bnc7f82d042020-01-03 12:18:53 -0800271 const quiche::QuicheStringPiece key) {
QUICHE teamded03512019-03-07 14:45:11 -0800272 SPDY_DVLOG(2) << "Operator[] saw key: " << key;
bnc7f82d042020-01-03 12:18:53 -0800273 quiche::QuicheStringPiece out_key;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800274 auto iter = map_.find(key);
275 if (iter == map_.end()) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500276 // We write the key first, to assure that the ValueProxy has a
bnc7f82d042020-01-03 12:18:53 -0800277 // reference to a valid QuicheStringPiece in its operator=.
QUICHE team82dee2f2019-01-18 12:35:12 -0500278 out_key = WriteKey(key);
QUICHE teamded03512019-03-07 14:45:11 -0800279 SPDY_DVLOG(2) << "Key written as: " << std::hex
280 << static_cast<const void*>(key.data()) << ", " << std::dec
281 << key.size();
QUICHE team82dee2f2019-01-18 12:35:12 -0500282 } else {
283 out_key = iter->first;
284 }
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800285 return ValueProxy(this, iter, out_key, &value_size_);
QUICHE team82dee2f2019-01-18 12:35:12 -0500286}
287
bnc7f82d042020-01-03 12:18:53 -0800288void SpdyHeaderBlock::AppendValueOrAddHeader(
289 const quiche::QuicheStringPiece key,
290 const quiche::QuicheStringPiece value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500291 value_size_ += value.size();
292
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800293 auto iter = map_.find(key);
294 if (iter == map_.end()) {
QUICHE teamded03512019-03-07 14:45:11 -0800295 SPDY_DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500296
297 AppendHeader(key, value);
298 return;
299 }
QUICHE teamded03512019-03-07 14:45:11 -0800300 SPDY_DVLOG(1) << "Updating key: " << iter->first
301 << "; appending value: " << value;
QUICHE team82dee2f2019-01-18 12:35:12 -0500302 value_size_ += SeparatorForKey(key).size();
QUICHE team0b745502019-12-10 11:02:08 -0800303 iter->second.Append(storage_.Write(value));
QUICHE team82dee2f2019-01-18 12:35:12 -0500304}
305
306size_t SpdyHeaderBlock::EstimateMemoryUsage() const {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800307 // TODO(xunjieli): https://crbug.com/669108. Also include |map_| when EMU()
QUICHE team82dee2f2019-01-18 12:35:12 -0500308 // supports linked_hash_map.
309 return SpdyEstimateMemoryUsage(storage_);
310}
311
bnc7f82d042020-01-03 12:18:53 -0800312void SpdyHeaderBlock::AppendHeader(const quiche::QuicheStringPiece key,
313 const quiche::QuicheStringPiece value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500314 auto backed_key = WriteKey(key);
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800315 map_.emplace(std::make_pair(
QUICHE team0b745502019-12-10 11:02:08 -0800316 backed_key, HeaderValue(&storage_, backed_key, storage_.Write(value))));
QUICHE team82dee2f2019-01-18 12:35:12 -0500317}
318
bnc7f82d042020-01-03 12:18:53 -0800319quiche::QuicheStringPiece SpdyHeaderBlock::WriteKey(
320 const quiche::QuicheStringPiece key) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500321 key_size_ += key.size();
QUICHE team0b745502019-12-10 11:02:08 -0800322 return storage_.Write(key);
QUICHE team82dee2f2019-01-18 12:35:12 -0500323}
324
325size_t SpdyHeaderBlock::bytes_allocated() const {
QUICHE team0b745502019-12-10 11:02:08 -0800326 return storage_.bytes_allocated();
QUICHE team82dee2f2019-01-18 12:35:12 -0500327}
328
QUICHE team82dee2f2019-01-18 12:35:12 -0500329} // namespace spdy