GCC Code Coverage Report


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