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.4 % 876 748
Test Date: 2025-09-21 18:08:14 Functions: 85.4 % 96 82

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

Generated by: LCOV version 2.1