LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 81.4 % 726 591
Test Date: 2024-07-12 15:42:45 Functions: 82.9 % 41 34

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

Generated by: LCOV version 2.1