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 % 30 30
Test Date: 2025-06-06 17:40:41 Functions: 100.0 % 16 16

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

Generated by: LCOV version 2.1