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

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

Generated by: LCOV version 2.1