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

Generated by: LCOV version 2.3