LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 88.5 % 756 669
Test Date: 2025-05-26 06:53:20 Functions: 88.4 % 69 61

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2024 Mohammad Nejati
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/http_proto
       9              : //
      10              : 
      11              : #include <boost/http_proto/context.hpp>
      12              : #include <boost/http_proto/detail/except.hpp>
      13              : #include <boost/http_proto/error.hpp>
      14              : #include <boost/http_proto/parser.hpp>
      15              : #include <boost/http_proto/rfc/detail/rules.hpp>
      16              : #include <boost/http_proto/service/zlib_service.hpp>
      17              : 
      18              : #include <boost/assert.hpp>
      19              : #include <boost/buffers/copy.hpp>
      20              : #include <boost/buffers/prefix.hpp>
      21              : #include <boost/buffers/size.hpp>
      22              : #include <boost/buffers/make_buffer.hpp>
      23              : #include <boost/url/grammar/ci_string.hpp>
      24              : #include <boost/url/grammar/hexdig_chars.hpp>
      25              : 
      26              : #include "detail/filter.hpp"
      27              : 
      28              : namespace boost {
      29              : namespace http_proto {
      30              : 
      31              : /*
      32              :     Principles for fixed-size buffer design
      33              : 
      34              :     axiom 1:
      35              :         To read data you must have a buffer.
      36              : 
      37              :     axiom 2:
      38              :         The size of the HTTP header is not
      39              :         known in advance.
      40              : 
      41              :     conclusion 3:
      42              :         A single I/O can produce a complete
      43              :         HTTP header and additional payload
      44              :         data.
      45              : 
      46              :     conclusion 4:
      47              :         A single I/O can produce multiple
      48              :         complete HTTP headers, complete
      49              :         payloads, and a partial header or
      50              :         payload.
      51              : 
      52              :     axiom 5:
      53              :         A process is in one of two states:
      54              :             1. at or below capacity
      55              :             2. above capacity
      56              : 
      57              :     axiom 6:
      58              :         A program which can allocate an
      59              :         unbounded number of resources can
      60              :         go above capacity.
      61              : 
      62              :     conclusion 7:
      63              :         A program can guarantee never going
      64              :         above capacity if all resources are
      65              :         provisioned at program startup.
      66              : 
      67              :     corollary 8:
      68              :         `parser` and `serializer` should each
      69              :         allocate a single buffer of calculated
      70              :         size, and never resize it.
      71              : 
      72              :     axiom #:
      73              :         A parser and a serializer are always
      74              :         used in pairs.
      75              : 
      76              : Buffer Usage
      77              : 
      78              : |                                               | begin
      79              : | H |   p   |                               | f | read headers
      80              : | H |   p   |                           | T | f | set T body
      81              : | H |   p   |                       | C | T | f | make codec C
      82              : | H |   p           |       b       | C | T | f | decode p into b
      83              : | H |       p       |       b       | C | T | f | read/parse loop
      84              : | H |                                   | T | f | destroy codec
      85              : | H |                                   | T | f | finished
      86              : 
      87              :     H   headers
      88              :     C   codec
      89              :     T   body
      90              :     f   table
      91              :     p   partial payload
      92              :     b   body data
      93              : 
      94              :     "payload" is the bytes coming in from
      95              :         the stream.
      96              : 
      97              :     "body" is the logical body, after transfer
      98              :         encoding is removed. This can be the
      99              :         same as the payload.
     100              : 
     101              :     A "plain payload" is when the payload and
     102              :         body are identical (no transfer encodings).
     103              : 
     104              :     A "buffered payload" is any payload which is
     105              :         not plain. A second buffer is required
     106              :         for reading.
     107              : 
     108              :     "overread" is additional data received past
     109              :     the end of the headers when reading headers,
     110              :     or additional data received past the end of
     111              :     the message payload.
     112              : */
     113              : 
     114              : namespace {
     115              : class inflator_filter
     116              :     : public http_proto::detail::filter
     117              : {
     118              :     zlib::stream& inflator_;
     119              : 
     120              : public:
     121           73 :     inflator_filter(
     122              :         context& ctx,
     123              :         http_proto::detail::workspace& ws,
     124              :         bool use_gzip)
     125          292 :         : inflator_{ ctx.get_service<zlib::service>()
     126           73 :             .make_inflator(ws, use_gzip ? 31 : 15) }
     127              :     {
     128           73 :     }
     129              : 
     130              :     virtual filter::results
     131       119418 :     on_process(
     132              :         buffers::mutable_buffer out,
     133              :         buffers::const_buffer in,
     134              :         bool more) override
     135              :     {
     136       119418 :         auto flush =
     137       119418 :             more ? zlib::flush::none : zlib::flush::finish;
     138       119418 :         filter::results results;
     139              : 
     140              :         for(;;)
     141              :         {
     142       119418 :             auto params = zlib::params{in.data(), in.size(),
     143       119418 :                 out.data(), out.size() };
     144       119418 :             auto ec = inflator_.write(params, flush);
     145              : 
     146       119418 :             results.in_bytes  += in.size() - params.avail_in;
     147       119418 :             results.out_bytes += out.size() - params.avail_out;
     148              : 
     149       178340 :             if( ec.failed() &&
     150       178340 :                 ec != zlib::error::buf_err )
     151              :             {
     152            1 :                 results.ec = ec;
     153       119418 :                 return results;
     154              :             }
     155              : 
     156       119417 :             if( ec == zlib::error::stream_end )
     157              :             {
     158          120 :                 results.finished = true;
     159          120 :                 return results;
     160              :             }
     161              : 
     162       119297 :             in  = buffers::suffix(in, params.avail_in);
     163       119297 :             out = buffers::suffix(out, params.avail_out);
     164              : 
     165       119297 :             if( in.size() == 0 || out.size() == 0 )
     166       119297 :                 return results;
     167            0 :         }
     168              :     }
     169              : };
     170              : 
     171              : class chained_sequence
     172              : {
     173              :     char const* pos_;
     174              :     char const* end_;
     175              :     char const* begin_b_;
     176              :     char const* end_b_;
     177              : 
     178              : public:
     179       125668 :     chained_sequence(buffers::const_buffer_pair const& cbp)
     180       125668 :         : pos_(static_cast<char const*>(cbp[0].data()))
     181       125668 :         , end_(pos_ + cbp[0].size())
     182       125668 :         , begin_b_(static_cast<char const*>(cbp[1].data()))
     183       125668 :         , end_b_(begin_b_ + cbp[1].size())
     184              :     {
     185       125668 :     }
     186              : 
     187              :     char const*
     188       651569 :     next() noexcept
     189              :     {
     190       651569 :         ++pos_;
     191              :         // most frequently taken branch
     192       651569 :         if(pos_ < end_)
     193       630246 :             return pos_;
     194              : 
     195              :         // swap with the second range
     196        21323 :         if(begin_b_ != end_b_)
     197              :         {
     198           39 :             pos_ = begin_b_;
     199           39 :             end_ = end_b_;
     200           39 :             begin_b_ = end_b_;
     201           39 :             return pos_;
     202              :         }
     203              : 
     204              :         // undo the increament
     205        21284 :         pos_ = end_;
     206        21284 :         return nullptr;
     207              :     }
     208              : 
     209              :     bool
     210       432880 :     empty() const noexcept
     211              :     {
     212       432880 :         return pos_ == end_;
     213              :     }
     214              : 
     215              :     char
     216       637061 :     value() const noexcept
     217              :     {
     218       637061 :         return *pos_;
     219              :     }
     220              : 
     221              :     std::size_t
     222       446856 :     size() const noexcept
     223              :     {
     224       446856 :         return (end_ - pos_) + (end_b_ - begin_b_);
     225              :     }
     226              : };
     227              : 
     228              : std::uint64_t
     229       121428 : parse_hex(
     230              :     chained_sequence& cs,
     231              :     system::error_code& ec) noexcept
     232              : {
     233       121428 :     std::uint64_t v   = 0;
     234       121428 :     std::size_t init_size = cs.size();
     235       319189 :     while(!cs.empty())
     236              :     {
     237       299906 :         auto n = grammar::hexdig_value(cs.value());
     238       299906 :         if(n < 0)
     239              :         {
     240       102144 :             if(init_size == cs.size())
     241              :             {
     242            2 :                 ec = BOOST_HTTP_PROTO_ERR(
     243              :                     error::bad_payload);
     244            1 :                 return 0;
     245              :             }
     246       102143 :             return v;
     247              :         }
     248              : 
     249              :         // at least 4 significant bits are free
     250       197762 :         if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
     251              :         {
     252            2 :             ec = BOOST_HTTP_PROTO_ERR(
     253              :                 error::bad_payload);
     254            1 :             return 0;
     255              :         }
     256              : 
     257       197761 :         v = (v << 4) | static_cast<std::uint64_t>(n);
     258       197761 :         cs.next();
     259              :     }
     260        38566 :     ec = BOOST_HTTP_PROTO_ERR(
     261              :         error::need_data);
     262        19283 :     return 0;
     263              : }
     264              : 
     265              : void
     266       102495 : find_eol(
     267              :     chained_sequence& cs,
     268              :     system::error_code& ec) noexcept
     269              : {
     270       109184 :     while(!cs.empty())
     271              :     {
     272       109096 :         if(cs.value() == '\r')
     273              :         {
     274       102407 :             if(!cs.next())
     275           10 :                 break;
     276       102397 :             if(cs.value() != '\n')
     277              :             {
     278            4 :                 ec = BOOST_HTTP_PROTO_ERR(
     279              :                     error::bad_payload);
     280            2 :                 return;
     281              :             }
     282       102395 :             cs.next();
     283       102395 :             return;
     284              :         }
     285         6689 :         cs.next();
     286              :     }
     287          196 :     ec = BOOST_HTTP_PROTO_ERR(
     288              :         error::need_data);
     289              : }
     290              : 
     291              : void
     292       117028 : parse_eol(
     293              :     chained_sequence& cs,
     294              :     system::error_code& ec) noexcept
     295              : {
     296       117028 :     if(cs.size() >= 2)
     297              :     {
     298              :         // we are sure size is at least 2
     299       117014 :         if(cs.value() == '\r' && *cs.next() == '\n')
     300              :         {
     301       117011 :             cs.next();
     302       117011 :             return;
     303              :         }
     304            6 :         ec = BOOST_HTTP_PROTO_ERR(
     305              :             error::bad_payload);
     306            3 :         return;
     307              :     }
     308           28 :     ec = BOOST_HTTP_PROTO_ERR(
     309              :         error::need_data);
     310              : }
     311              : 
     312              : void
     313         4223 : skip_trailer_headers(
     314              :     chained_sequence& cs,
     315              :     system::error_code& ec) noexcept
     316              : {
     317         4507 :     while(!cs.empty())
     318              :     {
     319         4501 :         if(cs.value() == '\r')
     320              :         {
     321         4149 :             if(!cs.next())
     322            2 :                 break;
     323         4147 :             if(cs.value() != '\n')
     324              :             {
     325            4 :                 ec = BOOST_HTTP_PROTO_ERR(
     326              :                     error::bad_payload);
     327            2 :                 return;
     328              :             }
     329         4145 :             cs.next();
     330         4145 :             return;
     331              :         }
     332              :         // skip to the end of field
     333          352 :         find_eol(cs, ec);
     334          352 :         if(ec)
     335           68 :             return;
     336              :     }
     337           16 :     ec = BOOST_HTTP_PROTO_ERR(
     338              :         error::need_data);
     339              : }
     340              : 
     341              : template<class UInt>
     342              : std::size_t
     343       377847 : clamp(
     344              :     UInt x,
     345              :     std::size_t limit = (std::numeric_limits<
     346              :         std::size_t>::max)()) noexcept
     347              : {
     348       377847 :     if(x >= limit)
     349       105520 :         return limit;
     350       272327 :     return static_cast<std::size_t>(x);
     351              : }
     352              : } // namespace
     353              : 
     354              : class parser_service
     355              :     : public service
     356              : {
     357              : public:
     358              :     parser::config_base cfg;
     359              :     std::size_t space_needed = 0;
     360              :     std::size_t max_codec = 0;
     361              :     zlib::service const* zlib_svc = nullptr;
     362              : 
     363              :     parser_service(
     364              :         context& ctx,
     365              :         parser::config_base const& cfg_);
     366              : 
     367              :     std::size_t
     368        55253 :     max_overread() const noexcept
     369              :     {
     370              :         return
     371        55253 :             cfg.headers.max_size +
     372        55253 :             cfg.min_buffer;
     373              :     }
     374              : };
     375              : 
     376           40 : parser_service::
     377              : parser_service(
     378              :     context& ctx,
     379           40 :     parser::config_base const& cfg_)
     380           40 :         : cfg(cfg_)
     381              : {
     382              : /*
     383              :     | fb |     cb0     |     cb1     | C | T | f |
     384              : 
     385              :     fb  flat_buffer         headers.max_size
     386              :     cb0 circular_buffer     min_buffer
     387              :     cb1 circular_buffer     min_buffer
     388              :     C   codec               max_codec
     389              :     T   body                max_type_erase
     390              :     f   table               max_table_space
     391              : 
     392              : */
     393              :     // validate
     394              :     //if(cfg.min_prepare > cfg.max_prepare)
     395              :         //detail::throw_invalid_argument();
     396              : 
     397           40 :     if(cfg.max_prepare < 1)
     398            0 :         detail::throw_invalid_argument();
     399              : 
     400              :     // VFALCO TODO OVERFLOW CHECING
     401              :     {
     402              :         //fb_.size() - h_.size +
     403              :         //svc_.cfg.min_buffer +
     404              :         //svc_.cfg.min_buffer +
     405              :         //svc_.max_codec;
     406              :     }
     407              : 
     408              :     // VFALCO OVERFLOW CHECKING ON THIS
     409           40 :     space_needed +=
     410           40 :         cfg.headers.valid_space_needed();
     411              : 
     412              :     // cb0_, cb1_
     413              :     // VFALCO OVERFLOW CHECKING ON THIS
     414           40 :     space_needed +=
     415           40 :         cfg.min_buffer +
     416              :         cfg.min_buffer;
     417              : 
     418              :     // T
     419           40 :     space_needed += cfg.max_type_erase;
     420              : 
     421              :     // max_codec
     422              :     {
     423           40 :         if(cfg.apply_deflate_decoder)
     424              :         {
     425              :             auto const n = ctx.get_service<
     426            3 :                 zlib::service>().inflator_space_needed(15);
     427            3 :             if( max_codec < n)
     428            3 :                 max_codec = n;
     429              :         }
     430              :     }
     431           40 :     space_needed += max_codec;
     432              : 
     433              :     // round up to alignof(detail::header::entry)
     434           40 :     auto const al = alignof(
     435              :         detail::header::entry);
     436           40 :     space_needed = al * ((
     437           40 :         space_needed + al - 1) / al);
     438           40 : }
     439              : 
     440              : void
     441           40 : install_parser_service(
     442              :     context& ctx,
     443              :     parser::config_base const& cfg)
     444              : {
     445              :     ctx.make_service<
     446           40 :         parser_service>(cfg);
     447           40 : }
     448              : 
     449              : //------------------------------------------------
     450              : //
     451              : // Special Members
     452              : //
     453              : //------------------------------------------------
     454              : 
     455         1052 : parser::
     456         1052 : parser(context& ctx, detail::kind k)
     457         1052 :     : ctx_(ctx)
     458         1052 :     , svc_(ctx.get_service<parser_service>())
     459         1052 :     , h_(detail::empty{ k })
     460         1052 :     , st_(state::reset)
     461         2104 :     , got_header_(false)
     462              : {
     463         1052 :     auto const n = svc_.space_needed;
     464         1052 :     ws_.allocate(n);
     465         1052 :     h_.cap = n;
     466         1052 : }
     467              : 
     468         1052 : parser::
     469              : ~parser()
     470              : {
     471         1052 : }
     472              : 
     473              : //--------------------------------------------
     474              : //
     475              : // Observers
     476              : //
     477              : //--------------------------------------------
     478              : 
     479              : bool
     480        11938 : parser::got_header() const noexcept
     481              : {
     482        11938 :     return got_header_;
     483              : }
     484              : 
     485              : bool
     486        51343 : parser::is_complete() const noexcept
     487              : {
     488        51343 :     return st_ >= state::complete_in_place;
     489              : }
     490              : 
     491              : //------------------------------------------------
     492              : //
     493              : // Modifiers
     494              : //
     495              : //------------------------------------------------
     496              : 
     497              : void
     498         2480 : parser::
     499              : reset() noexcept
     500              : {
     501         2480 :     ws_.clear();
     502         2480 :     st_ = state::start;
     503         2480 :     got_header_ = false;
     504         2480 :     got_eof_ = false;
     505         2480 : }
     506              : 
     507              : void
     508        10307 : parser::start()
     509              : {
     510        10307 :     start_impl(false);
     511        10302 : }
     512              : 
     513              : void
     514        10307 : parser::
     515              : start_impl(
     516              :     bool head_response)
     517              : {
     518        10307 :     std::size_t leftover = 0;
     519        10307 :     switch(st_)
     520              :     {
     521            1 :     default:
     522              :     case state::reset:
     523              :         // reset must be called first
     524            1 :         detail::throw_logic_error();
     525              : 
     526         2255 :     case state::start:
     527              :         // reset required on eof
     528         2255 :         if(got_eof_)
     529            0 :             detail::throw_logic_error();
     530         2255 :         break;
     531              : 
     532            3 :     case state::header:
     533            3 :         if(fb_.size() == 0)
     534              :         {
     535              :             // start() called twice
     536            2 :             detail::throw_logic_error();
     537              :         }
     538              :         BOOST_FALLTHROUGH;
     539              : 
     540              :     case state::header_done:
     541              :     case state::body:
     542              :     case state::set_body:
     543              :         // current message is incomplete
     544            2 :         detail::throw_logic_error();
     545              : 
     546         8015 :     case state::complete_in_place:
     547              :         // remove available body.
     548         8015 :         if(is_plain())
     549         4000 :             cb0_.consume(body_avail_);
     550              :         BOOST_FALLTHROUGH;
     551              : 
     552              :     case state::complete:
     553              :     {
     554              :         // move leftovers to front
     555              : 
     556         8047 :         ws_.clear();
     557         8047 :         leftover = cb0_.size();
     558              : 
     559         8047 :         auto* dest = reinterpret_cast<char*>(ws_.data());
     560         8047 :         auto cbp   = cb0_.data();
     561         8047 :         auto* a    = static_cast<char const*>(cbp[0].data());
     562         8047 :         auto* b    = static_cast<char const*>(cbp[1].data());
     563         8047 :         auto an    = cbp[0].size();
     564         8047 :         auto bn    = cbp[1].size();
     565              : 
     566         8047 :         if(bn == 0)
     567              :         {
     568         7609 :             std::memmove(dest, a, an);
     569              :         }
     570              :         else
     571              :         {
     572              :             // if `a` can fit between `dest` and `b`, shift `b` to the left
     573              :             // and copy `a` to its position. if `a` fits perfectly, the
     574              :             // shift will be of size 0.
     575              :             // if `a` requires more space, shift `b` to the right and
     576              :             // copy `a` to its position. this process may require multiple
     577              :             // iterations and should be done chunk by chunk to prevent `b`
     578              :             // from overlapping with `a`.
     579              :             do
     580              :             {
     581              :                 // clamp right shifts to prevent overlap with `a`
     582          438 :                 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
     583          438 :                 b = static_cast<char const*>(std::memmove(bp, b, bn));
     584              : 
     585              :                 // a chunk or all of `a` based on available space
     586          438 :                 auto chunk_a = static_cast<std::size_t>(b - dest);
     587          438 :                 std::memcpy(dest, a, chunk_a); // never overlap
     588          438 :                 an   -= chunk_a;
     589          438 :                 dest += chunk_a;
     590          438 :                 a    += chunk_a;
     591          438 :             } while(an);
     592              :         }
     593              : 
     594         8047 :         break;
     595              :     }
     596              :     }
     597              : 
     598        10302 :     ws_.clear();
     599              : 
     600        20604 :     fb_ = {
     601        10302 :         ws_.data(),
     602        10302 :         svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
     603              :         leftover };
     604              : 
     605        10302 :     BOOST_ASSERT(
     606              :         fb_.capacity() == svc_.max_overread() - leftover);
     607              : 
     608        10302 :     BOOST_ASSERT(
     609              :         head_response == false ||
     610              :         h_.kind == detail::kind::response);
     611              : 
     612        10302 :     h_ = detail::header(detail::empty{h_.kind});
     613        10302 :     h_.buf = reinterpret_cast<char*>(ws_.data());
     614        10302 :     h_.cbuf = h_.buf;
     615        10302 :     h_.cap = ws_.size();
     616              : 
     617        10302 :     st_ = state::header;
     618        10302 :     how_ = how::in_place;
     619              : 
     620              :     // reset to the configured default
     621        10302 :     body_limit_ = svc_.cfg.body_limit;
     622              : 
     623        10302 :     body_total_ = 0;
     624        10302 :     payload_remain_ = 0;
     625        10302 :     chunk_remain_ = 0;
     626        10302 :     body_avail_ = 0;
     627        10302 :     nprepare_ = 0;
     628              : 
     629        10302 :     filter_ = nullptr;
     630        10302 :     eb_ = nullptr;
     631        10302 :     sink_ = nullptr;
     632              : 
     633        10302 :     got_header_ = false;
     634        10302 :     head_response_ = head_response;
     635        10302 :     needs_chunk_close_ = false;
     636        10302 :     trailer_headers_ = false;
     637        10302 :     chunked_body_ended = false;
     638        10302 : }
     639              : 
     640              : auto
     641        51100 : parser::
     642              : prepare() ->
     643              :     mutable_buffers_type
     644              : {
     645        51100 :     nprepare_ = 0;
     646              : 
     647        51100 :     switch(st_)
     648              :     {
     649            1 :     default:
     650              :     case state::reset:
     651              :         // reset must be called first
     652            1 :         detail::throw_logic_error();
     653              : 
     654            1 :     case state::start:
     655              :         // start must be called first
     656            1 :         detail::throw_logic_error();
     657              : 
     658        10374 :     case state::header:
     659              :     {
     660        10374 :         BOOST_ASSERT(
     661              :             h_.size < svc_.cfg.headers.max_size);
     662        10374 :         std::size_t n = fb_.capacity() - fb_.size();
     663        10374 :         BOOST_ASSERT(n <= svc_.max_overread());
     664        10374 :         n = clamp(n, svc_.cfg.max_prepare);
     665        10374 :         mbp_[0] = fb_.prepare(n);
     666        10374 :         nprepare_ = n;
     667        10374 :         return mutable_buffers_type(&mbp_[0], 1);
     668              :     }
     669              : 
     670            0 :     case state::header_done:
     671              :         // forgot to call parse()
     672            0 :         detail::throw_logic_error();
     673              : 
     674        40723 :     case state::body:
     675              :     {
     676        40723 :         if(got_eof_)
     677              :         {
     678              :             // forgot to call parse()
     679            0 :             detail::throw_logic_error();
     680              :         }
     681              : 
     682        40723 :         if(! is_plain())
     683              :         {
     684              :             // buffered payload
     685        21673 :             std::size_t n = cb0_.capacity();
     686        21673 :             n = clamp(n, svc_.cfg.max_prepare);
     687        21673 :             nprepare_ = n;
     688        21673 :             mbp_ = cb0_.prepare(n);
     689        21673 :             return mutable_buffers_type(mbp_);
     690              :         }
     691              :         else
     692              :         {
     693        19050 :             switch(how_)
     694              :             {
     695        19029 :             default:
     696              :             case how::in_place:
     697              :             case how::sink:
     698              :             {
     699        19029 :                 std::size_t n = cb0_.capacity();
     700        19029 :                 n = clamp(n, svc_.cfg.max_prepare);
     701              : 
     702        19029 :                 if(h_.md.payload == payload::size)
     703              :                 {
     704        19004 :                     if(n > payload_remain_)
     705              :                     {
     706        17797 :                         std::size_t overread =
     707        17797 :                             n - static_cast<std::size_t>(payload_remain_);
     708        17797 :                         if(overread > svc_.max_overread())
     709         7877 :                             n = static_cast<std::size_t>(payload_remain_) +
     710         7877 :                                 svc_.max_overread();
     711              :                     }
     712              :                 }
     713              :                 else
     714              :                 {
     715           25 :                     BOOST_ASSERT(
     716              :                         h_.md.payload == payload::to_eof);
     717           25 :                     n = clamp(body_limit_remain() + 1, n);
     718              :                 }
     719              : 
     720        19029 :                 nprepare_ = n;
     721        19029 :                 mbp_ = cb0_.prepare(n);
     722        19029 :                 return mutable_buffers_type(mbp_);
     723              :             }
     724           21 :             case how::elastic:
     725              :             {
     726           21 :                 BOOST_ASSERT(cb0_.size() == 0);
     727           21 :                 BOOST_ASSERT(body_avail_ == 0);
     728              : 
     729           21 :                 std::size_t n = svc_.cfg.min_buffer;
     730              : 
     731           21 :                 if(h_.md.payload == payload::size)
     732              :                 {
     733              :                     // Overreads are not allowed, or
     734              :                     // else the caller will see extra
     735              :                     // unrelated data.
     736            6 :                     n = clamp(payload_remain_, n);
     737              :                 }
     738              :                 else
     739              :                 {
     740           15 :                     BOOST_ASSERT(
     741              :                         h_.md.payload == payload::to_eof);
     742           15 :                     n = clamp(body_limit_remain() + 1, n);
     743           15 :                     n = clamp(n, eb_->max_size() - eb_->size());
     744              :                     // fill capacity first to avoid an allocation
     745              :                     std::size_t avail =
     746           15 :                         eb_->capacity() - eb_->size();
     747           15 :                     if(avail != 0)
     748           15 :                         n = clamp(n, avail);
     749              : 
     750           15 :                     if(n == 0)
     751              :                     {
     752              :                         // dynamic buffer is full
     753              :                         // attempt a 1 byte read so
     754              :                         // we can detect overflow
     755            1 :                         nprepare_ = 1;
     756            1 :                         mbp_ = cb0_.prepare(1);
     757            1 :                         return mutable_buffers_type(mbp_);
     758              :                     }
     759              :                 }
     760              : 
     761           20 :                 n = clamp(n, svc_.cfg.max_prepare);
     762           20 :                 BOOST_ASSERT(n != 0);
     763           20 :                 nprepare_ = n;
     764           20 :                 return eb_->prepare(n);
     765              :             }
     766              :             }
     767              :         }
     768              :     }
     769              : 
     770            0 :     case state::set_body:
     771              :         // forgot to call parse()
     772            0 :         detail::throw_logic_error();
     773              : 
     774            1 :     case state::complete_in_place:
     775              :     case state::complete:
     776              :         // already complete
     777            1 :         detail::throw_logic_error();
     778              :     }
     779              : }
     780              : 
     781              : void
     782        51097 : parser::
     783              : commit(
     784              :     std::size_t n)
     785              : {
     786        51097 :     switch(st_)
     787              :     {
     788            1 :     default:
     789              :     case state::reset:
     790              :     {
     791              :         // reset must be called first
     792            1 :         detail::throw_logic_error();
     793              :     }
     794              : 
     795            1 :     case state::start:
     796              :     {
     797              :         // forgot to call start()
     798            1 :         detail::throw_logic_error();
     799              :     }
     800              : 
     801        10374 :     case state::header:
     802              :     {
     803        10374 :         if(n > nprepare_)
     804              :         {
     805              :             // n can't be greater than size of
     806              :             // the buffers returned by prepare()
     807            1 :             detail::throw_invalid_argument();
     808              :         }
     809              : 
     810        10373 :         if(got_eof_)
     811              :         {
     812              :             // can't commit after EOF
     813            1 :             detail::throw_logic_error();
     814              :         }
     815              : 
     816        10372 :         nprepare_ = 0; // invalidate
     817        10372 :         fb_.commit(n);
     818        10372 :         break;
     819              :     }
     820              : 
     821            0 :     case state::header_done:
     822              :     {
     823              :         // forgot to call parse()
     824            0 :         detail::throw_logic_error();
     825              :     }
     826              : 
     827        40720 :     case state::body:
     828              :     {
     829        40720 :         if(n > nprepare_)
     830              :         {
     831              :             // n can't be greater than size of
     832              :             // the buffers returned by prepare()
     833            2 :             detail::throw_invalid_argument();
     834              :         }
     835              : 
     836        40718 :         if(got_eof_)
     837              :         {
     838              :             // can't commit after EOF
     839            0 :             detail::throw_logic_error();
     840              :         }
     841              :     
     842        40718 :         nprepare_ = 0; // invalidate
     843        40718 :         if(is_plain() && how_ == how::elastic)
     844              :         {
     845           20 :             if(eb_->max_size() == eb_->size())
     846              :             {
     847              :                 // borrowed 1 byte from
     848              :                 // cb0_ in prepare()
     849            1 :                 BOOST_ASSERT(n <= 1);
     850            1 :                 cb0_.commit(n);
     851              :             }
     852              :             else
     853              :             {
     854           19 :                 eb_->commit(n);
     855           19 :                 payload_remain_ -= n;
     856           19 :                 body_total_     += n;
     857              :             }
     858              :         }
     859              :         else
     860              :         {
     861        40698 :             cb0_.commit(n);
     862              :         }
     863        40718 :         break;
     864              :     }
     865              : 
     866            0 :     case state::set_body:
     867              :     {
     868              :         // forgot to call parse()
     869            0 :         detail::throw_logic_error();
     870              :     }
     871              : 
     872            1 :     case state::complete_in_place:
     873              :     case state::complete:
     874              :     {
     875              :         // already complete
     876            1 :         detail::throw_logic_error();
     877              :     }
     878              :     }
     879        51090 : }
     880              : 
     881              : void
     882          402 : parser::
     883              : commit_eof()
     884              : {
     885          402 :     nprepare_ = 0; // invalidate
     886              : 
     887          402 :     switch(st_)
     888              :     {
     889            1 :     default:
     890              :     case state::reset:
     891              :         // reset must be called first
     892            1 :         detail::throw_logic_error();
     893              : 
     894            1 :     case state::start:
     895              :         // forgot to call prepare()
     896            1 :         detail::throw_logic_error();
     897              : 
     898           31 :     case state::header:
     899           31 :         got_eof_ = true;
     900           31 :         break;
     901              : 
     902            0 :     case state::header_done:
     903              :         // forgot to call parse()
     904            0 :         detail::throw_logic_error();
     905              : 
     906          368 :     case state::body:
     907          368 :         got_eof_ = true;
     908          368 :         break;
     909              : 
     910            0 :     case state::set_body:
     911              :         // forgot to call parse()
     912            0 :         detail::throw_logic_error();
     913              : 
     914            1 :     case state::complete_in_place:
     915              :     case state::complete:
     916              :         // can't commit eof when complete
     917            1 :         detail::throw_logic_error();
     918              :     }
     919          399 : }
     920              : 
     921              : void
     922        69899 : parser::
     923              : parse(
     924              :     system::error_code& ec)
     925              : {
     926        69899 :     ec = {};
     927        69899 :     switch(st_)
     928              :     {
     929            1 :     default:
     930              :     case state::reset:
     931              :         // reset must be called first
     932            1 :         detail::throw_logic_error();
     933              : 
     934            1 :     case state::start:
     935              :         // start must be called first
     936            1 :         detail::throw_logic_error();
     937              : 
     938        16649 :     case state::header:
     939              :     {
     940        16649 :         BOOST_ASSERT(h_.buf == static_cast<
     941              :             void const*>(ws_.data()));
     942        16649 :         BOOST_ASSERT(h_.cbuf == static_cast<
     943              :             void const*>(ws_.data()));
     944              : 
     945        16649 :         h_.parse(fb_.size(), svc_.cfg.headers, ec);
     946              : 
     947        16649 :         if(ec == condition::need_more_input)
     948              :         {
     949         6384 :             if(! got_eof_)
     950              :             {
     951              :                 // headers incomplete
     952         6357 :                 return;
     953              :             }
     954              : 
     955           27 :             if(fb_.size() == 0)
     956              :             {
     957              :                 // stream closed cleanly
     958           12 :                 st_ = state::reset;
     959           24 :                 ec = BOOST_HTTP_PROTO_ERR(
     960              :                     error::end_of_stream);
     961           12 :                 return;
     962              :             }
     963              : 
     964              :             // stream closed with a
     965              :             // partial message received
     966           15 :             st_ = state::reset;
     967           30 :             ec = BOOST_HTTP_PROTO_ERR(
     968              :                 error::incomplete);
     969           15 :             return;
     970              :         }
     971        10265 :         else if(ec.failed())
     972              :         {
     973              :             // other error,
     974              :             //
     975              :             // VFALCO map this to a bad
     976              :             // request or bad response error?
     977              :             //
     978          259 :             st_ = state::reset; // unrecoverable
     979          259 :             return;
     980              :         }
     981              : 
     982        10006 :         got_header_ = true;
     983              : 
     984              :         // reserve headers + table
     985        10006 :         ws_.reserve_front(h_.size);
     986        10006 :         ws_.reserve_back(h_.table_space());
     987              : 
     988              :         // no payload
     989        10006 :         if(h_.md.payload == payload::none ||
     990         9084 :             head_response_)
     991              :         {
     992              :             // octets of the next message
     993          922 :             auto overread = fb_.size() - h_.size;
     994          922 :             cb0_ = { ws_.data(), overread, overread };
     995          922 :             ws_.reserve_front(overread);
     996          922 :             st_ = state::complete_in_place;
     997          922 :             return;
     998              :         }
     999              : 
    1000         9084 :         st_ = state::header_done;
    1001         9084 :         break;
    1002              :     }
    1003              : 
    1004         9083 :     case state::header_done:
    1005              :     {
    1006              :         // metadata error
    1007         9083 :         if(h_.md.payload == payload::error)
    1008              :         {
    1009              :             // VFALCO This needs looking at
    1010          360 :             ec = BOOST_HTTP_PROTO_ERR(
    1011              :                 error::bad_payload);
    1012          180 :             st_ = state::reset; // unrecoverable
    1013          180 :             return;
    1014              :         }
    1015              : 
    1016              :         // overread currently includes any and all octets that
    1017              :         // extend beyond the current end of the header
    1018              :         // this can include associated body octets for the
    1019              :         // current message or octets of the next message in the
    1020              :         // stream, e.g. pipelining is being used
    1021         8903 :         auto const overread = fb_.size() - h_.size;
    1022         8903 :         BOOST_ASSERT(overread <= svc_.max_overread());
    1023              : 
    1024         8903 :         auto cap = fb_.capacity() + overread +
    1025         8903 :             svc_.cfg.min_buffer;
    1026              : 
    1027              :         // reserve body buffers first, as the decoder
    1028              :         // must be installed after them.
    1029         8903 :         auto const p = ws_.reserve_front(cap);
    1030              : 
    1031         8903 :         if(svc_.cfg.apply_deflate_decoder &&
    1032           73 :             h_.md.content_encoding.encoding == encoding::deflate)
    1033              :         {
    1034           37 :             filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, false);
    1035              :         }
    1036         8866 :         else if(svc_.cfg.apply_gzip_decoder &&
    1037           36 :             h_.md.content_encoding.encoding == encoding::gzip)
    1038              :         {
    1039           36 :             filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, true);
    1040              :         }
    1041              :         else
    1042              :         {
    1043         8830 :             cap += svc_.max_codec;
    1044         8830 :             ws_.reserve_front(svc_.max_codec);
    1045              :         }
    1046              : 
    1047         8903 :         if(is_plain() || how_ == how::elastic)
    1048              :         {
    1049         4721 :             cb0_ = { p, cap, overread };
    1050         4721 :             cb1_ = {};
    1051              :         }
    1052              :         else
    1053              :         {
    1054              :             // buffered payload
    1055         8364 :             std::size_t n0 = (overread > svc_.cfg.min_buffer)
    1056         4182 :                 ? overread
    1057         4158 :                 : svc_.cfg.min_buffer;
    1058         4182 :             std::size_t n1 = svc_.cfg.min_buffer;
    1059              : 
    1060         4182 :             cb0_ = { p      , n0, overread };
    1061         4182 :             cb1_ = { p + n0 , n1 };
    1062              :         }
    1063              : 
    1064         8903 :         if(h_.md.payload == payload::size)
    1065              :         {
    1066         4373 :             if(!filter_ &&
    1067         4349 :                 body_limit_ < h_.md.payload_size)
    1068              :             {
    1069            2 :                 ec = BOOST_HTTP_PROTO_ERR(
    1070              :                     error::body_too_large);
    1071            1 :                 st_ = state::reset;
    1072            1 :                 return;
    1073              :             }
    1074         4372 :             payload_remain_ = h_.md.payload_size;
    1075              :         }
    1076              : 
    1077         8902 :         st_ = state::body;
    1078              :         BOOST_FALLTHROUGH;
    1079              :     }
    1080              : 
    1081              :     case state::body:
    1082              :     {
    1083        50274 :     do_body:
    1084        50274 :         BOOST_ASSERT(st_ == state::body);
    1085        50274 :         BOOST_ASSERT(h_.md.payload != payload::none);
    1086        50274 :         BOOST_ASSERT(h_.md.payload != payload::error);
    1087              : 
    1088         8796 :         auto set_state_to_complete = [&]()
    1089              :         {
    1090         8796 :             if(how_ == how::in_place)
    1091              :             {
    1092         8336 :                 st_ = state::complete_in_place;
    1093         8336 :                 return;
    1094              :             }
    1095          460 :             st_ = state::complete;
    1096        50274 :         };
    1097              : 
    1098        50274 :         if(h_.md.payload == payload::chunked)
    1099              :         {
    1100              :             for(;;)
    1101              :             {
    1102       131016 :                 if(chunk_remain_ == 0
    1103       129813 :                     && !chunked_body_ended)
    1104              :                 {
    1105       125668 :                     auto cs = chained_sequence(cb0_.data());
    1106        19412 :                     auto check_ec = [&]()
    1107              :                     {
    1108        19412 :                         if(ec == condition::need_more_input && got_eof_)
    1109              :                         {
    1110            0 :                             ec = BOOST_HTTP_PROTO_ERR(error::incomplete);
    1111            0 :                             st_ = state::reset;
    1112              :                         }
    1113       145080 :                     };
    1114              : 
    1115       125668 :                     if(needs_chunk_close_)
    1116              :                     {
    1117       117028 :                         parse_eol(cs, ec);
    1118       117028 :                         if(ec)
    1119              :                         {
    1120           17 :                             check_ec();
    1121        19412 :                             return;
    1122              :                         }
    1123              :                     }
    1124         8640 :                     else if(trailer_headers_)
    1125              :                     {
    1126         4223 :                         skip_trailer_headers(cs, ec);
    1127         4223 :                         if(ec)
    1128              :                         {
    1129           78 :                             check_ec();
    1130           78 :                             return;
    1131              :                         }
    1132         4145 :                         cb0_.consume(cb0_.size() - cs.size());
    1133         4145 :                         chunked_body_ended = true;
    1134         8292 :                         continue;
    1135              :                     }
    1136              :                     
    1137       121428 :                     auto chunk_size = parse_hex(cs, ec);
    1138       121428 :                     if(ec)
    1139              :                     {
    1140        19285 :                         check_ec();
    1141        19285 :                         return;
    1142              :                     }
    1143              : 
    1144              :                     // skip chunk extensions
    1145       102143 :                     find_eol(cs, ec);
    1146       102143 :                     if(ec)
    1147              :                     {
    1148           32 :                         check_ec();
    1149           32 :                         return;
    1150              :                     }
    1151              : 
    1152       102111 :                     cb0_.consume(cb0_.size() - cs.size());
    1153       102111 :                     chunk_remain_ = chunk_size;
    1154              : 
    1155       102111 :                     needs_chunk_close_ = true;
    1156       102111 :                     if(chunk_remain_ == 0)
    1157              :                     {
    1158         4147 :                         needs_chunk_close_ = false;
    1159         4147 :                         trailer_headers_ = true;
    1160         4147 :                         continue;
    1161              :                     }
    1162              :                 }
    1163              : 
    1164       103312 :                 if(cb0_.size() == 0 && !chunked_body_ended)
    1165              :                 {
    1166          365 :                     if(got_eof_)
    1167              :                     {
    1168            2 :                         ec = BOOST_HTTP_PROTO_ERR(
    1169              :                             error::incomplete);
    1170            1 :                         st_ = state::reset;
    1171            1 :                         return;
    1172              :                     }
    1173              : 
    1174          728 :                     ec = BOOST_HTTP_PROTO_ERR(
    1175              :                         error::need_data);
    1176          364 :                     return;
    1177              :                 }
    1178              : 
    1179       102947 :                 if(filter_)
    1180              :                 {
    1181        56565 :                     chunk_remain_ -= apply_filter(
    1182              :                         ec,
    1183              :                         clamp(chunk_remain_, cb0_.size()),
    1184        56565 :                         !chunked_body_ended);
    1185              : 
    1186        56565 :                     if(ec || chunked_body_ended)
    1187          564 :                         return;
    1188              :                 }
    1189              :                 else
    1190              :                 {
    1191              :                     const std::size_t chunk_avail =
    1192        46382 :                         clamp(chunk_remain_, cb0_.size());
    1193              :                     const auto chunk =
    1194        46382 :                         buffers::prefix(cb0_.data(), chunk_avail);
    1195              : 
    1196        46382 :                     if(body_limit_remain() < chunk_avail)
    1197              :                     {
    1198            0 :                         ec = BOOST_HTTP_PROTO_ERR(
    1199              :                             error::body_too_large);
    1200            0 :                         st_ = state::reset;
    1201         4121 :                         return;
    1202              :                     }
    1203              : 
    1204        46382 :                     switch(how_)
    1205              :                     {
    1206        46382 :                     case how::in_place:
    1207              :                     {
    1208        46382 :                         auto copied = buffers::copy(
    1209        46382 :                             cb1_.prepare(cb1_.capacity()),
    1210              :                             chunk);
    1211        46382 :                         chunk_remain_ -= copied;
    1212        46382 :                         body_avail_   += copied;
    1213        46382 :                         body_total_   += copied;
    1214        46382 :                         cb0_.consume(copied);
    1215        46382 :                         cb1_.commit(copied);
    1216        46382 :                         if(cb1_.capacity() == 0
    1217        46382 :                             && !chunked_body_ended)
    1218              :                         {
    1219            0 :                             ec = BOOST_HTTP_PROTO_ERR(
    1220              :                                 error::in_place_overflow);
    1221            0 :                             return;
    1222              :                         }
    1223        46382 :                         break;
    1224              :                     }
    1225            0 :                     case how::sink:
    1226              :                     {
    1227            0 :                         auto sink_rs = sink_->write(
    1228            0 :                             chunk, !chunked_body_ended);
    1229            0 :                         chunk_remain_ -= sink_rs.bytes;
    1230            0 :                         body_total_   += sink_rs.bytes;
    1231            0 :                         cb0_.consume(sink_rs.bytes);
    1232            0 :                         if(sink_rs.ec.failed())
    1233              :                         {
    1234            0 :                             body_avail_ += 
    1235            0 :                                 chunk_avail - sink_rs.bytes;
    1236            0 :                             ec  = sink_rs.ec;
    1237            0 :                             st_ = state::reset;
    1238            0 :                             return;
    1239              :                         }
    1240            0 :                         break;
    1241              :                     }
    1242            0 :                     case how::elastic:
    1243              :                     {
    1244            0 :                         if(eb_->max_size() - eb_->size()
    1245            0 :                             < chunk_avail)
    1246              :                         {
    1247            0 :                             ec = BOOST_HTTP_PROTO_ERR(
    1248              :                                 error::buffer_overflow);
    1249            0 :                             st_ = state::reset;
    1250            0 :                             return;
    1251              :                         }
    1252            0 :                         buffers::copy(
    1253            0 :                             eb_->prepare(chunk_avail),
    1254              :                             chunk);
    1255            0 :                         chunk_remain_ -= chunk_avail;
    1256            0 :                         body_total_   += chunk_avail;
    1257            0 :                         cb0_.consume(chunk_avail);
    1258            0 :                         eb_->commit(chunk_avail);
    1259            0 :                         break;
    1260              :                     }
    1261              :                     }
    1262              : 
    1263        46382 :                     if(chunked_body_ended)
    1264              :                     {
    1265         4121 :                         set_state_to_complete();
    1266         4121 :                         return;
    1267              :                     }
    1268              :                 }
    1269       106554 :             }
    1270              :         }
    1271              :         else
    1272              :         {
    1273              :             // non-chunked payload
    1274              : 
    1275        77436 :             const std::size_t payload_avail = [&]()
    1276              :             {
    1277        25812 :                 auto ret = cb0_.size();
    1278        25812 :                 if(!filter_)
    1279        24119 :                     ret -= body_avail_;
    1280        25812 :                 if(h_.md.payload == payload::size)
    1281        24181 :                     return clamp(payload_remain_, ret);
    1282              :                 // payload::eof
    1283         1631 :                 return ret;
    1284        25812 :             }();
    1285              : 
    1286        77436 :             const bool is_complete = [&]()
    1287              :             {
    1288        25812 :                 if(h_.md.payload == payload::size)
    1289        24181 :                     return payload_avail == payload_remain_;
    1290              :                 // payload::eof
    1291         1631 :                 return got_eof_;
    1292        25812 :             }();
    1293              : 
    1294        25812 :             if(filter_)
    1295              :             {
    1296         3386 :                 payload_remain_ -= apply_filter(
    1297         1693 :                     ec, payload_avail, !is_complete);
    1298         1693 :                 if(ec || is_complete)
    1299         1129 :                     return;
    1300              :             }
    1301              :             else
    1302              :             {
    1303              :                 // plain body
    1304              : 
    1305        24119 :                 if(h_.md.payload == payload::to_eof)
    1306              :                 {
    1307          764 :                     if(body_limit_remain() < payload_avail)
    1308              :                     {
    1309            2 :                         ec = BOOST_HTTP_PROTO_ERR(
    1310              :                             error::body_too_large);
    1311            1 :                         st_ = state::reset;
    1312            1 :                         return;
    1313              :                     }
    1314              :                 }
    1315              : 
    1316        24118 :                 switch(how_)
    1317              :                 {
    1318        23361 :                 case how::in_place:
    1319              :                 {
    1320        23361 :                     payload_remain_ -= payload_avail;
    1321        23361 :                     body_avail_     += payload_avail;
    1322        23361 :                     body_total_     += payload_avail;
    1323        23361 :                     if(cb0_.capacity() == 0 && !is_complete)
    1324              :                     {
    1325            2 :                         ec = BOOST_HTTP_PROTO_ERR(
    1326              :                             error::in_place_overflow);
    1327            1 :                         return;
    1328              :                     }
    1329        23360 :                     break;
    1330              :                 }
    1331          371 :                 case how::sink:
    1332              :                 {
    1333          371 :                     payload_remain_ -= payload_avail;
    1334          371 :                     body_total_     += payload_avail;
    1335          371 :                     auto sink_rs = sink_->write(
    1336          371 :                         buffers::prefix(
    1337            0 :                             cb0_.data(),
    1338              :                             payload_avail),
    1339          371 :                         !is_complete);
    1340          371 :                     cb0_.consume(sink_rs.bytes);
    1341          371 :                     if(sink_rs.ec.failed())
    1342              :                     {
    1343            0 :                         body_avail_ += 
    1344            0 :                             payload_avail - sink_rs.bytes;
    1345            0 :                         ec  = sink_rs.ec;
    1346            0 :                         st_ = state::reset;
    1347            0 :                         return;
    1348              :                     }
    1349          371 :                     break;
    1350              :                 }
    1351          386 :                 case how::elastic:
    1352              :                 {
    1353              :                     // payload_remain_ and body_total_
    1354              :                     // are already updated in commit()
    1355              : 
    1356              :                     // cb0_ contains data
    1357          386 :                     if(payload_avail != 0)
    1358              :                     {
    1359          193 :                         if(eb_->max_size() - eb_->size()
    1360          193 :                             < payload_avail)
    1361              :                         {
    1362            2 :                             ec = BOOST_HTTP_PROTO_ERR(
    1363              :                                 error::buffer_overflow);
    1364            1 :                             st_ = state::reset;
    1365            1 :                             return;
    1366              :                         }
    1367              :                         // only happens when an elastic body
    1368              :                         // is attached in header_done state
    1369          192 :                         buffers::copy(
    1370          192 :                             eb_->prepare(payload_avail),
    1371          192 :                             cb0_.data());
    1372          192 :                         cb0_.consume(payload_avail);
    1373          192 :                         eb_->commit(payload_avail);
    1374          192 :                         payload_remain_ -= payload_avail;
    1375          192 :                         body_total_ += payload_avail;
    1376              :                     }
    1377          385 :                     break;
    1378              :                 }
    1379              :                 }
    1380              : 
    1381        24116 :                 if(is_complete)
    1382              :                 {
    1383         4675 :                     set_state_to_complete();
    1384         4675 :                     return;
    1385              :                 }
    1386              :             }
    1387              : 
    1388        20005 :             if(h_.md.payload == payload::size && got_eof_)
    1389              :             {
    1390            2 :                 ec = BOOST_HTTP_PROTO_ERR(
    1391              :                     error::incomplete);
    1392            1 :                 st_ = state::reset;
    1393            1 :                 return;
    1394              :             }
    1395              : 
    1396        40008 :             ec = BOOST_HTTP_PROTO_ERR(
    1397              :                 error::need_data);
    1398        20004 :             return;
    1399              :         }
    1400              : 
    1401              :         break;
    1402              :     }
    1403              : 
    1404         2333 :     case state::set_body:
    1405              :     case state::complete_in_place:
    1406              :     {
    1407         2333 :         auto& body_buf = is_plain() ? cb0_ : cb1_;
    1408              : 
    1409         2333 :         switch(how_)
    1410              :         {
    1411         2216 :         case how::in_place:
    1412         2216 :             return; // no-op
    1413           58 :         case how::sink:
    1414              :         {
    1415           58 :             auto rs = sink_->write(
    1416           58 :                 buffers::prefix(
    1417            0 :                     body_buf.data(),
    1418              :                     body_avail_),
    1419           58 :                 st_ == state::set_body);
    1420           58 :             body_buf.consume(rs.bytes);
    1421           58 :             body_avail_ -= rs.bytes;
    1422           58 :             if(rs.ec.failed())
    1423              :             {
    1424            0 :                 ec  = rs.ec;
    1425            0 :                 st_ = state::reset;
    1426            0 :                 return;
    1427              :             }
    1428           58 :             break;
    1429              :         }
    1430           59 :         case how::elastic:
    1431              :         {
    1432           59 :             if(eb_->max_size() - eb_->size()
    1433           59 :                 < body_avail_)
    1434              :             {
    1435            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1436              :                     error::buffer_overflow);
    1437            0 :                 return;
    1438              :             }
    1439           59 :             buffers::copy(
    1440           59 :                 eb_->prepare(body_avail_),
    1441           59 :                 body_buf.data());
    1442           59 :             body_buf.consume(body_avail_);
    1443           59 :             eb_->commit(body_avail_);
    1444           59 :             body_avail_ = 0;
    1445              :             // TODO: expand cb0_ when possible?
    1446           59 :             break;
    1447              :         }
    1448              :         }
    1449              : 
    1450          117 :         if(st_ == state::set_body)
    1451              :         {
    1452            0 :             st_ = state::body;
    1453            0 :             goto do_body;
    1454              :         }
    1455              : 
    1456          117 :         st_ = state::complete;
    1457          117 :         break;
    1458              :     }
    1459              : 
    1460          460 :     case state::complete:
    1461          460 :         break;
    1462              :     }
    1463              : }
    1464              : 
    1465              : auto
    1466        41250 : parser::
    1467              : pull_body() ->
    1468              :     const_buffers_type
    1469              : {
    1470        41250 :     switch(st_)
    1471              :     {
    1472            0 :     case state::header_done:
    1473            0 :         return {};
    1474        41250 :     case state::body:
    1475              :     case state::complete_in_place:
    1476        41250 :         cbp_ = buffers::prefix(
    1477        41250 :             (is_plain() ? cb0_ : cb1_).data(),
    1478              :             body_avail_);
    1479        41250 :         return const_buffers_type(cbp_);
    1480            0 :     default:
    1481            0 :         detail::throw_logic_error();
    1482              :     }
    1483              : }
    1484              : 
    1485              : void
    1486        39606 : parser::
    1487              : consume_body(std::size_t n)
    1488              : {
    1489        39606 :     switch(st_)
    1490              :     {
    1491            0 :     case state::header_done:
    1492            0 :         return;
    1493        39606 :     case state::body:
    1494              :     case state::complete_in_place:
    1495        39606 :         n = clamp(n, body_avail_);
    1496        39606 :         (is_plain() ? cb0_ : cb1_).consume(n);
    1497        39606 :         body_avail_ -= n;
    1498        39606 :         return;
    1499            0 :     default:
    1500            0 :         detail::throw_logic_error();
    1501              :     }
    1502              : }
    1503              : 
    1504              : core::string_view
    1505          699 : parser::
    1506              : body() const noexcept
    1507              : {
    1508          699 :     if(st_ != state::complete_in_place)
    1509              :     {
    1510              :         // Precondition violation
    1511            0 :         detail::throw_logic_error();
    1512              :     }
    1513              : 
    1514          699 :     if(body_avail_ != body_total_)
    1515              :     {
    1516              :         // Precondition violation
    1517            0 :         detail::throw_logic_error();
    1518              :     }
    1519              : 
    1520          699 :     auto cbp = (is_plain() ? cb0_ : cb1_).data();
    1521          699 :     BOOST_ASSERT(cbp[1].size() == 0);
    1522          699 :     BOOST_ASSERT(cbp[0].size() == body_avail_);
    1523          699 :     return core::string_view(
    1524          699 :         static_cast<char const*>(cbp[0].data()),
    1525         1398 :         body_avail_);
    1526              : }
    1527              : 
    1528              : core::string_view
    1529            0 : parser::
    1530              : release_buffered_data() noexcept
    1531              : {
    1532            0 :     return {};
    1533              : }
    1534              : 
    1535              : void
    1536           77 : parser::
    1537              : set_body_limit(std::uint64_t n)
    1538              : {
    1539           77 :     switch(st_)
    1540              :     {
    1541           73 :     case state::header:
    1542              :     case state::header_done:
    1543           73 :         body_limit_ = n;
    1544           73 :         break;
    1545            2 :     case state::complete_in_place:
    1546              :         // only allowed for empty bodies
    1547            2 :         if(body_total_ == 0)
    1548            1 :             break;
    1549              :         BOOST_FALLTHROUGH;
    1550              :     default:
    1551              :         // set body_limit before parsing the body
    1552            3 :         detail::throw_logic_error();
    1553              :     }
    1554           74 : }
    1555              : 
    1556              : //------------------------------------------------
    1557              : //
    1558              : // Implementation
    1559              : //
    1560              : //------------------------------------------------
    1561              : 
    1562              : void
    1563          755 : parser::
    1564              : on_set_body() noexcept
    1565              : {
    1566          755 :     BOOST_ASSERT(
    1567              :         st_ == state::header_done ||
    1568              :         st_ == state::body ||
    1569              :         st_ == state::complete_in_place);
    1570              : 
    1571          755 :     nprepare_ = 0; // invalidate
    1572              : 
    1573          755 :     if(st_ == state::body)
    1574            0 :         st_ = state::set_body;
    1575          755 : }
    1576              : 
    1577              : std::size_t
    1578        58258 : parser::
    1579              : apply_filter(
    1580              :     system::error_code& ec,
    1581              :     std::size_t payload_avail,
    1582              :     bool more)
    1583              : {
    1584        58258 :     std::size_t p0 = payload_avail;
    1585              :     for(;;)
    1586              :     {
    1587       116601 :         if(payload_avail == 0 && more)
    1588        56638 :             break;
    1589              : 
    1590            0 :         auto f_rs = [&](){
    1591        60084 :             BOOST_ASSERT(filter_ != nullptr);
    1592        60084 :             if(how_ == how::elastic)
    1593              :             {
    1594        19887 :                 std::size_t n = clamp(body_limit_remain());
    1595        19887 :                 n = clamp(n, svc_.cfg.min_buffer);
    1596        19887 :                 n = clamp(n, eb_->max_size() - eb_->size());
    1597              : 
    1598              :                 // fill capacity first to avoid
    1599              :                 // an allocation
    1600              :                 std::size_t avail = 
    1601        19887 :                     eb_->capacity() - eb_->size();
    1602        19887 :                 if(avail != 0)
    1603        19886 :                     n = clamp(n, avail);
    1604              : 
    1605        19887 :                 return filter_->process(
    1606        19887 :                     eb_->prepare(n),
    1607        19887 :                     buffers::prefix(
    1608        19887 :                         cb0_.data(),
    1609              :                         payload_avail),
    1610        39774 :                     more);
    1611              :             }
    1612              :             else // in-place and sink 
    1613              :             {
    1614        40197 :                 std::size_t n = clamp(body_limit_remain());
    1615        40197 :                 n = clamp(n, cb1_.capacity());
    1616              : 
    1617        40197 :                 return filter_->process(
    1618        40197 :                     cb1_.prepare(n),
    1619        40197 :                     buffers::prefix(
    1620        40197 :                         cb0_.data(),
    1621              :                         payload_avail),
    1622        80394 :                     more);
    1623              :             }
    1624        60084 :         }();
    1625              : 
    1626        60084 :         cb0_.consume(f_rs.in_bytes);
    1627        60084 :         payload_avail -= f_rs.in_bytes;
    1628        60084 :         body_total_   += f_rs.out_bytes;
    1629              : 
    1630       120048 :         bool needs_more_space = !f_rs.finished &&
    1631        59964 :             payload_avail != 0;
    1632              : 
    1633        60084 :         switch(how_)
    1634              :         {
    1635        20238 :         case how::in_place:
    1636              :         {
    1637        20238 :             cb1_.commit(f_rs.out_bytes);
    1638        20238 :             body_avail_ += f_rs.out_bytes;
    1639        20238 :             if(cb1_.capacity() == 0 &&
    1640              :                 needs_more_space)
    1641              :             {
    1642         3240 :                 ec = BOOST_HTTP_PROTO_ERR(
    1643              :                     error::in_place_overflow);
    1644         1620 :                 goto done;
    1645              :             }
    1646        18618 :             break;
    1647              :         }
    1648        19959 :         case how::sink:
    1649              :         {
    1650        19959 :             cb1_.commit(f_rs.out_bytes);
    1651        19959 :             auto sink_rs = sink_->write(
    1652        19959 :                 cb1_.data(), !f_rs.finished);
    1653        19959 :             cb1_.consume(sink_rs.bytes);
    1654        19959 :             if(sink_rs.ec.failed())
    1655              :             {
    1656            0 :                 ec  = sink_rs.ec;
    1657            0 :                 st_ = state::reset;
    1658            0 :                 goto done;
    1659              :             }
    1660        19959 :             break;
    1661              :         }
    1662        19887 :         case how::elastic:
    1663              :         {
    1664        19887 :             eb_->commit(f_rs.out_bytes);
    1665        19887 :             if(eb_->max_size() - eb_->size() == 0 &&
    1666              :                 needs_more_space)
    1667              :             {
    1668            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1669              :                     error::buffer_overflow);
    1670            0 :                 st_ = state::reset;
    1671            0 :                 goto done;
    1672              :             }
    1673        19887 :             break;
    1674              :         }
    1675              :         }
    1676              : 
    1677        58464 :         if(f_rs.ec.failed())
    1678              :         {
    1679            1 :             ec = f_rs.ec;
    1680            1 :             st_ = state::reset;
    1681            1 :             break;
    1682              :         }
    1683              : 
    1684        58463 :         if(body_limit_remain() == 0 &&
    1685              :             needs_more_space)
    1686              :         {
    1687            0 :             ec = BOOST_HTTP_PROTO_ERR(
    1688              :                 error::body_too_large);
    1689            0 :             st_ = state::reset;
    1690            0 :             break;
    1691              :         }
    1692              : 
    1693        58463 :         if(f_rs.finished)
    1694              :         {
    1695          120 :             if(!more)
    1696              :             {
    1697           72 :                 st_ = (how_ == how::in_place)
    1698           72 :                     ? state::complete_in_place
    1699              :                     : state::complete;
    1700              :             }
    1701          120 :             break;
    1702              :         }
    1703        58343 :     }
    1704              : 
    1705        58258 : done:
    1706        58258 :     return p0 - payload_avail;
    1707              : }
    1708              : 
    1709              : detail::header const*
    1710          315 : parser::
    1711              : safe_get_header() const
    1712              : {
    1713              :     // headers must be received
    1714          315 :     if(! got_header_)
    1715            0 :         detail::throw_logic_error();
    1716              : 
    1717          315 :     return &h_;
    1718              : }
    1719              : 
    1720              : bool
    1721       182247 : parser::
    1722              : is_plain() const noexcept
    1723              : {
    1724       354589 :     return ! filter_ &&
    1725       354589 :         h_.md.payload != payload::chunked;
    1726              : }
    1727              : 
    1728              : std::uint64_t
    1729       165733 : parser::
    1730              : body_limit_remain() const noexcept
    1731              : {
    1732       165733 :     return body_limit_ - body_total_;
    1733              : }
    1734              : 
    1735              : } // http_proto
    1736              : } // boost
        

Generated by: LCOV version 2.1