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

Generated by: LCOV version 2.1