LCOV - code coverage report
Current view: top level - boost/http_proto/serializer.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 51 51
Test Date: 2025-06-18 09:40:26 Functions: 86.8 % 38 33

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/http_proto
       8              : //
       9              : 
      10              : #ifndef BOOST_HTTP_PROTO_SERIALIZER_HPP
      11              : #define BOOST_HTTP_PROTO_SERIALIZER_HPP
      12              : 
      13              : #include <boost/http_proto/context.hpp>
      14              : #include <boost/http_proto/detail/array_of_const_buffers.hpp>
      15              : #include <boost/http_proto/detail/config.hpp>
      16              : #include <boost/http_proto/detail/except.hpp>
      17              : #include <boost/http_proto/detail/header.hpp>
      18              : #include <boost/http_proto/detail/workspace.hpp>
      19              : #include <boost/http_proto/source.hpp>
      20              : 
      21              : #include <boost/buffers/circular_buffer.hpp>
      22              : #include <boost/buffers/const_buffer_span.hpp>
      23              : #include <boost/buffers/range.hpp>
      24              : #include <boost/buffers/type_traits.hpp>
      25              : #include <boost/system/result.hpp>
      26              : 
      27              : #include <memory>
      28              : #include <numeric>
      29              : #include <type_traits>
      30              : #include <utility>
      31              : 
      32              : namespace boost {
      33              : namespace http_proto {
      34              : 
      35              : #ifndef BOOST_HTTP_PROTO_DOCS
      36              : class message_view_base;
      37              : namespace detail {
      38              : class filter;
      39              : } // namespace detail
      40              : #endif
      41              : 
      42              : /** A serializer for HTTP/1 messages
      43              : 
      44              :     This is used to serialize one or more complete
      45              :     HTTP/1 messages. Each message consists of a
      46              :     required header followed by an optional body.
      47              : 
      48              :     Objects of this type operate using an "input area" and an
      49              :     "output area". Callers provide data to the input area
      50              :     using one of the @ref start or @ref start_stream member
      51              :     functions. After input is provided, serialized data
      52              :     becomes available in the serializer's output area in the
      53              :     form of a constant buffer sequence.
      54              : 
      55              :     Callers alternate between filling the input area and
      56              :     consuming the output area until all the input has been
      57              :     provided and all the output data has been consumed, or
      58              :     an error occurs.
      59              : 
      60              :     After calling @ref start, the caller must ensure that the
      61              :     contents of the associated message are not changed or
      62              :     destroyed until @ref is_done returns true, @ref reset is
      63              :     called, or the serializer is destroyed, otherwise the
      64              :     behavior is undefined.
      65              : */
      66              : class serializer
      67              : {
      68              : public:
      69              :     using const_buffers_type =
      70              :         buffers::const_buffer_span;
      71              : 
      72              :     struct stream;
      73              : 
      74              :     /** Destructor
      75              :     */
      76              :     BOOST_HTTP_PROTO_DECL
      77              :     ~serializer();
      78              : 
      79              :     /** Constructor
      80              :     */
      81              :     BOOST_HTTP_PROTO_DECL
      82              :     serializer(
      83              :         serializer&&) noexcept;
      84              : 
      85              :     /** Constructor
      86              : 
      87              :         @param ctx The serializer will access services
      88              :                    registered with this context.
      89              :     */
      90              :     BOOST_HTTP_PROTO_DECL
      91              :     serializer(
      92              :         context& ctx);
      93              : 
      94              :     /** Constructor
      95              :     */
      96              :     BOOST_HTTP_PROTO_DECL
      97              :     serializer(
      98              :         context& ctx,
      99              :         std::size_t buffer_size);
     100              : 
     101              :     //--------------------------------------------
     102              : 
     103              :     /** Prepare the serializer for a new stream
     104              :     */
     105              :     BOOST_HTTP_PROTO_DECL
     106              :     void
     107              :     reset() noexcept;
     108              : 
     109              :     /** Prepare the serializer for a new message
     110              : 
     111              :         The message will not contain a body.
     112              :         Changing the contents of the message
     113              :         after calling this function and before
     114              :         @ref is_done returns `true` results in
     115              :         undefined behavior.
     116              :     */
     117              :     void
     118            4 :     start(
     119              :         message_view_base const& m)
     120              :     {
     121            4 :         start_empty(m);
     122            4 :     }
     123              : 
     124              :     /** Prepare the serializer for a new message
     125              : 
     126              :         Changing the contents of the message
     127              :         after calling this function and before
     128              :         @ref is_done returns `true` results in
     129              :         undefined behavior.
     130              : 
     131              :         @par Constraints
     132              :         @code
     133              :         is_const_buffers< ConstBuffers >::value == true
     134              :         @endcode
     135              :     */
     136              :     template<
     137              :         class ConstBufferSequence
     138              : #ifndef BOOST_HTTP_PROTO_DOCS
     139              :         ,class = typename
     140              :             std::enable_if<
     141              :                 buffers::is_const_buffer_sequence<
     142              :                     ConstBufferSequence>::value
     143              :                         >::type
     144              : #endif
     145              :     >
     146              :     void
     147              :     start(
     148              :         message_view_base const& m,
     149              :         ConstBufferSequence&& body);
     150              : 
     151              :     /** Prepare the serializer for a new message
     152              : 
     153              :         Changing the contents of the message
     154              :         after calling this function and before
     155              :         @ref is_done returns `true` results in
     156              :         undefined behavior.
     157              :     */
     158              :     template<
     159              :         class Source,
     160              :         class... Args
     161              : #ifndef BOOST_HTTP_PROTO_DOCS
     162              :         ,class = typename std::enable_if<
     163              :             is_source<Source>::value>::type
     164              : #endif
     165              :     >
     166              :     Source&
     167              :     start(
     168              :         message_view_base const& m,
     169              :         Args&&... args);
     170              : 
     171              :     //--------------------------------------------
     172              : 
     173              :     /** Return a new stream for this serializer.
     174              : 
     175              :         After the serializer is destroyed, @ref reset is called,
     176              :         or @ref is_done returns true, the only valid operation
     177              :         on the stream is destruction.
     178              : 
     179              :         A stream may be used to invert the flow of control
     180              :         when the caller is supplying body data as a series
     181              :         of buffers.
     182              :      */
     183              :     BOOST_HTTP_PROTO_DECL
     184              :     stream
     185              :     start_stream(
     186              :         message_view_base const& m);
     187              : 
     188              :     //--------------------------------------------
     189              : 
     190              :     /** Return true if serialization is complete.
     191              :     */
     192              :     bool
     193         1400 :     is_done() const noexcept
     194              :     {
     195         1400 :         return is_done_;
     196              :     }
     197              : 
     198              :     /** Return the output area.
     199              : 
     200              :         This function will serialize some or
     201              :         all of the content and return the
     202              :         corresponding output buffers.
     203              : 
     204              :         @par Preconditions
     205              :         @code
     206              :         this->is_done() == false
     207              :         @endcode
     208              :     */
     209              :     BOOST_HTTP_PROTO_DECL
     210              :     auto
     211              :     prepare() ->
     212              :         system::result<
     213              :             const_buffers_type>;
     214              : 
     215              :     /** Consume bytes from the output area.
     216              :     */
     217              :     BOOST_HTTP_PROTO_DECL
     218              :     void
     219              :     consume(std::size_t n);
     220              : 
     221              : private:
     222              :     class const_buf_gen_base;
     223              : 
     224              :     template<class>
     225              :     class const_buf_gen;
     226              : 
     227              :     detail::array_of_const_buffers
     228              :     make_array(std::size_t n);
     229              : 
     230              :     template<
     231              :         class Source,
     232              :         class... Args,
     233              :         typename std::enable_if<
     234              :             std::is_constructible<
     235              :                 Source,
     236              :                 Args...>::value>::type* = nullptr>
     237              :     Source&
     238           33 :     construct_source(Args&&... args)
     239              :     {
     240           33 :         return ws_.emplace<Source>(
     241           33 :             std::forward<Args>(args)...);
     242              :     }
     243              : 
     244              :     template<
     245              :         class Source,
     246              :         class... Args,
     247              :         typename std::enable_if<
     248              :             std::is_constructible<
     249              :                 Source,
     250              :                 detail::workspace&,
     251              :                 Args...>::value>::type* = nullptr>
     252              :     Source&
     253              :     construct_source(Args&&... args)
     254              :     {
     255              :         return ws_.emplace<Source>(
     256              :             ws_, std::forward<Args>(args)...);
     257              :     }
     258              : 
     259              :     BOOST_HTTP_PROTO_DECL
     260              :     void
     261              :     start_init(
     262              :         message_view_base const&);
     263              : 
     264              :     BOOST_HTTP_PROTO_DECL
     265              :     void
     266              :     start_empty(
     267              :         message_view_base const&);
     268              : 
     269              :     BOOST_HTTP_PROTO_DECL
     270              :     void
     271              :     start_buffers(
     272              :         message_view_base const&);
     273              : 
     274              :     BOOST_HTTP_PROTO_DECL
     275              :     void
     276              :     start_source(
     277              :         message_view_base const&);
     278              : 
     279              :     enum class style
     280              :     {
     281              :         empty,
     282              :         buffers,
     283              :         source,
     284              :         stream
     285              :     };
     286              : 
     287              :     context& ctx_;
     288              :     detail::workspace ws_;
     289              : 
     290              :     const_buf_gen_base* buf_gen_;
     291              :     detail::filter* filter_;
     292              :     source* source_;
     293              : 
     294              :     buffers::circular_buffer cb0_;
     295              :     buffers::circular_buffer cb1_;
     296              :     detail::array_of_const_buffers prepped_;
     297              :     buffers::const_buffer tmp_;
     298              : 
     299              :     style st_;
     300              :     bool more_input_;
     301              :     bool is_done_;
     302              :     bool is_header_done_;
     303              :     bool is_chunked_;
     304              :     bool needs_exp100_continue_;
     305              :     bool filter_done_;
     306              : };
     307              : 
     308              : //------------------------------------------------
     309              : 
     310              : /** The type used for caller-provided body data during
     311              :     serialization.
     312              : 
     313              :     @code{.cpp}
     314              :     http_proto::serializer sr(128);
     315              : 
     316              :     http_proto::request req;
     317              :     auto stream = sr.start_stream(req);
     318              : 
     319              :     std::string_view msg = "Hello, world!";
     320              :     auto n = buffers::copy(
     321              :         stream.prepare(),
     322              :         buffers::make_buffer(
     323              :             msg.data(), msg.size()));
     324              : 
     325              :     stream.commit(n);
     326              : 
     327              :     auto cbs = sr.prepare().value();
     328              :     (void)cbs;
     329              :     @endcode
     330              : */
     331              : struct serializer::stream
     332              : {
     333              :     /** Constructor.
     334              : 
     335              :         The only valid operations on default constructed
     336              :         streams are assignment and destruction.
     337              :     */
     338              :     stream() = default;
     339              : 
     340              :     /** Constructor.
     341              : 
     342              :         The constructed stream will share the same
     343              :         serializer as `other`.
     344              :     */
     345              :     stream(stream const& other) = default;
     346              : 
     347              :     /** Assignment.
     348              : 
     349              :         The current stream will share the same serializer
     350              :         as `other`.
     351              :     */
     352              :     stream& operator= (
     353              :         stream const& other) = default;
     354              : 
     355              :     /** A MutableBufferSequence consisting of a buffer pair.
     356              :      */
     357              :     using buffers_type =
     358              :         buffers::mutable_buffer_pair;
     359              : 
     360              :     /** Returns the remaining available capacity.
     361              : 
     362              :         The returned value represents the available free
     363              :         space in the backing fixed-sized buffers used by the
     364              :         serializer associated with this stream.
     365              : 
     366              :         The capacity is absolute and does not do any
     367              :         accounting for any octets required by a chunked
     368              :         transfer encoding.
     369              :     */
     370              :     BOOST_HTTP_PROTO_DECL
     371              :     std::size_t
     372              :     capacity() const noexcept;
     373              : 
     374              :     /** Return true if the stream cannot currently hold
     375              :         additional output data.
     376              : 
     377              :         The fixed-sized buffers maintained by the associated
     378              :         serializer can be sufficiently full from previous
     379              :         calls to @ref stream::commit.
     380              : 
     381              :         This function can be called to determine if the caller
     382              :         should drain the serializer via @ref serializer::consume calls
     383              :         before attempting to fill the buffer sequence
     384              :         returned from @ref stream::prepare.
     385              :     */
     386              :     BOOST_HTTP_PROTO_DECL
     387              :     bool
     388              :     is_full() const noexcept;
     389              : 
     390              :     /** Returns a MutableBufferSequence for storing
     391              :         serializer input. If `n` bytes are written to the
     392              :         buffer sequence, @ref stream::commit must be called
     393              :         with `n` to update the backing serializer's buffers.
     394              : 
     395              :         The returned buffer sequence is as wide as is
     396              :         possible.
     397              : 
     398              :         @exception std::length_error Thrown if the stream
     399              :         has insufficient capacity and a chunked transfer
     400              :         encoding is being used
     401              :     */
     402              :     BOOST_HTTP_PROTO_DECL
     403              :     buffers_type
     404              :     prepare() const;
     405              : 
     406              :     /** Make `n` bytes available to the serializer.
     407              : 
     408              :         Once the buffer sequence returned from @ref stream::prepare
     409              :         has been filled, the input can be marked as ready
     410              :         for serialization by using this function.
     411              : 
     412              :         @exception std::logic_error Thrown if `commit` is
     413              :         called with 0.
     414              :     */
     415              :     BOOST_HTTP_PROTO_DECL
     416              :     void
     417              :     commit(std::size_t n) const;
     418              : 
     419              :     /** Indicate that no more data is coming and that the
     420              :         body should be treated as complete.
     421              : 
     422              :         @excpeption std::logic_error Thrown if the stream
     423              :         has been previously closed.
     424              :     */
     425              :     BOOST_HTTP_PROTO_DECL
     426              :     void
     427              :     close() const;
     428              : 
     429              : private:
     430              :     friend class serializer;
     431              : 
     432              :     explicit
     433           30 :     stream(
     434              :         serializer& sr) noexcept
     435           30 :         : sr_(&sr)
     436              :     {
     437           30 :     }
     438              : 
     439              :     serializer* sr_ = nullptr;
     440              : };
     441              : 
     442              : //---------------------------------------------------------
     443              : 
     444              : class serializer::const_buf_gen_base
     445              : {
     446              : public:
     447              :     // Returns the next non-empty buffer,
     448              :     // or an empty buffer if none remain.
     449              :     virtual
     450              :     buffers::const_buffer
     451              :     next() = 0;
     452              : 
     453              :     // Size of remaining buffers
     454              :     virtual
     455              :     std::size_t
     456              :     size() const = 0;
     457              : 
     458              :     // Count of remaining non-empty buffers
     459              :     virtual
     460              :     std::size_t
     461              :     count() const = 0;
     462              : 
     463              :     // Returns true when there is no buffer or
     464              :     // the remaining buffers are empty
     465              :     virtual
     466              :     bool
     467              :     is_empty() const = 0;
     468              : };
     469              : 
     470              : template<class ConstBufferSequence>
     471              : class serializer::const_buf_gen
     472              :     : public const_buf_gen_base
     473              : {
     474              :     using it_t = decltype(buffers::begin(
     475              :         std::declval<ConstBufferSequence>()));
     476              : 
     477              :     ConstBufferSequence cbs_;
     478              :     it_t current_;
     479              : public:
     480              :     using const_buffer =
     481              :         buffers::const_buffer;
     482              : 
     483              :     explicit
     484           32 :     const_buf_gen(ConstBufferSequence cbs)
     485           64 :         : cbs_(std::move(cbs))
     486           32 :         , current_(buffers::begin(cbs_))
     487              :     {
     488           32 :     }
     489              : 
     490              :     const_buffer
     491          449 :     next() override
     492              :     {
     493          449 :         while(current_ != buffers::end(cbs_))
     494              :         {
     495          431 :             const_buffer buf = *current_++;
     496          431 :             if(buf.size() != 0)
     497          431 :                 return buf;
     498              :         }
     499           18 :         return {};
     500              :     }
     501              : 
     502              :     std::size_t
     503            1 :     size() const override
     504              :     {
     505            2 :         return std::accumulate(
     506            1 :             current_,
     507            1 :             buffers::end(cbs_),
     508              :             std::size_t{},
     509           23 :             [](std::size_t sum, const_buffer cb) {
     510           24 :                 return sum + cb.size(); });
     511              :     }
     512              : 
     513              :     std::size_t
     514           32 :     count() const override
     515              :     {
     516           96 :         return std::count_if(
     517           32 :             current_,
     518           32 :             buffers::end(cbs_),
     519          642 :             [](const_buffer cb) {
     520          674 :                 return cb.size() != 0; });
     521              :     }
     522              : 
     523              :     bool
     524           35 :     is_empty() const override
     525              :     {
     526          105 :         return std::all_of(
     527           35 :             current_,
     528           35 :             buffers::end(cbs_),
     529          231 :             [](const_buffer cb) {
     530          266 :                 return cb.size() == 0; });
     531              :     }
     532              : };
     533              : 
     534              : //---------------------------------------------------------
     535              : 
     536              : template<
     537              :     class ConstBufferSequence,
     538              :     class>
     539              : void
     540           32 : serializer::
     541              : start(
     542              :     message_view_base const& m,
     543              :     ConstBufferSequence&& cbs)
     544              : {
     545              :     static_assert(buffers::is_const_buffer_sequence<
     546              :             ConstBufferSequence>::value,
     547              :         "ConstBufferSequence type requirements not met");
     548              : 
     549           32 :     start_init(m);
     550           64 :     buf_gen_ = std::addressof(
     551              :         ws_.emplace<const_buf_gen<typename
     552           32 :         std::decay<ConstBufferSequence>::type>>(
     553              :                 std::forward<ConstBufferSequence>(cbs)));
     554           32 :     start_buffers(m);
     555           32 : }
     556              : 
     557              : template<
     558              :     class Source,
     559              :     class... Args,
     560              :     class>
     561              : Source&
     562           33 : serializer::
     563              : start(
     564              :     message_view_base const& m,
     565              :     Args&&... args)
     566              : {
     567              :     static_assert(
     568              :         !std::is_abstract<Source>::value, "");
     569              :     static_assert(
     570              :         std::is_constructible<Source, Args...>::value ||
     571              :         std::is_constructible<Source, detail::workspace&, Args...>::value,
     572              :         "The Source cannot be constructed with the given arguments");
     573              : 
     574           33 :     start_init(m);
     575           33 :     auto& src = construct_source<Source>(
     576              :         std::forward<Args>(args)...);
     577           33 :     source_ = std::addressof(src);
     578           33 :     start_source(m);
     579           33 :     return src;
     580              : }
     581              : 
     582              : } // http_proto
     583              : } // boost
     584              : 
     585              : #endif
        

Generated by: LCOV version 2.1