LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 84.2 % 730 615
Test Date: 2024-09-10 13:08:43 Functions: 86.4 % 44 38

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

Generated by: LCOV version 2.1