LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 83.7 % 784 656
Test Date: 2024-12-31 05:34:48 Functions: 84.4 % 64 54

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

Generated by: LCOV version 2.1