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.7 % 750 635
Test Date: 2024-08-30 20:13:35 Functions: 85.7 % 42 36

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

Generated by: LCOV version 2.1