LCOV - code coverage report
Current view: top level - boost/http_proto - serializer.hpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 37 38 97.4 %
Date: 2024-04-22 17:18:35 Functions: 16 17 94.1 %

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

Generated by: LCOV version 1.15