GCC Code Coverage Report


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