Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/cppalliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_FIELDS_BASE_HPP | ||
11 | #define BOOST_HTTP_PROTO_FIELDS_BASE_HPP | ||
12 | |||
13 | #include <boost/http_proto/detail/config.hpp> | ||
14 | #include <boost/http_proto/fields_view_base.hpp> | ||
15 | #include <boost/core/detail/string_view.hpp> | ||
16 | #include <boost/system/result.hpp> | ||
17 | |||
18 | namespace boost { | ||
19 | namespace http_proto { | ||
20 | |||
21 | namespace detail { | ||
22 | struct prefix_op; | ||
23 | } // detail | ||
24 | |||
25 | /** Mixin for modifiable HTTP fields | ||
26 | |||
27 | @par Iterators | ||
28 | |||
29 | Iterators obtained from @ref fields | ||
30 | containers are not invalidated when | ||
31 | the underlying container is modified. | ||
32 | |||
33 | @note HTTP field names are case-insensitive. | ||
34 | */ | ||
35 | class BOOST_SYMBOL_VISIBLE | ||
36 | fields_base | ||
37 | : public virtual fields_view_base | ||
38 | { | ||
39 | detail::header h_; | ||
40 | |||
41 | class op_t; | ||
42 | using entry = | ||
43 | detail::header::entry; | ||
44 | using table = | ||
45 | detail::header::table; | ||
46 | |||
47 | friend class fields; | ||
48 | friend class request; | ||
49 | friend class response; | ||
50 | friend class serializer; | ||
51 | friend class message_base; | ||
52 | friend struct detail::header; | ||
53 | friend struct detail::prefix_op; | ||
54 | |||
55 | BOOST_HTTP_PROTO_DECL | ||
56 | explicit | ||
57 | fields_base( | ||
58 | detail::kind) noexcept; | ||
59 | |||
60 | BOOST_HTTP_PROTO_DECL | ||
61 | fields_base( | ||
62 | detail::kind, | ||
63 | core::string_view); | ||
64 | |||
65 | fields_base(detail::header const&); | ||
66 | |||
67 | public: | ||
68 | /** Destructor | ||
69 | */ | ||
70 | BOOST_HTTP_PROTO_DECL | ||
71 | ~fields_base(); | ||
72 | |||
73 | //-------------------------------------------- | ||
74 | // | ||
75 | // Capacity | ||
76 | // | ||
77 | //-------------------------------------------- | ||
78 | |||
79 | /** Returns the largest permissible capacity in bytes | ||
80 | */ | ||
81 | static | ||
82 | constexpr | ||
83 | std::size_t | ||
84 | 718 | max_capacity_in_bytes() noexcept | |
85 | { | ||
86 | using T = detail::header::entry; | ||
87 | return alignof(T) * | ||
88 | (((max_offset - 2 + sizeof(T) * ( | ||
89 | max_offset / 4)) + | ||
90 | alignof(T) - 1) / | ||
91 | 718 | alignof(T)); | |
92 | } | ||
93 | |||
94 | /** Returns the total number of bytes allocated by the container | ||
95 | */ | ||
96 | std::size_t | ||
97 | 61 | capacity_in_bytes() const noexcept | |
98 | { | ||
99 | 61 | return h_.cap; | |
100 | } | ||
101 | |||
102 | /** Clear the contents, but not the capacity | ||
103 | */ | ||
104 | BOOST_HTTP_PROTO_DECL | ||
105 | void | ||
106 | clear() noexcept; | ||
107 | |||
108 | /** Reserve a minimum capacity | ||
109 | */ | ||
110 | BOOST_HTTP_PROTO_DECL | ||
111 | void | ||
112 | reserve_bytes(std::size_t n); | ||
113 | |||
114 | /** Remove excess capacity | ||
115 | */ | ||
116 | BOOST_HTTP_PROTO_DECL | ||
117 | void | ||
118 | shrink_to_fit() noexcept; | ||
119 | |||
120 | //-------------------------------------------- | ||
121 | // | ||
122 | // Modifiers | ||
123 | // | ||
124 | //-------------------------------------------- | ||
125 | |||
126 | /** Append a header | ||
127 | |||
128 | This function appends a new header with the | ||
129 | specified id and value. The value must be | ||
130 | syntactically valid or else an error is returned. | ||
131 | Any leading or trailing whitespace in the new value | ||
132 | is ignored. | ||
133 | <br/> | ||
134 | No iterators are invalidated. | ||
135 | |||
136 | @par Example | ||
137 | @code | ||
138 | request req; | ||
139 | |||
140 | req.append( field::user_agent, "Boost" ); | ||
141 | @endcode | ||
142 | |||
143 | @par Complexity | ||
144 | Linear in `to_string( id ).size() + value.size()`. | ||
145 | |||
146 | @par Exception Safety | ||
147 | Strong guarantee. | ||
148 | Calls to allocate may throw. | ||
149 | |||
150 | @param id The field name constant, | ||
151 | which may not be @ref field::unknown. | ||
152 | |||
153 | @param value A value, which must be semantically | ||
154 | valid for the message. | ||
155 | |||
156 | @return The error, if any occurred. | ||
157 | */ | ||
158 | system::result<void> | ||
159 | 21 | append( | |
160 | field id, | ||
161 | core::string_view value) | ||
162 | { | ||
163 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | BOOST_ASSERT( |
164 | id != field::unknown); | ||
165 | return insert_impl( | ||
166 | 21 | id, to_string(id), value, h_.count); | |
167 | } | ||
168 | |||
169 | /** Append a header | ||
170 | |||
171 | This function appends a new header with the | ||
172 | specified name and value. Both values must be | ||
173 | syntactically valid or else an error is returned. | ||
174 | Any leading or trailing whitespace in the new | ||
175 | value is ignored. | ||
176 | <br/> | ||
177 | No iterators are invalidated. | ||
178 | |||
179 | @par Example | ||
180 | @code | ||
181 | request req; | ||
182 | |||
183 | req.append( "User-Agent", "Boost" ); | ||
184 | @endcode | ||
185 | |||
186 | @par Complexity | ||
187 | Linear in `name.size() + value.size()`. | ||
188 | |||
189 | @par Exception Safety | ||
190 | Strong guarantee. | ||
191 | Calls to allocate may throw. | ||
192 | |||
193 | @param name The header name. | ||
194 | |||
195 | @param value A value, which must be semantically | ||
196 | valid for the message. | ||
197 | |||
198 | @return The error, if any occurred. | ||
199 | */ | ||
200 | system::result<void> | ||
201 | 55 | append( | |
202 | core::string_view name, | ||
203 | core::string_view value) | ||
204 | { | ||
205 | return insert_impl( | ||
206 | string_to_field( | ||
207 | name), | ||
208 | name, | ||
209 | value, | ||
210 | 55 | h_.count); | |
211 | } | ||
212 | |||
213 | /** Insert a header | ||
214 | |||
215 | If a matching header with the same name | ||
216 | exists, it is not replaced. Instead, an | ||
217 | additional header with the same name is | ||
218 | inserted. Names are not case-sensitive. | ||
219 | Any leading or trailing whitespace in | ||
220 | the new value is ignored. | ||
221 | <br> | ||
222 | All iterators that are equal to `before` | ||
223 | or come after are invalidated. | ||
224 | |||
225 | @par Example | ||
226 | @code | ||
227 | request req; | ||
228 | |||
229 | req.insert( req.begin(), field::user_agent, "Boost" ); | ||
230 | @endcode | ||
231 | |||
232 | @par Complexity | ||
233 | Linear in `to_string( id ).size() + value.size()`. | ||
234 | |||
235 | @par Exception Safety | ||
236 | Strong guarantee. | ||
237 | Calls to allocate may throw. | ||
238 | |||
239 | @return An iterator the newly inserted header, or | ||
240 | an error if any occurred. | ||
241 | |||
242 | @param before Position to insert before. | ||
243 | |||
244 | @param id The field name constant, | ||
245 | which may not be @ref field::unknown. | ||
246 | |||
247 | @param value A value, which must be semantically | ||
248 | valid for the message. | ||
249 | */ | ||
250 | system::result<iterator> | ||
251 | 7 | insert( | |
252 | iterator before, | ||
253 | field id, | ||
254 | core::string_view value) | ||
255 | { | ||
256 | // TODO: this should probably return an error | ||
257 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | BOOST_ASSERT( |
258 | id != field::unknown); | ||
259 | |||
260 | auto rv = insert_impl( | ||
261 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
7 | id, to_string(id), value, before.i_); |
262 | |||
263 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
|
7 | if( rv.has_error() ) |
264 | 1 | return rv.error(); | |
265 | 6 | return before; | |
266 | } | ||
267 | |||
268 | /** Insert a header | ||
269 | |||
270 | If a matching header with the same name | ||
271 | exists, it is not replaced. Instead, an | ||
272 | additional header with the same name is | ||
273 | inserted. Names are not case-sensitive. | ||
274 | Any leading or trailing whitespace in | ||
275 | the new value is ignored. | ||
276 | <br> | ||
277 | All iterators that are equal to `before` | ||
278 | or come after are invalidated. | ||
279 | |||
280 | @par Example | ||
281 | @code | ||
282 | request req; | ||
283 | |||
284 | req.insert( req.begin(), "User-Agent", "Boost" ); | ||
285 | @endcode | ||
286 | |||
287 | @par Complexity | ||
288 | Linear in `name.size() + value.size()`. | ||
289 | |||
290 | @par Exception Safety | ||
291 | Strong guarantee. | ||
292 | Calls to allocate may throw. | ||
293 | |||
294 | @return An iterator the newly inserted header, or | ||
295 | an error if any occurred. | ||
296 | |||
297 | @param before Position to insert before. | ||
298 | |||
299 | @param name The header name. | ||
300 | |||
301 | @param value A value, which must be semantically | ||
302 | valid for the message. | ||
303 | */ | ||
304 | system::result<iterator> | ||
305 | 15 | insert( | |
306 | iterator before, | ||
307 | core::string_view name, | ||
308 | core::string_view value) | ||
309 | { | ||
310 | auto rv = insert_impl( | ||
311 | string_to_field( | ||
312 | name), | ||
313 | name, | ||
314 | value, | ||
315 |
1/2✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
15 | before.i_); |
316 | |||
317 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 12 times.
|
15 | if( rv.has_error() ) |
318 | 3 | return rv.error(); | |
319 | 12 | return before; | |
320 | } | ||
321 | |||
322 | //-------------------------------------------- | ||
323 | |||
324 | /** Erase headers | ||
325 | |||
326 | This function removes the header pointed | ||
327 | to by `it`. | ||
328 | <br> | ||
329 | All iterators that are equal to `it` | ||
330 | or come after are invalidated. | ||
331 | |||
332 | @par Complexity | ||
333 | Linear in `name.size() + value.size()`. | ||
334 | |||
335 | @par Exception Safety | ||
336 | Throws nothing. | ||
337 | |||
338 | @return An iterator to the inserted | ||
339 | element. | ||
340 | |||
341 | @param it An iterator to the element | ||
342 | to erase. | ||
343 | */ | ||
344 | iterator | ||
345 | 31 | erase(iterator it) noexcept | |
346 | { | ||
347 | 31 | erase_impl(it.i_, it->id); | |
348 | 31 | return it; | |
349 | } | ||
350 | |||
351 | /** Erase headers | ||
352 | |||
353 | This removes all headers whose name | ||
354 | constant is equal to `id`. | ||
355 | <br> | ||
356 | If any headers are erased, then all | ||
357 | iterators equal to or that come after | ||
358 | the first erased element are invalidated. | ||
359 | Otherwise, no iterators are invalidated. | ||
360 | |||
361 | @par Complexity | ||
362 | Linear in `this->string().size()`. | ||
363 | |||
364 | @par Exception Safety | ||
365 | Throws nothing. | ||
366 | |||
367 | @return The number of headers erased. | ||
368 | |||
369 | @param id The field name constant, | ||
370 | which may not be @ref field::unknown. | ||
371 | */ | ||
372 | BOOST_HTTP_PROTO_DECL | ||
373 | std::size_t | ||
374 | erase(field id) noexcept; | ||
375 | |||
376 | /** Erase all matching fields | ||
377 | |||
378 | This removes all headers with a matching | ||
379 | name, using a case-insensitive comparison. | ||
380 | <br> | ||
381 | If any headers are erased, then all | ||
382 | iterators equal to or that come after | ||
383 | the first erased element are invalidated. | ||
384 | Otherwise, no iterators are invalidated. | ||
385 | |||
386 | @par Complexity | ||
387 | Linear in `this->string().size()`. | ||
388 | |||
389 | @par Exception Safety | ||
390 | Throws nothing. | ||
391 | |||
392 | @return The number of fields erased | ||
393 | |||
394 | @param name The header name. | ||
395 | */ | ||
396 | BOOST_HTTP_PROTO_DECL | ||
397 | std::size_t | ||
398 | erase( | ||
399 | core::string_view name) noexcept; | ||
400 | |||
401 | //-------------------------------------------- | ||
402 | |||
403 | /** Set a header value | ||
404 | |||
405 | Uses the given value to overwrite the | ||
406 | current one in the header field pointed to by the | ||
407 | iterator. The value must be syntactically | ||
408 | valid or else an error is returned. | ||
409 | Any leading or trailing whitespace in the new value | ||
410 | is ignored. | ||
411 | |||
412 | @par Complexity | ||
413 | |||
414 | @par Exception Safety | ||
415 | Strong guarantee. | ||
416 | Calls to allocate may throw. | ||
417 | |||
418 | @return The error, if any occurred. | ||
419 | |||
420 | @param it An iterator to the header. | ||
421 | |||
422 | @param value A value, which must be semantically | ||
423 | valid for the message. | ||
424 | */ | ||
425 | BOOST_HTTP_PROTO_DECL | ||
426 | system::result<void> | ||
427 | set( | ||
428 | iterator it, | ||
429 | core::string_view value); | ||
430 | |||
431 | /** Set a header value | ||
432 | |||
433 | The container is modified to contain exactly | ||
434 | one field with the specified id set to the given value, | ||
435 | which must be syntactically valid or else an error is | ||
436 | returned. | ||
437 | Any leading or trailing whitespace in the new value | ||
438 | is ignored. | ||
439 | |||
440 | @par Postconditions | ||
441 | @code | ||
442 | this->count( id ) == 1 && this->at( id ) == value | ||
443 | @endcode | ||
444 | |||
445 | @par Complexity | ||
446 | |||
447 | @return The error, if any occurred. | ||
448 | |||
449 | @param id The field constant of the | ||
450 | header to set. | ||
451 | |||
452 | @param value A value, which must be semantically | ||
453 | valid for the message. | ||
454 | */ | ||
455 | BOOST_HTTP_PROTO_DECL | ||
456 | system::result<void> | ||
457 | set( | ||
458 | field id, | ||
459 | core::string_view value); | ||
460 | |||
461 | /** Set a header value | ||
462 | |||
463 | The container is modified to contain exactly | ||
464 | one field with the specified name set to the given value, | ||
465 | which must be syntactically valid or else an error is | ||
466 | returned. | ||
467 | Any leading or trailing whitespace in the new value | ||
468 | is ignored. | ||
469 | |||
470 | @par Postconditions | ||
471 | @code | ||
472 | this->count( name ) == 1 && this->at( name ) == value | ||
473 | @endcode | ||
474 | |||
475 | @return The error, if any occurred. | ||
476 | |||
477 | @param name The field name. | ||
478 | |||
479 | @param value A value, which must be semantically | ||
480 | valid for the message. | ||
481 | */ | ||
482 | BOOST_HTTP_PROTO_DECL | ||
483 | system::result<void> | ||
484 | set( | ||
485 | core::string_view name, | ||
486 | core::string_view value); | ||
487 | |||
488 | //-------------------------------------------- | ||
489 | |||
490 | private: | ||
491 | BOOST_HTTP_PROTO_DECL | ||
492 | void | ||
493 | copy_impl( | ||
494 | detail::header const&); | ||
495 | |||
496 | void | ||
497 | insert_impl_unchecked( | ||
498 | field id, | ||
499 | core::string_view name, | ||
500 | core::string_view value, | ||
501 | std::size_t before, | ||
502 | bool has_obs_fold); | ||
503 | |||
504 | BOOST_HTTP_PROTO_DECL | ||
505 | system::result<void> | ||
506 | insert_impl( | ||
507 | field id, | ||
508 | core::string_view name, | ||
509 | core::string_view value, | ||
510 | std::size_t before); | ||
511 | |||
512 | BOOST_HTTP_PROTO_DECL | ||
513 | void | ||
514 | erase_impl( | ||
515 | std::size_t i, | ||
516 | field id) noexcept; | ||
517 | |||
518 | void raw_erase( | ||
519 | std::size_t) noexcept; | ||
520 | |||
521 | std::size_t | ||
522 | erase_all_impl( | ||
523 | std::size_t i0, | ||
524 | field id) noexcept; | ||
525 | |||
526 | std::size_t | ||
527 | offset( | ||
528 | std::size_t i) const noexcept; | ||
529 | |||
530 | std::size_t | ||
531 | length( | ||
532 | std::size_t i) const noexcept; | ||
533 | |||
534 | void raw_erase_n(field, std::size_t) noexcept; | ||
535 | }; | ||
536 | |||
537 | //------------------------------------------------ | ||
538 | |||
539 | #ifndef BOOST_HTTP_PROTO_DOCS | ||
540 | namespace detail { | ||
541 | inline | ||
542 | header& | ||
543 | header:: | ||
544 | get(fields_base& f) noexcept | ||
545 | { | ||
546 | return f.h_; | ||
547 | } | ||
548 | } // detail | ||
549 | #endif | ||
550 | |||
551 | } // http_proto | ||
552 | } // boost | ||
553 | |||
554 | #endif | ||
555 |