GCC Code Coverage Report


Directory: ./
File: libs/http_proto/include/boost/http_proto/serializer.hpp
Date: 2025-12-04 09:48:34
Exec Total Coverage
Lines: 9 9 100.0%
Functions: 4 4 100.0%
Branches: 0 0 -%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2025 Mohammad Nejati
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_SERIALIZER_HPP
12 #define BOOST_HTTP_PROTO_SERIALIZER_HPP
13
14 #include <boost/http_proto/detail/config.hpp>
15 #include <boost/http_proto/detail/workspace.hpp>
16 #include <boost/http_proto/source.hpp>
17
18 #include <boost/buffers/buffer_pair.hpp>
19 #include <boost/core/span.hpp>
20 #include <boost/capy/polystore_fwd.hpp>
21 #include <boost/system/result.hpp>
22
23 #include <type_traits>
24 #include <utility>
25
26 namespace boost {
27 namespace http_proto {
28
29 // Forward declaration
30 class message_base;
31
32 /** A serializer for HTTP/1 messages
33
34 This is used to serialize one or more complete
35 HTTP/1 messages. Each message consists of a
36 required header followed by an optional body.
37
38 Objects of this type operate using an "input area" and an
39 "output area". Callers provide data to the input area
40 using one of the @ref start or @ref start_stream member
41 functions. After input is provided, serialized data
42 becomes available in the serializer's output area in the
43 form of a constant buffer sequence.
44
45 Callers alternate between filling the input area and
46 consuming the output area until all the input has been
47 provided and all the output data has been consumed, or
48 an error occurs.
49
50 After calling @ref start, the caller must ensure that the
51 contents of the associated message are not changed or
52 destroyed until @ref is_done returns true, @ref reset is
53 called, or the serializer is destroyed, otherwise the
54 behavior is undefined.
55 */
56 class serializer
57 {
58 public:
59 class stream;
60 struct config;
61
62 /** The type used to represent a sequence of
63 constant buffers that refers to the output
64 area.
65 */
66 using const_buffers_type =
67 boost::span<buffers::const_buffer const>;
68
69 /** Destructor
70 */
71 BOOST_HTTP_PROTO_DECL
72 ~serializer();
73
74 /** Constructor
75 Default-constructed serializers do not reference any implementation;
76 the only valid operations are destruction and assignment.
77 */
78 3 serializer() = default;
79
80 /** Constructor.
81
82 The states of `other` are transferred
83 to the newly constructed object,
84 which includes the allocated buffer.
85 After construction, the only valid
86 operations on the moved-from object
87 are destruction and assignment.
88
89 Buffer sequences previously obtained
90 using @ref prepare or @ref stream::prepare
91 remain valid.
92
93 @par Postconditions
94 @code
95 other.is_done() == true
96 @endcode
97
98 @par Complexity
99 Constant.
100
101 @param other The serializer to move from.
102 */
103 BOOST_HTTP_PROTO_DECL
104 serializer(
105 serializer&& other) noexcept;
106
107 /** Assignment.
108 The states of `other` are transferred
109 to this object, which includes the
110 allocated buffer. After assignment,
111 the only valid operations on the
112 moved-from object are destruction and
113 assignment.
114 Buffer sequences previously obtained
115 using @ref prepare or @ref stream::prepare
116 remain valid.
117 @par Complexity
118 Constant.
119 @param other The serializer to move from.
120 @return A reference to this object.
121 */
122 BOOST_HTTP_PROTO_DECL
123 serializer&
124 operator=(serializer&& other) noexcept;
125
126 /** Constructor.
127
128 Constructs a serializer that uses the @ref
129 config parameters installed on the
130 provided `ctx`.
131
132 The serializer will attempt to allocate
133 the required space on startup, with the
134 amount depending on the @ref config
135 parameters, and will not perform any
136 further allocations, except for Brotli
137 encoder instances, if enabled.
138
139 Depending on which compression algorithms
140 are enabled in the @ref config, the
141 serializer will attempt to access the
142 corresponding encoder services on the same
143 `ctx`.
144
145 @par Example
146 @code
147 serializer sr(ctx);
148 @endcode
149
150 @par Postconditions
151 @code
152 this->is_done() == true
153 @endcode
154
155 @par Complexity
156 Constant.
157
158 @par Exception Safety
159 Calls to allocate may throw.
160
161 @param ctx Context from which the
162 serializer will access registered
163 services. The caller is responsible for
164 ensuring that the provided ctx remains
165 valid for the lifetime of the serializer.
166
167 @see
168 @ref install_serializer_service,
169 @ref config.
170 */
171 BOOST_HTTP_PROTO_DECL
172 explicit
173 serializer(
174 capy::polystore& ctx);
175
176 /** Reset the serializer for a new message.
177
178 Aborts any ongoing serialization and
179 prepares the serializer to start
180 serialization of a new message.
181 */
182 BOOST_HTTP_PROTO_DECL
183 void
184 reset() noexcept;
185
186 /** Start serializing a message with an empty body
187
188 This function prepares the serializer to create a message which
189 has an empty body.
190 Ownership of the specified message is not transferred; the caller is
191 responsible for ensuring the lifetime of the object extends until the
192 serializer is done.
193
194 @par Preconditions
195 @code
196 this->is_done() == true
197 @endcode
198
199 @par Postconditions
200 @code
201 this->is_done() == false
202 @endcode
203
204 @par Exception Safety
205 Strong guarantee.
206 Exceptions thrown if there is insufficient internal buffer space
207 to start the operation.
208
209 @throw std::logic_error `this->is_done() == true`.
210
211 @throw std::length_error if there is insufficient internal buffer
212 space to start the operation.
213
214 @param m The request or response headers to serialize.
215
216 @see
217 @ref message_base.
218 */
219 void
220 BOOST_HTTP_PROTO_DECL
221 start(message_base const& m);
222
223 /** Start serializing a message with a buffer sequence body
224
225 Initializes the serializer with the HTTP start-line and headers from `m`,
226 and the provided `buffers` for reading the message body from.
227
228 Changing the contents of the message after calling this function and
229 before @ref is_done returns `true` results in undefined behavior.
230
231 At least one copy of the specified buffer sequence is maintained until
232 the serializer is done, gets reset, or ios destroyed, after which all
233 of its copies are destroyed. Ownership of the underlying memory is not
234 transferred; the caller must ensure the memory remains valid until the
235 serializer’s copies are destroyed.
236
237 @par Preconditions
238 @code
239 this->is_done() == true
240 @endcode
241
242 @par Postconditions
243 @code
244 this->is_done() == false
245 @endcode
246
247 @par Constraints
248 @code
249 buffers::is_const_buffer_sequence_v<ConstBufferSequence> == true
250 @endcode
251
252 @par Exception Safety
253 Strong guarantee.
254 Exceptions thrown if there is insufficient internal buffer space
255 to start the operation.
256
257 @throw std::logic_error `this->is_done() == true`.
258
259 @throw std::length_error If there is insufficient internal buffer
260 space to start the operation.
261
262 @param m The message to read the HTTP start-line and headers from.
263
264 @param buffers A buffer sequence containing the message body.
265
266 containing the message body data. While
267 the buffers object is copied, ownership of
268 the underlying memory remains with the
269 caller, who must ensure it stays valid
270 until @ref is_done returns `true`.
271
272 @see
273 @ref message_base.
274 */
275 template<
276 class ConstBufferSequence,
277 class = typename std::enable_if<
278 buffers::is_const_buffer_sequence<
279 ConstBufferSequence>::value>::type
280 >
281 void
282 start(
283 message_base const& m,
284 ConstBufferSequence&& buffers);
285
286 /** Start serializing a message with a @em Source body
287
288 Initializes the serializer with the HTTP start-line and headers from
289 `m`, and constructs a `Source` object to provide the message body.
290
291 Changing the contents of the message
292 after calling this function and before
293 @ref is_done returns `true` results in
294 undefined behavior.
295
296 The serializer destroys Source object when:
297 @li `this->is_done() == true`
298 @li An unrecoverable serialization error occurs
299 @li The serializer is destroyed
300
301 @par Example
302 @code
303 file f("example.zip", file_mode::scan);
304 response.set_payload_size(f.size());
305 serializer.start<file_source>(response, std::move(f));
306 @endcode
307
308 @par Preconditions
309 @code
310 this->is_done() == true
311 @endcode
312
313 @par Postconditions
314 @code
315 this->is_done() == false
316 @endcode
317
318 @par Constraints
319 @code
320 is_source<Source>::value == true
321 @endcode
322
323 @par Exception Safety
324 Strong guarantee.
325 Exceptions thrown if there is insufficient
326 internal buffer space to start the
327 operation.
328
329 @throw std::length_error if there is
330 insufficient internal buffer space to
331 start the operation.
332
333 @param m The message to read the HTTP
334 start-line and headers from.
335
336 @param args Arguments to be passed to the
337 `Source` constructor.
338
339 @return A reference to the constructed Source object.
340
341 @see
342 @ref source,
343 @ref file_source,
344 @ref message_base.
345 */
346 template<
347 class Source,
348 class... Args,
349 class = typename std::enable_if<
350 is_source<Source>::value>::type>
351 Source&
352 start(
353 message_base const& m,
354 Args&&... args);
355
356 /** Prepare the serializer for a new message using a stream interface.
357
358 Initializes the serializer with the HTTP
359 start-line and headers from `m`, and returns
360 a @ref stream object for writing the body
361 data into the serializer's internal buffer.
362
363 Once the serializer is destroyed, @ref reset
364 is called, or @ref is_done returns true, the
365 only valid operation on the stream is destruction.
366
367 The stream allows inverted control flow: the
368 caller supplies body data to the serializer’s
369 internal buffer while reading from an external
370 source.
371
372 Changing the contents of the message
373 after calling this function and before
374 @ref is_done returns `true` results in
375 undefined behavior.
376
377 @par Example
378 @code
379 serializer::stream st = serializer.start_stream(response);
380 do
381 {
382 if(st.is_open())
383 {
384 std::size_t n = source.read_some(st.prepare());
385
386 if(ec == error::eof)
387 st.close();
388 else
389 st.commit(n);
390 }
391
392 write_some(client, serializer);
393
394 } while(!serializer.is_done());
395 @endcode
396
397 @par Preconditions
398 @code
399 this->is_done() == true
400 @endcode
401
402 @par Postconditions
403 @code
404 this->is_done() == false
405 @endcode
406
407 @par Exception Safety
408 Strong guarantee.
409 Exceptions thrown if there is insufficient
410 internal buffer space to start the
411 operation.
412
413 @throw std::length_error if there is
414 insufficient internal buffer space to
415 start the operation.
416
417 @param m The message to read the HTTP
418 start-line and headers from.
419
420 @return A @ref stream object for writing the body
421 data into the serializer's internal buffer.
422
423 @see
424 @ref stream,
425 @ref message_base.
426 */
427 BOOST_HTTP_PROTO_DECL
428 stream
429 start_stream(
430 message_base const& m);
431
432 /** Return the output area.
433
434 This function serializes some or all of
435 the message and returns the corresponding
436 output buffers. Afterward, a call to @ref
437 consume is required to report the number
438 of bytes used, if any.
439
440 If the message includes an
441 `Expect: 100-continue` header and the
442 header section of the message has been
443 consumed, the returned result will contain
444 @ref error::expect_100_continue to
445 indicate that the header part of the
446 message is complete. The next call to @ref
447 prepare will produce output.
448
449 When the serializer is used through the
450 @ref stream interface, the result may
451 contain @ref error::need_data to indicate
452 that additional input is required to
453 produce output.
454
455 If a @ref source object is in use and a
456 call to @ref source::read returns an
457 error, the serializer enters a faulted
458 state and propagates the error to the
459 caller. This faulted state can only be
460 cleared by calling @ref reset. This
461 ensures the caller is explicitly aware
462 that the previous message was truncated
463 and that the stream must be terminated.
464
465 @par Preconditions
466 @code
467 this->is_done() == false
468 @endcode
469 No unrecoverable error reported from previous calls.
470
471 @par Exception Safety
472 Strong guarantee.
473 Calls to @ref source::read may throw if in use.
474
475 @throw std::logic_error
476 `this->is_done() == true`.
477
478 @return A result containing @ref
479 const_buffers_type that represents the
480 output area or an error if any occurred.
481
482 @see
483 @ref consume,
484 @ref is_done,
485 @ref const_buffers_type.
486 */
487 BOOST_HTTP_PROTO_DECL
488 auto
489 prepare() ->
490 system::result<
491 const_buffers_type>;
492
493 /** Consume bytes from the output area.
494
495 This function should be called after one
496 or more bytes contained in the buffers
497 provided in the prior call to @ref prepare
498 have been used.
499
500 After a call to @ref consume, callers
501 should check the return value of @ref
502 is_done to determine if the entire message
503 has been serialized.
504
505 @par Preconditions
506 @code
507 this->is_done() == false
508 @endcode
509
510 @par Exception Safety
511 Strong guarantee.
512
513 @throw std::logic_error
514 `this->is_done() == true`.
515
516 @param n The number of bytes to consume.
517 If `n` is greater than the size of the
518 buffer returned from @ref prepared the
519 entire output sequence is consumed and no
520 error is issued.
521
522 @see
523 @ref prepare,
524 @ref is_done,
525 @ref const_buffers_type.
526 */
527 BOOST_HTTP_PROTO_DECL
528 void
529 consume(std::size_t n);
530
531 /** Return true if serialization is complete.
532 */
533 BOOST_HTTP_PROTO_DECL
534 bool
535 is_done() const noexcept;
536
537 private:
538 class impl;
539 class cbs_gen;
540 template<class>
541 class cbs_gen_impl;
542
543 BOOST_HTTP_PROTO_DECL
544 detail::workspace&
545 ws();
546
547 BOOST_HTTP_PROTO_DECL
548 void
549 start_init(
550 message_base const&);
551
552 BOOST_HTTP_PROTO_DECL
553 void
554 start_buffers(
555 message_base const&,
556 cbs_gen&);
557
558 BOOST_HTTP_PROTO_DECL
559 void
560 start_source(
561 message_base const&,
562 source&);
563
564 impl* impl_ = nullptr;
565 };
566
567 /** Serializer configuration settings.
568
569 @see
570 @ref install_serializer_service,
571 @ref serializer.
572 */
573 struct serializer::config
574 {
575 /** Enable Brotli Content-Encoding.
576
577 Requires `boost::capy::brotli::encode_service` to be
578 installed, otherwise an exception is thrown.
579 */
580 bool apply_brotli_encoder = false;
581
582 /** Enable Deflate Content-Encoding.
583
584 Requires `boost::zlib::deflate_service` to be
585 installed, otherwise an exception is thrown.
586 */
587 bool apply_deflate_encoder = false;
588
589 /** Enable Gzip Content-Encoding.
590
591 Requires `boost::zlib::deflate_service` to be
592 installed, otherwise an exception is thrown.
593 */
594 bool apply_gzip_encoder = false;
595
596 /** Brotli compression quality (0–11).
597
598 Higher values yield better but slower compression.
599 */
600 std::uint32_t brotli_comp_quality = 5;
601
602 /** Brotli compression window size (10–24).
603
604 Larger windows improve compression but increase
605 memory usage.
606 */
607 std::uint32_t brotli_comp_window = 18;
608
609 /** Zlib compression level (0–9).
610
611 0 = no compression, 1 = fastest, 9 = best
612 compression.
613 */
614 int zlib_comp_level = 6;
615
616 /** Zlib window bits (9–15).
617
618 Controls the history buffer size. Larger values
619 improve compression but use more memory.
620 */
621 int zlib_window_bits = 15;
622
623 /** Zlib memory level (1–9).
624
625 Higher values use more memory, but offer faster
626 and more efficient compression.
627 */
628 int zlib_mem_level = 8;
629
630 /** Minimum buffer size for payloads (must be > 0). */
631 std::size_t payload_buffer = 8192;
632
633 /** Reserved space for type-erasure storage.
634
635 Used for:
636 @li User-defined @ref source objects.
637 @li User-defined ConstBufferSequence instances.
638 */
639 std::size_t max_type_erase = 1024;
640 };
641
642 /** Install the serializer service.
643
644 @par Example
645 @code
646 // default configuration settings
647 install_serializer_service(ctx, {});
648
649 serializer sr(ctx);
650 @endcode
651
652 @par Exception Safety
653 Strong guarantee.
654
655 @throw std::invalid_argument If the service is
656 already installed on the context.
657
658 @param ctx Reference to the context on which
659 the service should be installed.
660
661 @param cfg Configuration settings for the
662 serializer.
663
664 @see
665 @ref serializer::config,
666 @ref serializer.
667 */
668 BOOST_HTTP_PROTO_DECL
669 void
670 install_serializer_service(
671 capy::polystore& ctx,
672 serializer::config const& cfg);
673
674 //------------------------------------------------
675
676 /** Used for streaming body data during serialization.
677
678 Provides an interface for supplying serialized
679 body content from an external source. This
680 object is returned by @ref
681 serializer::start_stream and enables
682 incremental writing of the message body into
683 the serializer's internal buffer.
684
685 The stream supports an inverted control flow
686 model, where the caller pushes body data as
687 needed.
688
689 Valid operations depend on the state of the
690 serializer. Once the serializer is destroyed,
691 reset, or completes, the stream becomes
692 invalid and must only be destroyed.
693
694 @see
695 @ref serializer::start_stream
696 */
697 class serializer::stream
698 {
699 public:
700 /** The type used to represent a sequence
701 of mutable buffers.
702 */
703 using mutable_buffers_type =
704 buffers::mutable_buffer_pair;
705
706 /** Constructor.
707
708 A default-constructed stream is
709 considered closed.
710
711 @par Postconditions
712 @code
713 this->is_open() == false
714 @endcode
715 */
716 stream() noexcept = default;
717
718 /** Constructor.
719
720 After construction, the moved-from
721 object is as if default-constructed.
722
723 @par Postconditions
724 @code
725 other->is_open() == false
726 @endcode
727
728 @param other The object to move from.
729 */
730 stream(stream&& other) noexcept
731 : impl_(other.impl_)
732 {
733 other.impl_ = nullptr;
734 }
735
736 /** Move assignment.
737
738 After assignment, the moved-from
739 object is as if default-constructed.
740
741 @par Postconditions
742 @code
743 other->is_open() == false
744 @endcode
745
746 @param other The object to assign from.
747 @return A reference to this object.
748 */
749 stream&
750 operator=(stream&& other) noexcept
751 {
752 std::swap(impl_, other.impl_);
753 return *this;
754 }
755
756 /** Return true if the stream is open.
757 */
758 bool
759 5046 is_open() const noexcept
760 {
761 5046 return impl_ != nullptr;
762 }
763
764 /** Return the available capacity.
765
766 @par Preconditions
767 @code
768 this->is_open() == true
769 @endcode
770
771 @par Exception Safety
772 Strong guarantee.
773
774 @throw std::logic_error
775 `this->is_open() == false`.
776 */
777 BOOST_HTTP_PROTO_DECL
778 std::size_t
779 capacity() const;
780
781 /** Prepare a buffer for writing.
782
783 Retuns a mutable buffer sequence representing
784 the writable bytes. Use @ref commit to make the
785 written data available to the serializer.
786
787 All buffer sequences previously obtained
788 using @ref prepare are invalidated.
789
790 @par Preconditions
791 @code
792 this->is_open() == true && n <= this->capacity()
793 @endcode
794
795 @par Exception Safety
796 Strong guarantee.
797
798 @return An instance of @ref mutable_buffers_type
799 the underlying memory is owned by the serializer.
800
801 @throw std::logic_error
802 `this->is_open() == false`
803
804 @see
805 @ref commit,
806 @ref capacity.
807 */
808 BOOST_HTTP_PROTO_DECL
809 mutable_buffers_type
810 prepare();
811
812 /** Commit data to the serializer.
813
814 Makes `n` bytes available to the serializer.
815
816 All buffer sequences previously obtained
817 using @ref prepare are invalidated.
818
819 @par Preconditions
820 @code
821 this->is_open() == true && n <= this->capacity()
822 @endcode
823
824 @par Exception Safety
825 Strong guarantee.
826 Exceptions thrown on invalid input.
827
828 @param n The number of bytes to append.
829
830 @throw std::invalid_argument
831 `n > this->capacity()`
832
833 @throw std::logic_error
834 `this->is_open() == false`
835
836 @see
837 @ref prepare,
838 @ref capacity.
839 */
840 BOOST_HTTP_PROTO_DECL
841 void
842 commit(std::size_t n);
843
844 /** Close the stream if open.
845
846 Closes the stream and
847 notifies the serializer that the
848 message body has ended.
849
850 If the stream is already closed this
851 call has no effect.
852
853 @par Postconditions
854 @code
855 this->is_open() == false
856 @endcode
857 */
858 BOOST_HTTP_PROTO_DECL
859 void
860 close() noexcept;
861
862 /** Destructor.
863
864 Closes the stream if open.
865 */
866 24 ~stream()
867 {
868 24 close();
869 24 }
870
871 private:
872 friend class serializer;
873
874 explicit
875 23 stream(serializer::impl* impl) noexcept
876 23 : impl_(impl)
877 {
878 23 }
879
880 serializer::impl* impl_ = nullptr;
881 };
882
883 } // http_proto
884 } // boost
885
886 #include <boost/http_proto/impl/serializer.hpp>
887
888 #endif
889