xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/http/http_header_block.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // 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 "quiche/common/http/http_header_block.h"
6 
7 #include <string.h>
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "absl/strings/str_cat.h"
13 #include "quiche/common/platform/api/quiche_logging.h"
14 
15 namespace quiche {
16 namespace {
17 
18 // By default, linked_hash_map's internal map allocates space for 100 map
19 // buckets on construction, which is larger than necessary.  Standard library
20 // unordered map implementations use a list of prime numbers to set the bucket
21 // count for a particular capacity.  |kInitialMapBuckets| is chosen to reduce
22 // memory usage for small header blocks, at the cost of having to rehash for
23 // large header blocks.
24 const size_t kInitialMapBuckets = 11;
25 
26 const char kCookieKey[] = "cookie";
27 const char kNullSeparator = 0;
28 
SeparatorForKey(absl::string_view key)29 absl::string_view SeparatorForKey(absl::string_view key) {
30   if (key == kCookieKey) {
31     static absl::string_view cookie_separator = "; ";
32     return cookie_separator;
33   } else {
34     return absl::string_view(&kNullSeparator, 1);
35   }
36 }
37 
38 }  // namespace
39 
HeaderValue(HttpHeaderStorage * storage,absl::string_view key,absl::string_view initial_value)40 HttpHeaderBlock::HeaderValue::HeaderValue(HttpHeaderStorage* storage,
41                                           absl::string_view key,
42                                           absl::string_view initial_value)
43     : storage_(storage),
44       fragments_({initial_value}),
45       pair_({key, {}}),
46       size_(initial_value.size()),
47       separator_size_(SeparatorForKey(key).size()) {}
48 
HeaderValue(HeaderValue && other)49 HttpHeaderBlock::HeaderValue::HeaderValue(HeaderValue&& other)
50     : storage_(other.storage_),
51       fragments_(std::move(other.fragments_)),
52       pair_(std::move(other.pair_)),
53       size_(other.size_),
54       separator_size_(other.separator_size_) {}
55 
operator =(HeaderValue && other)56 HttpHeaderBlock::HeaderValue& HttpHeaderBlock::HeaderValue::operator=(
57     HeaderValue&& other) {
58   storage_ = other.storage_;
59   fragments_ = std::move(other.fragments_);
60   pair_ = std::move(other.pair_);
61   size_ = other.size_;
62   separator_size_ = other.separator_size_;
63   return *this;
64 }
65 
set_storage(HttpHeaderStorage * storage)66 void HttpHeaderBlock::HeaderValue::set_storage(HttpHeaderStorage* storage) {
67   storage_ = storage;
68 }
69 
70 HttpHeaderBlock::HeaderValue::~HeaderValue() = default;
71 
ConsolidatedValue() const72 absl::string_view HttpHeaderBlock::HeaderValue::ConsolidatedValue() const {
73   if (fragments_.empty()) {
74     return absl::string_view();
75   }
76   if (fragments_.size() > 1) {
77     fragments_ = {
78         storage_->WriteFragments(fragments_, SeparatorForKey(pair_.first))};
79   }
80   return fragments_[0];
81 }
82 
Append(absl::string_view fragment)83 void HttpHeaderBlock::HeaderValue::Append(absl::string_view fragment) {
84   size_ += (fragment.size() + separator_size_);
85   fragments_.push_back(fragment);
86 }
87 
88 const std::pair<absl::string_view, absl::string_view>&
as_pair() const89 HttpHeaderBlock::HeaderValue::as_pair() const {
90   pair_.second = ConsolidatedValue();
91   return pair_;
92 }
93 
iterator(MapType::const_iterator it)94 HttpHeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
95 
96 HttpHeaderBlock::iterator::iterator(const iterator& other) = default;
97 
98 HttpHeaderBlock::iterator::~iterator() = default;
99 
ValueProxy(HttpHeaderBlock * block,HttpHeaderBlock::MapType::iterator lookup_result,const absl::string_view key,size_t * spdy_header_block_value_size)100 HttpHeaderBlock::ValueProxy::ValueProxy(
101     HttpHeaderBlock* block, HttpHeaderBlock::MapType::iterator lookup_result,
102     const absl::string_view key, size_t* spdy_header_block_value_size)
103     : block_(block),
104       lookup_result_(lookup_result),
105       key_(key),
106       spdy_header_block_value_size_(spdy_header_block_value_size),
107       valid_(true) {}
108 
ValueProxy(ValueProxy && other)109 HttpHeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other)
110     : block_(other.block_),
111       lookup_result_(other.lookup_result_),
112       key_(other.key_),
113       spdy_header_block_value_size_(other.spdy_header_block_value_size_),
114       valid_(true) {
115   other.valid_ = false;
116 }
117 
operator =(HttpHeaderBlock::ValueProxy && other)118 HttpHeaderBlock::ValueProxy& HttpHeaderBlock::ValueProxy::operator=(
119     HttpHeaderBlock::ValueProxy&& other) {
120   block_ = other.block_;
121   lookup_result_ = other.lookup_result_;
122   key_ = other.key_;
123   valid_ = true;
124   other.valid_ = false;
125   spdy_header_block_value_size_ = other.spdy_header_block_value_size_;
126   return *this;
127 }
128 
~ValueProxy()129 HttpHeaderBlock::ValueProxy::~ValueProxy() {
130   // If the ValueProxy is destroyed while lookup_result_ == block_->end(),
131   // the assignment operator was never used, and the block's HttpHeaderStorage
132   // can reclaim the memory used by the key. This makes lookup-only access to
133   // HttpHeaderBlock through operator[] memory-neutral.
134   if (valid_ && lookup_result_ == block_->map_.end()) {
135     block_->storage_.Rewind(key_);
136   }
137 }
138 
operator =(absl::string_view value)139 HttpHeaderBlock::ValueProxy& HttpHeaderBlock::ValueProxy::operator=(
140     absl::string_view value) {
141   *spdy_header_block_value_size_ += value.size();
142   HttpHeaderStorage* storage = &block_->storage_;
143   if (lookup_result_ == block_->map_.end()) {
144     QUICHE_DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
145     lookup_result_ =
146         block_->map_
147             .emplace(std::make_pair(
148                 key_, HeaderValue(storage, key_, storage->Write(value))))
149             .first;
150   } else {
151     QUICHE_DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
152     *spdy_header_block_value_size_ -= lookup_result_->second.SizeEstimate();
153     lookup_result_->second = HeaderValue(storage, key_, storage->Write(value));
154   }
155   return *this;
156 }
157 
operator ==(absl::string_view value) const158 bool HttpHeaderBlock::ValueProxy::operator==(absl::string_view value) const {
159   if (lookup_result_ == block_->map_.end()) {
160     return false;
161   } else {
162     return value == lookup_result_->second.value();
163   }
164 }
165 
as_string() const166 std::string HttpHeaderBlock::ValueProxy::as_string() const {
167   if (lookup_result_ == block_->map_.end()) {
168     return "";
169   } else {
170     return std::string(lookup_result_->second.value());
171   }
172 }
173 
HttpHeaderBlock()174 HttpHeaderBlock::HttpHeaderBlock() : map_(kInitialMapBuckets) {}
175 
HttpHeaderBlock(HttpHeaderBlock && other)176 HttpHeaderBlock::HttpHeaderBlock(HttpHeaderBlock&& other)
177     : map_(kInitialMapBuckets) {
178   map_.swap(other.map_);
179   storage_ = std::move(other.storage_);
180   for (auto& p : map_) {
181     p.second.set_storage(&storage_);
182   }
183   key_size_ = other.key_size_;
184   value_size_ = other.value_size_;
185 }
186 
187 HttpHeaderBlock::~HttpHeaderBlock() = default;
188 
operator =(HttpHeaderBlock && other)189 HttpHeaderBlock& HttpHeaderBlock::operator=(HttpHeaderBlock&& other) {
190   map_.swap(other.map_);
191   storage_ = std::move(other.storage_);
192   for (auto& p : map_) {
193     p.second.set_storage(&storage_);
194   }
195   key_size_ = other.key_size_;
196   value_size_ = other.value_size_;
197   return *this;
198 }
199 
Clone() const200 HttpHeaderBlock HttpHeaderBlock::Clone() const {
201   HttpHeaderBlock copy;
202   for (const auto& p : *this) {
203     copy.AppendHeader(p.first, p.second);
204   }
205   return copy;
206 }
207 
operator ==(const HttpHeaderBlock & other) const208 bool HttpHeaderBlock::operator==(const HttpHeaderBlock& other) const {
209   return size() == other.size() && std::equal(begin(), end(), other.begin());
210 }
211 
operator !=(const HttpHeaderBlock & other) const212 bool HttpHeaderBlock::operator!=(const HttpHeaderBlock& other) const {
213   return !(operator==(other));
214 }
215 
DebugString() const216 std::string HttpHeaderBlock::DebugString() const {
217   if (empty()) {
218     return "{}";
219   }
220 
221   std::string output = "\n{\n";
222   for (auto it = begin(); it != end(); ++it) {
223     absl::StrAppend(&output, "  ", it->first, " ", it->second, "\n");
224   }
225   absl::StrAppend(&output, "}\n");
226   return output;
227 }
228 
erase(absl::string_view key)229 void HttpHeaderBlock::erase(absl::string_view key) {
230   auto iter = map_.find(key);
231   if (iter != map_.end()) {
232     QUICHE_DVLOG(1) << "Erasing header with name: " << key;
233     key_size_ -= key.size();
234     value_size_ -= iter->second.SizeEstimate();
235     map_.erase(iter);
236   }
237 }
238 
clear()239 void HttpHeaderBlock::clear() {
240   key_size_ = 0;
241   value_size_ = 0;
242   map_.clear();
243   storage_.Clear();
244 }
245 
insert(const HttpHeaderBlock::value_type & value)246 HttpHeaderBlock::InsertResult HttpHeaderBlock::insert(
247     const HttpHeaderBlock::value_type& value) {
248   // TODO(birenroy): Write new value in place of old value, if it fits.
249   value_size_ += value.second.size();
250 
251   auto iter = map_.find(value.first);
252   if (iter == map_.end()) {
253     QUICHE_DVLOG(1) << "Inserting: (" << value.first << ", " << value.second
254                     << ")";
255     AppendHeader(value.first, value.second);
256     return InsertResult::kInserted;
257   } else {
258     QUICHE_DVLOG(1) << "Updating key: " << iter->first
259                     << " with value: " << value.second;
260     value_size_ -= iter->second.SizeEstimate();
261     iter->second =
262         HeaderValue(&storage_, iter->first, storage_.Write(value.second));
263     return InsertResult::kReplaced;
264   }
265 }
266 
operator [](const absl::string_view key)267 HttpHeaderBlock::ValueProxy HttpHeaderBlock::operator[](
268     const absl::string_view key) {
269   QUICHE_DVLOG(2) << "Operator[] saw key: " << key;
270   absl::string_view out_key;
271   auto iter = map_.find(key);
272   if (iter == map_.end()) {
273     // We write the key first, to assure that the ValueProxy has a
274     // reference to a valid absl::string_view in its operator=.
275     out_key = WriteKey(key);
276     QUICHE_DVLOG(2) << "Key written as: " << std::hex
277                     << static_cast<const void*>(key.data()) << ", " << std::dec
278                     << key.size();
279   } else {
280     out_key = iter->first;
281   }
282   return ValueProxy(this, iter, out_key, &value_size_);
283 }
284 
AppendValueOrAddHeader(const absl::string_view key,const absl::string_view value)285 void HttpHeaderBlock::AppendValueOrAddHeader(const absl::string_view key,
286                                              const absl::string_view value) {
287   value_size_ += value.size();
288 
289   auto iter = map_.find(key);
290   if (iter == map_.end()) {
291     QUICHE_DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
292 
293     AppendHeader(key, value);
294     return;
295   }
296   QUICHE_DVLOG(1) << "Updating key: " << iter->first
297                   << "; appending value: " << value;
298   value_size_ += SeparatorForKey(key).size();
299   iter->second.Append(storage_.Write(value));
300 }
301 
AppendHeader(const absl::string_view key,const absl::string_view value)302 void HttpHeaderBlock::AppendHeader(const absl::string_view key,
303                                    const absl::string_view value) {
304   auto backed_key = WriteKey(key);
305   map_.emplace(std::make_pair(
306       backed_key, HeaderValue(&storage_, backed_key, storage_.Write(value))));
307 }
308 
WriteKey(const absl::string_view key)309 absl::string_view HttpHeaderBlock::WriteKey(const absl::string_view key) {
310   key_size_ += key.size();
311   return storage_.Write(key);
312 }
313 
bytes_allocated() const314 size_t HttpHeaderBlock::bytes_allocated() const {
315   return storage_.bytes_allocated();
316 }
317 
318 }  // namespace quiche
319