blob: b5790470e024f542fa29cee64e3ecb8d5a074992 [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
QUICHE team5be974e2020-12-29 18:35:24 -05005#include "spdy/core/spdy_header_block.h"
QUICHE team82dee2f2019-01-18 12:35:12 -05006
7#include <string.h>
8
9#include <algorithm>
10#include <utility>
11
bnc60cef582021-03-24 10:25:37 -070012#include "absl/strings/str_cat.h"
QUICHE team1b5d09e2021-04-08 13:36:42 -070013#include "common/platform/api/quiche_logging.h"
QUICHE team5be974e2020-12-29 18:35:24 -050014#include "spdy/platform/api/spdy_estimate_memory_usage.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
vasilvvc8ccdb22020-10-12 16:42:34 -070030absl::string_view SeparatorForKey(absl::string_view key) {
QUICHE team82dee2f2019-01-18 12:35:12 -050031 if (key == kCookieKey) {
vasilvvc8ccdb22020-10-12 16:42:34 -070032 static absl::string_view cookie_separator = "; ";
QUICHE team82dee2f2019-01-18 12:35:12 -050033 return cookie_separator;
34 } else {
vasilvvc8ccdb22020-10-12 16:42:34 -070035 return absl::string_view(&kNullSeparator, 1);
QUICHE team82dee2f2019-01-18 12:35:12 -050036 }
37}
38
39} // namespace
40
QUICHE team557dfbc2020-10-20 10:52:30 -070041Http2HeaderBlock::HeaderValue::HeaderValue(SpdyHeaderStorage* storage,
42 absl::string_view key,
43 absl::string_view initial_value)
QUICHE team82dee2f2019-01-18 12:35:12 -050044 : storage_(storage),
45 fragments_({initial_value}),
46 pair_({key, {}}),
47 size_(initial_value.size()),
48 separator_size_(SeparatorForKey(key).size()) {}
49
QUICHE team557dfbc2020-10-20 10:52:30 -070050Http2HeaderBlock::HeaderValue::HeaderValue(HeaderValue&& other)
QUICHE team82dee2f2019-01-18 12:35:12 -050051 : storage_(other.storage_),
52 fragments_(std::move(other.fragments_)),
53 pair_(std::move(other.pair_)),
54 size_(other.size_),
55 separator_size_(other.separator_size_) {}
56
QUICHE team557dfbc2020-10-20 10:52:30 -070057Http2HeaderBlock::HeaderValue& Http2HeaderBlock::HeaderValue::operator=(
QUICHE team82dee2f2019-01-18 12:35:12 -050058 HeaderValue&& other) {
59 storage_ = other.storage_;
60 fragments_ = std::move(other.fragments_);
61 pair_ = std::move(other.pair_);
62 size_ = other.size_;
63 separator_size_ = other.separator_size_;
64 return *this;
65}
66
QUICHE team557dfbc2020-10-20 10:52:30 -070067void Http2HeaderBlock::HeaderValue::set_storage(SpdyHeaderStorage* storage) {
QUICHE team0b745502019-12-10 11:02:08 -080068 storage_ = storage;
69}
70
QUICHE team557dfbc2020-10-20 10:52:30 -070071Http2HeaderBlock::HeaderValue::~HeaderValue() = default;
QUICHE team82dee2f2019-01-18 12:35:12 -050072
QUICHE team557dfbc2020-10-20 10:52:30 -070073absl::string_view Http2HeaderBlock::HeaderValue::ConsolidatedValue() const {
QUICHE team82dee2f2019-01-18 12:35:12 -050074 if (fragments_.empty()) {
vasilvvc8ccdb22020-10-12 16:42:34 -070075 return absl::string_view();
QUICHE team82dee2f2019-01-18 12:35:12 -050076 }
77 if (fragments_.size() > 1) {
78 fragments_ = {
79 storage_->WriteFragments(fragments_, SeparatorForKey(pair_.first))};
80 }
81 return fragments_[0];
82}
83
QUICHE team557dfbc2020-10-20 10:52:30 -070084void Http2HeaderBlock::HeaderValue::Append(absl::string_view fragment) {
QUICHE team82dee2f2019-01-18 12:35:12 -050085 size_ += (fragment.size() + separator_size_);
86 fragments_.push_back(fragment);
87}
88
vasilvvc8ccdb22020-10-12 16:42:34 -070089const std::pair<absl::string_view, absl::string_view>&
QUICHE team557dfbc2020-10-20 10:52:30 -070090Http2HeaderBlock::HeaderValue::as_pair() const {
QUICHE team82dee2f2019-01-18 12:35:12 -050091 pair_.second = ConsolidatedValue();
92 return pair_;
93}
94
QUICHE team557dfbc2020-10-20 10:52:30 -070095Http2HeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
QUICHE team82dee2f2019-01-18 12:35:12 -050096
QUICHE team557dfbc2020-10-20 10:52:30 -070097Http2HeaderBlock::iterator::iterator(const iterator& other) = default;
QUICHE team82dee2f2019-01-18 12:35:12 -050098
QUICHE team557dfbc2020-10-20 10:52:30 -070099Http2HeaderBlock::iterator::~iterator() = default;
QUICHE team82dee2f2019-01-18 12:35:12 -0500100
QUICHE team557dfbc2020-10-20 10:52:30 -0700101Http2HeaderBlock::ValueProxy::ValueProxy(
102 Http2HeaderBlock* block,
103 Http2HeaderBlock::MapType::iterator lookup_result,
vasilvvc8ccdb22020-10-12 16:42:34 -0700104 const absl::string_view key,
QUICHE team82dee2f2019-01-18 12:35:12 -0500105 size_t* spdy_header_block_value_size)
106 : block_(block),
QUICHE team82dee2f2019-01-18 12:35:12 -0500107 lookup_result_(lookup_result),
108 key_(key),
109 spdy_header_block_value_size_(spdy_header_block_value_size),
110 valid_(true) {}
111
QUICHE team557dfbc2020-10-20 10:52:30 -0700112Http2HeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other)
QUICHE team82dee2f2019-01-18 12:35:12 -0500113 : block_(other.block_),
QUICHE team82dee2f2019-01-18 12:35:12 -0500114 lookup_result_(other.lookup_result_),
115 key_(other.key_),
116 spdy_header_block_value_size_(other.spdy_header_block_value_size_),
117 valid_(true) {
118 other.valid_ = false;
119}
120
QUICHE team557dfbc2020-10-20 10:52:30 -0700121Http2HeaderBlock::ValueProxy& Http2HeaderBlock::ValueProxy::operator=(
122 Http2HeaderBlock::ValueProxy&& other) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500123 block_ = other.block_;
QUICHE team82dee2f2019-01-18 12:35:12 -0500124 lookup_result_ = other.lookup_result_;
125 key_ = other.key_;
126 valid_ = true;
127 other.valid_ = false;
128 spdy_header_block_value_size_ = other.spdy_header_block_value_size_;
129 return *this;
130}
131
QUICHE team557dfbc2020-10-20 10:52:30 -0700132Http2HeaderBlock::ValueProxy::~ValueProxy() {
QUICHE team82dee2f2019-01-18 12:35:12 -0500133 // If the ValueProxy is destroyed while lookup_result_ == block_->end(),
QUICHE team6b297002019-12-09 09:37:56 -0800134 // the assignment operator was never used, and the block's SpdyHeaderStorage
135 // can reclaim the memory used by the key. This makes lookup-only access to
QUICHE team557dfbc2020-10-20 10:52:30 -0700136 // Http2HeaderBlock through operator[] memory-neutral.
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800137 if (valid_ && lookup_result_ == block_->map_.end()) {
QUICHE team0b745502019-12-10 11:02:08 -0800138 block_->storage_.Rewind(key_);
QUICHE team82dee2f2019-01-18 12:35:12 -0500139 }
140}
141
QUICHE team557dfbc2020-10-20 10:52:30 -0700142Http2HeaderBlock::ValueProxy& Http2HeaderBlock::ValueProxy::operator=(
vasilvvc8ccdb22020-10-12 16:42:34 -0700143 absl::string_view value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500144 *spdy_header_block_value_size_ += value.size();
QUICHE team0b745502019-12-10 11:02:08 -0800145 SpdyHeaderStorage* storage = &block_->storage_;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800146 if (lookup_result_ == block_->map_.end()) {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700147 QUICHE_DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500148 lookup_result_ =
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800149 block_->map_
150 .emplace(std::make_pair(
151 key_, HeaderValue(storage, key_, storage->Write(value))))
QUICHE team82dee2f2019-01-18 12:35:12 -0500152 .first;
153 } else {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700154 QUICHE_DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
QUICHE team82dee2f2019-01-18 12:35:12 -0500155 *spdy_header_block_value_size_ -= lookup_result_->second.SizeEstimate();
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800156 lookup_result_->second = HeaderValue(storage, key_, storage->Write(value));
QUICHE team82dee2f2019-01-18 12:35:12 -0500157 }
158 return *this;
159}
160
QUICHE team557dfbc2020-10-20 10:52:30 -0700161bool Http2HeaderBlock::ValueProxy::operator==(absl::string_view value) const {
QUICHE team3f57c752019-12-16 14:09:53 -0800162 if (lookup_result_ == block_->map_.end()) {
163 return false;
164 } else {
165 return value == lookup_result_->second.value();
166 }
167}
168
QUICHE team557dfbc2020-10-20 10:52:30 -0700169std::string Http2HeaderBlock::ValueProxy::as_string() const {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800170 if (lookup_result_ == block_->map_.end()) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500171 return "";
172 } else {
bnc44712912019-08-15 18:58:14 -0700173 return std::string(lookup_result_->second.value());
QUICHE team82dee2f2019-01-18 12:35:12 -0500174 }
175}
176
QUICHE team557dfbc2020-10-20 10:52:30 -0700177Http2HeaderBlock::Http2HeaderBlock() : map_(kInitialMapBuckets) {}
QUICHE team82dee2f2019-01-18 12:35:12 -0500178
QUICHE team557dfbc2020-10-20 10:52:30 -0700179Http2HeaderBlock::Http2HeaderBlock(Http2HeaderBlock&& other)
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800180 : map_(kInitialMapBuckets) {
181 map_.swap(other.map_);
QUICHE team0b745502019-12-10 11:02:08 -0800182 storage_ = std::move(other.storage_);
183 for (auto& p : map_) {
184 p.second.set_storage(&storage_);
185 }
QUICHE team82dee2f2019-01-18 12:35:12 -0500186 key_size_ = other.key_size_;
187 value_size_ = other.value_size_;
188}
189
QUICHE team557dfbc2020-10-20 10:52:30 -0700190Http2HeaderBlock::~Http2HeaderBlock() = default;
QUICHE team82dee2f2019-01-18 12:35:12 -0500191
QUICHE team557dfbc2020-10-20 10:52:30 -0700192Http2HeaderBlock& Http2HeaderBlock::operator=(Http2HeaderBlock&& other) {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800193 map_.swap(other.map_);
QUICHE team0b745502019-12-10 11:02:08 -0800194 storage_ = std::move(other.storage_);
195 for (auto& p : map_) {
196 p.second.set_storage(&storage_);
197 }
QUICHE team82dee2f2019-01-18 12:35:12 -0500198 key_size_ = other.key_size_;
199 value_size_ = other.value_size_;
200 return *this;
201}
202
QUICHE team557dfbc2020-10-20 10:52:30 -0700203Http2HeaderBlock Http2HeaderBlock::Clone() const {
204 Http2HeaderBlock copy;
QUICHE team82dee2f2019-01-18 12:35:12 -0500205 for (const auto& p : *this) {
206 copy.AppendHeader(p.first, p.second);
207 }
208 return copy;
209}
210
QUICHE team557dfbc2020-10-20 10:52:30 -0700211bool Http2HeaderBlock::operator==(const Http2HeaderBlock& other) const {
QUICHE team82dee2f2019-01-18 12:35:12 -0500212 return size() == other.size() && std::equal(begin(), end(), other.begin());
213}
214
QUICHE team557dfbc2020-10-20 10:52:30 -0700215bool Http2HeaderBlock::operator!=(const Http2HeaderBlock& other) const {
QUICHE team82dee2f2019-01-18 12:35:12 -0500216 return !(operator==(other));
217}
218
QUICHE team557dfbc2020-10-20 10:52:30 -0700219std::string Http2HeaderBlock::DebugString() const {
QUICHE team82dee2f2019-01-18 12:35:12 -0500220 if (empty()) {
221 return "{}";
222 }
223
bnc44712912019-08-15 18:58:14 -0700224 std::string output = "\n{\n";
QUICHE team82dee2f2019-01-18 12:35:12 -0500225 for (auto it = begin(); it != end(); ++it) {
bnc60cef582021-03-24 10:25:37 -0700226 absl::StrAppend(&output, " ", it->first, " ", it->second, "\n");
QUICHE team82dee2f2019-01-18 12:35:12 -0500227 }
bnc60cef582021-03-24 10:25:37 -0700228 absl::StrAppend(&output, "}\n");
QUICHE team82dee2f2019-01-18 12:35:12 -0500229 return output;
230}
231
QUICHE team557dfbc2020-10-20 10:52:30 -0700232void Http2HeaderBlock::erase(absl::string_view key) {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800233 auto iter = map_.find(key);
234 if (iter != map_.end()) {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700235 QUICHE_DVLOG(1) << "Erasing header with name: " << key;
QUICHE team82dee2f2019-01-18 12:35:12 -0500236 key_size_ -= key.size();
237 value_size_ -= iter->second.SizeEstimate();
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800238 map_.erase(iter);
QUICHE team82dee2f2019-01-18 12:35:12 -0500239 }
240}
241
QUICHE team557dfbc2020-10-20 10:52:30 -0700242void Http2HeaderBlock::clear() {
QUICHE team82dee2f2019-01-18 12:35:12 -0500243 key_size_ = 0;
244 value_size_ = 0;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800245 map_.clear();
QUICHE team0b745502019-12-10 11:02:08 -0800246 storage_.Clear();
QUICHE team82dee2f2019-01-18 12:35:12 -0500247}
248
QUICHE team557dfbc2020-10-20 10:52:30 -0700249void Http2HeaderBlock::insert(const Http2HeaderBlock::value_type& value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500250 // TODO(birenroy): Write new value in place of old value, if it fits.
251 value_size_ += value.second.size();
252
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800253 auto iter = map_.find(value.first);
254 if (iter == map_.end()) {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700255 QUICHE_DVLOG(1) << "Inserting: (" << value.first << ", " << value.second
256 << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500257 AppendHeader(value.first, value.second);
258 } else {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700259 QUICHE_DVLOG(1) << "Updating key: " << iter->first
260 << " with value: " << value.second;
QUICHE team82dee2f2019-01-18 12:35:12 -0500261 value_size_ -= iter->second.SizeEstimate();
QUICHE team82dee2f2019-01-18 12:35:12 -0500262 iter->second =
QUICHE team0b745502019-12-10 11:02:08 -0800263 HeaderValue(&storage_, iter->first, storage_.Write(value.second));
QUICHE team82dee2f2019-01-18 12:35:12 -0500264 }
265}
266
QUICHE team557dfbc2020-10-20 10:52:30 -0700267Http2HeaderBlock::ValueProxy Http2HeaderBlock::operator[](
vasilvvc8ccdb22020-10-12 16:42:34 -0700268 const absl::string_view key) {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700269 QUICHE_DVLOG(2) << "Operator[] saw key: " << key;
vasilvvc8ccdb22020-10-12 16:42:34 -0700270 absl::string_view out_key;
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800271 auto iter = map_.find(key);
272 if (iter == map_.end()) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500273 // We write the key first, to assure that the ValueProxy has a
vasilvvc8ccdb22020-10-12 16:42:34 -0700274 // reference to a valid absl::string_view in its operator=.
QUICHE team82dee2f2019-01-18 12:35:12 -0500275 out_key = WriteKey(key);
QUICHE team1b5d09e2021-04-08 13:36:42 -0700276 QUICHE_DVLOG(2) << "Key written as: " << std::hex
277 << static_cast<const void*>(key.data()) << ", " << std::dec
278 << key.size();
QUICHE team82dee2f2019-01-18 12:35:12 -0500279 } else {
280 out_key = iter->first;
281 }
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800282 return ValueProxy(this, iter, out_key, &value_size_);
QUICHE team82dee2f2019-01-18 12:35:12 -0500283}
284
QUICHE team557dfbc2020-10-20 10:52:30 -0700285void Http2HeaderBlock::AppendValueOrAddHeader(const absl::string_view key,
286 const absl::string_view value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500287 value_size_ += value.size();
288
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800289 auto iter = map_.find(key);
290 if (iter == map_.end()) {
QUICHE team1b5d09e2021-04-08 13:36:42 -0700291 QUICHE_DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
QUICHE team82dee2f2019-01-18 12:35:12 -0500292
293 AppendHeader(key, value);
294 return;
295 }
QUICHE team1b5d09e2021-04-08 13:36:42 -0700296 QUICHE_DVLOG(1) << "Updating key: " << iter->first
297 << "; appending value: " << value;
QUICHE team82dee2f2019-01-18 12:35:12 -0500298 value_size_ += SeparatorForKey(key).size();
QUICHE team0b745502019-12-10 11:02:08 -0800299 iter->second.Append(storage_.Write(value));
QUICHE team82dee2f2019-01-18 12:35:12 -0500300}
301
QUICHE team557dfbc2020-10-20 10:52:30 -0700302size_t Http2HeaderBlock::EstimateMemoryUsage() const {
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800303 // TODO(xunjieli): https://crbug.com/669108. Also include |map_| when EMU()
QUICHE team82dee2f2019-01-18 12:35:12 -0500304 // supports linked_hash_map.
305 return SpdyEstimateMemoryUsage(storage_);
306}
307
QUICHE team557dfbc2020-10-20 10:52:30 -0700308void Http2HeaderBlock::AppendHeader(const absl::string_view key,
309 const absl::string_view value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500310 auto backed_key = WriteKey(key);
QUICHE team8e3bb9d2019-12-06 16:03:22 -0800311 map_.emplace(std::make_pair(
QUICHE team0b745502019-12-10 11:02:08 -0800312 backed_key, HeaderValue(&storage_, backed_key, storage_.Write(value))));
QUICHE team82dee2f2019-01-18 12:35:12 -0500313}
314
QUICHE team557dfbc2020-10-20 10:52:30 -0700315absl::string_view Http2HeaderBlock::WriteKey(const absl::string_view key) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500316 key_size_ += key.size();
QUICHE team0b745502019-12-10 11:02:08 -0800317 return storage_.Write(key);
QUICHE team82dee2f2019-01-18 12:35:12 -0500318}
319
QUICHE team557dfbc2020-10-20 10:52:30 -0700320size_t Http2HeaderBlock::bytes_allocated() const {
QUICHE team0b745502019-12-10 11:02:08 -0800321 return storage_.bytes_allocated();
QUICHE team82dee2f2019-01-18 12:35:12 -0500322}
323
QUICHE team82dee2f2019-01-18 12:35:12 -0500324} // namespace spdy