LCOV - code coverage report
Current view: top level - boost/beast2/server - body_source.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 96.8 % 125 121
Test Date: 2025-12-24 17:07:59 Functions: 96.8 % 31 30

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/beast2
       8              : //
       9              : 
      10              : #ifndef BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
      11              : #define BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
      12              : 
      13              : #include <boost/beast2/detail/config.hpp>
      14              : #include <boost/beast2/detail/except.hpp>
      15              : #include <boost/beast2/detail/type_traits.hpp>
      16              : #include <boost/http_proto/error.hpp>
      17              : #include <boost/buffers/buffer.hpp>
      18              : #include <boost/buffers/copy.hpp>
      19              : #include <boost/buffers/slice.hpp>
      20              : #include <boost/buffers/data_source.hpp>
      21              : #include <boost/buffers/read_source.hpp>
      22              : #include <boost/core/span.hpp>
      23              : #include <boost/system/error_code.hpp>
      24              : 
      25              : namespace boost {
      26              : namespace beast2 {
      27              : 
      28              : /** Tag for customizing body construction
      29              : */
      30              : struct construct_body_tag {};
      31              : 
      32              : //-----------------------------------------------
      33              : 
      34              : /** A type erased HTTP message body.
      35              : 
      36              :     This type erases the specific body type used to represent the message body.
      37              :     It provides a uniform interface for reading the body data regardless of
      38              :     how the body is implemented. Accessing the bytes is achieved by calling
      39              :     @ref read which reads data into a caller-provided buffer. Alternatively,
      40              :     when @ref has_buffers returns `true` the body consists of buffers in memory,
      41              :     and they can be accessed directly by calling @ref get_buffers.
      42              : 
      43              :     Example bodies include:
      44              :     - in-memory buffers
      45              :     - file-backed bodies
      46              :     - generated bodies
      47              : 
      48              :     @note A body_source is movable but not copyable.
      49              : 
      50              :     Type-erased bodies can always be rewound to the beginning by calling
      51              :     @ref rewind. Therefore, bodies can be read multiple times.
      52              : 
      53              :     @par Thread Safety
      54              :     Unsafe.
      55              : */
      56              : class body_source
      57              : {
      58              : public:
      59              :     /** Destructor.
      60              :     */
      61              :     BOOST_BEAST2_DECL ~body_source();
      62              : 
      63              :     /** Constructor
      64              : 
      65              :         Default-constructed bodies are empty.
      66              :     */
      67              :     body_source() = default;
      68              : 
      69              :     /** Constructor
      70              : 
      71              :         The content of @p other is transferred to `*this`. The
      72              :         moved-from body is left empty, as if default-constructed.
      73              :     */
      74            2 :     body_source(body_source&& other) noexcept
      75            2 :         : impl_(other.impl_)
      76              :     {
      77            2 :         other.impl_ = nullptr;
      78            2 :     }
      79              : 
      80              :     /** Assignment
      81              : 
      82              :         The previous body, if any, is released and the
      83              :         content of @p other is transferred to `*this`.
      84              :     */
      85              :     BOOST_BEAST2_DECL
      86              :     body_source& operator=(body_source&& other) noexcept;
      87              : 
      88              :     /** Construct a streaming body source.
      89              :     */
      90              :     template<class ReadSource
      91              :         , typename std::enable_if<
      92              :             ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
      93              :             buffers::is_read_source<typename std::decay<ReadSource>::type>::value
      94              :         , int>::type = 0>
      95              :     body_source(ReadSource&& body);
      96              : 
      97              :     /** Construct a streaming body source with a known size.
      98              :     */
      99              :     template<class ReadSource
     100              :         , typename std::enable_if<
     101              :             ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
     102              :             buffers::is_read_source<typename std::decay<ReadSource>::type>::value
     103              :         , int>::type = 0>
     104              :     body_source(
     105              :         std::size_t known_size,
     106              :         ReadSource&& body);
     107              : 
     108              :     /** Construct a buffers body source.
     109              :     */
     110              :     template<class DataSource
     111              :         , typename std::enable_if<
     112              :             ! std::is_same<typename std::decay<DataSource>::type, body_source>::value &&
     113              :             buffers::is_data_source<typename std::decay<DataSource>::type>::value
     114              :         , int>::type = 0>
     115              :     body_source(DataSource&& body);
     116              : 
     117              :     //template<class T>
     118              :     //body(T&& t);
     119              : 
     120              :     /** Return `true` if the size of the body is known.
     121              :     */
     122           10 :     bool has_size() const noexcept
     123              :     {
     124           10 :         if(impl_) 
     125            8 :             return impl_->has_size();
     126            2 :         return true; // empty
     127              :     }
     128              : 
     129              :     /** Return `true` if the body consists of buffers in memory.
     130              :         When the body consists of buffers in memory, they can
     131              :         also be accessed directly using @ref get_buffers.
     132              :     */
     133           10 :     bool has_buffers() const noexcept
     134              :     {
     135           10 :         if(impl_)
     136            8 :             return impl_->has_buffers();
     137            2 :         return true; // empty
     138              :     }
     139              : 
     140              :     /** Return the size of the body, if available.
     141              :         @throw std::invalid_argument if @ref has_size returns `false`.
     142              :         @return The size of the body in bytes.
     143              :     */
     144           10 :     auto size() const -> std::size_t
     145              :     {
     146           10 :         if(impl_)
     147            8 :             return impl_->size();
     148            2 :         return 0; // empty
     149              :     }
     150              : 
     151              :     /** Return the buffers representing the body, if available.
     152              :         @throw std::invalid_argument if @ref has_buffers returns `false`.
     153              :         @return A span of buffers representing the body.
     154              :     */
     155           10 :     auto data() const ->
     156              :         span<buffers::const_buffer const>
     157              :     {
     158           10 :         if(impl_)
     159            8 :             return impl_->data();
     160            2 :         return {}; // empty
     161              :     }
     162              : 
     163              :     /** Rewind the body to the beginning.
     164              :         This allows the body to be accessed from the start when calling @read.
     165              :     */
     166          154 :     void rewind()
     167              :     {
     168          154 :         if(impl_)
     169          136 :             return impl_->rewind();
     170              :         // empty
     171              :     }
     172              : 
     173              :     /** Read from the body into a caller-provided buffer.
     174              : 
     175              :         @param dest A pointer to the buffer to read into.
     176              :         @param n The maximum number of bytes to read.
     177              :         @param ec Set to the error, if any occurred.
     178              :         @return The number of bytes read, which may be
     179              :         less than the number requested. A return value of
     180              :         zero indicates end-of-body.
     181              :     */
     182              :     auto
     183          320 :     read(void* dest, std::size_t n,
     184              :         system::error_code& ec) ->
     185              :             std::size_t
     186              :     {
     187          320 :         if(impl_)
     188          304 :             return impl_->read(dest, n, ec);
     189              :         // empty
     190           16 :         ec = http::error::end_of_stream;
     191           16 :         return 0;
     192              :     }
     193              : 
     194              : private:
     195              :     struct impl
     196              :     {
     197            6 :         virtual ~impl() = default;
     198            4 :         virtual bool has_size() const noexcept { return false; }
     199            5 :         virtual bool has_buffers() const noexcept { return false; }
     200            4 :         virtual std::size_t size() const
     201              :         {
     202            4 :             detail::throw_invalid_argument();
     203              :         }
     204            5 :         virtual auto data() const ->
     205              :             span<buffers::const_buffer const>
     206              :         {
     207            5 :             detail::throw_invalid_argument();
     208              :         }
     209              :         virtual void rewind() = 0;
     210              :         virtual std::size_t read(
     211              :             void* dest, std::size_t n,
     212              :             system::error_code& ec) = 0;
     213              :     };
     214              : 
     215              :     impl* impl_ = nullptr;
     216              : };
     217              : 
     218              : //-----------------------------------------------
     219              : 
     220              : template<class ReadSource
     221              :     , typename std::enable_if<
     222              :         ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
     223              :         buffers::is_read_source<typename std::decay<ReadSource>::type>::value
     224              :     , int>::type>
     225            3 : body_source::
     226              : body_source(
     227            3 :     ReadSource&& body)
     228              : {
     229              :     struct model : impl
     230              :     {
     231              :         system::error_code ec_;
     232              :         typename std::decay<ReadSource>::type body_;
     233              : 
     234            3 :         explicit model(ReadSource&& body)
     235            3 :             : body_(std::forward<ReadSource>(body))
     236              :         {
     237            3 :         }
     238              : 
     239           68 :         void rewind() override
     240              :         {
     241           68 :             ec_ = {};
     242           68 :             body_.rewind();
     243           68 :         }
     244              : 
     245          154 :         std::size_t read(
     246              :             void* dest,
     247              :             std::size_t size,
     248              :             system::error_code& ec) override
     249              :         {
     250          154 :             if(ec_.failed())
     251              :             {
     252            0 :                 ec = ec_;
     253            0 :                 return 0;
     254              :             }
     255          154 :             auto nread = body_.read(
     256          154 :                 buffers::mutable_buffer(dest, size), ec);
     257          154 :             ec_ = ec;
     258          154 :             return nread;
     259              :         }
     260              :     };
     261              : 
     262            3 :     auto p = ::operator new(sizeof(model));
     263            3 :     impl_ = ::new(p) model(
     264              :         std::forward<ReadSource>(body));
     265            3 : }
     266              : 
     267              : /** Construct a streaming body source with a known size.
     268              : */
     269              : template<class ReadSource
     270              :     , typename std::enable_if<
     271              :         ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
     272              :         buffers::is_read_source<typename std::decay<ReadSource>::type>::value
     273              :     , int>::type>
     274            1 : body_source::
     275              : body_source(
     276              :     std::size_t known_size,
     277            1 :     ReadSource&& body)
     278              : {
     279              :     struct model : impl
     280              :     {
     281              :         std::size_t size_;
     282              :         system::error_code ec_;
     283              :         typename std::decay<ReadSource>::type body_;
     284              : 
     285            1 :         model(
     286              :             ReadSource&& body,
     287              :             std::size_t known_size)
     288            1 :             : size_(known_size)
     289            1 :             , body_(std::forward<ReadSource>(body))
     290              :         {
     291            1 :         }
     292              : 
     293            1 :         bool has_size() const noexcept override
     294              :         {
     295            1 :             return true;
     296              :         }
     297              : 
     298            1 :         std::size_t size() const override
     299              :         {
     300            1 :             return size_;
     301              :         }
     302              : 
     303           17 :         void rewind() override
     304              :         {
     305           17 :             ec_ = {};
     306           17 :             body_.rewind();
     307           17 :         }
     308              : 
     309           24 :         std::size_t read(
     310              :             void* dest,
     311              :             std::size_t size,
     312              :             system::error_code& ec) override
     313              :         {
     314           24 :             if(ec_.failed())
     315              :             {
     316            0 :                 ec = ec_;
     317            0 :                 return 0;
     318              :             }
     319           24 :             auto nread = body_.read(
     320           24 :                 buffers::mutable_buffer(dest, size), ec);
     321           24 :             ec_ = ec;
     322           24 :             return nread;
     323              :         }
     324              :     };
     325              : 
     326            1 :     auto p = ::operator new(sizeof(model));
     327            1 :     impl_ = ::new(p) model(
     328              :         std::forward<ReadSource>(body), known_size);
     329            1 : }
     330              : 
     331              : /** Construct a buffers body source.
     332              : */
     333              : template<class DataSource
     334              :     , typename std::enable_if<
     335              :         ! std::is_same<typename std::decay<DataSource>::type, body_source>::value &&
     336              :         buffers::is_data_source<typename std::decay<DataSource>::type>::value
     337              :     , int>::type>
     338            2 : body_source::
     339              : body_source(
     340            2 :     DataSource&& body)
     341              : {
     342              :     struct model : impl
     343              :     {
     344              :         typename std::decay<DataSource>::type body_;
     345              :         std::size_t size_ = 0;
     346              :         span<boost::buffers::const_buffer> bs_;
     347              :         std::size_t nread_ = 0;
     348              : 
     349            2 :         explicit model(
     350              :             DataSource&& body) noexcept
     351            2 :             : body_(std::forward<DataSource>(body))
     352              :         {
     353            2 :             auto const& data = body_.data();
     354            2 :             auto const& end = buffers::end(data);
     355            2 :             auto p = reinterpret_cast<
     356              :                 buffers::const_buffer*>(this+1);
     357            2 :             std::size_t length = 0;
     358            4 :             for(auto it = buffers::begin(data); it != end; ++it)
     359              :             {
     360            2 :                 boost::buffers::const_buffer cb(*it);
     361            2 :                 size_ += cb.size();
     362            2 :                 *p++ = cb;
     363            2 :                 ++length;
     364              :             }
     365            2 :             bs_ = { reinterpret_cast<
     366              :                 buffers::const_buffer*>(this + 1), length };
     367            2 :         }
     368              : 
     369            3 :         bool has_size() const noexcept override
     370              :         {
     371            3 :             return true;
     372              :         }
     373              : 
     374            3 :         bool has_buffers() const noexcept override
     375              :         {
     376            3 :             return true;
     377              :         }
     378              : 
     379            3 :         std::size_t size() const override
     380              :         {
     381            3 :             return size_;
     382              :         }
     383              : 
     384              :         span<buffers::const_buffer const>
     385            3 :         data() const override
     386              :         {
     387            3 :             return bs_;
     388              :         }
     389              : 
     390           51 :         void rewind() override
     391              :         {
     392           51 :             nread_ = 0;
     393           51 :         }
     394              : 
     395          126 :         std::size_t read(
     396              :             void* dest,
     397              :             std::size_t n0,
     398              :             system::error_code& ec) override
     399              :         {
     400          126 :             std::size_t n = buffers::copy(
     401          126 :                 buffers::mutable_buffer(dest, n0),
     402          126 :                 buffers::sans_prefix(bs_, nread_));
     403          126 :             nread_ += n;
     404          126 :             if(nread_ >= size_)
     405           48 :                 ec = http::error::end_of_stream;
     406              :             else
     407           78 :                 ec = {};
     408          126 :             return n;
     409              :         }
     410              :     };
     411              : 
     412            2 :     std::size_t length = 0;
     413            2 :     auto const& data = body.data();
     414            2 :     auto const& end = buffers::end(data);
     415            2 :     for(auto it = buffers::begin(data);
     416            4 :         it != end; ++it)
     417              :     {
     418            2 :         ++length;
     419              :     }
     420              : 
     421              :     // VFALCO this requires DataSource to be nothrow
     422              :     // move constructible for strong exception safety.
     423            4 :     auto p = ::operator new(sizeof(model) +
     424            2 :         length * sizeof(buffers::const_buffer));
     425            2 :     impl_ = ::new(p) model(
     426              :         std::forward<DataSource>(body));
     427            2 : }
     428              : 
     429              : } // beast2
     430              : } // boost
     431              : 
     432              : #endif
        

Generated by: LCOV version 2.1