userver: userver/http/header_map.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
header_map.hpp
1#pragma once
2
3#include <initializer_list>
4#include <iterator>
5#include <string>
6#include <vector>
7
8#include <userver/formats/json_fwd.hpp>
9#include <userver/formats/parse/to.hpp>
10#include <userver/http/predefined_header.hpp>
11#include <userver/utils/fast_pimpl.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace http::headers {
16
17class TestsHelper;
18
19namespace header_map {
20class Map;
21}
22
23/// @ingroup userver_universal userver_containers
24///
25/// @brief Container that maps case-insensitive header name into header value.
26///
27/// Allows storing up to 24576 name->value pairs, after that an attempt to
28/// insert a new pair will throw.
29///
30/// Has an anti-hashdos collisions resolution built-in, and the capacity limit
31/// might be lowered in case of an attack being detected.
32///
33/// Iterators/pointers invalidation loosely matches that of std::vector:
34/// * if an insertion took place, iterators/pointers are invalidated;
35/// * successful erase invalidates the iterator being erased and `begin` (it's
36/// implemented via swap and pop_back idiom, and `begin` is actually an `rbegin`
37/// of underlying vector).
38class HeaderMap final {
39 public:
40 /// Iterator
41 class Iterator;
42 /// Const iterator
43 class ConstIterator;
44
45 using iterator = Iterator;
46 using const_iterator = ConstIterator;
47
48 using key_type = std::string;
49 using mapped_type = std::string;
50
51 /// The exception being thrown in case of capacity overflow
52 class TooManyHeadersException final : public std::runtime_error {
53 using std::runtime_error::runtime_error;
54 };
55
56 /// Default constructor.
57 HeaderMap();
58 /// Constructor from initializer list: `HeaderMap({{"a", "b"}, {"c", "d"}})`.
59 /// Its unspecified which pair is inserted in case of names not being unique.
60 HeaderMap(std::initializer_list<std::pair<std::string, std::string>> headers);
61 /// Constructor from initializer list: `HeaderMap({{"a", "b"}, {"c", "d"}})`.
62 /// Its unspecified which pair is inserted in case of names not being unique.
64 std::initializer_list<std::pair<PredefinedHeader, std::string>> headers);
65 /// Constructor with capacity: preallocates `capacity` elements for internal
66 /// storage.
67 HeaderMap(std::size_t capacity);
68 /// Constructor from iterator pair:
69 /// `HeaderMap{key_value_pairs.begin(), key_value_pairs.end()}`.
70 /// Its unspecified which pair is inserted in case of names not being unique.
71 template <typename InputIt>
72 HeaderMap(InputIt first, InputIt last);
73
74 /// Destructor
75 ~HeaderMap();
76
77 /// Copy constructor
78 HeaderMap(const HeaderMap& other);
79 /// Move constructor
80 HeaderMap(HeaderMap&& other) noexcept;
81 /// Copy assignment operator
82 HeaderMap& operator=(const HeaderMap& other);
83 /// Move assignment operator
84 HeaderMap& operator=(HeaderMap&& other) noexcept;
85
86 /// Non-binding call to reserve `capacity` elements for internal storage.
87 void reserve(std::size_t capacity);
88 /// Returns the amount of name-value-pairs being stored.
89 std::size_t size() const noexcept;
90 /// Return true if no name-value-pairs are being stored, false otherwise.
91 bool empty() const noexcept;
92 /// Removes all the key-value-pairs being stored,
93 /// doesn't shrink underlying storage.
94 void clear();
95
96 /// Returns 1 if the key exists, 0 otherwise.
97 std::size_t count(std::string_view key) const noexcept;
98 /// @overload
99 std::size_t count(const PredefinedHeader& key) const noexcept;
100
101 template <std::size_t Size>
102 [[noreturn]] std::size_t count(const char (&)[Size]) const noexcept {
103 ReportMisuse<Size>();
104 }
105
106 /// Returns true if the key exists, false otherwise.
107 bool contains(std::string_view key) const noexcept;
108 /// @overload
109 bool contains(const PredefinedHeader& key) const noexcept;
110
111 template <std::size_t Size>
112 [[noreturn]] bool contains(const char (&)[Size]) const noexcept {
113 ReportMisuse<Size>();
114 }
115
116 /// If the key is present, returns reference to it's header value,
117 /// otherwise inserts a pair (key, "") and returns reference
118 /// to newly inserted empty string.
119 /// In an insertion took place, key is moved-out,
120 /// otherwise key is left unchanged.
121 std::string& operator[](std::string&& key);
122 /// If the key is present, returns reference to it's header value,
123 /// otherwise inserts a pair (key, "") and returns reference
124 /// to newly inserted empty string.
125 std::string& operator[](std::string_view key);
126 /// @overload
127 std::string& operator[](const PredefinedHeader& key);
128
129 template <std::size_t Size>
130 [[noreturn]] std::string& operator[](const char (&)[Size]) {
131 ReportMisuse<Size>();
132 }
133
134 /// If the key is present, returns iterator to its name-value pair,
135 /// otherwise return end().
136 Iterator find(std::string_view key) noexcept;
137 /// @overload
138 ConstIterator find(std::string_view key) const noexcept;
139
140 /// If the key is present, returns iterator to its name-value pair,
141 /// otherwise return end().
142 Iterator find(const PredefinedHeader& key) noexcept;
143 /// @overload
144 ConstIterator find(const PredefinedHeader& key) const noexcept;
145
146 template <std::size_t Size>
147 [[noreturn]] Iterator find(const char (&)[Size]) noexcept;
148
149 template <std::size_t Size>
150 [[noreturn]] ConstIterator find(const char (&)[Size]) const noexcept;
151
152 /// If the key is already present in the map, does nothing.
153 /// Otherwise inserts a pair of key and in-place constructed value.
154 template <typename... Args>
155 void emplace(std::string_view key, Args&&... args) {
156 Emplace(std::move(key), std::forward<Args>(args)...);
157 }
158
159 /// If the key is already present in the map, does nothing.
160 /// Otherwise inserts a pair of key and inplace constructed value.
161 template <typename... Args>
162 void emplace(std::string key, Args&&... args) {
163 Emplace(std::move(key), std::forward<Args>(args)...);
164 }
165
166 /// If the key is already present in the map, does nothing.
167 /// Otherwise inserts a pair of key and inplace constructed value.
168 template <typename... Args>
169 void try_emplace(std::string key, Args&&... args) {
170 Emplace(std::move(key), std::forward<Args>(args)...);
171 }
172
173 /// For every iterator it in [first, last) inserts *it.
174 /// Its unspecified which pair is inserted in case of names not being unique.
175 template <typename InputIt>
176 void insert(InputIt first, InputIt last);
177
178 /// If kvp.first is already present in the map, does nothing,
179 /// otherwise inserts the pair into container.
180 void insert(const std::pair<std::string, std::string>& kvp);
181 /// If kvp.first is already present in the map, does nothing,
182 /// otherwise inserts the pair into container.
183 void insert(std::pair<std::string, std::string>&& kvp);
184
185 /// If key is already present in the map, changes corresponding header value
186 /// to provided value, otherwise inserts the pair into container.
187 void insert_or_assign(std::string key, std::string value);
188 /// @overload
189 void insert_or_assign(const PredefinedHeader& key, std::string value);
190
191 /// If key is already present in the map, appends ",{value}" to the
192 /// corresponding header value, otherwise inserts the pair into container.
193 void InsertOrAppend(std::string key, std::string value);
194 /// @overload
195 void InsertOrAppend(const PredefinedHeader& key, std::string value);
196
197 /// Erases the pair to which the iterator points, returns iterator following
198 /// the last removed element.
199 /// Iterators/pointers to erased value and `begin` are invalidated.
200 Iterator erase(Iterator it);
201 /// Erases the pair to which the iterator points, returns iterator following
202 /// the last removed element.
203 /// Iterators/pointers to erased value and `begin` are invalidated.
204 Iterator erase(ConstIterator it);
205
206 /// If the key is present in container, behaves exactly like erase(Iterator).
207 /// Otherwise does nothing and returns `end()`.
208 Iterator erase(std::string_view key);
209 /// @overload
210 Iterator erase(const PredefinedHeader& key);
211
212 template <std::size_t Size>
213 [[noreturn]] Iterator erase(const char (&)[Size]);
214
215 /// If the key is present in container, returns reference to its header value,
216 /// otherwise throws std::out_of_range.
217 std::string& at(std::string_view key);
218 /// @overload
219 std::string& at(const PredefinedHeader& key);
220 /// If the key is present in container, returns reference to its header value,
221 /// otherwise throws std::out_of_range.
222 const std::string& at(std::string_view key) const;
223 /// @overload
224 const std::string& at(const PredefinedHeader& key) const;
225
226 template <std::size_t Size>
227 [[noreturn]] std::string& at(const char (&)[Size]) {
228 ReportMisuse<Size>();
229 }
230 template <std::size_t Size>
231 [[noreturn]] const std::string& at(const char (&)[Size]) const {
232 ReportMisuse<Size>();
233 }
234
235 /// Returns an iterator to the first name-value-pair being stored.
236 Iterator begin() noexcept;
237 /// Returns an iterator to the first name-value-pair being stored.
238 ConstIterator begin() const noexcept;
239 /// Returns an iterator to the first name-value-pair being stored.
240 ConstIterator cbegin() const noexcept;
241
242 /// Returns an iterator to the end (valid but not dereferenceable).
243 Iterator end() noexcept;
244 /// Returns an iterator to the end (valid but not dereferenceable).
245 ConstIterator end() const noexcept;
246 /// Returns an iterator to the end (valid but not dereferenceable).
247 ConstIterator cend() const noexcept;
248
249 /// Returns true if `other` contains exactly the same set of name-value-pairs,
250 /// false otherwise.
251 bool operator==(const HeaderMap& other) const noexcept;
252
253 /// Appends container content in http headers format to provided buffer,
254 /// that is appends
255 /// @code
256 /// header1: value1\r\n
257 /// header2: value2\r\n
258 /// ...
259 /// @endcode
260 /// resizing buffer as needed.
261 void OutputInHttpFormat(HeadersString& buffer) const;
262
263 private:
264 friend class TestsHelper;
265
266 template <typename KeyType, typename... Args>
267 void Emplace(KeyType&& key, Args&&... args);
268
269 template <std::size_t Size>
270 [[noreturn]] static void ReportMisuse();
271
272 utils::FastPimpl<header_map::Map, 272, 8> impl_;
273};
274
275template <typename InputIt>
276HeaderMap::HeaderMap(InputIt first, InputIt last) : HeaderMap{} {
277 insert(first, last);
278}
279
280template <typename InputIt>
281void HeaderMap::insert(InputIt first, InputIt last) {
282 for (; first != last; ++first) {
283 insert(*first);
284 }
285}
286
287template <typename KeyType, typename... Args>
288void HeaderMap::Emplace(KeyType&& key, Args&&... args) {
289 static_assert(std::is_rvalue_reference_v<decltype(key)>);
290 // This is a private function, and we know what we are doing here.
291 // NOLINTNEXTLINE(bugprone-move-forwarding-reference)
292 auto& value = operator[](std::move(key));
293 if (value.empty()) {
294 value = std::string{std::forward<Args>(args)...};
295 }
296}
297
298template <std::size_t Size>
299void HeaderMap::ReportMisuse() {
300 static_assert(!Size,
301 "Please create a 'constexpr PredefinedHeader' and use that "
302 "instead of passing header name as a string literal.");
303}
304
305namespace header_map {
306
307class MapEntry final {
308 public:
309 MapEntry();
310 ~MapEntry();
311
312 MapEntry(std::string&& key, std::string&& value);
313
314 MapEntry(const MapEntry& other);
315 MapEntry& operator=(const MapEntry& other);
316 MapEntry(MapEntry&& other) noexcept;
317 MapEntry& operator=(MapEntry&& other) noexcept;
318
319 std::pair<const std::string, std::string>& Get();
320 const std::pair<const std::string, std::string>& Get() const;
321
322 std::pair<std::string, std::string>& GetMutable();
323
324 bool operator==(const MapEntry& other) const;
325
326 private:
327 // The interface requires std::pair<CONST std::string, std::string>, but we
328 // don't want to copy where move would do, so this.
329 // Only 'mutable_value' is ever the active member of 'Slot', but we still
330 // can access it through `value` due to
331 // https://eel.is/c++draft/class.union.general#note-1
332 // The idea was taken from abseil:
333 // https://github.com/abseil/abseil-cpp/blob/1ae9b71c474628d60eb251a3f62967fe64151bb2/absl/container/internal/container_memory.h#L302
334 union Slot {
335 Slot();
336 ~Slot();
337
338 std::pair<std::string, std::string> mutable_value;
339 std::pair<const std::string, std::string> value;
340 };
341
342 Slot slot_{};
343};
344
345} // namespace header_map
346
347class HeaderMap::Iterator final {
348 public:
349 using iterator_category = std::forward_iterator_tag;
350 using difference_type = std::ptrdiff_t;
351 using value_type = std::pair<const std::string, std::string>;
352 using reference = value_type&;
353 using const_reference = const value_type&;
354 using pointer = value_type*;
355 using const_pointer = const value_type*;
356
357 // The underlying iterator is a reversed one to do not invalidate
358 // end() on erase - end() is actually an rbegin() of underlying storage and is
359 // only invalidated on reallocation.
360 using UnderlyingIterator =
361 std::vector<header_map::MapEntry>::reverse_iterator;
362
363 Iterator();
364 explicit Iterator(UnderlyingIterator it);
365 ~Iterator();
366
367 Iterator(const Iterator& other);
368 Iterator(Iterator&& other) noexcept;
369 Iterator& operator=(const Iterator& other);
370 Iterator& operator=(Iterator&& other) noexcept;
371
372 Iterator operator++(int);
373 Iterator& operator++();
374
375 reference operator*();
376 const_reference operator*() const;
377 pointer operator->();
378 const_pointer operator->() const;
379
380 bool operator==(const Iterator& other) const;
381 bool operator!=(const Iterator& other) const;
382
383 bool operator==(const ConstIterator& other) const;
384
385 private:
386 friend class HeaderMap::ConstIterator;
387
388 UnderlyingIterator it_{};
389};
390
391class HeaderMap::ConstIterator final {
392 public:
393 using iterator_category = std::forward_iterator_tag;
394 using difference_type = std::ptrdiff_t;
395 using value_type = std::pair<const std::string, std::string>;
396 using reference = const value_type&;
397 using const_reference = const value_type&;
398 using pointer = const value_type*;
399 using const_pointer = const value_type*;
400
401 // The underlying iterator is a reversed one to do not invalidate
402 // end() on erase - end() is actually an rbegin() of underlying storage and is
403 // only invalidated on reallocation.
404 using UnderlyingIterator =
405 std::vector<header_map::MapEntry>::const_reverse_iterator;
406
407 ConstIterator();
408 explicit ConstIterator(UnderlyingIterator it);
409 ~ConstIterator();
410
411 ConstIterator(const ConstIterator& other);
412 ConstIterator(ConstIterator&& other) noexcept;
413 ConstIterator& operator=(const ConstIterator& other);
414 ConstIterator& operator=(ConstIterator&& other) noexcept;
415
416 ConstIterator operator++(int);
417 ConstIterator& operator++();
418
419 reference operator*();
420 const_reference operator*() const;
421 pointer operator->();
422 const_pointer operator->() const;
423
424 bool operator==(const ConstIterator& other) const;
425 bool operator!=(const ConstIterator& other) const;
426
427 bool operator==(const Iterator& other) const;
428
429 private:
430 friend class HeaderMap::Iterator;
431
432 UnderlyingIterator it_{};
433};
434
435template <std::size_t Size>
436HeaderMap::Iterator HeaderMap::find(const char (&)[Size]) noexcept {
437 ReportMisuse<Size>();
438}
439
440template <std::size_t Size>
441HeaderMap::ConstIterator HeaderMap::find(const char (&)[Size]) const noexcept {
442 ReportMisuse<Size>();
443}
444
445template <std::size_t Size>
446HeaderMap::Iterator HeaderMap::erase(const char (&)[Size]) {
447 ReportMisuse<Size>();
448}
449
450} // namespace http::headers
451
452USERVER_NAMESPACE_END