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