LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 81.5 % 729 594
Test Date: 2024-07-18 19:38:54 Functions: 82.9 % 41 34

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

Generated by: LCOV version 2.1