LCOV - code coverage report
Current view: top level - boost/http - serializer.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 21 21
Test Date: 2026-02-02 17:02:49 Functions: 100.0 % 7 7

            Line data    Source code
       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/config.hpp>
      15              : #include <boost/http/detail/workspace.hpp>
      16              : #include <boost/http/error.hpp>
      17              : 
      18              : #include <boost/capy/buffers.hpp>
      19              : #include <boost/capy/buffers/buffer_pair.hpp>
      20              : #include <boost/capy/concept/buffer_sink.hpp>
      21              : #include <boost/capy/concept/write_stream.hpp>
      22              : #include <boost/capy/io_task.hpp>
      23              : #include <boost/core/span.hpp>
      24              : #include <boost/system/result.hpp>
      25              : 
      26              : #include <cstddef>
      27              : #include <type_traits>
      28              : #include <utility>
      29              : 
      30              : namespace boost {
      31              : namespace http {
      32              : 
      33              : // Forward declaration
      34              : class message_base;
      35              : 
      36              : //------------------------------------------------
      37              : 
      38              : /** A serializer for HTTP/1 messages
      39              : 
      40              :     This is used to serialize one or more complete
      41              :     HTTP/1 messages. Each message consists of a
      42              :     required header followed by an optional body.
      43              : 
      44              :     Objects of this type operate using an "input area" and an
      45              :     "output area". Callers provide data to the input area
      46              :     using one of the @ref start or @ref start_stream member
      47              :     functions. After input is provided, serialized data
      48              :     becomes available in the serializer's output area in the
      49              :     form of a constant buffer sequence.
      50              : 
      51              :     Callers alternate between filling the input area and
      52              :     consuming the output area until all the input has been
      53              :     provided and all the output data has been consumed, or
      54              :     an error occurs.
      55              : 
      56              :     After calling @ref start, the caller must ensure that the
      57              :     contents of the associated message are not changed or
      58              :     destroyed until @ref is_done returns true, @ref reset is
      59              :     called, or the serializer is destroyed, otherwise the
      60              :     behavior is undefined.
      61              : */
      62              : class serializer
      63              : {
      64              : public:
      65              :     template<capy::WriteStream Stream>
      66              :     class sink;
      67              : 
      68              :     /** The type used to represent a sequence
      69              :         of mutable buffers for streaming.
      70              :     */
      71              :     using mutable_buffers_type =
      72              :         capy::mutable_buffer_pair;
      73              : 
      74              :     /** The type used to represent a sequence of
      75              :         constant buffers that refers to the output
      76              :         area.
      77              :     */
      78              :     using const_buffers_type =
      79              :         boost::span<capy::const_buffer const>;
      80              : 
      81              :     /** Destructor
      82              :     */
      83              :     BOOST_HTTP_DECL
      84              :     ~serializer();
      85              : 
      86              :     /** Default constructor.
      87              : 
      88              :         Constructs a serializer with no allocated state.
      89              :         The serializer must be assigned from a valid
      90              :         serializer before use.
      91              : 
      92              :         @par Postconditions
      93              :         The serializer has no allocated state.
      94              :     */
      95            2 :     serializer() = default;
      96              : 
      97              :     /** Constructor.
      98              : 
      99              :         Constructs a serializer with the provided configuration.
     100              : 
     101              :         @par Postconditions
     102              :         @code
     103              :         this->is_done() == true
     104              :         @endcode
     105              : 
     106              :         @param cfg Shared pointer to serializer configuration.
     107              : 
     108              :         @see @ref make_serializer_config, @ref serializer_config.
     109              :     */
     110              :     BOOST_HTTP_DECL
     111              :     explicit
     112              :     serializer(
     113              :         std::shared_ptr<serializer_config_impl const> cfg);
     114              : 
     115              :     /** Constructor with associated message.
     116              : 
     117              :         Constructs a serializer with the provided configuration
     118              :         and associates a message for the serializer's lifetime.
     119              :         The message must remain valid until the serializer is
     120              :         destroyed.
     121              : 
     122              :         @par Postconditions
     123              :         @code
     124              :         this->is_done() == true
     125              :         @endcode
     126              : 
     127              :         @param cfg Shared pointer to serializer configuration.
     128              : 
     129              :         @param m The message to associate.
     130              : 
     131              :         @see @ref make_serializer_config, @ref serializer_config.
     132              :     */
     133              :     BOOST_HTTP_DECL
     134              :     serializer(
     135              :         std::shared_ptr<serializer_config_impl const> cfg,
     136              :         message_base const& m);
     137              : 
     138              :     /** Constructor.
     139              : 
     140              :         The states of `other` are transferred
     141              :         to the newly constructed object,
     142              :         which includes the allocated buffer.
     143              :         After construction, the only valid
     144              :         operations on the moved-from object
     145              :         are destruction and assignment.
     146              : 
     147              :         Buffer sequences previously obtained
     148              :         using @ref prepare remain valid.
     149              : 
     150              :         @par Postconditions
     151              :         @code
     152              :         other.is_done() == true
     153              :         @endcode
     154              : 
     155              :         @par Complexity
     156              :         Constant.
     157              : 
     158              :         @param other The serializer to move from.
     159              :     */
     160              :     BOOST_HTTP_DECL
     161              :     serializer(
     162              :         serializer&& other) noexcept;
     163              : 
     164              :     /** Assignment.
     165              :         The states of `other` are transferred
     166              :         to this object, which includes the
     167              :         allocated buffer. After assignment,
     168              :         the only valid operations on the
     169              :         moved-from object are destruction and
     170              :         assignment.
     171              :         Buffer sequences previously obtained
     172              :         using @ref prepare remain valid.
     173              :         @par Complexity
     174              :         Constant.
     175              :         @param other The serializer to move from.
     176              :         @return A reference to this object.
     177              :     */
     178              :     BOOST_HTTP_DECL
     179              :     serializer&
     180              :     operator=(serializer&& other) noexcept;
     181              : 
     182              :     /** Reset the serializer for a new message.
     183              : 
     184              :         Aborts any ongoing serialization and
     185              :         prepares the serializer to start
     186              :         serialization of a new message.
     187              :     */
     188              :     BOOST_HTTP_DECL
     189              :     void
     190              :     reset() noexcept;
     191              : 
     192              :     /** Set the message to serialize.
     193              : 
     194              :         Associates a message with the serializer for subsequent
     195              :         streaming operations. The message is not copied; the caller
     196              :         must ensure it remains valid until serialization completes.
     197              : 
     198              :         @param m The message to associate.
     199              :     */
     200              :     BOOST_HTTP_DECL
     201              :     void
     202              :     set_message(message_base const& m) noexcept;
     203              : 
     204              :     /** Start serializing a message with an empty body
     205              : 
     206              :         This function prepares the serializer to create a message which
     207              :         has an empty body.
     208              :         Ownership of the specified message is not transferred; the caller is
     209              :         responsible for ensuring the lifetime of the object extends until the
     210              :         serializer is done.
     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 Exception Safety
     223              :         Strong guarantee.
     224              :         Exceptions thrown if there is insufficient internal buffer space
     225              :         to start the operation.
     226              : 
     227              :         @throw std::logic_error `this->is_done() == true`.
     228              : 
     229              :         @throw std::length_error if there is insufficient internal buffer
     230              :         space to start the operation.
     231              : 
     232              :         @param m The request or response headers to serialize.
     233              : 
     234              :         @see
     235              :             @ref message_base.
     236              :     */
     237              :     void
     238              :     BOOST_HTTP_DECL
     239              :     start(message_base const& m);
     240              : 
     241              :     /** Start serializing the associated message with an empty body.
     242              : 
     243              :         Uses the message associated at construction time.
     244              : 
     245              :         @par Preconditions
     246              :         A message was associated at construction.
     247              :         @code
     248              :         this->is_done() == true
     249              :         @endcode
     250              : 
     251              :         @par Postconditions
     252              :         @code
     253              :         this->is_done() == false
     254              :         @endcode
     255              : 
     256              :         @par Exception Safety
     257              :         Strong guarantee.
     258              : 
     259              :         @throw std::logic_error if no message is associated or
     260              :         `this->is_done() == false`.
     261              : 
     262              :         @throw std::length_error if there is insufficient internal buffer
     263              :         space to start the operation.
     264              :     */
     265              :     void
     266              :     BOOST_HTTP_DECL
     267              :     start();
     268              : 
     269              :     /** Start serializing a message with a buffer sequence body
     270              : 
     271              :         Initializes the serializer with the HTTP start-line and headers from `m`,
     272              :         and the provided `buffers` for reading the message body from.
     273              : 
     274              :         Changing the contents of the message after calling this function and
     275              :         before @ref is_done returns `true` results in undefined behavior.
     276              : 
     277              :         At least one copy of the specified buffer sequence is maintained until
     278              :         the serializer is done, gets reset, or ios destroyed, after which all
     279              :         of its copies are destroyed. Ownership of the underlying memory is not
     280              :         transferred; the caller must ensure the memory remains valid until the
     281              :         serializer’s copies are destroyed.
     282              : 
     283              :         @par Preconditions
     284              :         @code
     285              :         this->is_done() == true
     286              :         @endcode
     287              : 
     288              :         @par Postconditions
     289              :         @code
     290              :         this->is_done() == false
     291              :         @endcode
     292              : 
     293              :         @par Constraints
     294              :         @code
     295              :         capy::ConstBufferSequence<ConstBufferSequence>
     296              :         @endcode
     297              : 
     298              :         @par Exception Safety
     299              :         Strong guarantee.
     300              :         Exceptions thrown if there is insufficient internal buffer space
     301              :         to start the operation.
     302              : 
     303              :         @throw std::logic_error `this->is_done() == true`.
     304              : 
     305              :         @throw std::length_error If there is insufficient internal buffer
     306              :         space to start the operation.
     307              : 
     308              :         @param m The message to read the HTTP start-line and headers from.
     309              : 
     310              :         @param buffers A buffer sequence containing the message body.
     311              : 
     312              :         containing the message body data. While
     313              :         the buffers object is copied, ownership of
     314              :         the underlying memory remains with the
     315              :         caller, who must ensure it stays valid
     316              :         until @ref is_done returns `true`.
     317              : 
     318              :         @see
     319              :             @ref message_base.
     320              :     */
     321              :     template<
     322              :         class ConstBufferSequence,
     323              :         class = typename std::enable_if<
     324              :             capy::ConstBufferSequence<ConstBufferSequence>>::type
     325              :     >
     326              :     void
     327              :     start(
     328              :         message_base const& m,
     329              :         ConstBufferSequence&& buffers);
     330              : 
     331              :     /** Prepare the serializer for streaming body data.
     332              : 
     333              :         Initializes the serializer with the HTTP
     334              :         start-line and headers from `m` for streaming
     335              :         mode. After calling this function, use
     336              :         @ref stream_prepare, @ref stream_commit, and
     337              :         @ref stream_close to write body data.
     338              : 
     339              :         Changing the contents of the message
     340              :         after calling this function and before
     341              :         @ref is_done returns `true` results in
     342              :         undefined behavior.
     343              : 
     344              :         @par Example
     345              :         @code
     346              :         auto sink = sr.sink_for(socket);
     347              :         sr.start_stream(response);
     348              : 
     349              :         capy::mutable_buffer arr[16];
     350              :         auto bufs = sink.prepare(arr);
     351              :         std::memcpy(bufs[0].data(), "Hello", 5);
     352              :         co_await sink.commit(5, true);
     353              :         @endcode
     354              : 
     355              :         @par Preconditions
     356              :         @code
     357              :         this->is_done() == true
     358              :         @endcode
     359              : 
     360              :         @par Postconditions
     361              :         @code
     362              :         this->is_done() == false
     363              :         @endcode
     364              : 
     365              :         @par Exception Safety
     366              :         Strong guarantee.
     367              :         Exceptions thrown if there is insufficient
     368              :         internal buffer space to start the
     369              :         operation.
     370              : 
     371              :         @throw std::length_error if there is
     372              :         insufficient internal buffer space to
     373              :         start the operation.
     374              : 
     375              :         @param m The message to read the HTTP
     376              :         start-line and headers from.
     377              : 
     378              :         @see
     379              :             @ref stream_prepare,
     380              :             @ref stream_commit,
     381              :             @ref stream_close,
     382              :             @ref message_base.
     383              :      */
     384              :     BOOST_HTTP_DECL
     385              :     void
     386              :     start_stream(
     387              :         message_base const& m);
     388              : 
     389              :     /** Start streaming the associated message.
     390              : 
     391              :         Uses the message associated at construction time.
     392              : 
     393              :         @par Preconditions
     394              :         A message was associated at construction.
     395              :         @code
     396              :         this->is_done() == true
     397              :         @endcode
     398              : 
     399              :         @par Postconditions
     400              :         @code
     401              :         this->is_done() == false
     402              :         @endcode
     403              : 
     404              :         @par Exception Safety
     405              :         Strong guarantee.
     406              : 
     407              :         @throw std::logic_error if no message is associated or
     408              :         `this->is_done() == false`.
     409              : 
     410              :         @throw std::length_error if there is insufficient internal buffer
     411              :         space to start the operation.
     412              : 
     413              :         @see
     414              :             @ref stream_prepare,
     415              :             @ref stream_commit,
     416              :             @ref stream_close.
     417              :     */
     418              :     BOOST_HTTP_DECL
     419              :     void
     420              :     start_stream();
     421              : 
     422              :     /** Get a sink wrapper for writing body data.
     423              : 
     424              :         Returns a @ref sink object that can be used to write body
     425              :         data to the provided stream. This function does not call
     426              :         @ref start_stream. The caller must call @ref start_stream
     427              :         before using the sink.
     428              : 
     429              :         This allows the sink to be obtained early (e.g., at
     430              :         construction time) and stored, with streaming started
     431              :         later when the message is ready.
     432              : 
     433              :         @par Example
     434              :         @code
     435              :         http::serializer sr(cfg, res);
     436              :         auto sink = sr.sink_for(socket);
     437              :         // ... later ...
     438              :         sr.start_stream();  // Configure for streaming
     439              : 
     440              :         capy::mutable_buffer arr[16];
     441              :         auto bufs = sink.prepare(arr);
     442              :         std::memcpy(bufs[0].data(), "Hello", 5);
     443              :         co_await sink.commit(5, true);
     444              :         @endcode
     445              : 
     446              :         @tparam Stream The output stream type satisfying
     447              :             @ref capy::WriteStream.
     448              : 
     449              :         @param ws The output stream to write serialized data to.
     450              : 
     451              :         @return A @ref sink object for writing body data.
     452              : 
     453              :         @see @ref sink, @ref start_stream.
     454              :     */
     455              :     template<capy::WriteStream Stream>
     456              :     sink<Stream>
     457              :     sink_for(Stream& ws) noexcept;
     458              : 
     459              :     /** Return the output area.
     460              : 
     461              :         This function serializes some or all of
     462              :         the message and returns the corresponding
     463              :         output buffers. Afterward, a call to @ref
     464              :         consume is required to report the number
     465              :         of bytes used, if any.
     466              : 
     467              :         If the message includes an
     468              :         `Expect: 100-continue` header and the
     469              :         header section of the message has been
     470              :         consumed, the returned result will contain
     471              :         @ref error::expect_100_continue to
     472              :         indicate that the header part of the
     473              :         message is complete. The next call to @ref
     474              :         prepare will produce output.
     475              : 
     476              :         When the serializer is in streaming mode,
     477              :         the result may contain @ref error::need_data
     478              :         to indicate that additional input is required
     479              :         to produce output.
     480              : 
     481              :         If a @ref source object is in use and a
     482              :         call to @ref source::read returns an
     483              :         error, the serializer enters a faulted
     484              :         state and propagates the error to the
     485              :         caller. This faulted state can only be
     486              :         cleared by calling @ref reset. This
     487              :         ensures the caller is explicitly aware
     488              :         that the previous message was truncated
     489              :         and that the stream must be terminated.
     490              : 
     491              :         @par Preconditions
     492              :         @code
     493              :         this->is_done() == false
     494              :         @endcode
     495              :         No unrecoverable error reported from previous calls.
     496              : 
     497              :         @par Exception Safety
     498              :         Strong guarantee.
     499              :         Calls to @ref source::read may throw if in use.
     500              : 
     501              :         @throw std::logic_error
     502              :         `this->is_done() == true`.
     503              : 
     504              :         @return A result containing @ref
     505              :         const_buffers_type that represents the
     506              :         output area or an error if any occurred.
     507              : 
     508              :         @see
     509              :             @ref consume,
     510              :             @ref is_done,
     511              :             @ref const_buffers_type.
     512              :     */
     513              :     BOOST_HTTP_DECL
     514              :     auto
     515              :     prepare() ->
     516              :         system::result<
     517              :             const_buffers_type>;
     518              : 
     519              :     /** Consume bytes from the output area.
     520              : 
     521              :         This function should be called after one
     522              :         or more bytes contained in the buffers
     523              :         provided in the prior call to @ref prepare
     524              :         have been used.
     525              : 
     526              :         After a call to @ref consume, callers
     527              :         should check the return value of @ref
     528              :         is_done to determine if the entire message
     529              :         has been serialized.
     530              : 
     531              :         @par Preconditions
     532              :         @code
     533              :         this->is_done() == false
     534              :         @endcode
     535              : 
     536              :         @par Exception Safety
     537              :         Strong guarantee.
     538              : 
     539              :         @throw std::logic_error
     540              :         `this->is_done() == true`.
     541              : 
     542              :         @param n The number of bytes to consume.
     543              :         If `n` is greater than the size of the
     544              :         buffer returned from @ref prepared the
     545              :         entire output sequence is consumed and no
     546              :         error is issued.
     547              : 
     548              :         @see
     549              :             @ref prepare,
     550              :             @ref is_done,
     551              :             @ref const_buffers_type.
     552              :     */
     553              :     BOOST_HTTP_DECL
     554              :     void
     555              :     consume(std::size_t n);
     556              : 
     557              :     /** Return true if serialization is complete.
     558              :     */
     559              :     BOOST_HTTP_DECL
     560              :     bool
     561              :     is_done() const noexcept;
     562              : 
     563              :     /** Return the available capacity for streaming.
     564              : 
     565              :         Returns the number of bytes that can be written
     566              :         to the serializer's internal buffer.
     567              : 
     568              :         @par Preconditions
     569              :         The serializer is in streaming mode (after calling
     570              :         @ref start_stream).
     571              : 
     572              :         @par Exception Safety
     573              :         Strong guarantee.
     574              : 
     575              :         @throw std::logic_error if not in streaming mode.
     576              :     */
     577              :     BOOST_HTTP_DECL
     578              :     std::size_t
     579              :     stream_capacity() const;
     580              : 
     581              :     /** Prepare a buffer for writing stream data.
     582              : 
     583              :         Returns a mutable buffer sequence representing
     584              :         the writable bytes. Use @ref stream_commit to make the
     585              :         written data available to the serializer.
     586              : 
     587              :         All buffer sequences previously obtained
     588              :         using @ref stream_prepare are invalidated.
     589              : 
     590              :         @par Preconditions
     591              :         The serializer is in streaming mode.
     592              : 
     593              :         @par Exception Safety
     594              :         Strong guarantee.
     595              : 
     596              :         @return An instance of @ref mutable_buffers_type.
     597              :             The underlying memory is owned by the serializer.
     598              : 
     599              :         @throw std::logic_error if not in streaming mode.
     600              : 
     601              :         @see
     602              :             @ref stream_commit,
     603              :             @ref stream_capacity.
     604              :     */
     605              :     BOOST_HTTP_DECL
     606              :     mutable_buffers_type
     607              :     stream_prepare();
     608              : 
     609              :     /** Commit data to the serializer stream.
     610              : 
     611              :         Makes `n` bytes available to the serializer.
     612              : 
     613              :         All buffer sequences previously obtained
     614              :         using @ref stream_prepare are invalidated.
     615              : 
     616              :         @par Preconditions
     617              :         The serializer is in streaming mode and
     618              :         `n <= stream_capacity()`.
     619              : 
     620              :         @par Exception Safety
     621              :         Strong guarantee.
     622              :         Exceptions thrown on invalid input.
     623              : 
     624              :         @param n The number of bytes to commit.
     625              : 
     626              :         @throw std::invalid_argument if `n > stream_capacity()`.
     627              : 
     628              :         @throw std::logic_error if not in streaming mode.
     629              : 
     630              :         @see
     631              :             @ref stream_prepare,
     632              :             @ref stream_capacity.
     633              :     */
     634              :     BOOST_HTTP_DECL
     635              :     void
     636              :     stream_commit(std::size_t n);
     637              : 
     638              :     /** Close the stream.
     639              : 
     640              :         Notifies the serializer that the message body
     641              :         has ended. After calling this function, no more
     642              :         data can be written to the stream.
     643              : 
     644              :         @par Preconditions
     645              :         The serializer is in streaming mode.
     646              : 
     647              :         @par Postconditions
     648              :         The stream is closed.
     649              :     */
     650              :     BOOST_HTTP_DECL
     651              :     void
     652              :     stream_close() noexcept;
     653              : 
     654              : private:
     655              :     class impl;
     656              :     class cbs_gen;
     657              :     template<class>
     658              :     class cbs_gen_impl;
     659              : 
     660              :     BOOST_HTTP_DECL
     661              :     detail::workspace&
     662              :     ws();
     663              : 
     664              :     BOOST_HTTP_DECL
     665              :     void
     666              :     start_init(
     667              :         message_base const&);
     668              : 
     669              :     BOOST_HTTP_DECL
     670              :     void
     671              :     start_buffers(
     672              :         message_base const&,
     673              :         cbs_gen&);
     674              : 
     675              :     impl* impl_ = nullptr;
     676              : };
     677              : 
     678              : //------------------------------------------------
     679              : 
     680              : /** A sink adapter for writing HTTP message bodies.
     681              : 
     682              :     This class wraps an underlying @ref capy::WriteStream and a
     683              :     @ref serializer to provide a @ref capy::BufferSink interface
     684              :     for writing message body data. The caller writes directly into
     685              :     the serializer's internal buffer (zero-copy); the serializer
     686              :     automatically handles:
     687              : 
     688              :     @li Chunked transfer-encoding (chunk framing added automatically)
     689              :     @li Content-Encoding compression (gzip, deflate, brotli if configured)
     690              :     @li Content-Length validation (if specified in headers)
     691              : 
     692              :     For @ref capy::WriteSink semantics (caller owns buffers), wrap
     693              :     this sink with @ref capy::any_buffer_sink which provides both
     694              :     interfaces.
     695              : 
     696              :     @tparam Stream The underlying stream type satisfying
     697              :         @ref capy::WriteStream.
     698              : 
     699              :     @par Thread Safety
     700              :     Distinct objects: Safe.
     701              :     Shared objects: Unsafe.
     702              : 
     703              :     @par Example
     704              :     @code
     705              :     capy::task<void>
     706              :     send_response(capy::WriteStream auto& socket)
     707              :     {
     708              :         http::response res;
     709              :         res.set_chunked(true);
     710              :         http::serializer sr(cfg, res);
     711              : 
     712              :         auto sink = sr.sink_for(socket);
     713              :         sr.start_stream();
     714              : 
     715              :         // Zero-copy write using BufferSink interface
     716              :         capy::mutable_buffer arr[16];
     717              :         auto bufs = sink.prepare(arr);
     718              :         std::memcpy(bufs[0].data(), "Hello", 5);
     719              :         co_await sink.commit(5, true);
     720              :     }
     721              :     @endcode
     722              : 
     723              :     @see capy::BufferSink, capy::any_buffer_sink, serializer
     724              : */
     725              : template<capy::WriteStream Stream>
     726              : class serializer::sink
     727              : {
     728              :     Stream* stream_ = nullptr;
     729              :     serializer* sr_ = nullptr;
     730              : 
     731              : public:
     732              :     /** Constructor.
     733              : 
     734              :         A default-constructed sink is in an empty state.
     735              :     */
     736              :     sink() noexcept = default;
     737              : 
     738              :     /** Constructor.
     739              : 
     740              :         @param stream The underlying stream to write serialized data to.
     741              :         @param sr The serializer performing HTTP framing.
     742              :     */
     743           68 :     sink(
     744              :         Stream& stream,
     745              :         serializer& sr) noexcept
     746           68 :         : stream_(&stream)
     747           68 :         , sr_(&sr)
     748              :     {
     749           68 :     }
     750              : 
     751              :     /** Prepare writable buffers.
     752              : 
     753              :         Fills the provided span with mutable buffer descriptors
     754              :         pointing to the serializer's internal storage. This
     755              :         operation is synchronous.
     756              : 
     757              :         @param dest Span of mutable_buffer to fill.
     758              : 
     759              :         @return A span of filled buffers.
     760              :     */
     761              :     std::span<capy::mutable_buffer>
     762           62 :     prepare(std::span<capy::mutable_buffer> dest)
     763              :     {
     764           62 :         auto bufs = sr_->stream_prepare();
     765           62 :         std::size_t count = 0;
     766          124 :         for(auto const& b : bufs)
     767              :         {
     768          124 :             if(count >= dest.size() || b.size() == 0)
     769           62 :                 break;
     770           62 :             dest[count++] = b;
     771              :         }
     772          124 :         return dest.first(count);
     773              :     }
     774              : 
     775              :     /** Commit bytes written to the prepared buffers.
     776              : 
     777              :         Commits `n` bytes written to the buffers returned by the
     778              :         most recent call to @ref prepare. The operation flushes
     779              :         serialized output to the underlying stream.
     780              : 
     781              :         @param n The number of bytes to commit.
     782              : 
     783              :         @return An awaitable yielding `(error_code)`.
     784              :     */
     785              :     auto
     786           50 :     commit(std::size_t n)
     787              :         -> capy::io_task<>
     788              :     {
     789           50 :         return commit(n, false);
     790              :     }
     791              : 
     792              :     /** Commit bytes written with optional end-of-stream.
     793              : 
     794              :         Commits `n` bytes written to the buffers returned by the
     795              :         most recent call to @ref prepare. If `eof` is true, also
     796              :         signals end-of-stream.
     797              : 
     798              :         @param n The number of bytes to commit.
     799              :         @param eof If true, signals end-of-stream after committing.
     800              : 
     801              :         @return An awaitable yielding `(error_code)`.
     802              :     */
     803              :     auto
     804           68 :     commit(std::size_t n, bool eof)
     805              :         -> capy::io_task<>
     806              :     {
     807              :         sr_->stream_commit(n);
     808              : 
     809              :         if(eof)
     810              :             sr_->stream_close();
     811              : 
     812              :         while(!sr_->is_done())
     813              :         {
     814              :             auto cbs = sr_->prepare();
     815              :             if(cbs.has_error())
     816              :             {
     817              :                 if(cbs.error() == error::need_data && !eof)
     818              :                     break;
     819              :                 if(cbs.error() == error::need_data)
     820              :                     continue;
     821              :                 co_return {cbs.error()};
     822              :             }
     823              : 
     824              :             if(capy::buffer_empty(*cbs))
     825              :             {
     826              :                 sr_->consume(0);
     827              :                 continue;
     828              :             }
     829              : 
     830              :             auto [ec, written] = co_await stream_->write_some(*cbs);
     831              :             sr_->consume(written);
     832              : 
     833              :             if(ec)
     834              :                 co_return {ec};
     835              :         }
     836              : 
     837              :         co_return {};
     838          136 :     }
     839              : 
     840              :     /** Signal end-of-stream.
     841              : 
     842              :         Closes the body stream and flushes any remaining serializer
     843              :         output to the underlying stream. For chunked encoding, this
     844              :         writes the final zero-length chunk.
     845              : 
     846              :         @return An awaitable yielding `(error_code)`.
     847              : 
     848              :         @post The serializer's `is_done()` returns `true` on success.
     849              :     */
     850              :     auto
     851           28 :     commit_eof()
     852              :         -> capy::io_task<>
     853              :     {
     854              :         sr_->stream_close();
     855              : 
     856              :         while(!sr_->is_done())
     857              :         {
     858              :             auto cbs = sr_->prepare();
     859              :             if(cbs.has_error())
     860              :             {
     861              :                 if(cbs.error() == error::need_data)
     862              :                     continue;
     863              :                 co_return {cbs.error()};
     864              :             }
     865              : 
     866              :             if(capy::buffer_empty(*cbs))
     867              :             {
     868              :                 sr_->consume(0);
     869              :                 continue;
     870              :             }
     871              : 
     872              :             auto [ec, written] = co_await stream_->write_some(*cbs);
     873              :             sr_->consume(written);
     874              : 
     875              :             if(ec)
     876              :                 co_return {ec};
     877              :         }
     878              : 
     879              :         co_return {};
     880           56 :     }
     881              : };
     882              : 
     883              : //------------------------------------------------
     884              : 
     885              : template<capy::WriteStream Stream>
     886              : serializer::sink<Stream>
     887           68 : serializer::sink_for(Stream& ws) noexcept
     888              : {
     889           68 :     return sink<Stream>(ws, *this);
     890              : }
     891              : 
     892              : } // http
     893              : } // boost
     894              : 
     895              : #include <boost/http/impl/serializer.hpp>
     896              : 
     897              : #endif
        

Generated by: LCOV version 2.3