LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 89.1 % 716 638
Test Date: 2025-01-06 18:34:48 Functions: 85.9 % 64 55

            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/algorithm.hpp>
      20              : #include <boost/buffers/buffer_copy.hpp>
      21              : #include <boost/buffers/buffer_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       119613 :     on_process(
     132              :         buffers::mutable_buffer out,
     133              :         buffers::const_buffer in,
     134              :         bool more) override
     135              :     {
     136       119613 :         auto flush =
     137       119613 :             more ? zlib::flush::none : zlib::flush::finish;
     138       119613 :         filter::results results;
     139              : 
     140              :         for(;;)
     141              :         {
     142       119613 :             auto params = zlib::params{in.data(), in.size(),
     143       119613 :                 out.data(), out.size() };
     144       119613 :             auto ec = inflator_.write(params, flush);
     145              : 
     146       119613 :             results.in_bytes  += in.size() - params.avail_in;
     147       119613 :             results.out_bytes += out.size() - params.avail_out;
     148              : 
     149       178637 :             if( ec.failed() &&
     150       178637 :                 ec != zlib::error::buf_err )
     151              :             {
     152            1 :                 results.ec = ec;
     153       119613 :                 return results;
     154              :             }
     155              : 
     156       119612 :             if( ec == zlib::error::stream_end )
     157              :             {
     158          120 :                 results.finished = true;
     159          120 :                 return results;
     160              :             }
     161              : 
     162       119492 :             in  = buffers::suffix(in, params.avail_in);
     163       119492 :             out = buffers::suffix(out, params.avail_out);
     164              : 
     165       119492 :             if( in.size() == 0 || out.size() == 0 )
     166       119492 :                 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       125355 :     chained_sequence(buffers::const_buffer_pair const& cbp)
     180       125355 :         : pos_(static_cast<char const*>(cbp[0].data()))
     181       125355 :         , end_(pos_ + cbp[0].size())
     182       125355 :         , begin_b_(static_cast<char const*>(cbp[1].data()))
     183       125355 :         , end_b_(begin_b_ + cbp[1].size())
     184              :     {
     185       125355 :     }
     186              : 
     187              :     char const*
     188       650739 :     next() noexcept
     189              :     {
     190       650739 :         ++pos_;
     191              :         // most frequently taken branch
     192       650739 :         if(pos_ < end_)
     193       629440 :             return pos_;
     194              : 
     195              :         // swap with the second range
     196        21299 :         if(begin_b_ != end_b_)
     197              :         {
     198           58 :             pos_ = begin_b_;
     199           58 :             end_ = end_b_;
     200           58 :             begin_b_ = end_b_;
     201           58 :             return pos_;
     202              :         }
     203              : 
     204              :         // undo the increament
     205        21241 :         pos_ = end_;
     206        21241 :         return nullptr;
     207              :     }
     208              : 
     209              :     bool
     210       431711 :     empty() const noexcept
     211              :     {
     212       431711 :         return pos_ == end_;
     213              :     }
     214              : 
     215              :     char
     216       636159 :     value() const noexcept
     217              :     {
     218       636159 :         return *pos_;
     219              :     }
     220              : 
     221              :     std::size_t
     222       446563 :     size() const noexcept
     223              :     {
     224       446563 :         return (end_ - pos_) + (end_b_ - begin_b_);
     225              :     }
     226              : };
     227              : 
     228              : std::uint64_t
     229       121166 : parse_hex(
     230              :     chained_sequence& cs,
     231              :     system::error_code& ec) noexcept
     232              : {
     233       121166 :     std::uint64_t v   = 0;
     234       121166 :     std::size_t init_size = cs.size();
     235       318912 :     while(!cs.empty())
     236              :     {
     237       299875 :         auto n = grammar::hexdig_value(cs.value());
     238       299875 :         if(n < 0)
     239              :         {
     240       102128 :             if(init_size == cs.size())
     241              :             {
     242            2 :                 ec = BOOST_HTTP_PROTO_ERR(
     243              :                     error::bad_payload);
     244            1 :                 return 0;
     245              :             }
     246       102127 :             return v;
     247              :         }
     248              : 
     249              :         // at least 4 significant bits are free
     250       197747 :         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       197746 :         v = (v << 4) | static_cast<std::uint64_t>(n);
     258       197746 :         cs.next();
     259              :     }
     260        38074 :     ec = BOOST_HTTP_PROTO_ERR(
     261              :         error::need_data);
     262        19037 :     return 0;
     263              : }
     264              : 
     265              : void
     266       102421 : find_eol(
     267              :     chained_sequence& cs,
     268              :     system::error_code& ec) noexcept
     269              : {
     270       108355 :     while(!cs.empty())
     271              :     {
     272       108311 :         if(cs.value() == '\r')
     273              :         {
     274       102377 :             if(!cs.next())
     275            5 :                 break;
     276       102372 :             if(cs.value() != '\n')
     277              :             {
     278            4 :                 ec = BOOST_HTTP_PROTO_ERR(
     279              :                     error::bad_payload);
     280            2 :                 return;
     281              :             }
     282       102370 :             cs.next();
     283       102370 :             return;
     284              :         }
     285         5934 :         cs.next();
     286              :     }
     287           98 :     ec = BOOST_HTTP_PROTO_ERR(
     288              :         error::need_data);
     289              : }
     290              : 
     291              : void
     292       117014 : parse_eol(
     293              :     chained_sequence& cs,
     294              :     system::error_code& ec) noexcept
     295              : {
     296       117014 :     if(cs.size() >= 2)
     297              :     {
     298              :         // we are sure size is at least 2
     299       117012 :         if(cs.value() == '\r' && *cs.next() == '\n')
     300              :         {
     301       117009 :             cs.next();
     302       117009 :             return;
     303              :         }
     304            6 :         ec = BOOST_HTTP_PROTO_ERR(
     305              :             error::bad_payload);
     306            3 :         return;
     307              :     }
     308            4 :     ec = BOOST_HTTP_PROTO_ERR(
     309              :         error::need_data);
     310              : }
     311              : 
     312              : void
     313         4184 : skip_trailer_headers(
     314              :     chained_sequence& cs,
     315              :     system::error_code& ec) noexcept
     316              : {
     317         4444 :     while(!cs.empty())
     318              :     {
     319         4442 :         if(cs.value() == '\r')
     320              :         {
     321         4148 :             if(!cs.next())
     322            1 :                 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          294 :         find_eol(cs, ec);
     334          294 :         if(ec)
     335           34 :             return;
     336              :     }
     337            6 :     ec = BOOST_HTTP_PROTO_ERR(
     338              :         error::need_data);
     339              : }
     340              : 
     341              : template<class UInt>
     342              : std::size_t
     343       378049 : clamp(
     344              :     UInt x,
     345              :     std::size_t limit = (std::numeric_limits<
     346              :         std::size_t>::max)()) noexcept
     347              : {
     348       378049 :     if(x >= limit)
     349       105787 :         return limit;
     350       272262 :     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        56352 :     max_overread() const noexcept
     369              :     {
     370              :         return
     371        56352 :             cfg.headers.max_size +
     372        56352 :             cfg.min_buffer;
     373              :     }
     374              : };
     375              : 
     376           38 : parser_service::
     377              : parser_service(
     378              :     context& ctx,
     379           38 :     parser::config_base const& cfg_)
     380           38 :         : 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           38 :     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           38 :     space_needed +=
     410           38 :         cfg.headers.valid_space_needed();
     411              : 
     412              :     // cb0_, cb1_
     413              :     // VFALCO OVERFLOW CHECKING ON THIS
     414           38 :     space_needed +=
     415           38 :         cfg.min_buffer +
     416              :         cfg.min_buffer;
     417              : 
     418              :     // T
     419           38 :     space_needed += cfg.max_type_erase;
     420              : 
     421              :     // max_codec
     422              :     {
     423           38 :         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           38 :     space_needed += max_codec;
     432              : 
     433              :     // round up to alignof(detail::header::entry)
     434           38 :     auto const al = alignof(
     435              :         detail::header::entry);
     436           38 :     space_needed = al * ((
     437           38 :         space_needed + al - 1) / al);
     438           38 : }
     439              : 
     440              : void
     441           38 : install_parser_service(
     442              :     context& ctx,
     443              :     parser::config_base const& cfg)
     444              : {
     445              :     ctx.make_service<
     446           38 :         parser_service>(cfg);
     447           38 : }
     448              : 
     449              : //------------------------------------------------
     450              : //
     451              : // Special Members
     452              : //
     453              : //------------------------------------------------
     454              : 
     455         1050 : parser::
     456              : parser(
     457              :     context& ctx,
     458         1050 :     detail::kind k)
     459         1050 :     : ctx_(ctx)
     460         1050 :     , svc_(ctx.get_service<
     461         1050 :         parser_service>())
     462         1050 :     , h_(detail::empty{k})
     463         1050 :     , eb_(nullptr)
     464         2100 :     , st_(state::reset)
     465              : {
     466         1050 :     auto const n =
     467         1050 :         svc_.space_needed;
     468         1050 :     ws_.allocate(n);
     469         1050 :     h_.cap = n;
     470         1050 : }
     471              : 
     472              : //------------------------------------------------
     473              : 
     474         1050 : parser::
     475              : ~parser()
     476              : {
     477         1050 : }
     478              : 
     479              : //------------------------------------------------
     480              : //
     481              : // Modifiers
     482              : //
     483              : //------------------------------------------------
     484              : 
     485              : // prepare for a new stream
     486              : void
     487         1744 : parser::
     488              : reset() noexcept
     489              : {
     490         1744 :     ws_.clear();
     491         1744 :     eb_ = nullptr;
     492         1744 :     st_ = state::start;
     493         1744 :     got_eof_ = false;
     494         1744 : }
     495              : 
     496              : void
     497        10303 : parser::
     498              : start_impl(
     499              :     bool head_response)
     500              : {
     501        10303 :     std::size_t leftover = 0;
     502        10303 :     switch(st_)
     503              :     {
     504            1 :     default:
     505              :     case state::reset:
     506              :         // reset must be called first
     507            1 :         detail::throw_logic_error();
     508              : 
     509         1728 :     case state::start:
     510              :         // reset required on eof
     511         1728 :         if(got_eof_)
     512            0 :             detail::throw_logic_error();
     513         1728 :         break;
     514              : 
     515            3 :     case state::header:
     516            3 :         if(fb_.size() == 0)
     517              :         {
     518              :             // start() called twice
     519            2 :             detail::throw_logic_error();
     520              :         }
     521              :         BOOST_FALLTHROUGH;
     522              : 
     523              :     case state::body:
     524              :     case state::set_body:
     525              :         // current message is incomplete
     526            2 :         detail::throw_logic_error();
     527              : 
     528         8189 :     case state::complete_in_place:
     529              :         // remove available body.
     530         8189 :         if(is_plain())
     531         4174 :             cb0_.consume(body_avail_);
     532              :         BOOST_FALLTHROUGH;
     533              : 
     534              :     case state::complete:
     535              :     {
     536              :         // move leftovers to front
     537              : 
     538         8570 :         ws_.clear();
     539         8570 :         leftover = cb0_.size();
     540              : 
     541         8570 :         auto* dest = reinterpret_cast<char*>(ws_.data());
     542         8570 :         auto cbp   = cb0_.data();
     543         8570 :         auto* a    = static_cast<char const*>(cbp[0].data());
     544         8570 :         auto* b    = static_cast<char const*>(cbp[1].data());
     545         8570 :         auto an    = cbp[0].size();
     546         8570 :         auto bn    = cbp[1].size();
     547              : 
     548         8570 :         if(bn == 0)
     549              :         {
     550         8132 :             std::memmove(dest, a, an);
     551              :         }
     552              :         else
     553              :         {
     554              :             // if `a` can fit between `dest` and `b`, shift `b` to the left
     555              :             // and copy `a` to its position. if `a` fits perfectly, the
     556              :             // shift will be of size 0.
     557              :             // if `a` requires more space, shift `b` to the right and
     558              :             // copy `a` to its position. this process may require multiple
     559              :             // iterations and should be done chunk by chunk to prevent `b`
     560              :             // from overlapping with `a`.
     561              :             do
     562              :             {
     563              :                 // clamp right shifts to prevent overlap with `a`
     564          438 :                 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
     565          438 :                 b = static_cast<char const*>(std::memmove(bp, b, bn));
     566              : 
     567              :                 // a chunk or all of `a` based on available space
     568          438 :                 auto chunk_a = static_cast<std::size_t>(b - dest);
     569          438 :                 std::memcpy(dest, a, chunk_a); // never overlap
     570          438 :                 an   -= chunk_a;
     571          438 :                 dest += chunk_a;
     572          438 :                 a    += chunk_a;
     573          438 :             } while(an);
     574              :         }
     575              : 
     576         8570 :         break;
     577              :     }
     578              :     }
     579              : 
     580        10298 :     ws_.clear();
     581              : 
     582        20596 :     fb_ = {
     583        10298 :         ws_.data(),
     584        10298 :         svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
     585              :         leftover };
     586              : 
     587        10298 :     BOOST_ASSERT(
     588              :         fb_.capacity() == svc_.max_overread() - leftover);
     589              : 
     590        10298 :     h_ = detail::header(detail::empty{h_.kind});
     591        10298 :     h_.buf = reinterpret_cast<char*>(ws_.data());
     592        10298 :     h_.cbuf = h_.buf;
     593        10298 :     h_.cap = ws_.size();
     594              : 
     595        10298 :     BOOST_ASSERT(! head_response ||
     596              :         h_.kind == detail::kind::response);
     597        10298 :     head_response_ = head_response;
     598              : 
     599              :     // begin with in_place mode
     600        10298 :     how_ = how::in_place;
     601        10298 :     st_ = state::header;
     602        10298 :     nprepare_ = 0;
     603        10298 :     chunk_remain_ = 0;
     604        10298 :     needs_chunk_close_ = false;
     605        10298 :     trailer_headers_ = false;
     606        10298 :     chunked_body_ended = false;
     607        10298 :     filter_ = nullptr;
     608        10298 :     sink_ = nullptr;
     609        10298 :     body_avail_ = 0;
     610        10298 :     body_total_ = 0;
     611        10298 : }
     612              : 
     613              : auto
     614        51209 : parser::
     615              : prepare() ->
     616              :     mutable_buffers_type
     617              : {
     618        51209 :     nprepare_ = 0;
     619              : 
     620        51209 :     switch(st_)
     621              :     {
     622            1 :     default:
     623              :     case state::reset:
     624              :         // reset must be called first
     625            1 :         detail::throw_logic_error();
     626              : 
     627            1 :     case state::start:
     628              :         // start must be called first
     629            1 :         detail::throw_logic_error();
     630              : 
     631        10378 :     case state::header:
     632              :     {
     633        10378 :         BOOST_ASSERT(
     634              :             h_.size < svc_.cfg.headers.max_size);
     635        10378 :         std::size_t n = fb_.capacity() - fb_.size();
     636        10378 :         BOOST_ASSERT(n <= svc_.max_overread());
     637        10378 :         n = clamp(n, svc_.cfg.max_prepare);
     638        10378 :         mbp_[0] = fb_.prepare(n);
     639        10378 :         nprepare_ = n;
     640        10378 :         return mutable_buffers_type(&mbp_[0], 1);
     641              :     }
     642              : 
     643        40828 :     case state::body:
     644              :     {
     645        40828 :         if(got_eof_)
     646              :         {
     647              :             // forgot to call parse()
     648            0 :             detail::throw_logic_error();
     649              :         }
     650              : 
     651        40828 :         if(! is_plain())
     652              :         {
     653              :             // buffered payload
     654        21778 :             std::size_t n = cb0_.capacity();
     655        21778 :             n = clamp(n, svc_.cfg.max_prepare);
     656        21778 :             nprepare_ = n;
     657        21778 :             mbp_ = cb0_.prepare(n);
     658        21778 :             return mutable_buffers_type(mbp_);
     659              :         }
     660              :         else
     661              :         {
     662        19050 :             switch(how_)
     663              :             {
     664        19029 :             default:
     665              :             case how::in_place:
     666              :             case how::sink:
     667              :             {
     668        19029 :                 std::size_t n = cb0_.capacity();
     669        19029 :                 n = clamp(n, svc_.cfg.max_prepare);
     670              : 
     671        19029 :                 if(h_.md.payload == payload::size)
     672              :                 {
     673        19004 :                     if(n > payload_remain_)
     674              :                     {
     675        17797 :                         std::size_t overread =
     676        17797 :                             n - static_cast<std::size_t>(payload_remain_);
     677        17797 :                         if(overread > svc_.max_overread())
     678         7877 :                             n = static_cast<std::size_t>(payload_remain_) +
     679         7877 :                                 svc_.max_overread();
     680              :                     }
     681              :                 }
     682              :                 else
     683              :                 {
     684           25 :                     BOOST_ASSERT(
     685              :                         h_.md.payload == payload::to_eof);
     686              : 
     687           25 :                     n = clamp(
     688           25 :                         svc_.cfg.body_limit - body_total_ + 1, n);
     689              :                 }
     690              : 
     691        19029 :                 nprepare_ = n;
     692        19029 :                 mbp_ = cb0_.prepare(n);
     693        19029 :                 return mutable_buffers_type(mbp_);
     694              :             }
     695           21 :             case how::elastic:
     696              :             {
     697           21 :                 BOOST_ASSERT(cb0_.size() == 0);
     698           21 :                 BOOST_ASSERT(body_avail_ == 0);
     699              : 
     700           21 :                 std::size_t n = svc_.cfg.min_buffer;
     701              : 
     702           21 :                 if(h_.md.payload == payload::size)
     703              :                 {
     704              :                     // Overreads are not allowed, or
     705              :                     // else the caller will see extra
     706              :                     // unrelated data.
     707            6 :                     n = clamp(payload_remain_, n);
     708              :                 }
     709              :                 else
     710              :                 {
     711           15 :                     BOOST_ASSERT(
     712              :                         h_.md.payload == payload::to_eof);
     713           15 :                     n = clamp(
     714           15 :                         svc_.cfg.body_limit - body_total_, n);
     715           15 :                     n = clamp(
     716           15 :                         n, eb_->max_size() - eb_->size());
     717              :                     // fill capacity first to avoid an allocation
     718              :                     std::size_t avail =
     719           15 :                         eb_->capacity() - eb_->size();
     720           15 :                     if(avail != 0)
     721           15 :                         n = clamp(n, avail);
     722              : 
     723           15 :                     if(n == 0)
     724              :                     {
     725              :                         // dynamic buffer is full
     726              :                         // attempt a 1 byte read so
     727              :                         // we can detect overflow
     728            1 :                         nprepare_ = 1;
     729            1 :                         mbp_ = cb0_.prepare(1);
     730            1 :                         return mutable_buffers_type(mbp_);
     731              :                     }
     732              :                 }
     733              : 
     734           20 :                 n = clamp(n, svc_.cfg.max_prepare);
     735           20 :                 BOOST_ASSERT(n != 0);
     736           20 :                 nprepare_ = n;
     737           20 :                 return eb_->prepare(n);
     738              :             }
     739              :             }
     740              :         }
     741              :     }
     742              : 
     743            0 :     case state::set_body:
     744              :         // forgot to call parse()
     745            0 :         detail::throw_logic_error();
     746              : 
     747            1 :     case state::complete_in_place:
     748              :     case state::complete:
     749              :         // already complete
     750            1 :         detail::throw_logic_error();
     751              :     }
     752              : }
     753              : 
     754              : void
     755        51206 : parser::
     756              : commit(
     757              :     std::size_t n)
     758              : {
     759        51206 :     if(n > nprepare_)
     760              :     {
     761              :         // n can't be greater than size of
     762              :         // the buffers returned by prepare()
     763            4 :         detail::throw_invalid_argument();
     764              :     }
     765              : 
     766        51202 :     if(got_eof_)
     767              :     {
     768              :         // can't commit after EOF
     769            1 :         detail::throw_logic_error();
     770              :     }
     771              : 
     772        51201 :     switch(st_)
     773              :     {
     774            1 :     default:
     775              :     case state::reset:
     776              :     {
     777              :         // reset must be called first
     778            1 :         detail::throw_logic_error();
     779              :     }
     780              : 
     781            1 :     case state::start:
     782              :     {
     783              :         // forgot to call start()
     784            1 :         detail::throw_logic_error();
     785              :     }
     786              : 
     787        10376 :     case state::header:
     788              :     {
     789        10376 :         nprepare_ = 0; // invalidate
     790        10376 :         fb_.commit(n);
     791        10376 :         break;
     792              :     }
     793              : 
     794        40823 :     case state::body:
     795              :     {
     796        40823 :         nprepare_ = 0; // invalidate
     797        40823 :         if(is_plain() && how_ == how::elastic)
     798              :         {
     799           20 :             if(eb_->max_size() == eb_->size())
     800              :             {
     801              :                 // borrowed 1 byte from cb0_
     802            1 :                 BOOST_ASSERT(n <= 1);
     803            1 :                 cb0_.commit(n);
     804            1 :                 break;
     805              :             }
     806           19 :             eb_->commit(n);
     807           19 :             payload_remain_ -= n;
     808           19 :             body_total_     += n;
     809              :         }
     810              :         else
     811              :         {
     812        40803 :             cb0_.commit(n);
     813              :         }
     814        40822 :         break;
     815              :     }
     816              : 
     817            0 :     case state::set_body:
     818              :     {
     819              :         // forgot to call parse()
     820            0 :         detail::throw_logic_error();
     821              :     }
     822              : 
     823            0 :     case state::complete_in_place:
     824              :     case state::complete:
     825              :     {
     826              :         // already complete
     827            0 :         detail::throw_logic_error();
     828              :     }
     829              :     }
     830        51199 : }
     831              : 
     832              : void
     833          402 : parser::
     834              : commit_eof()
     835              : {
     836          402 :     nprepare_ = 0; // invalidate
     837              : 
     838          402 :     switch(st_)
     839              :     {
     840            1 :     default:
     841              :     case state::reset:
     842              :         // reset must be called first
     843            1 :         detail::throw_logic_error();
     844              : 
     845            1 :     case state::start:
     846              :         // forgot to call prepare()
     847            1 :         detail::throw_logic_error();
     848              : 
     849           31 :     case state::header:
     850           31 :         got_eof_ = true;
     851           31 :         break;
     852              : 
     853          368 :     case state::body:
     854          368 :         got_eof_ = true;
     855          368 :         break;
     856              : 
     857            0 :     case state::set_body:
     858              :         // forgot to call parse()
     859            0 :         detail::throw_logic_error();
     860              : 
     861            1 :     case state::complete_in_place:
     862              :     case state::complete:
     863              :         // can't commit eof when complete
     864            1 :         detail::throw_logic_error();
     865              :     }
     866          399 : }
     867              : 
     868              : //-----------------------------------------------
     869              : 
     870              : // process input data then
     871              : // eof if input data runs out.
     872              : void
     873        57081 : parser::
     874              : parse(
     875              :     system::error_code& ec)
     876              : {
     877        57081 :     ec = {};
     878        57081 :     switch(st_)
     879              :     {
     880            1 :     default:
     881              :     case state::reset:
     882              :         // reset must be called first
     883            1 :         detail::throw_logic_error();
     884              : 
     885            1 :     case state::start:
     886              :         // start must be called first
     887            1 :         detail::throw_logic_error();
     888              : 
     889        14393 :     case state::header:
     890              :     {
     891        14393 :         BOOST_ASSERT(h_.buf == static_cast<
     892              :             void const*>(ws_.data()));
     893        14393 :         BOOST_ASSERT(h_.cbuf == static_cast<
     894              :             void const*>(ws_.data()));
     895              : 
     896        14393 :         h_.parse(fb_.size(), svc_.cfg.headers, ec);
     897              : 
     898        14393 :         if(ec == condition::need_more_input)
     899              :         {
     900         4132 :             if(! got_eof_)
     901              :             {
     902              :                 // headers incomplete
     903         4105 :                 return;
     904              :             }
     905              : 
     906           27 :             if(fb_.size() == 0)
     907              :             {
     908              :                 // stream closed cleanly
     909           12 :                 st_ = state::complete_in_place;
     910           24 :                 ec = BOOST_HTTP_PROTO_ERR(
     911              :                     error::end_of_stream);
     912           12 :                 return;
     913              :             }
     914              : 
     915              :             // stream closed with a
     916              :             // partial message received
     917           15 :             st_ = state::reset;
     918           30 :             ec = BOOST_HTTP_PROTO_ERR(
     919              :                 error::incomplete);
     920           15 :             return;
     921              :         }
     922        10261 :         if(ec.failed())
     923              :         {
     924              :             // other error,
     925              :             //
     926              :             // VFALCO map this to a bad
     927              :             // request or bad response error?
     928              :             //
     929          259 :             st_ = state::reset; // unrecoverable
     930          259 :             return;
     931              :         }
     932              : 
     933              :         // headers are complete
     934        10002 :         on_headers(ec);
     935        10002 :         if(ec.failed())
     936          180 :             return;
     937         9822 :         if(st_ == state::complete_in_place)
     938          921 :             break;
     939              : 
     940              :         BOOST_FALLTHROUGH;
     941              :     }
     942              : 
     943              :     case state::body:
     944              :     {
     945         8901 :     do_body:
     946        50367 :         BOOST_ASSERT(st_ == state::body);
     947        50367 :         BOOST_ASSERT(h_.md.payload != payload::none);
     948        50367 :         BOOST_ASSERT(h_.md.payload != payload::error);
     949              : 
     950         8796 :         auto set_state_to_complete = [&]()
     951              :         {
     952         8796 :             if(how_ == how::in_place)
     953              :             {
     954         8558 :                 st_ = state::complete_in_place;
     955         8558 :                 return;
     956              :             }
     957          238 :             st_ = state::complete;
     958        50367 :         };
     959              : 
     960        50367 :         if(h_.md.payload == payload::chunked)
     961              :         {
     962              :             for(;;)
     963              :             {
     964       130820 :                 if(chunk_remain_ == 0
     965       129629 :                     && !chunked_body_ended)
     966              :                 {
     967       125484 :                     if(cb0_.size() == 0)
     968              :                     {
     969          258 :                         ec = BOOST_HTTP_PROTO_ERR(
     970              :                             error::need_data);
     971        19229 :                         return;
     972              :                     }
     973              : 
     974       125355 :                     auto cs = chained_sequence(cb0_.data());
     975              : 
     976       125355 :                     if(needs_chunk_close_)
     977              :                     {
     978       117014 :                         parse_eol(cs, ec);
     979       117014 :                         if(ec)
     980            5 :                             return;
     981              :                     }
     982         8341 :                     else if(trailer_headers_)
     983              :                     {
     984         4184 :                         skip_trailer_headers(cs, ec);
     985         4184 :                         if(ec)
     986           39 :                             return;
     987         4145 :                         cb0_.consume(cb0_.size() - cs.size());
     988         4145 :                         chunked_body_ended = true;
     989         8292 :                         continue;
     990              :                     }
     991              :                     
     992       121166 :                     auto chunk_size = parse_hex(cs, ec);
     993       121166 :                     if(ec)
     994        19039 :                         return;
     995              : 
     996              :                     // skip chunk extensions
     997       102127 :                     find_eol(cs, ec);
     998       102127 :                     if(ec)
     999           17 :                         return;
    1000              : 
    1001       102110 :                     cb0_.consume(cb0_.size() - cs.size());
    1002       102110 :                     chunk_remain_ = chunk_size;
    1003              : 
    1004       102110 :                     needs_chunk_close_ = true;
    1005       102110 :                     if(chunk_remain_ == 0)
    1006              :                     {
    1007         4147 :                         needs_chunk_close_ = false;
    1008         4147 :                         trailer_headers_ = true;
    1009         4147 :                         continue;
    1010              :                     }
    1011              :                 }
    1012              : 
    1013       103299 :                 if(cb0_.size() == 0 && !chunked_body_ended)
    1014              :                 {
    1015          325 :                     if(got_eof_)
    1016              :                     {
    1017            0 :                         ec = BOOST_HTTP_PROTO_ERR(
    1018              :                             error::incomplete);
    1019            0 :                         st_ = state::reset;
    1020            0 :                         return;
    1021              :                     }
    1022              : 
    1023          650 :                     ec = BOOST_HTTP_PROTO_ERR(
    1024              :                         error::need_data);
    1025          325 :                     return;
    1026              :                 }
    1027              : 
    1028       102974 :                 if(filter_)
    1029              :                 {
    1030        56592 :                     chunk_remain_ -= apply_filter(
    1031              :                         ec,
    1032              :                         clamp(chunk_remain_, cb0_.size()),
    1033        56592 :                         !chunked_body_ended);
    1034              : 
    1035        56592 :                     if(ec || chunked_body_ended)
    1036          572 :                         return;
    1037              :                 }
    1038              :                 else
    1039              :                 {
    1040              :                     const std::size_t chunk_avail =
    1041        46382 :                         clamp(chunk_remain_, cb0_.size());
    1042              :                     const auto chunk =
    1043        46382 :                         buffers::prefix(cb0_.data(), chunk_avail);
    1044              : 
    1045        46382 :                     if(svc_.cfg.body_limit - body_total_
    1046              :                         < chunk_avail)
    1047              :                     {
    1048            0 :                         ec = BOOST_HTTP_PROTO_ERR(
    1049              :                             error::body_too_large);
    1050            0 :                         st_ = state::reset;
    1051         4121 :                         return;
    1052              :                     }
    1053              : 
    1054        46382 :                     switch(how_)
    1055              :                     {
    1056        46382 :                     case how::in_place:
    1057              :                     {
    1058        46382 :                         auto copied = buffers::buffer_copy(
    1059        46382 :                             cb1_.prepare(cb1_.capacity()),
    1060              :                             chunk);
    1061        46382 :                         chunk_remain_ -= copied;
    1062        46382 :                         body_avail_   += copied;
    1063        46382 :                         body_total_   += copied;
    1064        46382 :                         cb0_.consume(copied);
    1065        46382 :                         cb1_.commit(copied);
    1066        46382 :                         if(cb1_.capacity() == 0
    1067        46382 :                             && !chunked_body_ended)
    1068              :                         {
    1069            0 :                             ec = BOOST_HTTP_PROTO_ERR(
    1070              :                                 error::in_place_overflow);
    1071            0 :                             return;
    1072              :                         }
    1073        46382 :                         break;
    1074              :                     }
    1075            0 :                     case how::sink:
    1076              :                     {
    1077            0 :                         auto sink_rs = sink_->write(
    1078            0 :                             chunk, !chunked_body_ended);
    1079            0 :                         chunk_remain_ -= sink_rs.bytes;
    1080            0 :                         body_total_   += sink_rs.bytes;
    1081            0 :                         cb0_.consume(sink_rs.bytes);
    1082            0 :                         if(sink_rs.ec.failed())
    1083              :                         {
    1084            0 :                             body_avail_ += 
    1085            0 :                                 chunk_avail - sink_rs.bytes;
    1086            0 :                             ec  = sink_rs.ec;
    1087            0 :                             st_ = state::reset;
    1088            0 :                             return;
    1089              :                         }
    1090            0 :                         break;
    1091              :                     }
    1092            0 :                     case how::elastic:
    1093              :                     {
    1094            0 :                         if(eb_->max_size() - eb_->size()
    1095            0 :                             < chunk_avail)
    1096              :                         {
    1097            0 :                             ec = BOOST_HTTP_PROTO_ERR(
    1098              :                                 error::buffer_overflow);
    1099            0 :                             st_ = state::reset;
    1100            0 :                             return;
    1101              :                         }
    1102            0 :                         buffers::buffer_copy(
    1103            0 :                             eb_->prepare(chunk_avail),
    1104              :                             chunk);
    1105            0 :                         chunk_remain_ -= chunk_avail;
    1106            0 :                         body_total_   += chunk_avail;
    1107            0 :                         cb0_.consume(chunk_avail);
    1108            0 :                         eb_->commit(chunk_avail);
    1109            0 :                         break;
    1110              :                     }
    1111              :                     }
    1112              : 
    1113        46382 :                     if(chunked_body_ended)
    1114              :                     {
    1115         4121 :                         set_state_to_complete();
    1116         4121 :                         return;
    1117              :                     }
    1118              :                 }
    1119       106573 :             }
    1120              :         }
    1121              :         else
    1122              :         {
    1123              :             // non-chunked payload
    1124              : 
    1125        78360 :             const std::size_t payload_avail = [&]()
    1126              :             {
    1127        26120 :                 auto ret = cb0_.size();
    1128        26120 :                 if(!filter_)
    1129        24327 :                     ret -= body_avail_;
    1130        26120 :                 if(h_.md.payload == payload::size)
    1131        24241 :                     return clamp(payload_remain_, ret);
    1132              :                 // payload::eof
    1133         1879 :                 return ret;
    1134        26120 :             }();
    1135              : 
    1136        78360 :             const bool is_complete = [&]()
    1137              :             {
    1138        26120 :                 if(h_.md.payload == payload::size)
    1139        24241 :                     return payload_avail == payload_remain_;
    1140              :                 // payload::eof
    1141         1879 :                 return got_eof_;
    1142        26120 :             }();
    1143              : 
    1144        26120 :             if(filter_)
    1145              :             {
    1146         3586 :                 payload_remain_ -= apply_filter(
    1147         1793 :                     ec, payload_avail, !is_complete);
    1148         1793 :                 if(ec || is_complete)
    1149         1145 :                     return;
    1150              :             }
    1151              :             else
    1152              :             {
    1153        24327 :                 switch(how_)
    1154              :                 {
    1155        23818 :                 case how::in_place:
    1156              :                 {
    1157        23818 :                     payload_remain_ -= payload_avail;
    1158        23818 :                     body_avail_     += payload_avail;
    1159        23818 :                     body_total_     += payload_avail;
    1160        23818 :                     if(cb0_.capacity() == 0 && !is_complete)
    1161              :                     {
    1162            2 :                         ec = BOOST_HTTP_PROTO_ERR(
    1163              :                             error::in_place_overflow);
    1164            1 :                         return;
    1165              :                     }
    1166        23817 :                     break;
    1167              :                 }
    1168          249 :                 case how::sink:
    1169              :                 {
    1170          249 :                     payload_remain_ -= payload_avail;
    1171          249 :                     body_total_     += payload_avail;
    1172          249 :                     auto sink_rs = sink_->write(
    1173          249 :                         buffers::prefix(
    1174            0 :                             cb0_.data(),
    1175              :                             payload_avail),
    1176          249 :                         !is_complete);
    1177          249 :                     cb0_.consume(sink_rs.bytes);
    1178          249 :                     if(sink_rs.ec.failed())
    1179              :                     {
    1180            0 :                         body_avail_ += 
    1181            0 :                             payload_avail - sink_rs.bytes;
    1182            0 :                         ec  = sink_rs.ec;
    1183            0 :                         st_ = state::reset;
    1184            0 :                         return;
    1185              :                     }
    1186          249 :                     break;
    1187              :                 }
    1188          260 :                 case how::elastic:
    1189              :                 {
    1190              :                     // payload_remain_ and body_total_
    1191              :                     // are already updated in commit()
    1192              : 
    1193          260 :                     if(cb0_.size() != 0)
    1194              :                     {
    1195              :                         // a non-empty cb0_ indicates that
    1196              :                         // the elastic buffer ran out of space
    1197            0 :                         ec  = BOOST_HTTP_PROTO_ERR(
    1198              :                             error::buffer_overflow);
    1199            0 :                         st_ = state::reset;
    1200            0 :                         return;
    1201              :                     }
    1202          260 :                     break;
    1203              :                 }
    1204              :                 }
    1205              : 
    1206        24326 :                 if(body_total_ > svc_.cfg.body_limit)
    1207              :                 {
    1208            2 :                     ec  = BOOST_HTTP_PROTO_ERR(
    1209              :                         error::body_too_large);
    1210            1 :                     st_ = state::reset;
    1211            1 :                     return;
    1212              :                 }
    1213              : 
    1214        24325 :                 if(is_complete)
    1215              :                 {
    1216         4675 :                     set_state_to_complete();
    1217         4675 :                     return;
    1218              :                 }
    1219              :             }
    1220              : 
    1221        20298 :             if(h_.md.payload == payload::size && got_eof_)
    1222              :             {
    1223            2 :                 ec = BOOST_HTTP_PROTO_ERR(
    1224              :                     error::incomplete);
    1225            1 :                 st_ = state::reset;
    1226            1 :                 return;
    1227              :             }
    1228              : 
    1229        40594 :             ec = BOOST_HTTP_PROTO_ERR(
    1230              :                 error::need_data);
    1231        20297 :             return;
    1232              :         }
    1233              : 
    1234              :         break;
    1235              :     }
    1236              : 
    1237          922 :     case state::set_body:
    1238              :     case state::complete_in_place:
    1239              :     {
    1240          922 :         auto& body_buf = is_plain() ? cb0_ : cb1_;
    1241              : 
    1242          922 :         BOOST_ASSERT(body_avail_ == body_buf.size());
    1243          922 :         BOOST_ASSERT(body_total_ == body_avail_);
    1244              : 
    1245          922 :         switch(how_)
    1246              :         {
    1247          288 :         case how::in_place:
    1248          288 :             return; // no-op
    1249          312 :         case how::sink:
    1250              :         {
    1251          312 :             auto rs = sink_->write(
    1252          312 :                 buffers::prefix(
    1253            0 :                     body_buf.data(),
    1254              :                     body_avail_),
    1255          312 :                 st_ == state::set_body);
    1256          312 :             body_buf.consume(rs.bytes);
    1257          312 :             body_avail_ -= rs.bytes;
    1258          312 :             if(rs.ec.failed())
    1259              :             {
    1260            0 :                 ec  = rs.ec;
    1261            0 :                 st_ = state::reset;
    1262            0 :                 return;
    1263              :             }
    1264          312 :             break;
    1265              :         }
    1266          322 :         case how::elastic:
    1267              :         {
    1268          322 :             if(eb_->max_size() - eb_->size()
    1269          322 :                 < body_avail_)
    1270              :             {
    1271            2 :                 ec = BOOST_HTTP_PROTO_ERR(
    1272              :                     error::buffer_overflow);
    1273            1 :                 return;
    1274              :             }
    1275          321 :             buffers::buffer_copy(
    1276          321 :                 eb_->prepare(body_avail_),
    1277          321 :                 body_buf.data());
    1278          321 :             body_buf.consume(body_avail_);
    1279          321 :             eb_->commit(body_avail_);
    1280          321 :             body_avail_ = 0;
    1281              :             // TODO: expand cb_0 when possible?
    1282          321 :             break;
    1283              :         }
    1284              :         }
    1285              : 
    1286          633 :         if(st_ == state::set_body)
    1287              :         {
    1288          278 :             st_ = state::body;
    1289          278 :             goto do_body;
    1290              :         }
    1291              : 
    1292          355 :         st_ = state::complete;
    1293          355 :         break;
    1294              :     }
    1295              : 
    1296          576 :     case state::complete:
    1297          576 :         break;
    1298              :     }
    1299              : }
    1300              : 
    1301              : //------------------------------------------------
    1302              : 
    1303              : auto
    1304        41250 : parser::
    1305              : pull_body() ->
    1306              :     const_buffers_type
    1307              : {
    1308        41250 :     switch(st_)
    1309              :     {
    1310        41250 :     case state::body:
    1311              :     case state::complete_in_place:
    1312        41250 :         cbp_ = buffers::prefix(
    1313        41250 :             (is_plain() ? cb0_ : cb1_).data(),
    1314              :             body_avail_);
    1315        41250 :         return const_buffers_type(cbp_);
    1316            0 :     default:
    1317            0 :         detail::throw_logic_error();
    1318              :     }
    1319              : }
    1320              : 
    1321              : void
    1322        39606 : parser::
    1323              : consume_body(std::size_t n)
    1324              : {
    1325        39606 :     switch(st_)
    1326              :     {
    1327        39606 :     case state::body:
    1328              :     case state::complete_in_place:
    1329        39606 :         n = clamp(n, body_avail_);
    1330        39606 :         (is_plain() ? cb0_ : cb1_).consume(n);
    1331        39606 :         body_avail_ -= n;
    1332        39606 :         return;
    1333            0 :     default:
    1334            0 :         detail::throw_logic_error();
    1335              :     }
    1336              : }
    1337              : 
    1338              : core::string_view
    1339         1968 : parser::
    1340              : body() const noexcept
    1341              : {
    1342         1968 :     if(st_ == state::complete_in_place)
    1343              :     {
    1344          697 :         auto cbp = (is_plain() ? cb0_ : cb1_).data();
    1345          697 :         BOOST_ASSERT(cbp[1].size() == 0);
    1346          697 :         BOOST_ASSERT(cbp[0].size() == body_avail_);
    1347          697 :         return core::string_view(
    1348          697 :             static_cast<char const*>(cbp[0].data()),
    1349         1394 :             body_avail_);
    1350              :     }
    1351         1271 :     return {};
    1352              : }
    1353              : 
    1354              : core::string_view
    1355            0 : parser::
    1356              : release_buffered_data() noexcept
    1357              : {
    1358            0 :     return {};
    1359              : }
    1360              : 
    1361              : //------------------------------------------------
    1362              : //
    1363              : // Implementation
    1364              : //
    1365              : //------------------------------------------------
    1366              : 
    1367              : auto
    1368          314 : parser::
    1369              : safe_get_header() const ->
    1370              :     detail::header const*
    1371              : {
    1372              :     // headers must be received
    1373          628 :     if( ! got_header() ||
    1374          314 :         fb_.size() == 0) // happens on eof
    1375            0 :         detail::throw_logic_error();
    1376              : 
    1377          314 :     return &h_;
    1378              : }
    1379              : 
    1380              : bool
    1381       181216 : parser::
    1382              : is_plain() const noexcept
    1383              : {
    1384       352259 :     return ! filter_ &&
    1385       352259 :         h_.md.payload != payload::chunked;
    1386              : }
    1387              : 
    1388              : // Called immediately after complete headers are received
    1389              : // to setup the circular buffers for subsequent operations.
    1390              : // We leave fb_ as-is to indicate whether any data was
    1391              : // received before eof.
    1392              : //
    1393              : void
    1394        10002 : parser::
    1395              : on_headers(
    1396              :     system::error_code& ec)
    1397              : {
    1398              :     // overread currently includes any and all octets that
    1399              :     // extend beyond the current end of the header
    1400              :     // this can include associated body octets for the
    1401              :     // current message or octets of the next message in the
    1402              :     // stream, e.g. pipelining is being used
    1403        10002 :     auto const overread = fb_.size() - h_.size;
    1404        10002 :     BOOST_ASSERT(
    1405              :         overread <= svc_.max_overread());
    1406              : 
    1407              :     // metadata error
    1408        10002 :     if(h_.md.payload == payload::error)
    1409              :     {
    1410              :         // VFALCO This needs looking at
    1411          360 :         ec = BOOST_HTTP_PROTO_ERR(
    1412              :             error::bad_payload);
    1413          180 :         st_ = state::reset; // unrecoverable
    1414          180 :         return;
    1415              :     }
    1416              : 
    1417              :     // reserve headers + table
    1418         9822 :     ws_.reserve_front(h_.size);
    1419         9822 :     ws_.reserve_back(h_.table_space());
    1420              : 
    1421              :     // no payload
    1422         9822 :     if( h_.md.payload == payload::none ||
    1423         8901 :         head_response_ )
    1424              :     {
    1425              :         // set cb0_ to overread
    1426          921 :         cb0_ = {
    1427          921 :             ws_.data(),
    1428          921 :             overread + fb_.capacity(),
    1429              :             overread };
    1430          921 :         st_ = state::complete_in_place;
    1431          921 :         return;
    1432              :     }
    1433              : 
    1434         8901 :     auto cap = fb_.capacity() + overread +
    1435         8901 :         svc_.cfg.min_buffer;
    1436              : 
    1437              :     // reserve body buffers first, as the decoder
    1438              :     // must be installed after them.
    1439         8901 :     auto const p = ws_.reserve_front(cap);
    1440              : 
    1441         8901 :     if( svc_.cfg.apply_deflate_decoder &&
    1442           73 :         h_.md.content_encoding.encoding == encoding::deflate )
    1443              :     {
    1444           74 :         filter_ = &ws_.emplace<inflator_filter>(
    1445           37 :             ctx_, ws_, false);
    1446              :     }
    1447         8864 :     else if( svc_.cfg.apply_gzip_decoder &&
    1448           36 :         h_.md.content_encoding.encoding == encoding::gzip )
    1449              :     {
    1450           72 :         filter_ = &ws_.emplace<inflator_filter>(
    1451           36 :             ctx_, ws_, true);
    1452              :     }
    1453              :     else
    1454              :     {
    1455         8828 :         cap += svc_.max_codec;
    1456         8828 :         ws_.reserve_front(svc_.max_codec);
    1457              :     }
    1458              : 
    1459         8901 :     if(h_.md.payload == payload::size )
    1460         4372 :         payload_remain_ = h_.md.payload_size;
    1461              : 
    1462         8901 :     if(is_plain())
    1463              :     {
    1464         4696 :         cb0_ = { p, cap, overread };
    1465         4696 :         if(h_.md.payload == payload::size)
    1466              :         {
    1467         4348 :             if(h_.md.payload_size >
    1468         4348 :                 svc_.cfg.body_limit)
    1469              :             {
    1470            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1471              :                     error::body_too_large);
    1472            0 :                 st_ = state::reset;
    1473            0 :                 return;
    1474              :             }
    1475              :         }
    1476         4696 :         st_ = state::body;
    1477         4696 :         return;
    1478              :     }
    1479              : 
    1480              :     // buffered payload
    1481              : 
    1482         8410 :     const auto n0 = (overread > svc_.cfg.min_buffer)
    1483         4205 :         ? overread
    1484         4169 :         : svc_.cfg.min_buffer;
    1485         4205 :     const auto n1 = svc_.cfg.min_buffer;
    1486              : 
    1487         4205 :     cb0_ = { p      , n0, overread };
    1488         4205 :     cb1_ = { p + n0 , n1 };
    1489         4205 :     st_  = state::body;
    1490              : }
    1491              : 
    1492              : // Called at the end of set_body
    1493              : void
    1494          635 : parser::
    1495              : on_set_body()
    1496              : {
    1497              :     // This function is called after all
    1498              :     // limit checking and calculation of
    1499              :     // chunked or filter.
    1500              : 
    1501          635 :     BOOST_ASSERT(got_header());
    1502              : 
    1503          635 :     nprepare_ = 0; // invalidate
    1504              : 
    1505          635 :     if(st_ == state::body)
    1506              :     {
    1507          278 :         st_ = state::set_body;
    1508          278 :         return;
    1509              :     }
    1510              : 
    1511          357 :     BOOST_ASSERT(
    1512              :         st_ == state::complete_in_place);
    1513              : }
    1514              : 
    1515              : std::size_t
    1516        58385 : parser::
    1517              : apply_filter(
    1518              :     system::error_code& ec,
    1519              :     std::size_t payload_avail,
    1520              :     bool more)
    1521              : {
    1522        58385 :     std::size_t p0 = payload_avail;
    1523              : 
    1524              :     for(;;)
    1525              :     {
    1526       116790 :         if(payload_avail == 0 && more)
    1527        58385 :             return p0 - payload_avail;
    1528              : 
    1529            0 :         auto f_rs = [&](){
    1530        60170 :             if(how_ == how::elastic)
    1531              :             {
    1532        19806 :                 std::size_t n = clamp(
    1533        19806 :                     svc_.cfg.body_limit - body_total_);
    1534        19806 :                 n = clamp(n, svc_.cfg.min_buffer);
    1535        19806 :                 n = clamp(n, eb_->max_size() - eb_->size());
    1536              : 
    1537              :                 // fill capacity first to avoid
    1538              :                 // an allocation
    1539              :                 std::size_t avail = 
    1540        19806 :                     eb_->capacity() - eb_->size();
    1541        19806 :                 if(avail != 0)
    1542        19801 :                     n = clamp(n, avail);
    1543              : 
    1544        19806 :                 return filter_->process(
    1545        19806 :                     eb_->prepare(n),
    1546        19806 :                     buffers::prefix(
    1547        19806 :                         cb0_.data(),
    1548              :                         payload_avail),
    1549        39612 :                     more);
    1550              :             }
    1551              :             else // in-place and sink 
    1552              :             {
    1553        40364 :                 std::size_t n = clamp(
    1554        40364 :                     svc_.cfg.body_limit - body_total_);
    1555        40364 :                 n = clamp(n, cb1_.capacity());
    1556              : 
    1557        40364 :                 return filter_->process(
    1558        40364 :                     cb1_.prepare(n),
    1559        40364 :                     buffers::prefix(
    1560        40364 :                         cb0_.data(),
    1561              :                         payload_avail),
    1562        80728 :                     more);
    1563              :             }
    1564        60170 :         }();
    1565              : 
    1566        60170 :         cb0_.consume(f_rs.in_bytes);
    1567        60170 :         payload_avail -= f_rs.in_bytes;
    1568        60170 :         body_total_   += f_rs.out_bytes;
    1569              : 
    1570       120220 :         bool needs_more_space = !f_rs.finished &&
    1571        60050 :             payload_avail != 0;
    1572              : 
    1573        60170 :         switch(how_)
    1574              :         {
    1575        20567 :         case how::in_place:
    1576              :         {
    1577        20567 :             cb1_.commit(f_rs.out_bytes);
    1578        20567 :             body_avail_ += f_rs.out_bytes;
    1579        20567 :             if( cb1_.capacity() == 0 &&
    1580              :                 needs_more_space)
    1581              :             {
    1582         3288 :                 ec = BOOST_HTTP_PROTO_ERR(
    1583              :                     error::in_place_overflow);
    1584         1644 :                 return p0 - payload_avail;
    1585              :             }
    1586        18923 :             break;
    1587              :         }
    1588        19797 :         case how::sink:
    1589              :         {
    1590        19797 :             cb1_.commit(f_rs.out_bytes);
    1591        19797 :             auto sink_rs = sink_->write(
    1592        19797 :                 cb1_.data(), !f_rs.finished);
    1593        19797 :             cb1_.consume(sink_rs.bytes);
    1594        19797 :             if(sink_rs.ec.failed())
    1595              :             {
    1596            0 :                 ec  = sink_rs.ec;
    1597            0 :                 st_ = state::reset;
    1598            0 :                 return p0 - payload_avail;
    1599              :             }
    1600        19797 :             break;
    1601              :         }
    1602        19806 :         case how::elastic:
    1603              :         {
    1604        19806 :             eb_->commit(f_rs.out_bytes);
    1605        19806 :             if( eb_->max_size() - eb_->size() == 0 &&
    1606              :                 needs_more_space)
    1607              :             {
    1608            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1609              :                     error::buffer_overflow);
    1610            0 :                 st_ = state::reset;
    1611            0 :                 return p0 - payload_avail;
    1612              :             }
    1613        19806 :             break;
    1614              :         }
    1615              :         }
    1616              : 
    1617        58526 :         if(f_rs.ec.failed())
    1618              :         {
    1619            1 :             ec = f_rs.ec;
    1620            1 :             st_ = state::reset;
    1621            1 :             return p0 - payload_avail;
    1622              :         }
    1623              : 
    1624        58525 :         if( svc_.cfg.body_limit == body_total_ &&
    1625              :             needs_more_space)
    1626              :         {
    1627            0 :             ec  = BOOST_HTTP_PROTO_ERR(
    1628              :                 error::body_too_large);
    1629            0 :             st_ = state::reset;
    1630            0 :             return p0 - payload_avail;
    1631              :         }
    1632              : 
    1633        58525 :         if(f_rs.finished)
    1634              :         {
    1635          120 :             if(!more)
    1636              :             {
    1637           72 :                 st_ = (how_ == how::in_place)
    1638           72 :                     ? state::complete_in_place
    1639              :                     : state::complete;
    1640              :             }
    1641              : 
    1642          120 :             return p0 - payload_avail;
    1643              :         }
    1644        58405 :     }
    1645              : }
    1646              : 
    1647              : } // http_proto
    1648              : } // boost
        

Generated by: LCOV version 2.1