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-07-22 18:52:55 Functions: 80.9 % 89 72 17

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

Generated by: LCOV version 2.3