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