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

Generated by: LCOV version 2.1