GCC Code Coverage Report


Directory: ./
File: libs/http/include/boost/http/serializer.hpp
Date: 2026-01-15 20:43:58
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
9 //
10
11 #ifndef BOOST_HTTP_SERIALIZER_HPP
12 #define BOOST_HTTP_SERIALIZER_HPP
13
14 #include <boost/http/detail/config.hpp>
15 #include <boost/http/detail/workspace.hpp>
16 #include <boost/http/source.hpp>
17
18 #include <boost/capy/buffers/buffer_pair.hpp>
19 #include <boost/core/span.hpp>
20 #include <boost/capy/core/polystore_fwd.hpp>
21 #include <boost/system/result.hpp>
22
23 #include <type_traits>
24 #include <utility>
25
26 namespace boost {
27 namespace http {
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<capy::const_buffer const>;
68
69 /** Destructor
70 */
71 BOOST_HTTP_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_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_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_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_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_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 capy::const_buffer_sequence<ConstBufferSequence>
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 capy::const_buffer_sequence<ConstBufferSequence>>::type
279 >
280 void
281 start(
282 message_base const& m,
283 ConstBufferSequence&& buffers);
284
285 /** Start serializing a message with a @em Source body
286
287 Initializes the serializer with the HTTP start-line and headers from
288 `m`, and constructs a `Source` object to provide the message body.
289
290 Changing the contents of the message
291 after calling this function and before
292 @ref is_done returns `true` results in
293 undefined behavior.
294
295 The serializer destroys Source object when:
296 @li `this->is_done() == true`
297 @li An unrecoverable serialization error occurs
298 @li The serializer is destroyed
299
300 @par Example
301 @code
302 file f("example.zip", file_mode::scan);
303 response.set_payload_size(f.size());
304 serializer.start<file_source>(response, std::move(f));
305 @endcode
306
307 @par Preconditions
308 @code
309 this->is_done() == true
310 @endcode
311
312 @par Postconditions
313 @code
314 this->is_done() == false
315 @endcode
316
317 @par Constraints
318 @code
319 is_source<Source>::value == true
320 @endcode
321
322 @par Exception Safety
323 Strong guarantee.
324 Exceptions thrown if there is insufficient
325 internal buffer space to start the
326 operation.
327
328 @throw std::length_error if there is
329 insufficient internal buffer space to
330 start the operation.
331
332 @param m The message to read the HTTP
333 start-line and headers from.
334
335 @param args Arguments to be passed to the
336 `Source` constructor.
337
338 @return A reference to the constructed Source object.
339
340 @see
341 @ref source,
342 @ref file_source,
343 @ref message_base.
344 */
345 template<
346 class Source,
347 class... Args,
348 class = typename std::enable_if<
349 is_source<Source>::value>::type>
350 Source&
351 start(
352 message_base const& m,
353 Args&&... args);
354
355 /** Prepare the serializer for a new message using a stream interface.
356
357 Initializes the serializer with the HTTP
358 start-line and headers from `m`, and returns
359 a @ref stream object for writing the body
360 data into the serializer's internal buffer.
361
362 Once the serializer is destroyed, @ref reset
363 is called, or @ref is_done returns true, the
364 only valid operation on the stream is destruction.
365
366 The stream allows inverted control flow: the
367 caller supplies body data to the serializer’s
368 internal buffer while reading from an external
369 source.
370
371 Changing the contents of the message
372 after calling this function and before
373 @ref is_done returns `true` results in
374 undefined behavior.
375
376 @par Example
377 @code
378 serializer::stream st = serializer.start_stream(response);
379 do
380 {
381 if(st.is_open())
382 {
383 std::size_t n = source.read_some(st.prepare());
384
385 if(ec == error::eof)
386 st.close();
387 else
388 st.commit(n);
389 }
390
391 write_some(client, serializer);
392
393 } while(!serializer.is_done());
394 @endcode
395
396 @par Preconditions
397 @code
398 this->is_done() == true
399 @endcode
400
401 @par Postconditions
402 @code
403 this->is_done() == false
404 @endcode
405
406 @par Exception Safety
407 Strong guarantee.
408 Exceptions thrown if there is insufficient
409 internal buffer space to start the
410 operation.
411
412 @throw std::length_error if there is
413 insufficient internal buffer space to
414 start the operation.
415
416 @param m The message to read the HTTP
417 start-line and headers from.
418
419 @return A @ref stream object for writing the body
420 data into the serializer's internal buffer.
421
422 @see
423 @ref stream,
424 @ref message_base.
425 */
426 BOOST_HTTP_DECL
427 stream
428 start_stream(
429 message_base const& m);
430
431 /** Return the output area.
432
433 This function serializes some or all of
434 the message and returns the corresponding
435 output buffers. Afterward, a call to @ref
436 consume is required to report the number
437 of bytes used, if any.
438
439 If the message includes an
440 `Expect: 100-continue` header and the
441 header section of the message has been
442 consumed, the returned result will contain
443 @ref error::expect_100_continue to
444 indicate that the header part of the
445 message is complete. The next call to @ref
446 prepare will produce output.
447
448 When the serializer is used through the
449 @ref stream interface, the result may
450 contain @ref error::need_data to indicate
451 that additional input is required to
452 produce output.
453
454 If a @ref source object is in use and a
455 call to @ref source::read returns an
456 error, the serializer enters a faulted
457 state and propagates the error to the
458 caller. This faulted state can only be
459 cleared by calling @ref reset. This
460 ensures the caller is explicitly aware
461 that the previous message was truncated
462 and that the stream must be terminated.
463
464 @par Preconditions
465 @code
466 this->is_done() == false
467 @endcode
468 No unrecoverable error reported from previous calls.
469
470 @par Exception Safety
471 Strong guarantee.
472 Calls to @ref source::read may throw if in use.
473
474 @throw std::logic_error
475 `this->is_done() == true`.
476
477 @return A result containing @ref
478 const_buffers_type that represents the
479 output area or an error if any occurred.
480
481 @see
482 @ref consume,
483 @ref is_done,
484 @ref const_buffers_type.
485 */
486 BOOST_HTTP_DECL
487 auto
488 prepare() ->
489 system::result<
490 const_buffers_type>;
491
492 /** Consume bytes from the output area.
493
494 This function should be called after one
495 or more bytes contained in the buffers
496 provided in the prior call to @ref prepare
497 have been used.
498
499 After a call to @ref consume, callers
500 should check the return value of @ref
501 is_done to determine if the entire message
502 has been serialized.
503
504 @par Preconditions
505 @code
506 this->is_done() == false
507 @endcode
508
509 @par Exception Safety
510 Strong guarantee.
511
512 @throw std::logic_error
513 `this->is_done() == true`.
514
515 @param n The number of bytes to consume.
516 If `n` is greater than the size of the
517 buffer returned from @ref prepared the
518 entire output sequence is consumed and no
519 error is issued.
520
521 @see
522 @ref prepare,
523 @ref is_done,
524 @ref const_buffers_type.
525 */
526 BOOST_HTTP_DECL
527 void
528 consume(std::size_t n);
529
530 /** Return true if serialization is complete.
531 */
532 BOOST_HTTP_DECL
533 bool
534 is_done() const noexcept;
535
536 private:
537 class impl;
538 class cbs_gen;
539 template<class>
540 class cbs_gen_impl;
541
542 BOOST_HTTP_DECL
543 detail::workspace&
544 ws();
545
546 BOOST_HTTP_DECL
547 void
548 start_init(
549 message_base const&);
550
551 BOOST_HTTP_DECL
552 void
553 start_buffers(
554 message_base const&,
555 cbs_gen&);
556
557 BOOST_HTTP_DECL
558 void
559 start_source(
560 message_base const&,
561 source&);
562
563 impl* impl_ = nullptr;
564 };
565
566 /** Serializer configuration settings.
567
568 @see
569 @ref install_serializer_service,
570 @ref serializer.
571 */
572 struct serializer::config
573 {
574 /** Enable Brotli Content-Encoding.
575
576 Requires `boost::capy::brotli::encode_service` to be
577 installed, otherwise an exception is thrown.
578 */
579 bool apply_brotli_encoder = false;
580
581 /** Enable Deflate Content-Encoding.
582
583 Requires `boost::zlib::deflate_service` to be
584 installed, otherwise an exception is thrown.
585 */
586 bool apply_deflate_encoder = false;
587
588 /** Enable Gzip Content-Encoding.
589
590 Requires `boost::zlib::deflate_service` to be
591 installed, otherwise an exception is thrown.
592 */
593 bool apply_gzip_encoder = false;
594
595 /** Brotli compression quality (0–11).
596
597 Higher values yield better but slower compression.
598 */
599 std::uint32_t brotli_comp_quality = 5;
600
601 /** Brotli compression window size (10–24).
602
603 Larger windows improve compression but increase
604 memory usage.
605 */
606 std::uint32_t brotli_comp_window = 18;
607
608 /** Zlib compression level (0–9).
609
610 0 = no compression, 1 = fastest, 9 = best
611 compression.
612 */
613 int zlib_comp_level = 6;
614
615 /** Zlib window bits (9–15).
616
617 Controls the history buffer size. Larger values
618 improve compression but use more memory.
619 */
620 int zlib_window_bits = 15;
621
622 /** Zlib memory level (1–9).
623
624 Higher values use more memory, but offer faster
625 and more efficient compression.
626 */
627 int zlib_mem_level = 8;
628
629 /** Minimum buffer size for payloads (must be > 0). */
630 std::size_t payload_buffer = 8192;
631
632 /** Reserved space for type-erasure storage.
633
634 Used for:
635 @li User-defined @ref source objects.
636 @li User-defined ConstBufferSequence instances.
637 */
638 std::size_t max_type_erase = 1024;
639 };
640
641 /** Install the serializer service.
642
643 @par Example
644 @code
645 // default configuration settings
646 install_serializer_service(ctx, {});
647
648 serializer sr(ctx);
649 @endcode
650
651 @par Exception Safety
652 Strong guarantee.
653
654 @throw std::invalid_argument If the service is
655 already installed on the context.
656
657 @param ctx Reference to the context on which
658 the service should be installed.
659
660 @param cfg Configuration settings for the
661 serializer.
662
663 @see
664 @ref serializer::config,
665 @ref serializer.
666 */
667 BOOST_HTTP_DECL
668 void
669 install_serializer_service(
670 capy::polystore& ctx,
671 serializer::config const& cfg);
672
673 //------------------------------------------------
674
675 /** Used for streaming body data during serialization.
676
677 Provides an interface for supplying serialized
678 body content from an external source. This
679 object is returned by @ref
680 serializer::start_stream and enables
681 incremental writing of the message body into
682 the serializer's internal buffer.
683
684 The stream supports an inverted control flow
685 model, where the caller pushes body data as
686 needed.
687
688 Valid operations depend on the state of the
689 serializer. Once the serializer is destroyed,
690 reset, or completes, the stream becomes
691 invalid and must only be destroyed.
692
693 @see
694 @ref serializer::start_stream
695 */
696 class serializer::stream
697 {
698 public:
699 /** The type used to represent a sequence
700 of mutable buffers.
701 */
702 using mutable_buffers_type =
703 capy::mutable_buffer_pair;
704
705 /** Constructor.
706
707 A default-constructed stream is
708 considered closed.
709
710 @par Postconditions
711 @code
712 this->is_open() == false
713 @endcode
714 */
715 stream() noexcept = default;
716
717 /** Constructor.
718
719 After construction, the moved-from
720 object is as if default-constructed.
721
722 @par Postconditions
723 @code
724 other->is_open() == false
725 @endcode
726
727 @param other The object to move from.
728 */
729 stream(stream&& other) noexcept
730 : impl_(other.impl_)
731 {
732 other.impl_ = nullptr;
733 }
734
735 /** Move assignment.
736
737 After assignment, the moved-from
738 object is as if default-constructed.
739
740 @par Postconditions
741 @code
742 other->is_open() == false
743 @endcode
744
745 @param other The object to assign from.
746 @return A reference to this object.
747 */
748 stream&
749 operator=(stream&& other) noexcept
750 {
751 std::swap(impl_, other.impl_);
752 return *this;
753 }
754
755 /** Return true if the stream is open.
756 */
757 bool
758 5030 is_open() const noexcept
759 {
760 5030 return impl_ != nullptr;
761 }
762
763 /** Return the available capacity.
764
765 @par Preconditions
766 @code
767 this->is_open() == true
768 @endcode
769
770 @par Exception Safety
771 Strong guarantee.
772
773 @throw std::logic_error
774 `this->is_open() == false`.
775 */
776 BOOST_HTTP_DECL
777 std::size_t
778 capacity() const;
779
780 /** Prepare a buffer for writing.
781
782 Retuns a mutable buffer sequence representing
783 the writable bytes. Use @ref commit to make the
784 written data available to the serializer.
785
786 All buffer sequences previously obtained
787 using @ref prepare are invalidated.
788
789 @par Preconditions
790 @code
791 this->is_open() == true && n <= this->capacity()
792 @endcode
793
794 @par Exception Safety
795 Strong guarantee.
796
797 @return An instance of @ref mutable_buffers_type
798 the underlying memory is owned by the serializer.
799
800 @throw std::logic_error
801 `this->is_open() == false`
802
803 @see
804 @ref commit,
805 @ref capacity.
806 */
807 BOOST_HTTP_DECL
808 mutable_buffers_type
809 prepare();
810
811 /** Commit data to the serializer.
812
813 Makes `n` bytes available to the serializer.
814
815 All buffer sequences previously obtained
816 using @ref prepare are invalidated.
817
818 @par Preconditions
819 @code
820 this->is_open() == true && n <= this->capacity()
821 @endcode
822
823 @par Exception Safety
824 Strong guarantee.
825 Exceptions thrown on invalid input.
826
827 @param n The number of bytes to append.
828
829 @throw std::invalid_argument
830 `n > this->capacity()`
831
832 @throw std::logic_error
833 `this->is_open() == false`
834
835 @see
836 @ref prepare,
837 @ref capacity.
838 */
839 BOOST_HTTP_DECL
840 void
841 commit(std::size_t n);
842
843 /** Close the stream if open.
844
845 Closes the stream and
846 notifies the serializer that the
847 message body has ended.
848
849 If the stream is already closed this
850 call has no effect.
851
852 @par Postconditions
853 @code
854 this->is_open() == false
855 @endcode
856 */
857 BOOST_HTTP_DECL
858 void
859 close() noexcept;
860
861 /** Destructor.
862
863 Closes the stream if open.
864 */
865 24 ~stream()
866 {
867 24 close();
868 24 }
869
870 private:
871 friend class serializer;
872
873 explicit
874 23 stream(serializer::impl* impl) noexcept
875 23 : impl_(impl)
876 {
877 23 }
878
879 serializer::impl* impl_ = nullptr;
880 };
881
882 } // http
883 } // boost
884
885 #include <boost/http/impl/serializer.hpp>
886
887 #endif
888