LCOV - code coverage report
Current view: top level - libs/http/src - parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 77.1 % 664 512
Test Date: 2026-02-02 17:02:49 Functions: 80.2 % 86 69

            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
       9              : //
      10              : 
      11              : #include <boost/http/detail/except.hpp>
      12              : #include <boost/http/detail/workspace.hpp>
      13              : #include <boost/http/error.hpp>
      14              : #include <boost/http/parser.hpp>
      15              : #include <boost/http/static_request.hpp>
      16              : #include <boost/http/static_response.hpp>
      17              : 
      18              : #include <boost/assert.hpp>
      19              : #include <boost/capy/buffers/circular_dynamic_buffer.hpp>
      20              : #include <boost/capy/buffers/buffer_copy.hpp>
      21              : #include <boost/capy/buffers/flat_dynamic_buffer.hpp>
      22              : #include <boost/capy/buffers/front.hpp>
      23              : #include <boost/capy/buffers/slice.hpp>
      24              : #include <boost/capy/ex/system_context.hpp>
      25              : #include <boost/http/brotli/decode.hpp>
      26              : #include <boost/http/zlib/error.hpp>
      27              : #include <boost/http/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              : #include "src/detail/brotli_filter_base.hpp"
      33              : #include "src/detail/buffer_utils.hpp"
      34              : #include "src/detail/zlib_filter_base.hpp"
      35              : 
      36              : #include <memory>
      37              : 
      38              : namespace boost {
      39              : namespace http {
      40              : 
      41              : /*
      42              :     Principles for fixed-size buffer design
      43              : 
      44              :     axiom 1:
      45              :         To read data you must have a buffer.
      46              : 
      47              :     axiom 2:
      48              :         The size of the HTTP header is not
      49              :         known in advance.
      50              : 
      51              :     conclusion 3:
      52              :         A single I/O can produce a complete
      53              :         HTTP header and additional payload
      54              :         data.
      55              : 
      56              :     conclusion 4:
      57              :         A single I/O can produce multiple
      58              :         complete HTTP headers, complete
      59              :         payloads, and a partial header or
      60              :         payload.
      61              : 
      62              :     axiom 5:
      63              :         A process is in one of two states:
      64              :             1. at or below capacity
      65              :             2. above capacity
      66              : 
      67              :     axiom 6:
      68              :         A program which can allocate an
      69              :         unbounded number of resources can
      70              :         go above capacity.
      71              : 
      72              :     conclusion 7:
      73              :         A program can guarantee never going
      74              :         above capacity if all resources are
      75              :         provisioned at program startup.
      76              : 
      77              :     corollary 8:
      78              :         `parser` and `serializer` should each
      79              :         allocate a single buffer of calculated
      80              :         size, and never resize it.
      81              : 
      82              :     axiom #:
      83              :         A parser and a serializer are always
      84              :         used in pairs.
      85              : 
      86              : Buffer Usage
      87              : 
      88              : |                                               | begin
      89              : | H |   p   |                               | f | read headers
      90              : | H |   p   |                           | T | f | set T body
      91              : | H |   p   |                       | C | T | f | make codec C
      92              : | H |   p           |       b       | C | T | f | decode p into b
      93              : | H |       p       |       b       | C | T | f | read/parse loop
      94              : | H |                                   | T | f | destroy codec
      95              : | H |                                   | T | f | finished
      96              : 
      97              :     H   headers
      98              :     C   codec
      99              :     T   body
     100              :     f   table
     101              :     p   partial payload
     102              :     b   body data
     103              : 
     104              :     "payload" is the bytes coming in from
     105              :         the stream.
     106              : 
     107              :     "body" is the logical body, after transfer
     108              :         encoding is removed. This can be the
     109              :         same as the payload.
     110              : 
     111              :     A "plain payload" is when the payload and
     112              :         body are identical (no transfer encodings).
     113              : 
     114              :     A "buffered payload" is any payload which is
     115              :         not plain. A second buffer is required
     116              :         for reading.
     117              : 
     118              :     "overread" is additional data received past
     119              :     the end of the headers when reading headers,
     120              :     or additional data received past the end of
     121              :     the message payload.
     122              : */
     123              : 
     124              : namespace {
     125              : 
     126              : class chained_sequence
     127              : {
     128              :     char const* pos_;
     129              :     char const* end_;
     130              :     char const* begin_b_;
     131              :     char const* end_b_;
     132              : 
     133              : public:
     134        71111 :     chained_sequence(capy::const_buffer_pair const& cbp)
     135        71111 :         : pos_(static_cast<char const*>(cbp[0].data()))
     136        71111 :         , end_(pos_ + cbp[0].size())
     137        71111 :         , begin_b_(static_cast<char const*>(cbp[1].data()))
     138        71111 :         , end_b_(begin_b_ + cbp[1].size())
     139              :     {
     140        71111 :     }
     141              : 
     142              :     char const*
     143       319026 :     next() noexcept
     144              :     {
     145       319026 :         ++pos_;
     146              :         // most frequently taken branch
     147       319026 :         if(pos_ < end_)
     148       296983 :             return pos_;
     149              : 
     150              :         // bring the second range
     151        22043 :         if(begin_b_ != end_b_)
     152              :         {
     153            0 :             pos_ = begin_b_;
     154            0 :             end_ = end_b_;
     155            0 :             begin_b_ = end_b_;
     156            0 :             return pos_;
     157              :         }
     158              : 
     159              :         // undo the increament
     160        22043 :         pos_ = end_;
     161        22043 :         return nullptr;
     162              :     }
     163              : 
     164              :     bool
     165       211814 :     is_empty() const noexcept
     166              :     {
     167       211814 :         return pos_ == end_;
     168              :     }
     169              : 
     170              :     char
     171       304559 :     value() const noexcept
     172              :     {
     173       304559 :         return *pos_;
     174              :     }
     175              : 
     176              :     std::size_t
     177       225998 :     size() const noexcept
     178              :     {
     179       225998 :         return (end_ - pos_) + (end_b_ - begin_b_);
     180              :     }
     181              : };
     182              : 
     183              : std::uint64_t
     184        66555 : parse_hex(
     185              :     chained_sequence& cs,
     186              :     system::error_code& ec) noexcept
     187              : {
     188        66555 :     std::uint64_t v   = 0;
     189        66555 :     std::size_t init_size = cs.size();
     190       153453 :     while(!cs.is_empty())
     191              :     {
     192       133707 :         auto n = grammar::hexdig_value(cs.value());
     193       133707 :         if(n < 0)
     194              :         {
     195        46808 :             if(init_size == cs.size())
     196              :             {
     197            2 :                 ec = BOOST_HTTP_ERR(
     198              :                     error::bad_payload);
     199            1 :                 return 0;
     200              :             }
     201        46807 :             return v;
     202              :         }
     203              : 
     204              :         // at least 4 significant bits are free
     205        86899 :         if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
     206              :         {
     207            2 :             ec = BOOST_HTTP_ERR(
     208              :                 error::bad_payload);
     209            1 :             return 0;
     210              :         }
     211              : 
     212        86898 :         v = (v << 4) | static_cast<std::uint64_t>(n);
     213        86898 :         cs.next();
     214              :     }
     215        39492 :     ec = BOOST_HTTP_ERR(
     216              :         error::need_data);
     217        19746 :     return 0;
     218              : }
     219              : 
     220              : void
     221        47159 : find_eol(
     222              :     chained_sequence& cs,
     223              :     system::error_code& ec) noexcept
     224              : {
     225        53848 :     while(!cs.is_empty())
     226              :     {
     227        53760 :         if(cs.value() == '\r')
     228              :         {
     229        47071 :             if(!cs.next())
     230          238 :                 break;
     231        46833 :             if(cs.value() != '\n')
     232              :             {
     233            4 :                 ec = BOOST_HTTP_ERR(
     234              :                     error::bad_payload);
     235            2 :                 return;
     236              :             }
     237        46831 :             cs.next();
     238        46831 :             return;
     239              :         }
     240         6689 :         cs.next();
     241              :     }
     242          652 :     ec = BOOST_HTTP_ERR(
     243              :         error::need_data);
     244              : }
     245              : 
     246              : void
     247        61961 : parse_eol(
     248              :     chained_sequence& cs,
     249              :     system::error_code& ec) noexcept
     250              : {
     251        61961 :     if(cs.size() >= 2)
     252              :     {
     253              :         // we are sure size is at least 2
     254        61637 :         if(cs.value() == '\r' && *cs.next() == '\n')
     255              :         {
     256        61634 :             cs.next();
     257        61634 :             return;
     258              :         }
     259            6 :         ec = BOOST_HTTP_ERR(
     260              :             error::bad_payload);
     261            3 :         return;
     262              :     }
     263          648 :     ec = BOOST_HTTP_ERR(
     264              :         error::need_data);
     265              : }
     266              : 
     267              : void
     268         4229 : skip_trailer_headers(
     269              :     chained_sequence& cs,
     270              :     system::error_code& ec) noexcept
     271              : {
     272         4513 :     while(!cs.is_empty())
     273              :     {
     274         4493 :         if(cs.value() == '\r')
     275              :         {
     276         4141 :             if(!cs.next())
     277           12 :                 break;
     278         4129 :             if(cs.value() != '\n')
     279              :             {
     280            4 :                 ec = BOOST_HTTP_ERR(
     281              :                     error::bad_payload);
     282            2 :                 return;
     283              :             }
     284         4127 :             cs.next();
     285         4127 :             return;
     286              :         }
     287              :         // skip to the end of field
     288          352 :         find_eol(cs, ec);
     289          352 :         if(ec)
     290           68 :             return;
     291              :     }
     292           64 :     ec = BOOST_HTTP_ERR(
     293              :         error::need_data);
     294              : }
     295              : 
     296              : template<class UInt>
     297              : std::size_t
     298       179401 : clamp(
     299              :     UInt x,
     300              :     std::size_t limit = (std::numeric_limits<
     301              :         std::size_t>::max)()) noexcept
     302              : {
     303       179401 :     if(x >= limit)
     304        45506 :         return limit;
     305       133895 :     return static_cast<std::size_t>(x);
     306              : }
     307              : 
     308              : class zlib_filter
     309              :     : public detail::zlib_filter_base
     310              : {
     311              :     http::zlib::inflate_service& svc_;
     312              : 
     313              : public:
     314            0 :     zlib_filter(
     315              :         http::zlib::inflate_service& svc,
     316              :         int window_bits)
     317            0 :         : svc_(svc)
     318              :     {
     319              :         system::error_code ec = static_cast<http::zlib::error>(
     320            0 :             svc_.init2(strm_, window_bits));
     321            0 :         if(ec != http::zlib::error::ok)
     322            0 :             detail::throw_system_error(ec);
     323            0 :     }
     324              : 
     325              : private:
     326              :     virtual
     327              :     results
     328            0 :     do_process(
     329              :         capy::mutable_buffer out,
     330              :         capy::const_buffer in,
     331              :         bool more) noexcept override
     332              :     {
     333            0 :         strm_.next_out  = static_cast<unsigned char*>(out.data());
     334            0 :         strm_.avail_out = saturate_cast(out.size());
     335            0 :         strm_.next_in   = static_cast<unsigned char*>(const_cast<void *>(in.data()));
     336            0 :         strm_.avail_in  = saturate_cast(in.size());
     337              : 
     338              :         auto rs = static_cast<http::zlib::error>(
     339            0 :             svc_.inflate(
     340            0 :                 strm_,
     341              :                 more ? http::zlib::no_flush : http::zlib::finish));
     342              : 
     343            0 :         results rv;
     344            0 :         rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
     345            0 :         rv.in_bytes  = saturate_cast(in.size()) - strm_.avail_in;
     346            0 :         rv.finished  = (rs == http::zlib::error::stream_end);
     347              : 
     348            0 :         if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
     349            0 :             rv.ec = rs;
     350              : 
     351            0 :         return rv;
     352              :     }
     353              : };
     354              : 
     355              : class brotli_filter
     356              :     : public detail::brotli_filter_base
     357              : {
     358              :     http::brotli::decode_service& svc_;
     359              :     http::brotli::decoder_state* state_;
     360              : 
     361              : public:
     362            0 :     brotli_filter(http::brotli::decode_service& svc)
     363            0 :         : svc_(svc)
     364              :     {
     365            0 :         state_ = svc_.create_instance(nullptr, nullptr, nullptr);
     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              :         capy::mutable_buffer out,
     380              :         capy::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 == http::brotli::decoder_result::needs_more_input)
     402            0 :             rv.ec = BOOST_HTTP_ERR(error::bad_payload);
     403              : 
     404            0 :         if(rs == http::brotli::decoder_result::error)
     405            0 :             rv.ec = BOOST_HTTP_ERR(
     406              :                 svc_.get_error_code(state_));
     407              : 
     408            0 :         return rv;
     409              :     }
     410              : };
     411              : 
     412              : } // namespace
     413              : 
     414              : //------------------------------------------------
     415              : 
     416              : class parser::impl
     417              : {
     418              :     enum class state
     419              :     {
     420              :         reset,
     421              :         start,
     422              :         header,
     423              :         header_done,
     424              :         body,
     425              :         complete,
     426              :     };
     427              : 
     428              :     std::shared_ptr<parser_config_impl const> cfg_;
     429              : 
     430              :     detail::workspace ws_;
     431              :     static_request m_;
     432              :     std::uint64_t body_limit_;
     433              :     std::uint64_t body_total_;
     434              :     std::uint64_t payload_remain_;
     435              :     std::uint64_t chunk_remain_;
     436              :     std::size_t body_avail_;
     437              :     std::size_t nprepare_;
     438              : 
     439              :     capy::flat_dynamic_buffer fb_;
     440              :     capy::circular_dynamic_buffer cb0_;
     441              :     capy::circular_dynamic_buffer cb1_;
     442              : 
     443              :     capy::mutable_buffer_pair mbp_;
     444              :     capy::const_buffer_pair cbp_;
     445              : 
     446              :     std::unique_ptr<detail::filter> filter_;
     447              : 
     448              :     state state_;
     449              :     bool got_header_;
     450              :     bool got_eof_;
     451              :     bool head_response_;
     452              :     bool needs_chunk_close_;
     453              :     bool trailer_headers_;
     454              :     bool chunked_body_ended;
     455              : 
     456              : public:
     457         1689 :     impl(std::shared_ptr<parser_config_impl const> cfg, detail::kind k)
     458         1689 :         : cfg_(std::move(cfg))
     459         1689 :         , ws_(cfg_->space_needed)
     460         1689 :         , m_(ws_.data(), ws_.size())
     461         1689 :         , state_(state::reset)
     462         1689 :         , got_header_(false)
     463              :     {
     464         1689 :         m_.h_ = detail::header(detail::empty{ k });
     465         1689 :     }
     466              : 
     467              :     bool
     468        31380 :     got_header() const noexcept
     469              :     {
     470        31380 :         return got_header_;
     471              :     }
     472              : 
     473              :     bool
     474        50092 :     is_complete() const noexcept
     475              :     {
     476        50092 :         return state_ == state::complete;
     477              :     }
     478              : 
     479              :     static_request const&
     480          315 :     safe_get_request() const
     481              :     {
     482              :         // headers must be received
     483          315 :         if(! got_header_)
     484            0 :             detail::throw_logic_error();
     485              : 
     486          315 :         return m_;
     487              :     }
     488              : 
     489              :     static_response const&
     490            3 :     safe_get_response() const
     491              :     {
     492              :         // headers must be received
     493            3 :         if(! got_header_)
     494            0 :             detail::throw_logic_error();
     495              : 
     496              :         // TODO: use a union
     497            3 :         return reinterpret_cast<static_response const&>(m_);
     498              :     }
     499              : 
     500              :     void
     501         2235 :     reset() noexcept
     502              :     {
     503         2235 :         ws_.clear();
     504         2235 :         state_ = state::start;
     505         2235 :         got_header_ = false;
     506         2235 :         got_eof_ = false;
     507         2235 :     }
     508              : 
     509              :     void
     510        10164 :     start(
     511              :         bool head_response)
     512              :     {
     513        10164 :         std::size_t leftover = 0;
     514        10164 :         switch(state_)
     515              :         {
     516            1 :         default:
     517              :         case state::reset:
     518              :             // reset must be called first
     519            1 :             detail::throw_logic_error();
     520              : 
     521         2160 :         case state::start:
     522              :             // reset required on eof
     523         2160 :             if(got_eof_)
     524            0 :                 detail::throw_logic_error();
     525         2160 :             break;
     526              : 
     527            3 :         case state::header:
     528            3 :             if(fb_.size() == 0)
     529              :             {
     530              :                 // start() called twice
     531            2 :                 detail::throw_logic_error();
     532              :             }
     533              :             BOOST_FALLTHROUGH;
     534              : 
     535              :         case state::header_done:
     536              :         case state::body:
     537              :             // current message is incomplete
     538            2 :             detail::throw_logic_error();
     539              : 
     540         7999 :         case state::complete:
     541              :         {
     542              :             // remove available body.
     543         7999 :             if(is_plain())
     544         4000 :                 cb0_.consume(body_avail_);
     545              :             // move leftovers to front
     546              : 
     547         7999 :             ws_.clear();
     548         7999 :             leftover = cb0_.size();
     549              : 
     550         7999 :             auto* dest = reinterpret_cast<char*>(ws_.data());
     551         7999 :             auto cbp   = cb0_.data();
     552         7999 :             auto* a    = static_cast<char const*>(cbp[0].data());
     553         7999 :             auto* b    = static_cast<char const*>(cbp[1].data());
     554         7999 :             auto an    = cbp[0].size();
     555         7999 :             auto bn    = cbp[1].size();
     556              : 
     557         7999 :             if(bn == 0)
     558              :             {
     559         7561 :                 std::memmove(dest, a, an);
     560              :             }
     561              :             else
     562              :             {
     563              :                 // if `a` can fit between `dest` and `b`, shift `b` to the left
     564              :                 // and copy `a` to its position. if `a` fits perfectly, the
     565              :                 // shift will be of size 0.
     566              :                 // if `a` requires more space, shift `b` to the right and
     567              :                 // copy `a` to its position. this process may require multiple
     568              :                 // iterations and should be done chunk by chunk to prevent `b`
     569              :                 // from overlapping with `a`.
     570              :                 do
     571              :                 {
     572              :                     // clamp right shifts to prevent overlap with `a`
     573          438 :                     auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
     574          438 :                     b = static_cast<char const*>(std::memmove(bp, b, bn));
     575              : 
     576              :                     // a chunk or all of `a` based on available space
     577          438 :                     auto chunk_a = static_cast<std::size_t>(b - dest);
     578          438 :                     std::memcpy(dest, a, chunk_a); // never overlap
     579          438 :                     an   -= chunk_a;
     580          438 :                     dest += chunk_a;
     581          438 :                     a    += chunk_a;
     582          438 :                 } while(an);
     583              :             }
     584              : 
     585         7999 :             break;
     586              :         }
     587              :         }
     588              : 
     589        10159 :         ws_.clear();
     590              : 
     591        20318 :         fb_ = {
     592        10159 :             ws_.data(),
     593        10159 :             cfg_->headers.max_size + cfg_->min_buffer,
     594              :             leftover };
     595              : 
     596        10159 :         BOOST_ASSERT(
     597              :             fb_.capacity() == cfg_->max_overread() - leftover);
     598              : 
     599        10159 :         BOOST_ASSERT(
     600              :             head_response == false ||
     601              :             m_.h_.kind == detail::kind::response);
     602              : 
     603        10159 :         m_.h_ = detail::header(detail::empty{m_.h_.kind});
     604        10159 :         m_.h_.buf = reinterpret_cast<char*>(ws_.data());
     605        10159 :         m_.h_.cbuf = m_.h_.buf;
     606        10159 :         m_.h_.cap = ws_.size();
     607              : 
     608        10159 :         state_ = state::header;
     609              : 
     610              :         // reset to the configured default
     611        10159 :         body_limit_ = cfg_->body_limit;
     612              : 
     613        10159 :         body_total_ = 0;
     614        10159 :         payload_remain_ = 0;
     615        10159 :         chunk_remain_ = 0;
     616        10159 :         body_avail_ = 0;
     617        10159 :         nprepare_ = 0;
     618              : 
     619        10159 :         filter_.reset();
     620              : 
     621        10159 :         got_header_ = false;
     622        10159 :         head_response_ = head_response;
     623        10159 :         needs_chunk_close_ = false;
     624        10159 :         trailer_headers_ = false;
     625        10159 :         chunked_body_ended = false;
     626        10159 :     }
     627              : 
     628              :     auto
     629        68721 :     prepare() ->
     630              :         mutable_buffers_type
     631              :     {
     632        68721 :         nprepare_ = 0;
     633              : 
     634        68721 :         switch(state_)
     635              :         {
     636            1 :         default:
     637              :         case state::reset:
     638              :             // reset must be called first
     639            1 :             detail::throw_logic_error();
     640              : 
     641            1 :         case state::start:
     642              :             // start must be called first
     643            1 :             detail::throw_logic_error();
     644              : 
     645        27874 :         case state::header:
     646              :         {
     647        27874 :             BOOST_ASSERT(
     648              :                 m_.h_.size < cfg_->headers.max_size);
     649        27874 :             std::size_t n = fb_.capacity();
     650        27874 :             BOOST_ASSERT(n <= cfg_->max_overread());
     651        27874 :             n = clamp(n, cfg_->max_prepare);
     652        27874 :             mbp_[0] = fb_.prepare(n);
     653        27874 :             nprepare_ = n;
     654        27874 :             return mutable_buffers_type(&mbp_[0], 1);
     655              :         }
     656              : 
     657            0 :         case state::header_done:
     658              :             // forgot to call parse()
     659            0 :             detail::throw_logic_error();
     660              : 
     661        40844 :         case state::body:
     662              :         {
     663        40844 :             if(got_eof_)
     664              :             {
     665              :                 // forgot to call parse()
     666            0 :                 detail::throw_logic_error();
     667              :             }
     668              : 
     669        40844 :             if(! is_plain())
     670              :             {
     671              :                 // buffered payload
     672        21207 :                 std::size_t n = cb0_.capacity();
     673        21207 :                 n = clamp(n, cfg_->max_prepare);
     674        21207 :                 nprepare_ = n;
     675        21207 :                 mbp_ = cb0_.prepare(n);
     676        21207 :                 return detail::make_span(mbp_);
     677              :             }
     678              :             else
     679              :             {
     680              :                 // plain payload
     681        19637 :                 std::size_t n = cb0_.capacity();
     682        19637 :                 n = clamp(n, cfg_->max_prepare);
     683              : 
     684        19637 :                 if(m_.payload() == payload::size)
     685              :                 {
     686        19623 :                     if(n > payload_remain_)
     687              :                     {
     688        18416 :                         std::size_t overread =
     689        18416 :                             n - static_cast<std::size_t>(payload_remain_);
     690        18416 :                         if(overread > cfg_->max_overread())
     691         8504 :                             n = static_cast<std::size_t>(payload_remain_) +
     692         8504 :                                 cfg_->max_overread();
     693              :                     }
     694              :                 }
     695              :                 else
     696              :                 {
     697           14 :                     BOOST_ASSERT(
     698              :                         m_.payload() == payload::to_eof);
     699              :                     // No more messages can be pipelined, so
     700              :                     // limit the output buffer to the remaining
     701              :                     // body limit plus one byte to detect
     702              :                     // exhaustion.
     703           14 :                     std::uint64_t r = body_limit_remain();
     704           14 :                     if(r != std::uint64_t(-1))
     705           14 :                         r += 1;
     706           14 :                     n = clamp(r, n);
     707              :                 }
     708              : 
     709        19637 :                 nprepare_ = n;
     710        19637 :                 mbp_ = cb0_.prepare(n);
     711        19637 :                 return detail::make_span(mbp_);
     712              :             }
     713              :         }
     714              : 
     715            1 :         case state::complete:
     716              :             // already complete
     717            1 :             detail::throw_logic_error();
     718              :         }
     719              :     }
     720              : 
     721              :     void
     722        68128 :     commit(
     723              :         std::size_t n)
     724              :     {
     725        68128 :         switch(state_)
     726              :         {
     727            1 :         default:
     728              :         case state::reset:
     729              :         {
     730              :             // reset must be called first
     731            1 :             detail::throw_logic_error();
     732              :         }
     733              : 
     734            1 :         case state::start:
     735              :         {
     736              :             // forgot to call start()
     737            1 :             detail::throw_logic_error();
     738              :         }
     739              : 
     740        27444 :         case state::header:
     741              :         {
     742        27444 :             if(n > nprepare_)
     743              :             {
     744              :                 // n can't be greater than size of
     745              :                 // the buffers returned by prepare()
     746            1 :                 detail::throw_invalid_argument();
     747              :             }
     748              : 
     749        27443 :             if(got_eof_)
     750              :             {
     751              :                 // can't commit after EOF
     752            1 :                 detail::throw_logic_error();
     753              :             }
     754              : 
     755        27442 :             nprepare_ = 0; // invalidate
     756        27442 :             fb_.commit(n);
     757        27442 :             break;
     758              :         }
     759              : 
     760            0 :         case state::header_done:
     761              :         {
     762              :             // forgot to call parse()
     763            0 :             detail::throw_logic_error();
     764              :         }
     765              : 
     766        40682 :         case state::body:
     767              :         {
     768        40682 :             if(n > nprepare_)
     769              :             {
     770              :                 // n can't be greater than size of
     771              :                 // the buffers returned by prepare()
     772            2 :                 detail::throw_invalid_argument();
     773              :             }
     774              : 
     775        40680 :             if(got_eof_)
     776              :             {
     777              :                 // can't commit after EOF
     778            0 :                 detail::throw_logic_error();
     779              :             }
     780              :         
     781        40680 :             nprepare_ = 0; // invalidate
     782        40680 :             cb0_.commit(n);
     783        40680 :             break;
     784              :         }
     785              : 
     786            0 :         case state::complete:
     787              :         {
     788              :             // already complete
     789            0 :             detail::throw_logic_error();
     790              :         }
     791              :         }
     792        68122 :     }
     793              : 
     794              :     void
     795          132 :     commit_eof()
     796              :     {
     797          132 :         nprepare_ = 0; // invalidate
     798              : 
     799          132 :         switch(state_)
     800              :         {
     801            1 :         default:
     802              :         case state::reset:
     803              :             // reset must be called first
     804            1 :             detail::throw_logic_error();
     805              : 
     806            1 :         case state::start:
     807              :             // forgot to call start()
     808            1 :             detail::throw_logic_error();
     809              : 
     810           12 :         case state::header:
     811           12 :             got_eof_ = true;
     812           12 :             break;
     813              : 
     814            0 :         case state::header_done:
     815              :             // forgot to call parse()
     816            0 :             detail::throw_logic_error();
     817              : 
     818          117 :         case state::body:
     819          117 :             got_eof_ = true;
     820          117 :             break;
     821              : 
     822            1 :         case state::complete:
     823              :             // can't commit eof when complete
     824            1 :             detail::throw_logic_error();
     825              :         }
     826          129 :     }
     827              : 
     828              :     void
     829        85415 :     parse(
     830              :         system::error_code& ec)
     831              :     {
     832        85415 :         ec = {};
     833        85415 :         switch(state_)
     834              :         {
     835            1 :         default:
     836              :         case state::reset:
     837              :             // reset must be called first
     838            1 :             detail::throw_logic_error();
     839              : 
     840            1 :         case state::start:
     841              :             // start must be called first
     842            1 :             detail::throw_logic_error();
     843              : 
     844        32948 :         case state::header:
     845              :         {
     846        32948 :             BOOST_ASSERT(m_.h_.buf == static_cast<
     847              :                 void const*>(ws_.data()));
     848        32948 :             BOOST_ASSERT(m_.h_.cbuf == static_cast<
     849              :                 void const*>(ws_.data()));
     850              : 
     851        32948 :             m_.h_.parse(fb_.size(), cfg_->headers, ec);
     852              : 
     853        32948 :             if(ec == condition::need_more_input)
     854              :             {
     855        23238 :                 if(! got_eof_)
     856              :                 {
     857              :                     // headers incomplete
     858        23229 :                     return;
     859              :                 }
     860              : 
     861            9 :                 if(fb_.size() == 0)
     862              :                 {
     863              :                     // stream closed cleanly
     864            4 :                     state_ = state::reset;
     865            8 :                     ec = BOOST_HTTP_ERR(
     866              :                         error::end_of_stream);
     867            4 :                     return;
     868              :                 }
     869              : 
     870              :                 // stream closed with a
     871              :                 // partial message received
     872            5 :                 state_ = state::reset;
     873           10 :                 ec = BOOST_HTTP_ERR(
     874              :                     error::incomplete);
     875            5 :                 return;
     876              :             }
     877         9710 :             else if(ec)
     878              :             {
     879              :                 // other error,
     880              :                 //
     881              :                 // VFALCO map this to a bad
     882              :                 // request or bad response error?
     883              :                 //
     884          259 :                 state_ = state::reset; // unrecoverable
     885          259 :                 return;
     886              :             }
     887              : 
     888         9451 :             got_header_ = true;
     889              : 
     890              :             // reserve headers + table
     891         9451 :             ws_.reserve_front(m_.h_.size);
     892         9451 :             ws_.reserve_back(m_.h_.table_space());
     893              : 
     894              :             // no payload
     895        18096 :             if(m_.payload() == payload::none ||
     896         8645 :                 head_response_)
     897              :             {
     898              :                 // octets of the next message
     899          806 :                 auto overread = fb_.size() - m_.h_.size;
     900          806 :                 cb0_ = { ws_.data(), overread, overread };
     901          806 :                 ws_.reserve_front(overread);
     902          806 :                 state_ = state::complete;
     903          806 :                 return;
     904              :             }
     905              : 
     906         8645 :             state_ = state::header_done;
     907         8645 :             break;
     908              :         }
     909              : 
     910         8642 :         case state::header_done:
     911              :         {
     912              :             // metadata error
     913         8642 :             if(m_.payload() == payload::error)
     914              :             {
     915              :                 // VFALCO This needs looking at
     916          120 :                 ec = BOOST_HTTP_ERR(
     917              :                     error::bad_payload);
     918           60 :                 state_ = state::reset; // unrecoverable
     919           60 :                 return;
     920              :             }
     921              : 
     922              :             // overread currently includes any and all octets that
     923              :             // extend beyond the current end of the header
     924              :             // this can include associated body octets for the
     925              :             // current message or octets of the next message in the
     926              :             // stream, e.g. pipelining is being used
     927         8582 :             auto const overread = fb_.size() - m_.h_.size;
     928         8582 :             BOOST_ASSERT(overread <= cfg_->max_overread());
     929              : 
     930         8582 :             auto cap = fb_.capacity() + overread +
     931         8582 :                 cfg_->min_buffer;
     932              : 
     933              :             // reserve body buffers first, as the decoder
     934              :             // must be installed after them.
     935         8582 :             auto const p = ws_.reserve_front(cap);
     936              : 
     937              :             // Content-Encoding
     938         8582 :             switch(m_.metadata().content_encoding.coding)
     939              :             {
     940            0 :             case content_coding::deflate:
     941            0 :                 if(!cfg_->apply_deflate_decoder)
     942            0 :                     goto no_filter;
     943            0 :                 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
     944              :                 {
     945            0 :                     filter_.reset(new zlib_filter(
     946              :                         *svc,
     947            0 :                         cfg_->zlib_window_bits));
     948              :                 }
     949            0 :                 break;
     950              : 
     951            0 :             case content_coding::gzip:
     952            0 :                 if(!cfg_->apply_gzip_decoder)
     953            0 :                     goto no_filter;
     954            0 :                 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
     955              :                 {
     956            0 :                     filter_.reset(new zlib_filter(
     957              :                         *svc,
     958            0 :                         cfg_->zlib_window_bits + 16));
     959              :                 }
     960            0 :                 break;
     961              : 
     962            0 :             case content_coding::br:
     963            0 :                 if(!cfg_->apply_brotli_decoder)
     964            0 :                     goto no_filter;
     965            0 :                 if(auto* svc = capy::get_system_context().find_service<http::brotli::decode_service>())
     966              :                 {
     967            0 :                     filter_.reset(new brotli_filter(*svc));
     968              :                 }
     969            0 :                 break;
     970              : 
     971            0 :             no_filter:
     972         8582 :             default:
     973         8582 :                 break;
     974              :             }
     975              : 
     976         8582 :             if(is_plain())
     977              :             {
     978         4311 :                 cb0_ = { p, cap, overread };
     979         4311 :                 cb1_ = {};
     980              :             }
     981              :             else
     982              :             {
     983              :                 // buffered payload
     984         4271 :                 std::size_t n0 = (overread > cfg_->min_buffer)
     985         8542 :                     ? overread
     986         4271 :                     : cfg_->min_buffer;
     987         4271 :                 std::size_t n1 = cfg_->min_buffer;
     988              : 
     989         4271 :                 cb0_ = { p      , n0, overread };
     990         4271 :                 cb1_ = { p + n0 , n1 };
     991              :             }
     992              : 
     993         8582 :             if(m_.payload() == payload::size)
     994              :             {
     995         8388 :                 if(!filter_ &&
     996         4194 :                     body_limit_ < m_.payload_size())
     997              :                 {
     998            2 :                     ec = BOOST_HTTP_ERR(
     999              :                         error::body_too_large);
    1000            1 :                     state_ = state::reset;
    1001            1 :                     return;
    1002              :                 }
    1003         4193 :                 payload_remain_ = m_.payload_size();
    1004              :             }
    1005              : 
    1006         8581 :             state_ = state::body;
    1007              :             BOOST_FALLTHROUGH;
    1008              :         }
    1009              : 
    1010        50194 :         case state::body:
    1011              :         {
    1012        50194 :             BOOST_ASSERT(state_ == state::body);
    1013        50194 :             BOOST_ASSERT(m_.payload() != payload::none);
    1014        50194 :             BOOST_ASSERT(m_.payload() != payload::error);
    1015              : 
    1016         8349 :             auto set_state_to_complete = [&]()
    1017              :             {
    1018         8349 :                 state_ = state::complete;
    1019        58543 :             };
    1020              : 
    1021        50194 :             if(m_.payload() == payload::chunked)
    1022              :             {
    1023              :                 for(;;)
    1024              :                 {
    1025        77449 :                     if(chunk_remain_ == 0
    1026        75238 :                         && !chunked_body_ended)
    1027              :                     {
    1028        71111 :                         auto cs = chained_sequence(cb0_.data());
    1029        20437 :                         auto check_ec = [&]()
    1030              :                         {
    1031        20437 :                             if(ec == condition::need_more_input && got_eof_)
    1032              :                             {
    1033            0 :                                 ec = BOOST_HTTP_ERR(error::incomplete);
    1034            0 :                                 state_ = state::reset;
    1035              :                             }
    1036        91548 :                         };
    1037              : 
    1038        71111 :                         if(needs_chunk_close_)
    1039              :                         {
    1040        61961 :                             parse_eol(cs, ec);
    1041        61961 :                             if(ec)
    1042              :                             {
    1043          327 :                                 check_ec();
    1044        20437 :                                 return;
    1045              :                             }
    1046              :                         }
    1047         9150 :                         else if(trailer_headers_)
    1048              :                         {
    1049         4229 :                             skip_trailer_headers(cs, ec);
    1050         4229 :                             if(ec)
    1051              :                             {
    1052          102 :                                 check_ec();
    1053          102 :                                 return;
    1054              :                             }
    1055         4127 :                             cb0_.consume(cb0_.size() - cs.size());
    1056         4127 :                             chunked_body_ended = true;
    1057         8264 :                             continue;
    1058              :                         }
    1059              :                         
    1060        66555 :                         auto chunk_size = parse_hex(cs, ec);
    1061        66555 :                         if(ec)
    1062              :                         {
    1063        19748 :                             check_ec();
    1064        19748 :                             return;
    1065              :                         }
    1066              : 
    1067              :                         // skip chunk extensions
    1068        46807 :                         find_eol(cs, ec);
    1069        46807 :                         if(ec)
    1070              :                         {
    1071          260 :                             check_ec();
    1072          260 :                             return;
    1073              :                         }
    1074              : 
    1075        46547 :                         cb0_.consume(cb0_.size() - cs.size());
    1076        46547 :                         chunk_remain_ = chunk_size;
    1077              : 
    1078        46547 :                         needs_chunk_close_ = true;
    1079        46547 :                         if(chunk_remain_ == 0)
    1080              :                         {
    1081         4137 :                             needs_chunk_close_ = false;
    1082         4137 :                             trailer_headers_ = true;
    1083         4137 :                             continue;
    1084              :                         }
    1085              :                     }
    1086              : 
    1087        48748 :                     if(cb0_.size() == 0 && !chunked_body_ended)
    1088              :                     {
    1089         1432 :                         if(got_eof_)
    1090              :                         {
    1091            2 :                             ec = BOOST_HTTP_ERR(
    1092              :                                 error::incomplete);
    1093            1 :                             state_ = state::reset;
    1094            1 :                             return;
    1095              :                         }
    1096              : 
    1097         2862 :                         ec = BOOST_HTTP_ERR(
    1098              :                             error::need_data);
    1099         1431 :                         return;
    1100              :                     }
    1101              : 
    1102        47316 :                     if(filter_)
    1103              :                     {
    1104            0 :                         chunk_remain_ -= apply_filter(
    1105              :                             ec,
    1106              :                             clamp(chunk_remain_, cb0_.size()),
    1107            0 :                             !chunked_body_ended);
    1108              : 
    1109            0 :                         if(ec || chunked_body_ended)
    1110            0 :                             return;
    1111              :                     }
    1112              :                     else
    1113              :                     {
    1114              :                         const std::size_t chunk_avail =
    1115        47316 :                             clamp(chunk_remain_, cb0_.size());
    1116              :                         const auto chunk =
    1117        47316 :                             capy::prefix(cb0_.data(), chunk_avail);
    1118              : 
    1119        47316 :                         if(body_limit_remain() < chunk_avail)
    1120              :                         {
    1121            0 :                             ec = BOOST_HTTP_ERR(
    1122              :                                 error::body_too_large);
    1123            0 :                             state_ = state::reset;
    1124         4127 :                             return;
    1125              :                         }
    1126              : 
    1127              :                         // in_place style
    1128        47316 :                         auto copied = capy::buffer_copy(
    1129        47316 :                             cb1_.prepare(cb1_.capacity()),
    1130              :                             chunk);
    1131        47316 :                         chunk_remain_ -= copied;
    1132        47316 :                         body_avail_   += copied;
    1133        47316 :                         body_total_   += copied;
    1134        47316 :                         cb0_.consume(copied);
    1135        47316 :                         cb1_.commit(copied);
    1136        47316 :                         if(cb1_.capacity() == 0
    1137        47316 :                             && !chunked_body_ended)
    1138              :                         {
    1139            0 :                             ec = BOOST_HTTP_ERR(
    1140              :                                 error::in_place_overflow);
    1141            0 :                             return;
    1142              :                         }
    1143              : 
    1144        47316 :                         if(chunked_body_ended)
    1145              :                         {
    1146         4127 :                             set_state_to_complete();
    1147         4127 :                             return;
    1148              :                         }
    1149              :                     }
    1150        51453 :                 }
    1151              :             }
    1152              :             else
    1153              :             {
    1154              :                 // non-chunked payload
    1155              : 
    1156        72594 :                 const std::size_t payload_avail = [&]()
    1157              :                 {
    1158        24198 :                     auto ret = cb0_.size();
    1159        24198 :                     if(!filter_)
    1160        24198 :                         ret -= body_avail_;
    1161        24198 :                     if(m_.payload() == payload::size)
    1162        23941 :                         return clamp(payload_remain_, ret);
    1163              :                     // payload::eof
    1164          257 :                     return ret;
    1165        24198 :                 }();
    1166              : 
    1167        72594 :                 const bool is_complete = [&]()
    1168              :                 {
    1169        24198 :                     if(m_.payload() == payload::size)
    1170        23941 :                         return payload_avail == payload_remain_;
    1171              :                     // payload::eof
    1172          257 :                     return got_eof_;
    1173        24198 :                 }();
    1174              : 
    1175        24198 :                 if(filter_)
    1176              :                 {
    1177            0 :                     payload_remain_ -= apply_filter(
    1178            0 :                         ec, payload_avail, !is_complete);
    1179            0 :                     if(ec || is_complete)
    1180            0 :                         return;
    1181              :                 }
    1182              :                 else
    1183              :                 {
    1184              :                     // plain body
    1185              : 
    1186        24198 :                     if(m_.payload() == payload::to_eof)
    1187              :                     {
    1188          257 :                         if(body_limit_remain() < payload_avail)
    1189              :                         {
    1190            2 :                             ec = BOOST_HTTP_ERR(
    1191              :                                 error::body_too_large);
    1192            1 :                             state_ = state::reset;
    1193            1 :                             return;
    1194              :                         }
    1195              :                     }
    1196              : 
    1197              :                     // in_place style
    1198        24197 :                     payload_remain_ -= payload_avail;
    1199        24197 :                     body_avail_     += payload_avail;
    1200        24197 :                     body_total_     += payload_avail;
    1201        24197 :                     if(cb0_.capacity() == 0 && !is_complete)
    1202              :                     {
    1203            2 :                         ec = BOOST_HTTP_ERR(
    1204              :                             error::in_place_overflow);
    1205            1 :                         return;
    1206              :                     }
    1207              : 
    1208        24196 :                     if(is_complete)
    1209              :                     {
    1210         4222 :                         set_state_to_complete();
    1211         4222 :                         return;
    1212              :                     }
    1213              :                 }
    1214              : 
    1215        19974 :                 if(m_.payload() == payload::size && got_eof_)
    1216              :                 {
    1217            2 :                     ec = BOOST_HTTP_ERR(
    1218              :                         error::incomplete);
    1219            1 :                     state_ = state::reset;
    1220            1 :                     return;
    1221              :                 }
    1222              : 
    1223        39946 :                 ec = BOOST_HTTP_ERR(
    1224              :                     error::need_data);
    1225        19973 :                 return;
    1226              :             }
    1227              : 
    1228              :             break;
    1229              :         }
    1230              : 
    1231         2210 :         case state::complete:
    1232         2210 :             break;
    1233              :         }
    1234              :     }
    1235              : 
    1236              :     auto
    1237        41176 :     pull_body() ->
    1238              :         const_buffers_type
    1239              :     {
    1240        41176 :         switch(state_)
    1241              :         {
    1242            0 :         case state::header_done:
    1243            0 :             return {};
    1244        41176 :         case state::body:
    1245              :         case state::complete:
    1246        41176 :             cbp_ = capy::prefix(
    1247        41176 :                 (is_plain() ? cb0_ : cb1_).data(),
    1248              :                 body_avail_);
    1249        41176 :             return detail::make_span(cbp_);
    1250            0 :         default:
    1251            0 :             detail::throw_logic_error();
    1252              :         }
    1253              :     }
    1254              : 
    1255              :     void
    1256        39412 :     consume_body(std::size_t n)
    1257              :     {
    1258        39412 :         switch(state_)
    1259              :         {
    1260            0 :         case state::header_done:
    1261            0 :             return;
    1262        39412 :         case state::body:
    1263              :         case state::complete:
    1264        39412 :             n = clamp(n, body_avail_);
    1265        39412 :             (is_plain() ? cb0_ : cb1_).consume(n);
    1266        39412 :             body_avail_ -= n;
    1267        39412 :             return;
    1268            0 :         default:
    1269            0 :             detail::throw_logic_error();
    1270              :         }
    1271              :     }
    1272              : 
    1273              :     core::string_view
    1274          700 :     body() const
    1275              :     {
    1276              :         // Precondition violation
    1277          700 :         if(state_ != state::complete)
    1278            0 :             detail::throw_logic_error();
    1279              : 
    1280              :         // Precondition violation
    1281          700 :         if(body_avail_ != body_total_)
    1282            0 :             detail::throw_logic_error();
    1283              : 
    1284          700 :         auto cbp = (is_plain() ? cb0_ : cb1_).data();
    1285          700 :         BOOST_ASSERT(cbp[1].size() == 0);
    1286          700 :         BOOST_ASSERT(cbp[0].size() == body_avail_);
    1287          700 :         return core::string_view(
    1288          700 :             static_cast<char const*>(cbp[0].data()),
    1289         1400 :             body_avail_);
    1290              :     }
    1291              : 
    1292              :     void
    1293            5 :     set_body_limit(std::uint64_t n)
    1294              :     {
    1295            5 :         switch(state_)
    1296              :         {
    1297            1 :         case state::header:
    1298              :         case state::header_done:
    1299            1 :             body_limit_ = n;
    1300            1 :             break;
    1301            2 :         case state::complete:
    1302              :             // only allowed for empty bodies
    1303            2 :             if(body_total_ == 0)
    1304            1 :                 break;
    1305              :             BOOST_FALLTHROUGH;
    1306              :         default:
    1307              :             // set body_limit before parsing the body
    1308            3 :             detail::throw_logic_error();
    1309              :         }
    1310            2 :     }
    1311              : 
    1312              : private:
    1313              :     bool
    1314       138713 :     is_plain() const noexcept
    1315              :     {
    1316       277426 :         return ! filter_ &&
    1317       277426 :             m_.payload() != payload::chunked;
    1318              :     }
    1319              : 
    1320              :     std::uint64_t
    1321        47587 :     body_limit_remain() const noexcept
    1322              :     {
    1323        47587 :         return body_limit_ - body_total_;
    1324              :     }
    1325              : 
    1326              :     std::size_t
    1327            0 :     apply_filter(
    1328              :         system::error_code& ec,
    1329              :         std::size_t payload_avail,
    1330              :         bool more)
    1331              :     {
    1332            0 :         std::size_t p0 = payload_avail;
    1333              :         for(;;)
    1334              :         {
    1335            0 :             if(payload_avail == 0 && more)
    1336            0 :                 break;
    1337              : 
    1338            0 :             auto f_rs = [&](){
    1339            0 :                 BOOST_ASSERT(filter_ != nullptr);
    1340            0 :                 std::size_t n = clamp(body_limit_remain());
    1341            0 :                 n = clamp(n, cb1_.capacity());
    1342              : 
    1343            0 :                 return filter_->process(
    1344            0 :                     detail::make_span(cb1_.prepare(n)),
    1345            0 :                     capy::prefix(cb0_.data(), payload_avail),
    1346            0 :                     more);
    1347            0 :             }();
    1348              : 
    1349            0 :             cb0_.consume(f_rs.in_bytes);
    1350            0 :             payload_avail -= f_rs.in_bytes;
    1351            0 :             body_total_   += f_rs.out_bytes;
    1352              : 
    1353              :             // in_place style
    1354            0 :             cb1_.commit(f_rs.out_bytes);
    1355            0 :             body_avail_ += f_rs.out_bytes;
    1356            0 :             if(cb1_.capacity() == 0 &&
    1357            0 :                 !f_rs.finished && f_rs.in_bytes == 0)
    1358              :             {
    1359            0 :                 ec = BOOST_HTTP_ERR(
    1360              :                     error::in_place_overflow);
    1361            0 :                 goto done;
    1362              :             }
    1363              : 
    1364            0 :             if(f_rs.ec)
    1365              :             {
    1366            0 :                 ec = f_rs.ec;
    1367            0 :                 state_ = state::reset;
    1368            0 :                 break;
    1369              :             }
    1370              : 
    1371            0 :             if(body_limit_remain() == 0 &&
    1372            0 :                 !f_rs.finished && f_rs.in_bytes == 0)
    1373              :             {
    1374            0 :                 ec = BOOST_HTTP_ERR(
    1375              :                     error::body_too_large);
    1376            0 :                 state_ = state::reset;
    1377            0 :                 break;
    1378              :             }
    1379              : 
    1380            0 :             if(f_rs.finished)
    1381              :             {
    1382            0 :                 if(!more)
    1383            0 :                     state_ = state::complete;
    1384            0 :                 break;
    1385              :             }
    1386            0 :         }
    1387              : 
    1388            0 :     done:
    1389            0 :         return p0 - payload_avail;
    1390              :     }
    1391              : };
    1392              : 
    1393              : //------------------------------------------------
    1394              : //
    1395              : // Special Members
    1396              : //
    1397              : //------------------------------------------------
    1398              : 
    1399         1704 : parser::
    1400              : ~parser()
    1401              : {
    1402         1704 :     delete impl_;
    1403         1704 : }
    1404              : 
    1405           12 : parser::
    1406           12 : parser() noexcept
    1407           12 :     : impl_(nullptr)
    1408              : {
    1409           12 : }
    1410              : 
    1411            3 : parser::
    1412            3 : parser(parser&& other) noexcept
    1413            3 :     : impl_(other.impl_)
    1414              : {
    1415            3 :     other.impl_ = nullptr;
    1416            3 : }
    1417              : 
    1418         1689 : parser::
    1419              : parser(
    1420              :     std::shared_ptr<parser_config_impl const> cfg,
    1421         1689 :     detail::kind k)
    1422         1689 :     : impl_(new impl(std::move(cfg), k))
    1423              : {
    1424              :     // TODO: use a single allocation for
    1425              :     // impl and workspace buffer.
    1426         1689 : }
    1427              : 
    1428              : void
    1429            4 : parser::
    1430              : assign(parser&& other) noexcept
    1431              : {
    1432            4 :     if(this == &other)
    1433            0 :         return;
    1434            4 :     delete impl_;
    1435            4 :     impl_ = other.impl_;
    1436            4 :     other.impl_ = nullptr;
    1437              : }
    1438              : 
    1439              : //--------------------------------------------
    1440              : //
    1441              : // Observers
    1442              : //
    1443              : //--------------------------------------------
    1444              : 
    1445              : bool
    1446        31380 : parser::got_header() const noexcept
    1447              : {
    1448        31380 :     BOOST_ASSERT(impl_);
    1449        31380 :     return impl_->got_header();
    1450              : }
    1451              : 
    1452              : bool
    1453        50092 : parser::is_complete() const noexcept
    1454              : {
    1455        50092 :     BOOST_ASSERT(impl_);
    1456        50092 :     return impl_->is_complete();
    1457              : }
    1458              : 
    1459              : //------------------------------------------------
    1460              : //
    1461              : // Modifiers
    1462              : //
    1463              : //------------------------------------------------
    1464              : 
    1465              : void
    1466         2235 : parser::
    1467              : reset() noexcept
    1468              : {
    1469         2235 :     BOOST_ASSERT(impl_);
    1470         2235 :     impl_->reset();
    1471         2235 : }
    1472              : 
    1473              : void
    1474        10164 : parser::start()
    1475              : {
    1476        10164 :     BOOST_ASSERT(impl_);
    1477        10164 :     impl_->start(false);
    1478        10159 : }
    1479              : 
    1480              : auto
    1481        68721 : parser::
    1482              : prepare() ->
    1483              :     mutable_buffers_type
    1484              : {
    1485        68721 :     BOOST_ASSERT(impl_);
    1486        68721 :     return impl_->prepare();
    1487              : }
    1488              : 
    1489              : void
    1490        68128 : parser::
    1491              : commit(
    1492              :     std::size_t n)
    1493              : {
    1494        68128 :     BOOST_ASSERT(impl_);
    1495        68128 :     impl_->commit(n);
    1496        68122 : }
    1497              : 
    1498              : void
    1499          132 : parser::
    1500              : commit_eof()
    1501              : {
    1502          132 :     BOOST_ASSERT(impl_);
    1503          132 :     impl_->commit_eof();
    1504          129 : }
    1505              : 
    1506              : void
    1507        85415 : parser::
    1508              : parse(
    1509              :     system::error_code& ec)
    1510              : {
    1511        85415 :     BOOST_ASSERT(impl_);
    1512        85415 :     impl_->parse(ec);
    1513        85413 : }
    1514              : 
    1515              : auto
    1516        41176 : parser::
    1517              : pull_body() ->
    1518              :     const_buffers_type
    1519              : {
    1520        41176 :     BOOST_ASSERT(impl_);
    1521        41176 :     return impl_->pull_body();
    1522              : }
    1523              : 
    1524              : void
    1525        39412 : parser::
    1526              : consume_body(std::size_t n)
    1527              : {
    1528        39412 :     BOOST_ASSERT(impl_);
    1529        39412 :     impl_->consume_body(n);
    1530        39412 : }
    1531              : 
    1532              : core::string_view
    1533          700 : parser::
    1534              : body() const
    1535              : {
    1536          700 :     BOOST_ASSERT(impl_);
    1537          700 :     return impl_->body();
    1538              : }
    1539              : 
    1540              : core::string_view
    1541            0 : parser::
    1542              : release_buffered_data() noexcept
    1543              : {
    1544              :     // TODO
    1545            0 :     return {};
    1546              : }
    1547              : 
    1548              : void
    1549            5 : parser::
    1550              : set_body_limit(std::uint64_t n)
    1551              : {
    1552            5 :     BOOST_ASSERT(impl_);
    1553            5 :     impl_->set_body_limit(n);
    1554            2 : }
    1555              : 
    1556              : //------------------------------------------------
    1557              : //
    1558              : // Implementation
    1559              : //
    1560              : //------------------------------------------------
    1561              : 
    1562              : void
    1563            0 : parser::
    1564              : start_impl(bool head_response)
    1565              : {
    1566            0 :     BOOST_ASSERT(impl_);
    1567            0 :     impl_->start(head_response);
    1568            0 : }
    1569              : 
    1570              : static_request const&
    1571          315 : parser::
    1572              : safe_get_request() const
    1573              : {
    1574          315 :     BOOST_ASSERT(impl_);
    1575          315 :     return impl_->safe_get_request();
    1576              : }
    1577              : 
    1578              : static_response const&
    1579            3 : parser::
    1580              : safe_get_response() const
    1581              : {
    1582            3 :     BOOST_ASSERT(impl_);
    1583            3 :     return impl_->safe_get_response();
    1584              : }
    1585              : 
    1586              : } // http
    1587              : } // boost
        

Generated by: LCOV version 2.3