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