LCOV - code coverage report
Current view: top level - libs/http_proto/src - parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 85.5 % 894 764
Test Date: 2025-12-04 09:48:33 Functions: 86.0 % 100 86

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2024 Mohammad Nejati
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/http_proto
       9              : //
      10              : 
      11              : #include <boost/http_proto/detail/except.hpp>
      12              : #include <boost/http_proto/error.hpp>
      13              : #include <boost/http_proto/parser.hpp>
      14              : #include <boost/http_proto/static_request.hpp>
      15              : #include <boost/http_proto/static_response.hpp>
      16              : 
      17              : #include <boost/assert.hpp>
      18              : #include <boost/buffers/circular_buffer.hpp>
      19              : #include <boost/buffers/copy.hpp>
      20              : #include <boost/buffers/flat_buffer.hpp>
      21              : #include <boost/buffers/front.hpp>
      22              : #include <boost/buffers/slice.hpp>
      23              : #include <boost/capy/brotli/decode.hpp>
      24              : #include <boost/capy/polystore.hpp>
      25              : #include <boost/capy/zlib/error.hpp>
      26              : #include <boost/capy/zlib/inflate.hpp>
      27              : #include <boost/url/grammar/ci_string.hpp>
      28              : #include <boost/url/grammar/error.hpp>
      29              : #include <boost/url/grammar/hexdig_chars.hpp>
      30              : 
      31              : #include "src/detail/brotli_filter_base.hpp"
      32              : #include "src/detail/buffer_utils.hpp"
      33              : #include "src/detail/zlib_filter_base.hpp"
      34              : 
      35              : namespace boost {
      36              : namespace http_proto {
      37              : 
      38              : /*
      39              :     Principles for fixed-size buffer design
      40              : 
      41              :     axiom 1:
      42              :         To read data you must have a buffer.
      43              : 
      44              :     axiom 2:
      45              :         The size of the HTTP header is not
      46              :         known in advance.
      47              : 
      48              :     conclusion 3:
      49              :         A single I/O can produce a complete
      50              :         HTTP header and additional payload
      51              :         data.
      52              : 
      53              :     conclusion 4:
      54              :         A single I/O can produce multiple
      55              :         complete HTTP headers, complete
      56              :         payloads, and a partial header or
      57              :         payload.
      58              : 
      59              :     axiom 5:
      60              :         A process is in one of two states:
      61              :             1. at or below capacity
      62              :             2. above capacity
      63              : 
      64              :     axiom 6:
      65              :         A program which can allocate an
      66              :         unbounded number of resources can
      67              :         go above capacity.
      68              : 
      69              :     conclusion 7:
      70              :         A program can guarantee never going
      71              :         above capacity if all resources are
      72              :         provisioned at program startup.
      73              : 
      74              :     corollary 8:
      75              :         `parser` and `serializer` should each
      76              :         allocate a single buffer of calculated
      77              :         size, and never resize it.
      78              : 
      79              :     axiom #:
      80              :         A parser and a serializer are always
      81              :         used in pairs.
      82              : 
      83              : Buffer Usage
      84              : 
      85              : |                                               | begin
      86              : | H |   p   |                               | f | read headers
      87              : | H |   p   |                           | T | f | set T body
      88              : | H |   p   |                       | C | T | f | make codec C
      89              : | H |   p           |       b       | C | T | f | decode p into b
      90              : | H |       p       |       b       | C | T | f | read/parse loop
      91              : | H |                                   | T | f | destroy codec
      92              : | H |                                   | T | f | finished
      93              : 
      94              :     H   headers
      95              :     C   codec
      96              :     T   body
      97              :     f   table
      98              :     p   partial payload
      99              :     b   body data
     100              : 
     101              :     "payload" is the bytes coming in from
     102              :         the stream.
     103              : 
     104              :     "body" is the logical body, after transfer
     105              :         encoding is removed. This can be the
     106              :         same as the payload.
     107              : 
     108              :     A "plain payload" is when the payload and
     109              :         body are identical (no transfer encodings).
     110              : 
     111              :     A "buffered payload" is any payload which is
     112              :         not plain. A second buffer is required
     113              :         for reading.
     114              : 
     115              :     "overread" is additional data received past
     116              :     the end of the headers when reading headers,
     117              :     or additional data received past the end of
     118              :     the message payload.
     119              : */
     120              : 
     121              : namespace {
     122              : 
     123              : class chained_sequence
     124              : {
     125              :     char const* pos_;
     126              :     char const* end_;
     127              :     char const* begin_b_;
     128              :     char const* end_b_;
     129              : 
     130              : public:
     131       120197 :     chained_sequence(buffers::const_buffer_pair const& cbp)
     132       120197 :         : pos_(static_cast<char const*>(cbp[0].data()))
     133       120197 :         , end_(pos_ + cbp[0].size())
     134       120197 :         , begin_b_(static_cast<char const*>(cbp[1].data()))
     135       120197 :         , end_b_(begin_b_ + cbp[1].size())
     136              :     {
     137       120197 :     }
     138              : 
     139              :     char const*
     140       618747 :     next() noexcept
     141              :     {
     142       618747 :         ++pos_;
     143              :         // most frequently taken branch
     144       618747 :         if(pos_ < end_)
     145       597426 :             return pos_;
     146              : 
     147              :         // bring the second range
     148        21321 :         if(begin_b_ != end_b_)
     149              :         {
     150           38 :             pos_ = begin_b_;
     151           38 :             end_ = end_b_;
     152           38 :             begin_b_ = end_b_;
     153           38 :             return pos_;
     154              :         }
     155              : 
     156              :         // undo the increament
     157        21283 :         pos_ = end_;
     158        21283 :         return nullptr;
     159              :     }
     160              : 
     161              :     bool
     162       410999 :     is_empty() const noexcept
     163              :     {
     164       410999 :         return pos_ == end_;
     165              :     }
     166              : 
     167              :     char
     168       604240 :     value() const noexcept
     169              :     {
     170       604240 :         return *pos_;
     171              :     }
     172              : 
     173              :     std::size_t
     174       424974 :     size() const noexcept
     175              :     {
     176       424974 :         return (end_ - pos_) + (end_b_ - begin_b_);
     177              :     }
     178              : };
     179              : 
     180              : std::uint64_t
     181       115957 : parse_hex(
     182              :     chained_sequence& cs,
     183              :     system::error_code& ec) noexcept
     184              : {
     185       115957 :     std::uint64_t v   = 0;
     186       115957 :     std::size_t init_size = cs.size();
     187       302778 :     while(!cs.is_empty())
     188              :     {
     189       283496 :         auto n = grammar::hexdig_value(cs.value());
     190       283496 :         if(n < 0)
     191              :         {
     192        96674 :             if(init_size == cs.size())
     193              :             {
     194            2 :                 ec = BOOST_HTTP_PROTO_ERR(
     195              :                     error::bad_payload);
     196            1 :                 return 0;
     197              :             }
     198        96673 :             return v;
     199              :         }
     200              : 
     201              :         // at least 4 significant bits are free
     202       186822 :         if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
     203              :         {
     204            2 :             ec = BOOST_HTTP_PROTO_ERR(
     205              :                 error::bad_payload);
     206            1 :             return 0;
     207              :         }
     208              : 
     209       186821 :         v = (v << 4) | static_cast<std::uint64_t>(n);
     210       186821 :         cs.next();
     211              :     }
     212        38564 :     ec = BOOST_HTTP_PROTO_ERR(
     213              :         error::need_data);
     214        19282 :     return 0;
     215              : }
     216              : 
     217              : void
     218        97025 : find_eol(
     219              :     chained_sequence& cs,
     220              :     system::error_code& ec) noexcept
     221              : {
     222       103714 :     while(!cs.is_empty())
     223              :     {
     224       103626 :         if(cs.value() == '\r')
     225              :         {
     226        96937 :             if(!cs.next())
     227           10 :                 break;
     228        96927 :             if(cs.value() != '\n')
     229              :             {
     230            4 :                 ec = BOOST_HTTP_PROTO_ERR(
     231              :                     error::bad_payload);
     232            2 :                 return;
     233              :             }
     234        96925 :             cs.next();
     235        96925 :             return;
     236              :         }
     237         6689 :         cs.next();
     238              :     }
     239          196 :     ec = BOOST_HTTP_PROTO_ERR(
     240              :         error::need_data);
     241              : }
     242              : 
     243              : void
     244       111557 : parse_eol(
     245              :     chained_sequence& cs,
     246              :     system::error_code& ec) noexcept
     247              : {
     248       111557 :     if(cs.size() >= 2)
     249              :     {
     250              :         // we are sure size is at least 2
     251       111543 :         if(cs.value() == '\r' && *cs.next() == '\n')
     252              :         {
     253       111540 :             cs.next();
     254       111540 :             return;
     255              :         }
     256            6 :         ec = BOOST_HTTP_PROTO_ERR(
     257              :             error::bad_payload);
     258            3 :         return;
     259              :     }
     260           28 :     ec = BOOST_HTTP_PROTO_ERR(
     261              :         error::need_data);
     262              : }
     263              : 
     264              : void
     265         4223 : skip_trailer_headers(
     266              :     chained_sequence& cs,
     267              :     system::error_code& ec) noexcept
     268              : {
     269         4507 :     while(!cs.is_empty())
     270              :     {
     271         4501 :         if(cs.value() == '\r')
     272              :         {
     273         4149 :             if(!cs.next())
     274            2 :                 break;
     275         4147 :             if(cs.value() != '\n')
     276              :             {
     277            4 :                 ec = BOOST_HTTP_PROTO_ERR(
     278              :                     error::bad_payload);
     279            2 :                 return;
     280              :             }
     281         4145 :             cs.next();
     282         4145 :             return;
     283              :         }
     284              :         // skip to the end of field
     285          352 :         find_eol(cs, ec);
     286          352 :         if(ec)
     287           68 :             return;
     288              :     }
     289           16 :     ec = BOOST_HTTP_PROTO_ERR(
     290              :         error::need_data);
     291              : }
     292              : 
     293              : template<class UInt>
     294              : std::size_t
     295       360934 : clamp(
     296              :     UInt x,
     297              :     std::size_t limit = (std::numeric_limits<
     298              :         std::size_t>::max)()) noexcept
     299              : {
     300       360934 :     if(x >= limit)
     301       101557 :         return limit;
     302       259377 :     return static_cast<std::size_t>(x);
     303              : }
     304              : 
     305              : class zlib_filter
     306              :     : public detail::zlib_filter_base
     307              : {
     308              :     capy::zlib::inflate_service& svc_;
     309              : 
     310              : public:
     311           72 :     zlib_filter(
     312              :         const capy::polystore& ctx,
     313              :         http_proto::detail::workspace& ws,
     314              :         int window_bits)
     315           72 :         : zlib_filter_base(ws)
     316           72 :         , svc_(ctx.get<capy::zlib::inflate_service>())
     317              :     {
     318              :         system::error_code ec = static_cast<capy::zlib::error>(
     319           72 :             svc_.init2(strm_, window_bits));
     320           72 :         if(ec != capy::zlib::error::ok)
     321            0 :             detail::throw_system_error(ec);
     322           72 :     }
     323              : 
     324              : private:
     325              :     virtual
     326              :     results
     327        56583 :     do_process(
     328              :         buffers::mutable_buffer out,
     329              :         buffers::const_buffer in,
     330              :         bool more) noexcept override
     331              :     {
     332        56583 :         strm_.next_out  = static_cast<unsigned char*>(out.data());
     333        56583 :         strm_.avail_out = saturate_cast(out.size());
     334        56583 :         strm_.next_in   = static_cast<unsigned char*>(const_cast<void *>(in.data()));
     335        56583 :         strm_.avail_in  = saturate_cast(in.size());
     336              : 
     337              :         auto rs = static_cast<capy::zlib::error>(
     338        56583 :             svc_.inflate(
     339        56583 :                 strm_,
     340              :                 more ? capy::zlib::no_flush : capy::zlib::finish));
     341              : 
     342        56583 :         results rv;
     343        56583 :         rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
     344        56583 :         rv.in_bytes  = saturate_cast(in.size()) - strm_.avail_in;
     345        56583 :         rv.finished  = (rs == capy::zlib::error::stream_end);
     346              : 
     347        56583 :         if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err)
     348            0 :             rv.ec = rs;
     349              : 
     350        56583 :         return rv;
     351              :     }
     352              : };
     353              : 
     354              : class brotli_filter
     355              :     : public detail::brotli_filter_base
     356              : {
     357              :     capy::brotli::decode_service& svc_;
     358              :     capy::brotli::decoder_state* state_;
     359              : 
     360              : public:
     361            0 :     brotli_filter(
     362              :         const capy::polystore& ctx,
     363              :         http_proto::detail::workspace&)
     364            0 :         : svc_(ctx.get<capy::brotli::decode_service>())
     365              :     {
     366              :         // TODO: use custom allocator
     367            0 :         state_ = svc_.create_instance(nullptr, nullptr, nullptr);
     368              : 
     369            0 :         if(!state_)
     370            0 :             detail::throw_bad_alloc();
     371            0 :     }
     372              : 
     373            0 :     ~brotli_filter()
     374            0 :     {
     375            0 :         svc_.destroy_instance(state_);
     376            0 :     }
     377              : 
     378              : private:
     379              :     virtual
     380              :     results
     381            0 :     do_process(
     382              :         buffers::mutable_buffer out,
     383              :         buffers::const_buffer in,
     384              :         bool more) noexcept override
     385              :     {
     386            0 :         auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
     387            0 :         auto available_in = in.size();
     388            0 :         auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
     389            0 :         auto available_out = out.size();
     390              : 
     391            0 :         auto rs = svc_.decompress_stream(
     392              :             state_,
     393              :             &available_in,
     394              :             &next_in,
     395              :             &available_out,
     396              :             &next_out,
     397              :             nullptr);
     398              : 
     399            0 :         results rv;
     400            0 :         rv.in_bytes  = in.size()  - available_in;
     401            0 :         rv.out_bytes = out.size() - available_out;
     402            0 :         rv.finished  = svc_.is_finished(state_);
     403              : 
     404            0 :         if(!more && rs == capy::brotli::decoder_result::needs_more_input)
     405            0 :             rv.ec = BOOST_HTTP_PROTO_ERR(error::bad_payload);
     406              : 
     407            0 :         if(rs == capy::brotli::decoder_result::error)
     408            0 :             rv.ec = BOOST_HTTP_PROTO_ERR(
     409              :                 svc_.get_error_code(state_));
     410              : 
     411            0 :         return rv;
     412              :     }
     413              : };
     414              : 
     415              : class parser_service
     416              : {
     417              : public:
     418              :     parser::config_base cfg;
     419              :     std::size_t space_needed = 0;
     420              :     std::size_t max_codec = 0;
     421              : 
     422           43 :     parser_service(
     423              :         parser::config_base const& cfg_)
     424           43 :         : cfg(cfg_)
     425              :     {
     426              :     /*
     427              :         | fb |     cb0     |     cb1     | C | T | f |
     428              : 
     429              :         fb  flat_buffer         headers.max_size
     430              :         cb0 circular_buffer     min_buffer
     431              :         cb1 circular_buffer     min_buffer
     432              :         C   codec               max_codec
     433              :         T   body                max_type_erase
     434              :         f   table               max_table_space
     435              : 
     436              :     */
     437              :         // validate
     438              :         //if(cfg.min_prepare > cfg.max_prepare)
     439              :             //detail::throw_invalid_argument();
     440              : 
     441           43 :         if(cfg.max_prepare < 1)
     442            0 :             detail::throw_invalid_argument();
     443              : 
     444              :         // VFALCO TODO OVERFLOW CHECING
     445              :         {
     446              :             //fb_.size() - h_.size +
     447              :             //svc_.cfg.min_buffer +
     448              :             //svc_.cfg.min_buffer +
     449              :             //svc_.max_codec;
     450              :         }
     451              : 
     452              :         // VFALCO OVERFLOW CHECKING ON THIS
     453           43 :         space_needed +=
     454           43 :             cfg.headers.valid_space_needed();
     455              : 
     456              :         // cb0_, cb1_
     457              :         // VFALCO OVERFLOW CHECKING ON THIS
     458           43 :         space_needed +=
     459           43 :             cfg.min_buffer +
     460              :             cfg.min_buffer;
     461              : 
     462              :         // T
     463           43 :         space_needed += cfg.max_type_erase;
     464              : 
     465              :         // max_codec
     466           43 :         if(cfg.apply_deflate_decoder || cfg.apply_gzip_decoder)
     467              :         {
     468              :             // TODO: Account for the number of allocations and
     469              :             // their overhead in the workspace.
     470              : 
     471              :             // https://www.zlib.net/zlib_tech.html
     472              :             std::size_t n =
     473            1 :                 (1 << cfg.zlib_window_bits) +
     474              :                 (7 * 1024) +
     475              :                 #ifdef __s390x__
     476              :                 5768 +
     477              :                 #endif
     478              :                 detail::workspace::space_needed<
     479            1 :                     zlib_filter>();
     480              : 
     481            1 :             if(max_codec < n)
     482            1 :                 max_codec = n;
     483              :         }
     484           43 :         space_needed += max_codec;
     485              : 
     486              :         // round up to alignof(detail::header::entry)
     487           43 :         auto const al = alignof(
     488              :             detail::header::entry);
     489           43 :         space_needed = al * ((
     490           43 :             space_needed + al - 1) / al);
     491           43 :     }
     492              : 
     493              :     std::size_t
     494        55255 :     max_overread() const noexcept
     495              :     {
     496              :         return
     497        55255 :             cfg.headers.max_size +
     498        55255 :             cfg.min_buffer;
     499              :     }
     500              : };
     501              : 
     502              : } // namespace
     503              : 
     504              : //------------------------------------------------
     505              : 
     506              : void
     507           43 : install_parser_service(
     508              :     capy::polystore& ctx,
     509              :     parser::config_base const& cfg)
     510              : {
     511           43 :     ctx.emplace<parser_service>(cfg);
     512           43 : }
     513              : 
     514              : //------------------------------------------------
     515              : 
     516              : class parser::impl
     517              : {
     518              :     enum class state
     519              :     {
     520              :         reset,
     521              :         start,
     522              :         header,
     523              :         header_done,
     524              :         body,
     525              :         set_body,
     526              :         complete_in_place,
     527              :         complete
     528              :     };
     529              : 
     530              :     enum class style
     531              :     {
     532              :         in_place,
     533              :         sink,
     534              :         elastic,
     535              :     };
     536              : 
     537              :     const capy::polystore& ctx_;
     538              :     parser_service& svc_;
     539              : 
     540              :     detail::workspace ws_;
     541              :     static_request m_;
     542              :     std::uint64_t body_limit_;
     543              :     std::uint64_t body_total_;
     544              :     std::uint64_t payload_remain_;
     545              :     std::uint64_t chunk_remain_;
     546              :     std::size_t body_avail_;
     547              :     std::size_t nprepare_;
     548              : 
     549              :     buffers::flat_buffer fb_;
     550              :     buffers::circular_buffer cb0_;
     551              :     buffers::circular_buffer cb1_;
     552              : 
     553              :     buffers::mutable_buffer_pair mbp_;
     554              :     buffers::const_buffer_pair cbp_;
     555              : 
     556              :     detail::filter* filter_;
     557              :     buffers::any_dynamic_buffer* eb_;
     558              :     sink* sink_;
     559              : 
     560              :     state state_;
     561              :     style style_;
     562              :     bool got_header_;
     563              :     bool got_eof_;
     564              :     bool head_response_;
     565              :     bool needs_chunk_close_;
     566              :     bool trailer_headers_;
     567              :     bool chunked_body_ended;
     568              : 
     569              : public:
     570         1056 :     impl(const capy::polystore& ctx, detail::kind k)
     571         1056 :         : ctx_(ctx)
     572         1056 :         , svc_(ctx.get<parser_service>())
     573         1056 :         , ws_(svc_.space_needed)
     574         1056 :         , m_(ws_.data(), ws_.size())
     575         1056 :         , state_(state::reset)
     576         1056 :         , got_header_(false)
     577              :     {
     578         1056 :         m_.h_ = detail::header(detail::empty{ k });
     579         1056 :     }
     580              : 
     581              :     bool
     582        11938 :     got_header() const noexcept
     583              :     {
     584        11938 :         return got_header_;
     585              :     }
     586              : 
     587              :     bool
     588        51344 :     is_complete() const noexcept
     589              :     {
     590        51344 :         return state_ >= state::complete_in_place;
     591              :     }
     592              : 
     593              :     static_request const&
     594          315 :     safe_get_request() const
     595              :     {
     596              :         // headers must be received
     597          315 :         if(! got_header_)
     598            0 :             detail::throw_logic_error();
     599              : 
     600          315 :         return m_;
     601              :     }
     602              : 
     603              :     static_response const&
     604            1 :     safe_get_response() const
     605              :     {
     606              :         // headers must be received
     607            1 :         if(! got_header_)
     608            0 :             detail::throw_logic_error();
     609              : 
     610              :         // TODO: use a union
     611            1 :         return reinterpret_cast<static_response const&>(m_);
     612              :     }
     613              : 
     614              :     bool
     615          755 :     is_body_set() const noexcept
     616              :     {
     617          755 :         return style_ != style::in_place;
     618              :     }
     619              : 
     620              :     void
     621         2480 :     reset() noexcept
     622              :     {
     623         2480 :         ws_.clear();
     624         2480 :         state_ = state::start;
     625         2480 :         got_header_ = false;
     626         2480 :         got_eof_ = false;
     627         2480 :     }
     628              : 
     629              :     void
     630        10307 :     start(
     631              :         bool head_response)
     632              :     {
     633        10307 :         std::size_t leftover = 0;
     634        10307 :         switch(state_)
     635              :         {
     636            1 :         default:
     637              :         case state::reset:
     638              :             // reset must be called first
     639            1 :             detail::throw_logic_error();
     640              : 
     641         2255 :         case state::start:
     642              :             // reset required on eof
     643         2255 :             if(got_eof_)
     644            0 :                 detail::throw_logic_error();
     645         2255 :             break;
     646              : 
     647            3 :         case state::header:
     648            3 :             if(fb_.size() == 0)
     649              :             {
     650              :                 // start() called twice
     651            2 :                 detail::throw_logic_error();
     652              :             }
     653              :             BOOST_FALLTHROUGH;
     654              : 
     655              :         case state::header_done:
     656              :         case state::body:
     657              :         case state::set_body:
     658              :             // current message is incomplete
     659            2 :             detail::throw_logic_error();
     660              : 
     661         8015 :         case state::complete_in_place:
     662              :             // remove available body.
     663         8015 :             if(is_plain())
     664         4000 :                 cb0_.consume(body_avail_);
     665              :             BOOST_FALLTHROUGH;
     666              : 
     667              :         case state::complete:
     668              :         {
     669              :             // move leftovers to front
     670              : 
     671         8047 :             ws_.clear();
     672         8047 :             leftover = cb0_.size();
     673              : 
     674         8047 :             auto* dest = reinterpret_cast<char*>(ws_.data());
     675         8047 :             auto cbp   = cb0_.data();
     676         8047 :             auto* a    = static_cast<char const*>(cbp[0].data());
     677         8047 :             auto* b    = static_cast<char const*>(cbp[1].data());
     678         8047 :             auto an    = cbp[0].size();
     679         8047 :             auto bn    = cbp[1].size();
     680              : 
     681         8047 :             if(bn == 0)
     682              :             {
     683         7609 :                 std::memmove(dest, a, an);
     684              :             }
     685              :             else
     686              :             {
     687              :                 // if `a` can fit between `dest` and `b`, shift `b` to the left
     688              :                 // and copy `a` to its position. if `a` fits perfectly, the
     689              :                 // shift will be of size 0.
     690              :                 // if `a` requires more space, shift `b` to the right and
     691              :                 // copy `a` to its position. this process may require multiple
     692              :                 // iterations and should be done chunk by chunk to prevent `b`
     693              :                 // from overlapping with `a`.
     694              :                 do
     695              :                 {
     696              :                     // clamp right shifts to prevent overlap with `a`
     697          438 :                     auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
     698          438 :                     b = static_cast<char const*>(std::memmove(bp, b, bn));
     699              : 
     700              :                     // a chunk or all of `a` based on available space
     701          438 :                     auto chunk_a = static_cast<std::size_t>(b - dest);
     702          438 :                     std::memcpy(dest, a, chunk_a); // never overlap
     703          438 :                     an   -= chunk_a;
     704          438 :                     dest += chunk_a;
     705          438 :                     a    += chunk_a;
     706          438 :                 } while(an);
     707              :             }
     708              : 
     709         8047 :             break;
     710              :         }
     711              :         }
     712              : 
     713        10302 :         ws_.clear();
     714              : 
     715        20604 :         fb_ = {
     716        10302 :             ws_.data(),
     717        10302 :             svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
     718              :             leftover };
     719              : 
     720        10302 :         BOOST_ASSERT(
     721              :             fb_.capacity() == svc_.max_overread() - leftover);
     722              : 
     723        10302 :         BOOST_ASSERT(
     724              :             head_response == false ||
     725              :             m_.h_.kind == detail::kind::response);
     726              : 
     727        10302 :         m_.h_ = detail::header(detail::empty{m_.h_.kind});
     728        10302 :         m_.h_.buf = reinterpret_cast<char*>(ws_.data());
     729        10302 :         m_.h_.cbuf = m_.h_.buf;
     730        10302 :         m_.h_.cap = ws_.size();
     731              : 
     732        10302 :         state_ = state::header;
     733        10302 :         style_ = style::in_place;
     734              : 
     735              :         // reset to the configured default
     736        10302 :         body_limit_ = svc_.cfg.body_limit;
     737              : 
     738        10302 :         body_total_ = 0;
     739        10302 :         payload_remain_ = 0;
     740        10302 :         chunk_remain_ = 0;
     741        10302 :         body_avail_ = 0;
     742        10302 :         nprepare_ = 0;
     743              : 
     744        10302 :         filter_ = nullptr;
     745        10302 :         eb_ = nullptr;
     746        10302 :         sink_ = nullptr;
     747              : 
     748        10302 :         got_header_ = false;
     749        10302 :         head_response_ = head_response;
     750        10302 :         needs_chunk_close_ = false;
     751        10302 :         trailer_headers_ = false;
     752        10302 :         chunked_body_ended = false;
     753        10302 :     }
     754              : 
     755              :     auto
     756        51003 :     prepare() ->
     757              :         mutable_buffers_type
     758              :     {
     759        51003 :         nprepare_ = 0;
     760              : 
     761        51003 :         switch(state_)
     762              :         {
     763            1 :         default:
     764              :         case state::reset:
     765              :             // reset must be called first
     766            1 :             detail::throw_logic_error();
     767              : 
     768            1 :         case state::start:
     769              :             // start must be called first
     770            1 :             detail::throw_logic_error();
     771              : 
     772        10374 :         case state::header:
     773              :         {
     774        10374 :             BOOST_ASSERT(
     775              :                 m_.h_.size < svc_.cfg.headers.max_size);
     776        10374 :             std::size_t n = fb_.capacity() - fb_.size();
     777        10374 :             BOOST_ASSERT(n <= svc_.max_overread());
     778        10374 :             n = clamp(n, svc_.cfg.max_prepare);
     779        10374 :             mbp_[0] = fb_.prepare(n);
     780        10374 :             nprepare_ = n;
     781        10374 :             return mutable_buffers_type(&mbp_[0], 1);
     782              :         }
     783              : 
     784            0 :         case state::header_done:
     785              :             // forgot to call parse()
     786            0 :             detail::throw_logic_error();
     787              : 
     788        40626 :         case state::body:
     789              :         {
     790        40626 :             if(got_eof_)
     791              :             {
     792              :                 // forgot to call parse()
     793            0 :                 detail::throw_logic_error();
     794              :             }
     795              : 
     796        40626 :             if(! is_plain())
     797              :             {
     798              :                 // buffered payload
     799        21575 :                 std::size_t n = cb0_.capacity();
     800        21575 :                 n = clamp(n, svc_.cfg.max_prepare);
     801        21575 :                 nprepare_ = n;
     802        21575 :                 mbp_ = cb0_.prepare(n);
     803        21575 :                 return detail::make_span(mbp_);
     804              :             }
     805              :             else
     806              :             {
     807        19051 :                 switch(style_)
     808              :                 {
     809        19030 :                 default:
     810              :                 case style::in_place:
     811              :                 case style::sink:
     812              :                 {
     813        19030 :                     std::size_t n = cb0_.capacity();
     814        19030 :                     n = clamp(n, svc_.cfg.max_prepare);
     815              : 
     816        19030 :                     if(m_.payload() == payload::size)
     817              :                     {
     818        19005 :                         if(n > payload_remain_)
     819              :                         {
     820        17798 :                             std::size_t overread =
     821        17798 :                                 n - static_cast<std::size_t>(payload_remain_);
     822        17798 :                             if(overread > svc_.max_overread())
     823         7878 :                                 n = static_cast<std::size_t>(payload_remain_) +
     824         7878 :                                     svc_.max_overread();
     825              :                         }
     826              :                     }
     827              :                     else
     828              :                     {
     829           25 :                         BOOST_ASSERT(
     830              :                             m_.payload() == payload::to_eof);
     831              :                         // No more messages can be pipelined, so
     832              :                         // limit the output buffer to the remaining
     833              :                         // body limit plus one byte to detect
     834              :                         // exhaustion.
     835           25 :                         std::uint64_t r = body_limit_remain();
     836           25 :                         if(r != std::uint64_t(-1))
     837           25 :                             r += 1;
     838           25 :                         n = clamp(r, n);
     839              :                     }
     840              : 
     841        19030 :                     nprepare_ = n;
     842        19030 :                     mbp_ = cb0_.prepare(n);
     843        19030 :                     return detail::make_span(mbp_);
     844              :                 }
     845           21 :                 case style::elastic:
     846              :                 {
     847           21 :                     BOOST_ASSERT(cb0_.size() == 0);
     848           21 :                     BOOST_ASSERT(body_avail_ == 0);
     849              : 
     850           21 :                     std::size_t n = svc_.cfg.min_buffer;
     851              : 
     852           21 :                     if(m_.payload() == payload::size)
     853              :                     {
     854              :                         // Overreads are not allowed, or
     855              :                         // else the caller will see extra
     856              :                         // unrelated data.
     857            6 :                         n = clamp(payload_remain_, n);
     858              :                     }
     859              :                     else
     860              :                     {
     861           15 :                         BOOST_ASSERT(
     862              :                             m_.payload() == payload::to_eof);
     863              :                         // No more messages can be pipelined, so
     864              :                         // limit the output buffer to the remaining
     865              :                         // body limit plus one byte to detect
     866              :                         // exhaustion.
     867           15 :                         std::uint64_t r = body_limit_remain();
     868           15 :                         if(r != std::uint64_t(-1))
     869           15 :                             r += 1;
     870           15 :                         n = clamp(r, n);
     871           15 :                         n = clamp(n, eb_->max_size() - eb_->size());
     872              :                         // fill capacity first to avoid an allocation
     873              :                         std::size_t avail =
     874           15 :                             eb_->capacity() - eb_->size();
     875           15 :                         if(avail != 0)
     876           15 :                             n = clamp(n, avail);
     877              : 
     878           15 :                         if(n == 0)
     879              :                         {
     880              :                             // dynamic buffer is full
     881              :                             // attempt a 1 byte read so
     882              :                             // we can detect overflow
     883            1 :                             nprepare_ = 1;
     884            1 :                             mbp_ = cb0_.prepare(1);
     885            1 :                             return detail::make_span(mbp_);
     886              :                         }
     887              :                     }
     888              : 
     889           20 :                     n = clamp(n, svc_.cfg.max_prepare);
     890           20 :                     BOOST_ASSERT(n != 0);
     891           20 :                     nprepare_ = n;
     892           20 :                     return eb_->prepare(n);
     893              :                 }
     894              :                 }
     895              :             }
     896              :         }
     897              : 
     898            0 :         case state::set_body:
     899              :             // forgot to call parse()
     900            0 :             detail::throw_logic_error();
     901              : 
     902            1 :         case state::complete_in_place:
     903              :         case state::complete:
     904              :             // already complete
     905            1 :             detail::throw_logic_error();
     906              :         }
     907              :     }
     908              : 
     909              :     void
     910        51000 :     commit(
     911              :         std::size_t n)
     912              :     {
     913        51000 :         switch(state_)
     914              :         {
     915            1 :         default:
     916              :         case state::reset:
     917              :         {
     918              :             // reset must be called first
     919            1 :             detail::throw_logic_error();
     920              :         }
     921              : 
     922            1 :         case state::start:
     923              :         {
     924              :             // forgot to call start()
     925            1 :             detail::throw_logic_error();
     926              :         }
     927              : 
     928        10374 :         case state::header:
     929              :         {
     930        10374 :             if(n > nprepare_)
     931              :             {
     932              :                 // n can't be greater than size of
     933              :                 // the buffers returned by prepare()
     934            1 :                 detail::throw_invalid_argument();
     935              :             }
     936              : 
     937        10373 :             if(got_eof_)
     938              :             {
     939              :                 // can't commit after EOF
     940            1 :                 detail::throw_logic_error();
     941              :             }
     942              : 
     943        10372 :             nprepare_ = 0; // invalidate
     944        10372 :             fb_.commit(n);
     945        10372 :             break;
     946              :         }
     947              : 
     948            0 :         case state::header_done:
     949              :         {
     950              :             // forgot to call parse()
     951            0 :             detail::throw_logic_error();
     952              :         }
     953              : 
     954        40623 :         case state::body:
     955              :         {
     956        40623 :             if(n > nprepare_)
     957              :             {
     958              :                 // n can't be greater than size of
     959              :                 // the buffers returned by prepare()
     960            2 :                 detail::throw_invalid_argument();
     961              :             }
     962              : 
     963        40621 :             if(got_eof_)
     964              :             {
     965              :                 // can't commit after EOF
     966            0 :                 detail::throw_logic_error();
     967              :             }
     968              :         
     969        40621 :             nprepare_ = 0; // invalidate
     970        40621 :             if(is_plain() && style_ == style::elastic)
     971              :             {
     972           20 :                 if(eb_->max_size() == eb_->size())
     973              :                 {
     974              :                     // borrowed 1 byte from
     975              :                     // cb0_ in prepare()
     976            1 :                     BOOST_ASSERT(n <= 1);
     977            1 :                     cb0_.commit(n);
     978              :                 }
     979              :                 else
     980              :                 {
     981           19 :                     eb_->commit(n);
     982           19 :                     payload_remain_ -= n;
     983           19 :                     body_total_     += n;
     984              :                 }
     985              :             }
     986              :             else
     987              :             {
     988        40601 :                 cb0_.commit(n);
     989              :             }
     990        40621 :             break;
     991              :         }
     992              : 
     993            0 :         case state::set_body:
     994              :         {
     995              :             // forgot to call parse()
     996            0 :             detail::throw_logic_error();
     997              :         }
     998              : 
     999            1 :         case state::complete_in_place:
    1000              :         case state::complete:
    1001              :         {
    1002              :             // already complete
    1003            1 :             detail::throw_logic_error();
    1004              :         }
    1005              :         }
    1006        50993 :     }
    1007              : 
    1008              :     void
    1009          401 :     commit_eof()
    1010              :     {
    1011          401 :         nprepare_ = 0; // invalidate
    1012              : 
    1013          401 :         switch(state_)
    1014              :         {
    1015            1 :         default:
    1016              :         case state::reset:
    1017              :             // reset must be called first
    1018            1 :             detail::throw_logic_error();
    1019              : 
    1020            1 :         case state::start:
    1021              :             // forgot to call start()
    1022            1 :             detail::throw_logic_error();
    1023              : 
    1024           30 :         case state::header:
    1025           30 :             got_eof_ = true;
    1026           30 :             break;
    1027              : 
    1028            0 :         case state::header_done:
    1029              :             // forgot to call parse()
    1030            0 :             detail::throw_logic_error();
    1031              : 
    1032          368 :         case state::body:
    1033          368 :             got_eof_ = true;
    1034          368 :             break;
    1035              : 
    1036            0 :         case state::set_body:
    1037              :             // forgot to call parse()
    1038            0 :             detail::throw_logic_error();
    1039              : 
    1040            1 :         case state::complete_in_place:
    1041              :         case state::complete:
    1042              :             // can't commit eof when complete
    1043            1 :             detail::throw_logic_error();
    1044              :         }
    1045          398 :     }
    1046              : 
    1047              :     void
    1048        69827 :     parse(
    1049              :         system::error_code& ec)
    1050              :     {
    1051        69827 :         ec = {};
    1052        69827 :         switch(state_)
    1053              :         {
    1054            1 :         default:
    1055              :         case state::reset:
    1056              :             // reset must be called first
    1057            1 :             detail::throw_logic_error();
    1058              : 
    1059            1 :         case state::start:
    1060              :             // start must be called first
    1061            1 :             detail::throw_logic_error();
    1062              : 
    1063        16650 :         case state::header:
    1064              :         {
    1065        16650 :             BOOST_ASSERT(m_.h_.buf == static_cast<
    1066              :                 void const*>(ws_.data()));
    1067        16650 :             BOOST_ASSERT(m_.h_.cbuf == static_cast<
    1068              :                 void const*>(ws_.data()));
    1069              : 
    1070        16650 :             m_.h_.parse(fb_.size(), svc_.cfg.headers, ec);
    1071              : 
    1072        16650 :             if(ec == condition::need_more_input)
    1073              :             {
    1074         6385 :                 if(! got_eof_)
    1075              :                 {
    1076              :                     // headers incomplete
    1077         6358 :                     return;
    1078              :                 }
    1079              : 
    1080           27 :                 if(fb_.size() == 0)
    1081              :                 {
    1082              :                     // stream closed cleanly
    1083           12 :                     state_ = state::reset;
    1084           24 :                     ec = BOOST_HTTP_PROTO_ERR(
    1085              :                         error::end_of_stream);
    1086           12 :                     return;
    1087              :                 }
    1088              : 
    1089              :                 // stream closed with a
    1090              :                 // partial message received
    1091           15 :                 state_ = state::reset;
    1092           30 :                 ec = BOOST_HTTP_PROTO_ERR(
    1093              :                     error::incomplete);
    1094           15 :                 return;
    1095              :             }
    1096        10265 :             else if(ec.failed())
    1097              :             {
    1098              :                 // other error,
    1099              :                 //
    1100              :                 // VFALCO map this to a bad
    1101              :                 // request or bad response error?
    1102              :                 //
    1103          259 :                 state_ = state::reset; // unrecoverable
    1104          259 :                 return;
    1105              :             }
    1106              : 
    1107        10006 :             got_header_ = true;
    1108              : 
    1109              :             // reserve headers + table
    1110        10006 :             ws_.reserve_front(m_.h_.size);
    1111        10006 :             ws_.reserve_back(m_.h_.table_space());
    1112              : 
    1113              :             // no payload
    1114        19090 :             if(m_.payload() == payload::none ||
    1115         9084 :                 head_response_)
    1116              :             {
    1117              :                 // octets of the next message
    1118          922 :                 auto overread = fb_.size() - m_.h_.size;
    1119          922 :                 cb0_ = { ws_.data(), overread, overread };
    1120          922 :                 ws_.reserve_front(overread);
    1121          922 :                 state_ = state::complete_in_place;
    1122          922 :                 return;
    1123              :             }
    1124              : 
    1125         9084 :             state_ = state::header_done;
    1126         9084 :             break;
    1127              :         }
    1128              : 
    1129         9083 :         case state::header_done:
    1130              :         {
    1131              :             // metadata error
    1132         9083 :             if(m_.payload() == payload::error)
    1133              :             {
    1134              :                 // VFALCO This needs looking at
    1135          360 :                 ec = BOOST_HTTP_PROTO_ERR(
    1136              :                     error::bad_payload);
    1137          180 :                 state_ = state::reset; // unrecoverable
    1138          180 :                 return;
    1139              :             }
    1140              : 
    1141              :             // overread currently includes any and all octets that
    1142              :             // extend beyond the current end of the header
    1143              :             // this can include associated body octets for the
    1144              :             // current message or octets of the next message in the
    1145              :             // stream, e.g. pipelining is being used
    1146         8903 :             auto const overread = fb_.size() - m_.h_.size;
    1147         8903 :             BOOST_ASSERT(overread <= svc_.max_overread());
    1148              : 
    1149         8903 :             auto cap = fb_.capacity() + overread +
    1150         8903 :                 svc_.cfg.min_buffer;
    1151              : 
    1152              :             // reserve body buffers first, as the decoder
    1153              :             // must be installed after them.
    1154         8903 :             auto const p = ws_.reserve_front(cap);
    1155              : 
    1156         8903 :             switch(m_.metadata().content_encoding.coding)
    1157              :             {
    1158           36 :             case content_coding::deflate:
    1159           36 :                 if(!svc_.cfg.apply_deflate_decoder)
    1160            0 :                     goto no_filter;
    1161           72 :                 filter_ = &ws_.emplace<zlib_filter>(
    1162           36 :                     ctx_, ws_, svc_.cfg.zlib_window_bits);
    1163           36 :                 break;
    1164              : 
    1165           36 :             case content_coding::gzip:
    1166           36 :                 if(!svc_.cfg.apply_gzip_decoder)
    1167            0 :                     goto no_filter;
    1168           72 :                 filter_ = &ws_.emplace<zlib_filter>(
    1169           36 :                     ctx_, ws_, svc_.cfg.zlib_window_bits + 16);
    1170           36 :                 break;
    1171              : 
    1172            0 :             case content_coding::br:
    1173            0 :                 if(!svc_.cfg.apply_brotli_decoder)
    1174            0 :                     goto no_filter;
    1175            0 :                 filter_ = &ws_.emplace<brotli_filter>(
    1176            0 :                     ctx_, ws_);
    1177            0 :                 break;
    1178              : 
    1179            0 :             no_filter:
    1180         8831 :             default:
    1181         8831 :                 cap += svc_.max_codec;
    1182         8831 :                 ws_.reserve_front(svc_.max_codec);
    1183         8831 :                 break;
    1184              :             }
    1185              : 
    1186         8903 :             if(is_plain() || style_ == style::elastic)
    1187              :             {
    1188         4722 :                 cb0_ = { p, cap, overread };
    1189         4722 :                 cb1_ = {};
    1190              :             }
    1191              :             else
    1192              :             {
    1193              :                 // buffered payload
    1194         8362 :                 std::size_t n0 = (overread > svc_.cfg.min_buffer)
    1195         4181 :                     ? overread
    1196         4157 :                     : svc_.cfg.min_buffer;
    1197         4181 :                 std::size_t n1 = svc_.cfg.min_buffer;
    1198              : 
    1199         4181 :                 cb0_ = { p      , n0, overread };
    1200         4181 :                 cb1_ = { p + n0 , n1 };
    1201              :             }
    1202              : 
    1203         8903 :             if(m_.payload() == payload::size)
    1204              :             {
    1205         8724 :                 if(!filter_ &&
    1206         4350 :                     body_limit_ < m_.payload_size())
    1207              :                 {
    1208            2 :                     ec = BOOST_HTTP_PROTO_ERR(
    1209              :                         error::body_too_large);
    1210            1 :                     state_ = state::reset;
    1211            1 :                     return;
    1212              :                 }
    1213         4373 :                 payload_remain_ = m_.payload_size();
    1214              :             }
    1215              : 
    1216         8902 :             state_ = state::body;
    1217              :             BOOST_FALLTHROUGH;
    1218              :         }
    1219              : 
    1220              :         case state::body:
    1221              :         {
    1222        50201 :         do_body:
    1223        50201 :             BOOST_ASSERT(state_ == state::body);
    1224        50201 :             BOOST_ASSERT(m_.payload() != payload::none);
    1225        50201 :             BOOST_ASSERT(m_.payload() != payload::error);
    1226              : 
    1227         8797 :             auto set_state_to_complete = [&]()
    1228              :             {
    1229         8797 :                 if(style_ == style::in_place)
    1230              :                 {
    1231         8337 :                     state_ = state::complete_in_place;
    1232         8337 :                     return;
    1233              :                 }
    1234          460 :                 state_ = state::complete;
    1235        50201 :             };
    1236              : 
    1237        50201 :             if(m_.payload() == payload::chunked)
    1238              :             {
    1239              :                 for(;;)
    1240              :                 {
    1241       125495 :                     if(chunk_remain_ == 0
    1242       124342 :                         && !chunked_body_ended)
    1243              :                     {
    1244       120197 :                         auto cs = chained_sequence(cb0_.data());
    1245        19411 :                         auto check_ec = [&]()
    1246              :                         {
    1247        19411 :                             if(ec == condition::need_more_input && got_eof_)
    1248              :                             {
    1249            0 :                                 ec = BOOST_HTTP_PROTO_ERR(error::incomplete);
    1250            0 :                                 state_ = state::reset;
    1251              :                             }
    1252       139608 :                         };
    1253              : 
    1254       120197 :                         if(needs_chunk_close_)
    1255              :                         {
    1256       111557 :                             parse_eol(cs, ec);
    1257       111557 :                             if(ec)
    1258              :                             {
    1259           17 :                                 check_ec();
    1260        19411 :                                 return;
    1261              :                             }
    1262              :                         }
    1263         8640 :                         else if(trailer_headers_)
    1264              :                         {
    1265         4223 :                             skip_trailer_headers(cs, ec);
    1266         4223 :                             if(ec)
    1267              :                             {
    1268           78 :                                 check_ec();
    1269           78 :                                 return;
    1270              :                             }
    1271         4145 :                             cb0_.consume(cb0_.size() - cs.size());
    1272         4145 :                             chunked_body_ended = true;
    1273         8292 :                             continue;
    1274              :                         }
    1275              :                         
    1276       115957 :                         auto chunk_size = parse_hex(cs, ec);
    1277       115957 :                         if(ec)
    1278              :                         {
    1279        19284 :                             check_ec();
    1280        19284 :                             return;
    1281              :                         }
    1282              : 
    1283              :                         // skip chunk extensions
    1284        96673 :                         find_eol(cs, ec);
    1285        96673 :                         if(ec)
    1286              :                         {
    1287           32 :                             check_ec();
    1288           32 :                             return;
    1289              :                         }
    1290              : 
    1291        96641 :                         cb0_.consume(cb0_.size() - cs.size());
    1292        96641 :                         chunk_remain_ = chunk_size;
    1293              : 
    1294        96641 :                         needs_chunk_close_ = true;
    1295        96641 :                         if(chunk_remain_ == 0)
    1296              :                         {
    1297         4147 :                             needs_chunk_close_ = false;
    1298         4147 :                             trailer_headers_ = true;
    1299         4147 :                             continue;
    1300              :                         }
    1301              :                     }
    1302              : 
    1303        97792 :                     if(cb0_.size() == 0 && !chunked_body_ended)
    1304              :                     {
    1305          340 :                         if(got_eof_)
    1306              :                         {
    1307            2 :                             ec = BOOST_HTTP_PROTO_ERR(
    1308              :                                 error::incomplete);
    1309            1 :                             state_ = state::reset;
    1310            1 :                             return;
    1311              :                         }
    1312              : 
    1313          678 :                         ec = BOOST_HTTP_PROTO_ERR(
    1314              :                             error::need_data);
    1315          339 :                         return;
    1316              :                     }
    1317              : 
    1318        97452 :                     if(filter_)
    1319              :                     {
    1320        51070 :                         chunk_remain_ -= apply_filter(
    1321              :                             ec,
    1322              :                             clamp(chunk_remain_, cb0_.size()),
    1323        51070 :                             !chunked_body_ended);
    1324              : 
    1325        51070 :                         if(ec || chunked_body_ended)
    1326          564 :                             return;
    1327              :                     }
    1328              :                     else
    1329              :                     {
    1330              :                         const std::size_t chunk_avail =
    1331        46382 :                             clamp(chunk_remain_, cb0_.size());
    1332              :                         const auto chunk =
    1333        46382 :                             buffers::prefix(cb0_.data(), chunk_avail);
    1334              : 
    1335        46382 :                         if(body_limit_remain() < chunk_avail)
    1336              :                         {
    1337            0 :                             ec = BOOST_HTTP_PROTO_ERR(
    1338              :                                 error::body_too_large);
    1339            0 :                             state_ = state::reset;
    1340         4121 :                             return;
    1341              :                         }
    1342              : 
    1343        46382 :                         switch(style_)
    1344              :                         {
    1345        46382 :                         case style::in_place:
    1346              :                         {
    1347        46382 :                             auto copied = buffers::copy(
    1348        46382 :                                 cb1_.prepare(cb1_.capacity()),
    1349              :                                 chunk);
    1350        46382 :                             chunk_remain_ -= copied;
    1351        46382 :                             body_avail_   += copied;
    1352        46382 :                             body_total_   += copied;
    1353        46382 :                             cb0_.consume(copied);
    1354        46382 :                             cb1_.commit(copied);
    1355        46382 :                             if(cb1_.capacity() == 0
    1356        46382 :                                 && !chunked_body_ended)
    1357              :                             {
    1358            0 :                                 ec = BOOST_HTTP_PROTO_ERR(
    1359              :                                     error::in_place_overflow);
    1360            0 :                                 return;
    1361              :                             }
    1362        46382 :                             break;
    1363              :                         }
    1364            0 :                         case style::sink:
    1365              :                         {
    1366            0 :                             auto sink_rs = sink_->write(
    1367            0 :                                 chunk, !chunked_body_ended);
    1368            0 :                             chunk_remain_ -= sink_rs.bytes;
    1369            0 :                             body_total_   += sink_rs.bytes;
    1370            0 :                             cb0_.consume(sink_rs.bytes);
    1371            0 :                             if(sink_rs.ec.failed())
    1372              :                             {
    1373            0 :                                 body_avail_ += 
    1374            0 :                                     chunk_avail - sink_rs.bytes;
    1375            0 :                                 ec  = sink_rs.ec;
    1376            0 :                                 state_ = state::reset;
    1377            0 :                                 return;
    1378              :                             }
    1379            0 :                             break;
    1380              :                         }
    1381            0 :                         case style::elastic:
    1382              :                         {
    1383            0 :                             if(eb_->max_size() - eb_->size()
    1384            0 :                                 < chunk_avail)
    1385              :                             {
    1386            0 :                                 ec = BOOST_HTTP_PROTO_ERR(
    1387              :                                     error::buffer_overflow);
    1388            0 :                                 state_ = state::reset;
    1389            0 :                                 return;
    1390              :                             }
    1391            0 :                             buffers::copy(
    1392            0 :                                 eb_->prepare(chunk_avail),
    1393              :                                 chunk);
    1394            0 :                             chunk_remain_ -= chunk_avail;
    1395            0 :                             body_total_   += chunk_avail;
    1396            0 :                             cb0_.consume(chunk_avail);
    1397            0 :                             eb_->commit(chunk_avail);
    1398            0 :                             break;
    1399              :                         }
    1400              :                         }
    1401              : 
    1402        46382 :                         if(chunked_body_ended)
    1403              :                         {
    1404         4121 :                             set_state_to_complete();
    1405         4121 :                             return;
    1406              :                         }
    1407              :                     }
    1408       101059 :                 }
    1409              :             }
    1410              :             else
    1411              :             {
    1412              :                 // non-chunked payload
    1413              : 
    1414        77295 :                 const std::size_t payload_avail = [&]()
    1415              :                 {
    1416        25765 :                     auto ret = cb0_.size();
    1417        25765 :                     if(!filter_)
    1418        24121 :                         ret -= body_avail_;
    1419        25765 :                     if(m_.payload() == payload::size)
    1420        24159 :                         return clamp(payload_remain_, ret);
    1421              :                     // payload::eof
    1422         1606 :                     return ret;
    1423        25765 :                 }();
    1424              : 
    1425        77295 :                 const bool is_complete = [&]()
    1426              :                 {
    1427        25765 :                     if(m_.payload() == payload::size)
    1428        24159 :                         return payload_avail == payload_remain_;
    1429              :                     // payload::eof
    1430         1606 :                     return got_eof_;
    1431        25765 :                 }();
    1432              : 
    1433        25765 :                 if(filter_)
    1434              :                 {
    1435         3288 :                     payload_remain_ -= apply_filter(
    1436         1644 :                         ec, payload_avail, !is_complete);
    1437         1644 :                     if(ec || is_complete)
    1438         1128 :                         return;
    1439              :                 }
    1440              :                 else
    1441              :                 {
    1442              :                     // plain body
    1443              : 
    1444        24121 :                     if(m_.payload() == payload::to_eof)
    1445              :                     {
    1446          764 :                         if(body_limit_remain() < payload_avail)
    1447              :                         {
    1448            2 :                             ec = BOOST_HTTP_PROTO_ERR(
    1449              :                                 error::body_too_large);
    1450            1 :                             state_ = state::reset;
    1451            1 :                             return;
    1452              :                         }
    1453              :                     }
    1454              : 
    1455        24120 :                     switch(style_)
    1456              :                     {
    1457        23363 :                     case style::in_place:
    1458              :                     {
    1459        23363 :                         payload_remain_ -= payload_avail;
    1460        23363 :                         body_avail_     += payload_avail;
    1461        23363 :                         body_total_     += payload_avail;
    1462        23363 :                         if(cb0_.capacity() == 0 && !is_complete)
    1463              :                         {
    1464            2 :                             ec = BOOST_HTTP_PROTO_ERR(
    1465              :                                 error::in_place_overflow);
    1466            1 :                             return;
    1467              :                         }
    1468        23362 :                         break;
    1469              :                     }
    1470          371 :                     case style::sink:
    1471              :                     {
    1472          371 :                         payload_remain_ -= payload_avail;
    1473          371 :                         body_total_     += payload_avail;
    1474          371 :                         auto sink_rs = sink_->write(
    1475          371 :                             buffers::prefix(cb0_.data(), payload_avail),
    1476          371 :                             !is_complete);
    1477          371 :                         cb0_.consume(sink_rs.bytes);
    1478          371 :                         if(sink_rs.ec.failed())
    1479              :                         {
    1480            0 :                             body_avail_ += 
    1481            0 :                                 payload_avail - sink_rs.bytes;
    1482            0 :                             ec  = sink_rs.ec;
    1483            0 :                             state_ = state::reset;
    1484            0 :                             return;
    1485              :                         }
    1486          371 :                         break;
    1487              :                     }
    1488          386 :                     case style::elastic:
    1489              :                     {
    1490              :                         // payload_remain_ and body_total_
    1491              :                         // are already updated in commit()
    1492              : 
    1493              :                         // cb0_ contains data
    1494          386 :                         if(payload_avail != 0)
    1495              :                         {
    1496          193 :                             if(eb_->max_size() - eb_->size()
    1497          193 :                                 < payload_avail)
    1498              :                             {
    1499            2 :                                 ec = BOOST_HTTP_PROTO_ERR(
    1500              :                                     error::buffer_overflow);
    1501            1 :                                 state_ = state::reset;
    1502            1 :                                 return;
    1503              :                             }
    1504              :                             // only happens when an elastic body
    1505              :                             // is attached in header_done state
    1506          192 :                             buffers::copy(
    1507          192 :                                 eb_->prepare(payload_avail),
    1508          192 :                                 cb0_.data());
    1509          192 :                             cb0_.consume(payload_avail);
    1510          192 :                             eb_->commit(payload_avail);
    1511          192 :                             payload_remain_ -= payload_avail;
    1512          192 :                             body_total_ += payload_avail;
    1513              :                         }
    1514          385 :                         break;
    1515              :                     }
    1516              :                     }
    1517              : 
    1518        24118 :                     if(is_complete)
    1519              :                     {
    1520         4676 :                         set_state_to_complete();
    1521         4676 :                         return;
    1522              :                     }
    1523              :                 }
    1524              : 
    1525        19958 :                 if(m_.payload() == payload::size && got_eof_)
    1526              :                 {
    1527            2 :                     ec = BOOST_HTTP_PROTO_ERR(
    1528              :                         error::incomplete);
    1529            1 :                     state_ = state::reset;
    1530            1 :                     return;
    1531              :                 }
    1532              : 
    1533        39914 :                 ec = BOOST_HTTP_PROTO_ERR(
    1534              :                     error::need_data);
    1535        19957 :                 return;
    1536              :             }
    1537              : 
    1538              :             break;
    1539              :         }
    1540              : 
    1541         2333 :         case state::set_body:
    1542              :         case state::complete_in_place:
    1543              :         {
    1544         2333 :             auto& body_buf = is_plain() ? cb0_ : cb1_;
    1545              : 
    1546         2333 :             switch(style_)
    1547              :             {
    1548         2216 :             case style::in_place:
    1549         2216 :                 return; // no-op
    1550           58 :             case style::sink:
    1551              :             {
    1552           58 :                 auto rs = sink_->write(
    1553           58 :                     buffers::prefix(body_buf.data(), body_avail_),
    1554           58 :                     state_ == state::set_body);
    1555           58 :                 body_buf.consume(rs.bytes);
    1556           58 :                 body_avail_ -= rs.bytes;
    1557           58 :                 if(rs.ec.failed())
    1558              :                 {
    1559            0 :                     ec  = rs.ec;
    1560            0 :                     state_ = state::reset;
    1561            0 :                     return;
    1562              :                 }
    1563           58 :                 break;
    1564              :             }
    1565           59 :             case style::elastic:
    1566              :             {
    1567           59 :                 if(eb_->max_size() - eb_->size()
    1568           59 :                     < body_avail_)
    1569              :                 {
    1570            0 :                     ec = BOOST_HTTP_PROTO_ERR(
    1571              :                         error::buffer_overflow);
    1572            0 :                     return;
    1573              :                 }
    1574           59 :                 buffers::copy(
    1575           59 :                     eb_->prepare(body_avail_),
    1576           59 :                     body_buf.data());
    1577           59 :                 body_buf.consume(body_avail_);
    1578           59 :                 eb_->commit(body_avail_);
    1579           59 :                 body_avail_ = 0;
    1580              :                 // TODO: expand cb0_ when possible?
    1581           59 :                 break;
    1582              :             }
    1583              :             }
    1584              : 
    1585          117 :             if(state_ == state::set_body)
    1586              :             {
    1587            0 :                 state_ = state::body;
    1588            0 :                 goto do_body;
    1589              :             }
    1590              : 
    1591          117 :             state_ = state::complete;
    1592          117 :             break;
    1593              :         }
    1594              : 
    1595          460 :         case state::complete:
    1596          460 :             break;
    1597              :         }
    1598              :     }
    1599              : 
    1600              :     auto
    1601        41250 :     pull_body() ->
    1602              :         const_buffers_type
    1603              :     {
    1604        41250 :         switch(state_)
    1605              :         {
    1606            0 :         case state::header_done:
    1607            0 :             return {};
    1608        41250 :         case state::body:
    1609              :         case state::complete_in_place:
    1610        41250 :             cbp_ = buffers::prefix(
    1611        41250 :                 (is_plain() ? cb0_ : cb1_).data(),
    1612              :                 body_avail_);
    1613        41250 :             return detail::make_span(cbp_);
    1614            0 :         default:
    1615            0 :             detail::throw_logic_error();
    1616              :         }
    1617              :     }
    1618              : 
    1619              :     void
    1620        39606 :     consume_body(std::size_t n)
    1621              :     {
    1622        39606 :         switch(state_)
    1623              :         {
    1624            0 :         case state::header_done:
    1625            0 :             return;
    1626        39606 :         case state::body:
    1627              :         case state::complete_in_place:
    1628        39606 :             n = clamp(n, body_avail_);
    1629        39606 :             (is_plain() ? cb0_ : cb1_).consume(n);
    1630        39606 :             body_avail_ -= n;
    1631        39606 :             return;
    1632            0 :         default:
    1633            0 :             detail::throw_logic_error();
    1634              :         }
    1635              :     }
    1636              : 
    1637              :     core::string_view
    1638          700 :     body() const
    1639              :     {
    1640              :         // Precondition violation
    1641          700 :         if(state_ != state::complete_in_place)
    1642            0 :             detail::throw_logic_error();
    1643              : 
    1644              :         // Precondition violation
    1645          700 :         if(body_avail_ != body_total_)
    1646            0 :             detail::throw_logic_error();
    1647              : 
    1648          700 :         auto cbp = (is_plain() ? cb0_ : cb1_).data();
    1649          700 :         BOOST_ASSERT(cbp[1].size() == 0);
    1650          700 :         BOOST_ASSERT(cbp[0].size() == body_avail_);
    1651          700 :         return core::string_view(
    1652          700 :             static_cast<char const*>(cbp[0].data()),
    1653         1400 :             body_avail_);
    1654              :     }
    1655              : 
    1656              :     void
    1657           77 :     set_body_limit(std::uint64_t n)
    1658              :     {
    1659           77 :         switch(state_)
    1660              :         {
    1661           73 :         case state::header:
    1662              :         case state::header_done:
    1663           73 :             body_limit_ = n;
    1664           73 :             break;
    1665            2 :         case state::complete_in_place:
    1666              :             // only allowed for empty bodies
    1667            2 :             if(body_total_ == 0)
    1668            1 :                 break;
    1669              :             BOOST_FALLTHROUGH;
    1670              :         default:
    1671              :             // set body_limit before parsing the body
    1672            3 :             detail::throw_logic_error();
    1673              :         }
    1674           74 :     }
    1675              : 
    1676              :     void
    1677          383 :     set_body(
    1678              :         buffers::any_dynamic_buffer& eb) noexcept
    1679              :     {
    1680          383 :         eb_ = &eb;
    1681          383 :         style_ = style::elastic;
    1682          383 :         nprepare_ = 0; // invalidate
    1683          383 :         if(state_ == state::body)
    1684            0 :             state_ = state::set_body;
    1685          383 :     }
    1686              : 
    1687              :     void
    1688          372 :     set_body(sink& s) noexcept
    1689              :     {
    1690          372 :         sink_ = &s;
    1691          372 :         style_ = style::sink;
    1692          372 :         nprepare_ = 0; // invalidate
    1693          372 :         if(state_ == state::body)
    1694            0 :             state_ = state::set_body;
    1695          372 :     }
    1696              : 
    1697              :     detail::workspace&
    1698          755 :     ws() noexcept
    1699              :     {
    1700          755 :         return ws_;
    1701              :     }
    1702              : 
    1703              : private:
    1704              :     bool
    1705       182054 :     is_plain() const noexcept
    1706              :     {
    1707       354400 :         return ! filter_ &&
    1708       354400 :             m_.payload() != payload::chunked;
    1709              :     }
    1710              : 
    1711              :     std::uint64_t
    1712       157922 :     body_limit_remain() const noexcept
    1713              :     {
    1714       157922 :         return body_limit_ - body_total_;
    1715              :     }
    1716              : 
    1717              :     std::size_t
    1718        52714 :     apply_filter(
    1719              :         system::error_code& ec,
    1720              :         std::size_t payload_avail,
    1721              :         bool more)
    1722              :     {
    1723        52714 :         std::size_t p0 = payload_avail;
    1724              :         for(;;)
    1725              :         {
    1726       107152 :             if(payload_avail == 0 && more)
    1727        51094 :                 break;
    1728              : 
    1729            0 :             auto f_rs = [&](){
    1730        56178 :                 BOOST_ASSERT(filter_ != nullptr);
    1731        56178 :                 if(style_ == style::elastic)
    1732              :                 {
    1733        18143 :                     std::size_t n = clamp(body_limit_remain());
    1734        18143 :                     n = clamp(n, svc_.cfg.min_buffer);
    1735        18143 :                     n = clamp(n, eb_->max_size() - eb_->size());
    1736              : 
    1737              :                     // fill capacity first to avoid
    1738              :                     // an allocation
    1739              :                     std::size_t avail = 
    1740        18143 :                         eb_->capacity() - eb_->size();
    1741        18143 :                     if(avail != 0)
    1742        18143 :                         n = clamp(n, avail);
    1743              : 
    1744        36286 :                     return filter_->process(
    1745        18143 :                         eb_->prepare(n),
    1746        18143 :                         buffers::prefix(cb0_.data(), payload_avail),
    1747        36286 :                         more);
    1748              :                 }
    1749              :                 else // in-place and sink 
    1750              :                 {
    1751        38035 :                     std::size_t n = clamp(body_limit_remain());
    1752        38035 :                     n = clamp(n, cb1_.capacity());
    1753              : 
    1754        76070 :                     return filter_->process(
    1755        38035 :                         detail::make_span(cb1_.prepare(n)),
    1756        38035 :                         buffers::prefix(cb0_.data(), payload_avail),
    1757        76070 :                         more);
    1758              :                 }
    1759        56178 :             }();
    1760              : 
    1761        56178 :             cb0_.consume(f_rs.in_bytes);
    1762        56178 :             payload_avail -= f_rs.in_bytes;
    1763        56178 :             body_total_   += f_rs.out_bytes;
    1764              : 
    1765        56178 :             switch(style_)
    1766              :             {
    1767        20028 :             case style::in_place:
    1768              :             {
    1769        20028 :                 cb1_.commit(f_rs.out_bytes);
    1770        20028 :                 body_avail_ += f_rs.out_bytes;
    1771        20028 :                 if(cb1_.capacity() == 0 &&
    1772        20028 :                     !f_rs.finished && f_rs.in_bytes == 0)
    1773              :                 {
    1774         3240 :                     ec = BOOST_HTTP_PROTO_ERR(
    1775              :                         error::in_place_overflow);
    1776         1620 :                     goto done;
    1777              :                 }
    1778        18408 :                 break;
    1779              :             }
    1780        18007 :             case style::sink:
    1781              :             {
    1782        18007 :                 cb1_.commit(f_rs.out_bytes);
    1783        18007 :                 auto sink_rs = sink_->write(
    1784        18007 :                     cb1_.data(), !f_rs.finished || more);
    1785        18007 :                 cb1_.consume(sink_rs.bytes);
    1786        18007 :                 if(sink_rs.ec.failed())
    1787              :                 {
    1788            0 :                     ec  = sink_rs.ec;
    1789            0 :                     state_ = state::reset;
    1790            0 :                     goto done;
    1791              :                 }
    1792        18007 :                 break;
    1793              :             }
    1794        18143 :             case style::elastic:
    1795              :             {
    1796        18143 :                 eb_->commit(f_rs.out_bytes);
    1797        18143 :                 if(eb_->max_size() - eb_->size() == 0 &&
    1798        18143 :                     !f_rs.finished && f_rs.in_bytes == 0)
    1799              :                 {
    1800            0 :                     ec = BOOST_HTTP_PROTO_ERR(
    1801              :                         error::buffer_overflow);
    1802            0 :                     state_ = state::reset;
    1803            0 :                     goto done;
    1804              :                 }
    1805        18143 :                 break;
    1806              :             }
    1807              :             }
    1808              : 
    1809        54558 :             if(f_rs.ec.failed())
    1810              :             {
    1811            0 :                 ec = f_rs.ec;
    1812            0 :                 state_ = state::reset;
    1813            0 :                 break;
    1814              :             }
    1815              : 
    1816        54558 :             if(body_limit_remain() == 0 &&
    1817        54558 :                 !f_rs.finished && f_rs.in_bytes == 0)
    1818              :             {
    1819            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1820              :                     error::body_too_large);
    1821            0 :                 state_ = state::reset;
    1822            0 :                 break;
    1823              :             }
    1824              : 
    1825        54558 :             if(f_rs.finished)
    1826              :             {
    1827          120 :                 if(!more)
    1828              :                 {
    1829           72 :                     state_ = (style_ == style::in_place)
    1830           72 :                         ? state::complete_in_place
    1831              :                         : state::complete;
    1832              :                 }
    1833          120 :                 break;
    1834              :             }
    1835        54438 :         }
    1836              : 
    1837        52714 :     done:
    1838        52714 :         return p0 - payload_avail;
    1839              :     }
    1840              : };
    1841              : 
    1842              : //------------------------------------------------
    1843              : //
    1844              : // Special Members
    1845              : //
    1846              : //------------------------------------------------
    1847              : 
    1848         1070 : parser::
    1849              : ~parser()
    1850              : {
    1851         1070 :     delete impl_;
    1852         1070 : }
    1853              : 
    1854           11 : parser::
    1855           11 : parser() noexcept
    1856           11 :     : impl_(nullptr)
    1857              : {
    1858           11 : }
    1859              : 
    1860            3 : parser::
    1861            3 : parser(parser&& other) noexcept
    1862            3 :     : impl_(other.impl_)
    1863              : {
    1864            3 :     other.impl_ = nullptr;
    1865            3 : }
    1866              : 
    1867         1056 : parser::
    1868              : parser(
    1869              :     capy::polystore& ctx,
    1870         1056 :     detail::kind k)
    1871         1056 :     : impl_(new impl(ctx, k))
    1872              : {
    1873              :     // TODO: use a single allocation for
    1874              :     // impl and workspace buffer.
    1875         1056 : }
    1876              : 
    1877              : void
    1878            4 : parser::
    1879              : assign(parser&& other) noexcept
    1880              : {
    1881            4 :     if(this == &other)
    1882            0 :         return;
    1883            4 :     delete impl_;
    1884            4 :     impl_ = other.impl_;
    1885            4 :     other.impl_ = nullptr;
    1886              : }
    1887              : 
    1888              : //--------------------------------------------
    1889              : //
    1890              : // Observers
    1891              : //
    1892              : //--------------------------------------------
    1893              : 
    1894              : bool
    1895        11938 : parser::got_header() const noexcept
    1896              : {
    1897        11938 :     BOOST_ASSERT(impl_);
    1898        11938 :     return impl_->got_header();
    1899              : }
    1900              : 
    1901              : bool
    1902        51344 : parser::is_complete() const noexcept
    1903              : {
    1904        51344 :     BOOST_ASSERT(impl_);
    1905        51344 :     return impl_->is_complete();
    1906              : }
    1907              : 
    1908              : //------------------------------------------------
    1909              : //
    1910              : // Modifiers
    1911              : //
    1912              : //------------------------------------------------
    1913              : 
    1914              : void
    1915         2480 : parser::
    1916              : reset() noexcept
    1917              : {
    1918         2480 :     BOOST_ASSERT(impl_);
    1919         2480 :     impl_->reset();
    1920         2480 : }
    1921              : 
    1922              : void
    1923        10307 : parser::start()
    1924              : {
    1925        10307 :     BOOST_ASSERT(impl_);
    1926        10307 :     impl_->start(false);
    1927        10302 : }
    1928              : 
    1929              : auto
    1930        51003 : parser::
    1931              : prepare() ->
    1932              :     mutable_buffers_type
    1933              : {
    1934        51003 :     BOOST_ASSERT(impl_);
    1935        51003 :     return impl_->prepare();
    1936              : }
    1937              : 
    1938              : void
    1939        51000 : parser::
    1940              : commit(
    1941              :     std::size_t n)
    1942              : {
    1943        51000 :     BOOST_ASSERT(impl_);
    1944        51000 :     impl_->commit(n);
    1945        50993 : }
    1946              : 
    1947              : void
    1948          401 : parser::
    1949              : commit_eof()
    1950              : {
    1951          401 :     BOOST_ASSERT(impl_);
    1952          401 :     impl_->commit_eof();
    1953          398 : }
    1954              : 
    1955              : void
    1956        69827 : parser::
    1957              : parse(
    1958              :     system::error_code& ec)
    1959              : {
    1960        69827 :     BOOST_ASSERT(impl_);
    1961        69827 :     impl_->parse(ec);
    1962        69825 : }
    1963              : 
    1964              : auto
    1965        41250 : parser::
    1966              : pull_body() ->
    1967              :     const_buffers_type
    1968              : {
    1969        41250 :     BOOST_ASSERT(impl_);
    1970        41250 :     return impl_->pull_body();
    1971              : }
    1972              : 
    1973              : void
    1974        39606 : parser::
    1975              : consume_body(std::size_t n)
    1976              : {
    1977        39606 :     BOOST_ASSERT(impl_);
    1978        39606 :     impl_->consume_body(n);
    1979        39606 : }
    1980              : 
    1981              : core::string_view
    1982          700 : parser::
    1983              : body() const
    1984              : {
    1985          700 :     BOOST_ASSERT(impl_);
    1986          700 :     return impl_->body();
    1987              : }
    1988              : 
    1989              : core::string_view
    1990            0 : parser::
    1991              : release_buffered_data() noexcept
    1992              : {
    1993              :     // TODO
    1994            0 :     return {};
    1995              : }
    1996              : 
    1997              : void
    1998           77 : parser::
    1999              : set_body_limit(std::uint64_t n)
    2000              : {
    2001           77 :     BOOST_ASSERT(impl_);
    2002           77 :     impl_->set_body_limit(n);
    2003           74 : }
    2004              : 
    2005              : //------------------------------------------------
    2006              : //
    2007              : // Implementation
    2008              : //
    2009              : //------------------------------------------------
    2010              : 
    2011              : void
    2012            0 : parser::
    2013              : start_impl(bool head_response)
    2014              : {
    2015            0 :     BOOST_ASSERT(impl_);
    2016            0 :     impl_->start(head_response);
    2017            0 : }
    2018              : 
    2019              : static_request const&
    2020          315 : parser::
    2021              : safe_get_request() const
    2022              : {
    2023          315 :     BOOST_ASSERT(impl_);
    2024          315 :     return impl_->safe_get_request();
    2025              : }
    2026              : 
    2027              : static_response const&
    2028            1 : parser::
    2029              : safe_get_response() const
    2030              : {
    2031            1 :     BOOST_ASSERT(impl_);
    2032            1 :     return impl_->safe_get_response();
    2033              : }
    2034              : 
    2035              : detail::workspace&
    2036          755 : parser::
    2037              : ws() noexcept
    2038              : {
    2039          755 :     BOOST_ASSERT(impl_);
    2040          755 :     return impl_->ws();
    2041              : }
    2042              : 
    2043              : bool
    2044          755 : parser::
    2045              : is_body_set() const noexcept
    2046              : {
    2047          755 :     BOOST_ASSERT(impl_);
    2048          755 :     return impl_->is_body_set();
    2049              : }
    2050              : 
    2051              : void
    2052          383 : parser::
    2053              : set_body_impl(
    2054              :     buffers::any_dynamic_buffer& eb) noexcept
    2055              : {
    2056          383 :     BOOST_ASSERT(impl_);
    2057          383 :     impl_->set_body(eb);
    2058          383 : }
    2059              : 
    2060              : void
    2061          372 : parser::
    2062              : set_body_impl(sink& s) noexcept
    2063              : {
    2064          372 :     BOOST_ASSERT(impl_);
    2065          372 :     impl_->set_body(s);
    2066          372 : }
    2067              : 
    2068              : } // http_proto
    2069              : } // boost
        

Generated by: LCOV version 2.1