• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_H_
6 #define QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_H_
7 
8 #include <stddef.h>
9 
10 #include <functional>
11 #include <list>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "absl/base/attributes.h"
17 #include "quiche/common/platform/api/quiche_export.h"
18 #include "quiche/common/platform/api/quiche_logging.h"
19 #include "quiche/common/quiche_linked_hash_map.h"
20 #include "quiche/common/quiche_text_utils.h"
21 #include "quiche/spdy/core/http2_header_storage.h"
22 
23 namespace spdy {
24 
25 namespace test {
26 class Http2HeaderBlockPeer;
27 class ValueProxyPeer;
28 }  // namespace test
29 
30 #ifndef SPDY_HEADER_DEBUG
31 #if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
32 #define SPDY_HEADER_DEBUG 1
33 #else  // !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
34 #define SPDY_HEADER_DEBUG 0
35 #endif  // !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
36 #endif  // SPDY_HEADER_DEBUG
37 
38 // This class provides a key-value map that can be used to store SPDY header
39 // names and values. This data structure preserves insertion order.
40 //
41 // Under the hood, this data structure uses large, contiguous blocks of memory
42 // to store names and values. Lookups may be performed with absl::string_view
43 // keys, and values are returned as absl::string_views (via ValueProxy, below).
44 // Value absl::string_views are valid as long as the Http2HeaderBlock exists;
45 // allocated memory is never freed until Http2HeaderBlock's destruction.
46 //
47 // This implementation does not make much of an effort to minimize wasted space.
48 // It's expected that keys are rarely deleted from a Http2HeaderBlock.
49 class QUICHE_EXPORT Http2HeaderBlock {
50  private:
51   // Stores a list of value fragments that can be joined later with a
52   // key-dependent separator.
53   class QUICHE_EXPORT HeaderValue {
54    public:
55     HeaderValue(Http2HeaderStorage* storage, absl::string_view key,
56                 absl::string_view initial_value);
57 
58     // Moves are allowed.
59     HeaderValue(HeaderValue&& other);
60     HeaderValue& operator=(HeaderValue&& other);
61 
62     void set_storage(Http2HeaderStorage* storage);
63 
64     // Copies are not.
65     HeaderValue(const HeaderValue& other) = delete;
66     HeaderValue& operator=(const HeaderValue& other) = delete;
67 
68     ~HeaderValue();
69 
70     // Consumes at most |fragment.size()| bytes of memory.
71     void Append(absl::string_view fragment);
72 
value()73     absl::string_view value() const { return as_pair().second; }
74     const std::pair<absl::string_view, absl::string_view>& as_pair() const;
75 
76     // Size estimate including separators. Used when keys are erased from
77     // Http2HeaderBlock.
SizeEstimate()78     size_t SizeEstimate() const { return size_; }
79 
80    private:
81     // May allocate a large contiguous region of memory to hold the concatenated
82     // fragments and separators.
83     absl::string_view ConsolidatedValue() const;
84 
85     mutable Http2HeaderStorage* storage_;
86     mutable std::vector<absl::string_view> fragments_;
87     // The first element is the key; the second is the consolidated value.
88     mutable std::pair<absl::string_view, absl::string_view> pair_;
89     size_t size_ = 0;
90     size_t separator_size_ = 0;
91   };
92 
93   typedef quiche::QuicheLinkedHashMap<absl::string_view, HeaderValue,
94                                       quiche::StringPieceCaseHash,
95                                       quiche::StringPieceCaseEqual>
96       MapType;
97 
98  public:
99   typedef std::pair<absl::string_view, absl::string_view> value_type;
100 
101   // Provides iteration over a sequence of std::pair<absl::string_view,
102   // absl::string_view>, even though the underlying MapType::value_type is
103   // different. Dereferencing the iterator will result in memory allocation for
104   // multi-value headers.
105   class QUICHE_EXPORT iterator {
106    public:
107     // The following type definitions fulfill the requirements for iterator
108     // implementations.
109     typedef std::pair<absl::string_view, absl::string_view> value_type;
110     typedef value_type& reference;
111     typedef value_type* pointer;
112     typedef std::forward_iterator_tag iterator_category;
113     typedef MapType::iterator::difference_type difference_type;
114 
115     // In practice, this iterator only offers access to const value_type.
116     typedef const value_type& const_reference;
117     typedef const value_type* const_pointer;
118 
119     explicit iterator(MapType::const_iterator it);
120     iterator(const iterator& other);
121     ~iterator();
122 
123     // This will result in memory allocation if the value consists of multiple
124     // fragments.
125     const_reference operator*() const {
126 #if SPDY_HEADER_DEBUG
127       QUICHE_CHECK(!dereference_forbidden_);
128 #endif  // SPDY_HEADER_DEBUG
129       return it_->second.as_pair();
130     }
131 
132     const_pointer operator->() const { return &(this->operator*()); }
133     bool operator==(const iterator& it) const { return it_ == it.it_; }
134     bool operator!=(const iterator& it) const { return !(*this == it); }
135 
136     iterator& operator++() {
137       it_++;
138       return *this;
139     }
140 
141     iterator operator++(int) {
142       auto ret = *this;
143       this->operator++();
144       return ret;
145     }
146 
147 #if SPDY_HEADER_DEBUG
forbid_dereference()148     void forbid_dereference() { dereference_forbidden_ = true; }
149 #endif  // SPDY_HEADER_DEBUG
150 
151    private:
152     MapType::const_iterator it_;
153 #if SPDY_HEADER_DEBUG
154     bool dereference_forbidden_ = false;
155 #endif  // SPDY_HEADER_DEBUG
156   };
157   typedef iterator const_iterator;
158 
159   Http2HeaderBlock();
160   Http2HeaderBlock(const Http2HeaderBlock& other) = delete;
161   Http2HeaderBlock(Http2HeaderBlock&& other);
162   ~Http2HeaderBlock();
163 
164   Http2HeaderBlock& operator=(const Http2HeaderBlock& other) = delete;
165   Http2HeaderBlock& operator=(Http2HeaderBlock&& other);
166   Http2HeaderBlock Clone() const;
167 
168   bool operator==(const Http2HeaderBlock& other) const;
169   bool operator!=(const Http2HeaderBlock& other) const;
170 
171   // Provides a human readable multi-line representation of the stored header
172   // keys and values.
173   std::string DebugString() const;
174 
begin()175   iterator begin() { return wrap_iterator(map_.begin()); }
end()176   iterator end() { return wrap_iterator(map_.end()); }
begin()177   const_iterator begin() const { return wrap_const_iterator(map_.begin()); }
end()178   const_iterator end() const { return wrap_const_iterator(map_.end()); }
empty()179   bool empty() const { return map_.empty(); }
size()180   size_t size() const { return map_.size(); }
find(absl::string_view key)181   iterator find(absl::string_view key) { return wrap_iterator(map_.find(key)); }
find(absl::string_view key)182   const_iterator find(absl::string_view key) const {
183     return wrap_const_iterator(map_.find(key));
184   }
contains(absl::string_view key)185   bool contains(absl::string_view key) const { return find(key) != end(); }
186   void erase(absl::string_view key);
187 
188   // Clears both our MapType member and the memory used to hold headers.
189   void clear();
190 
191   // The next few methods copy data into our backing storage.
192 
193   // If key already exists in the block, replaces the value of that key. Else
194   // adds a new header to the end of the block.
195   void insert(const value_type& value);
196 
197   // If a header with the key is already present, then append the value to the
198   // existing header value, NUL ("\0") separated unless the key is cookie, in
199   // which case the separator is "; ".
200   // If there is no such key, a new header with the key and value is added.
201   void AppendValueOrAddHeader(const absl::string_view key,
202                               const absl::string_view value);
203 
204   // This object provides automatic conversions that allow Http2HeaderBlock to
205   // be nearly a drop-in replacement for
206   // SpdyLinkedHashMap<std::string, std::string>.
207   // It reads data from or writes data to a Http2HeaderStorage.
208   class QUICHE_EXPORT ValueProxy {
209    public:
210     ~ValueProxy();
211 
212     // Moves are allowed.
213     ValueProxy(ValueProxy&& other);
214     ValueProxy& operator=(ValueProxy&& other);
215 
216     // Copies are not.
217     ValueProxy(const ValueProxy& other) = delete;
218     ValueProxy& operator=(const ValueProxy& other) = delete;
219 
220     // Assignment modifies the underlying Http2HeaderBlock.
221     ValueProxy& operator=(absl::string_view value);
222 
223     // Provides easy comparison against absl::string_view.
224     bool operator==(absl::string_view value) const;
225 
226     std::string as_string() const;
227 
228    private:
229     friend class Http2HeaderBlock;
230     friend class test::ValueProxyPeer;
231 
232     ValueProxy(Http2HeaderBlock* block,
233                Http2HeaderBlock::MapType::iterator lookup_result,
234                const absl::string_view key,
235                size_t* spdy_header_block_value_size);
236 
237     Http2HeaderBlock* block_;
238     Http2HeaderBlock::MapType::iterator lookup_result_;
239     absl::string_view key_;
240     size_t* spdy_header_block_value_size_;
241     bool valid_;
242   };
243 
244   // Allows either lookup or mutation of the value associated with a key.
245   ABSL_MUST_USE_RESULT ValueProxy operator[](const absl::string_view key);
246 
TotalBytesUsed()247   size_t TotalBytesUsed() const { return key_size_ + value_size_; }
248 
249  private:
250   friend class test::Http2HeaderBlockPeer;
251 
wrap_iterator(MapType::const_iterator inner_iterator)252   inline iterator wrap_iterator(MapType::const_iterator inner_iterator) const {
253 #if SPDY_HEADER_DEBUG
254     iterator outer_iterator(inner_iterator);
255     if (inner_iterator == map_.end()) {
256       outer_iterator.forbid_dereference();
257     }
258     return outer_iterator;
259 #else   // SPDY_HEADER_DEBUG
260     return iterator(inner_iterator);
261 #endif  // SPDY_HEADER_DEBUG
262   }
263 
wrap_const_iterator(MapType::const_iterator inner_iterator)264   inline const_iterator wrap_const_iterator(
265       MapType::const_iterator inner_iterator) const {
266 #if SPDY_HEADER_DEBUG
267     const_iterator outer_iterator(inner_iterator);
268     if (inner_iterator == map_.end()) {
269       outer_iterator.forbid_dereference();
270     }
271     return outer_iterator;
272 #else   // SPDY_HEADER_DEBUG
273     return iterator(inner_iterator);
274 #endif  // SPDY_HEADER_DEBUG
275   }
276 
277   void AppendHeader(const absl::string_view key, const absl::string_view value);
278   absl::string_view WriteKey(const absl::string_view key);
279   size_t bytes_allocated() const;
280 
281   // absl::string_views held by |map_| point to memory owned by |storage_|.
282   MapType map_;
283   Http2HeaderStorage storage_;
284 
285   size_t key_size_ = 0;
286   size_t value_size_ = 0;
287 };
288 
289 }  // namespace spdy
290 
291 #endif  // QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_H_
292