LCOV - code coverage report
Current view: top level - src - parser.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 77.9 % 687 535 152
Test Date: 2026-08-26 15:14:03 Functions: 84.1 % 63 53 10

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

Generated by: LCOV version 2.3