LCOV - code coverage report
Current view: top level - http_proto/impl - parser.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 316 503 62.8 %
Date: 2023-03-01 04:29:22 Functions: 19 28 67.9 %

          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             : #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      11             : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      12             : 
      13             : #include <boost/http_proto/parser.hpp>
      14             : #include <boost/http_proto/context.hpp>
      15             : #include <boost/http_proto/error.hpp>
      16             : #include <boost/http_proto/service/zlib_service.hpp>
      17             : #include <boost/http_proto/detail/except.hpp>
      18             : #include <boost/buffers/buffer_copy.hpp>
      19             : #include <boost/url/grammar/ci_string.hpp>
      20             : #include <boost/assert.hpp>
      21             : #include <memory>
      22             : 
      23             : namespace boost {
      24             : namespace http_proto {
      25             : 
      26             : /*
      27             :     Principles for fixed-size buffer design
      28             : 
      29             :     axiom 1:
      30             :         To read data you must have a buffer.
      31             : 
      32             :     axiom 2:
      33             :         The size of the HTTP header is not
      34             :         known in advance.
      35             : 
      36             :     conclusion 3:
      37             :         A single I/O can produce a complete
      38             :         HTTP header and additional payload
      39             :         data.
      40             : 
      41             :     conclusion 4:
      42             :         A single I/O can produce multiple
      43             :         complete HTTP headers, complete
      44             :         payloads, and a partial header or
      45             :         payload.
      46             : 
      47             :     axiom 5:
      48             :         A process is in one of two states:
      49             :             1. at or below capacity
      50             :             2. above capacity
      51             : 
      52             :     axiom 6:
      53             :         A program which can allocate an
      54             :         unbounded number of resources can
      55             :         go above capacity.
      56             : 
      57             :     conclusion 7:
      58             :         A program can guarantee never going
      59             :         above capacity if all resources are
      60             :         provisioned at program startup.
      61             : 
      62             :     corollary 8:
      63             :         `parser` and `serializer` should each
      64             :         allocate a single buffer of calculated
      65             :         size, and never resize it.
      66             : 
      67             :     axiom #:
      68             :         A parser and a serializer are always
      69             :         used in pairs.
      70             : */
      71             : 
      72             : //------------------------------------------------
      73             : /*
      74             : Buffer Usage
      75             : 
      76             : |                                               | begin
      77             : | H |   p   |                               | f | read headers
      78             : | H |   p   |                           | T | f | set T body
      79             : | H |   p   |                       | C | T | f | make codec C
      80             : | H |   p           |       b       | C | T | f | decode p into b
      81             : | H |       p       |       b       | C | T | f | read/parse loop
      82             : | H |                                   | T | f | destroy codec
      83             : | H |                                   | T | f | finished
      84             : 
      85             :     H   headers
      86             :     C   codec
      87             :     T   body
      88             :     f   table
      89             :     p   partial payload
      90             :     b   body data
      91             : 
      92             :     "payload" is the bytes coming in from
      93             :         the stream.
      94             : 
      95             :     "body" is the logical body, after transfer
      96             :         encoding is removed. This can be the
      97             :         same as the payload.
      98             : 
      99             :     A "plain payload" is when the payload and
     100             :         body are identical (no transfer encodings).
     101             : 
     102             :     A "buffered payload" is any payload which is
     103             :         not plain. A second buffer is required
     104             :         for reading.
     105             : */
     106             : //------------------------------------------------
     107             : 
     108             : class parser_service
     109             :     : public service
     110             : {
     111             : public:
     112             :     parser::config_base cfg;
     113             :     std::size_t space_needed = 0;
     114             :     std::size_t max_codec = 0;
     115             :     zlib::deflate_decoder_service const*
     116             :         deflate_svc = nullptr;
     117             : 
     118             :     parser_service(
     119             :         context& ctx,
     120             :         parser::config_base const& cfg_);
     121             : 
     122             :     std::size_t
     123       83721 :     max_overread() const noexcept
     124             :     {
     125             :         return
     126       83721 :             cfg.headers.max_size +
     127       83721 :             cfg.min_buffer;
     128             :     }
     129             : };
     130             : 
     131           6 : parser_service::
     132             : parser_service(
     133             :     context& ctx,
     134           6 :     parser::config_base const& cfg_)
     135           6 :         : cfg(cfg_)
     136             : {
     137             : /*
     138             :     | fb |      cb0      |      cb1     | C | T | f |
     139             : 
     140             :     fb  flat_buffer         headers.max_size
     141             :     cb0 circular_buffer     min_buffer
     142             :     cb1 circular_buffer     min_buffer
     143             :     C   codec               max_codec
     144             :     T   body                max_type_erase
     145             :     f   table               max_table_space
     146             : 
     147             : */
     148             :     // validate
     149             :     //if(cfg.min_prepare > cfg.max_prepare)
     150             :         //detail::throw_invalid_argument();
     151             : 
     152           6 :     if( cfg.min_buffer < 1 ||
     153           6 :         cfg.min_buffer > cfg.body_limit)
     154           0 :         detail::throw_invalid_argument();
     155             : 
     156           6 :     if(cfg.max_prepare < 1)
     157           0 :         detail::throw_invalid_argument();
     158             : 
     159             :     // VFALCO TODO OVERFLOW CHECING
     160             :     {
     161             :         //fb_.size() - h_.size +
     162             :         //svc_.cfg.min_buffer +
     163             :         //svc_.cfg.min_buffer +
     164             :         //svc_.max_codec;
     165             :     }
     166             : 
     167             :     // VFALCO OVERFLOW CHECKING ON THIS
     168           6 :     space_needed +=
     169           6 :         cfg.headers.valid_space_needed();
     170             : 
     171             :     // cb0_, cb1_
     172             :     // VFALCO OVERFLOW CHECKING ON THIS
     173           6 :     space_needed +=
     174           6 :         cfg.min_buffer +
     175             :         cfg.min_buffer;
     176             : 
     177             :     // T
     178           6 :     space_needed += cfg.max_type_erase;
     179             : 
     180             :     // max_codec
     181             :     {
     182           6 :         if(cfg.apply_deflate_decoder)
     183             :         {
     184           0 :             deflate_svc = &ctx.get_service<
     185           0 :                 zlib::deflate_decoder_service>();
     186             :             auto const n = 
     187           0 :                 deflate_svc->space_needed();
     188           0 :             if( max_codec < n)
     189           0 :                 max_codec = n;
     190             :         }
     191             :     }
     192           6 :     space_needed += max_codec;
     193           6 : }
     194             : 
     195             : void
     196           6 : install_parser_service(
     197             :     context& ctx,
     198             :     parser::config_base const& cfg)
     199             : {
     200             :     ctx.make_service<
     201           6 :         parser_service>(cfg);
     202           6 : }
     203             : 
     204             : //------------------------------------------------
     205             : 
     206         738 : parser::
     207             : parser(
     208             :     context& ctx,
     209         738 :     detail::kind k)
     210             :     : ctx_(ctx)
     211             :     , svc_(ctx.get_service<
     212        1476 :         parser_service>())
     213             :     , h_(detail::empty{k})
     214         738 :     , st_(state::reset)
     215             : {
     216         738 :     auto const n =
     217         738 :         svc_.space_needed;
     218         738 :     ws_.allocate(n);
     219         738 :     h_.cap = n;
     220         738 : }
     221             : 
     222             : //------------------------------------------------
     223             : //
     224             : // Special Members
     225             : //
     226             : //------------------------------------------------
     227             : 
     228         738 : parser::
     229         738 : ~parser()
     230             : {
     231         738 : }
     232             : 
     233             : parser::
     234             : parser(
     235             :     parser&&) noexcept = default;
     236             : 
     237             : //------------------------------------------------
     238             : //
     239             : // Modifiers
     240             : //
     241             : //------------------------------------------------
     242             : 
     243             : // prepare for a new stream
     244             : void
     245        5355 : parser::
     246             : reset() noexcept
     247             : {
     248        5355 :     ws_.clear();
     249        5355 :     st_ = state::start;
     250        5355 :     got_eof_ = false;
     251        5355 : }
     252             : 
     253             : void
     254       14280 : parser::
     255             : start_impl(
     256             :     bool head_response)
     257             : {
     258       14280 :     std::size_t leftover = 0;
     259       14280 :     switch(st_)
     260             :     {
     261           0 :     default:
     262             :     case state::reset:
     263             :         // reset must be called first
     264           0 :         detail::throw_logic_error();
     265             : 
     266        5355 :     case state::start:
     267             :         // reset required on eof
     268        5355 :         if(got_eof_)
     269           0 :             detail::throw_logic_error();
     270        5355 :         break;
     271             : 
     272           0 :     case state::header:
     273             :         // start() called twice
     274           0 :         detail::throw_logic_error();
     275             : 
     276           0 :     case state::body:
     277             :     case state::body_set:
     278             :         // current message is incomplete
     279           0 :         detail::throw_logic_error();
     280             : 
     281        8925 :     case state::complete:
     282             :     {
     283             :         // remove partial body.
     284        8925 :         if(body_buf_ == &cb0_)
     285        8925 :             cb0_.consume(body_avail_);
     286             : 
     287        8925 :         if(cb0_.size() > 0)
     288             :         {
     289             : 
     290             :             // headers with no body
     291           0 :             BOOST_ASSERT(h_.size > 0);
     292           0 :             fb_.consume(h_.size);
     293           0 :             leftover = fb_.size();
     294             :             // move unused octets to front
     295           0 :             buffers::buffer_copy(
     296           0 :                 buffers::mutable_buffer(
     297           0 :                     ws_.data(),
     298             :                     leftover),
     299           0 :                 fb_.data());
     300             :         }
     301             :         else
     302             :         {
     303             :             // leftover data after body
     304             :         }
     305        8925 :         break;
     306             :     }
     307             :     }
     308             : 
     309       14280 :     ws_.clear();
     310             : 
     311       28560 :     fb_ = {
     312       14280 :         ws_.data(),
     313       14280 :         svc_.cfg.headers.max_size +
     314       14280 :             svc_.cfg.min_buffer,
     315             :         leftover };
     316       14280 :     BOOST_ASSERT(
     317             :         fb_.capacity() == svc_.max_overread());
     318             : 
     319       28560 :     h_ = detail::header(
     320       14280 :         detail::empty{h_.kind});
     321       14280 :     h_.buf = reinterpret_cast<
     322       14280 :         char*>(ws_.data());
     323       14280 :     h_.cbuf = h_.buf;
     324       14280 :     h_.cap = ws_.size();
     325             : 
     326       14280 :     BOOST_ASSERT(! head_response ||
     327             :         h_.kind == detail::kind::response);
     328       14280 :     head_response_ = head_response;
     329             : 
     330       14280 :     how_ = how::in_place;
     331       14280 :     st_ = state::header;
     332       14280 : }
     333             : 
     334             : auto
     335       42962 : parser::
     336             : prepare() ->
     337             :     mutable_buffers_type
     338             : {
     339       42962 :     switch(st_)
     340             :     {
     341           0 :     default:
     342             :     case state::reset:
     343             :         // reset must be called first
     344           0 :         detail::throw_logic_error();
     345             : 
     346           0 :     case state::start:
     347             :         // start must be called first
     348           0 :         detail::throw_logic_error();
     349             : 
     350       40397 :     case state::header:
     351             :     {
     352       40397 :         BOOST_ASSERT(h_.size <
     353             :             svc_.cfg.headers.max_size);
     354       40397 :         auto n = fb_.capacity() - fb_.size();
     355       40397 :         BOOST_ASSERT(n <= svc_.max_overread());
     356       40397 :         if( n > svc_.cfg.max_prepare)
     357           0 :             n = svc_.cfg.max_prepare;
     358       40397 :         mbp_[0] = fb_.prepare(n);
     359       40397 :         return mutable_buffers_type(
     360       80794 :             &mbp_[0], 1);
     361             :     }
     362             : 
     363        2565 :     case state::body:
     364             :     {
     365             :         // buffered payload
     366        2565 :         if(body_buf_ != &cb0_)
     367             :         {
     368           0 :             auto n = cb0_.capacity() -
     369           0 :                 cb0_.size();
     370           0 :             if( n > svc_.cfg.max_prepare)
     371           0 :                 n = svc_.cfg.max_prepare;
     372           0 :             mbp_ = cb0_.prepare(n);
     373           0 :             return mutable_buffers_type(mbp_);
     374             :         }
     375        2565 :         BOOST_ASSERT(is_plain());
     376             : 
     377             :         // plain payload
     378             : 
     379        2565 :         if(how_ == how::in_place)
     380             :         {
     381             :             auto n =
     382        1191 :                 body_buf_->capacity() -
     383        1191 :                 body_buf_->size();
     384        1191 :             if( n > svc_.cfg.max_prepare)
     385           0 :                 n = svc_.cfg.max_prepare;
     386        1191 :             mbp_ = body_buf_->prepare(n);
     387        1191 :             return mutable_buffers_type(mbp_);
     388             :         }
     389             : 
     390        1374 :         if(how_ == how::dynamic)
     391             :         {
     392             :             // Overreads are not allowed, or
     393             :             // else the caller will see extra
     394             :             // unrelated data.
     395             : 
     396        1374 :             if(h_.md.payload == payload::size)
     397             :             {
     398             :                 // set_body moves avail to dyn
     399         615 :                 BOOST_ASSERT(body_buf_->size() == 0);
     400         615 :                 BOOST_ASSERT(body_avail_ == 0);
     401         615 :                 auto n = payload_remain_;
     402         615 :                 if( n > svc_.cfg.max_prepare)
     403           0 :                     n = svc_.cfg.max_prepare;
     404         615 :                 return dyn_->prepare(n);
     405             :             }
     406             : 
     407             :             // heuristic size
     408         759 :             std::size_t n = 0;
     409         759 :             if(! got_eof_)
     410             :             {
     411         759 :                 n = svc_.cfg.min_buffer;
     412         759 :                 if( n > svc_.cfg.max_prepare)
     413           0 :                     n = svc_.cfg.max_prepare;
     414             :                 std::size_t avail;
     415         759 :                 avail =
     416         759 :                     dyn_->max_size() - dyn_->size();
     417         759 :                 if( n > avail)
     418         759 :                     n = avail;
     419         759 :                 avail = 
     420         759 :                     dyn_->capacity() - dyn_->size();
     421         759 :                 if( n > avail &&
     422             :                     avail != 0)
     423           0 :                     n = avail;
     424         759 :                 if(n == 0)
     425             :                 {
     426             :                     // dynamic buffer is full
     427         308 :                     if(body_avail_ == 0)
     428             :                     {
     429             :                         // attempt a 1 byte read so
     430             :                         // we can detect overflow
     431         273 :                         BOOST_ASSERT(
     432             :                             body_buf_->size() == 0);
     433         273 :                         mbp_ = body_buf_->prepare(1);
     434             :                         return
     435         273 :                             mutable_buffers_type(mbp_);
     436             :                     }
     437             :                     return
     438          35 :                         mutable_buffers_type{};
     439             :                 }
     440             :             }
     441         451 :             return dyn_->prepare(n);
     442             :         }
     443             : 
     444             :         // VFALCO TODO
     445           0 :         if(how_ == how::pull)
     446           0 :             detail::throw_logic_error();
     447             : 
     448             :         // VFALCO TODO
     449           0 :         detail::throw_logic_error();
     450             :     }
     451             : 
     452           0 :     case state::body_set:
     453             :     {
     454           0 :         BOOST_ASSERT(how_ != how::in_place);
     455           0 :         BOOST_ASSERT(how_ != how::dynamic);
     456           0 :         if(how_ == how::sink)
     457             :         {
     458             :             // this is a no-op, to get the
     459             :             // caller to call parse next.
     460           0 :             return mutable_buffers_type{};
     461             :         }
     462           0 :         detail::throw_logic_error();
     463             :     }
     464             : 
     465           0 :     case state::complete:
     466             :         // We allow the call for callers
     467             :         // who want normalized usage, but
     468             :         // just return a 0-sized sequence.
     469           0 :         return mutable_buffers_type{};
     470             :     }
     471             : }
     472             : 
     473             : void
     474       42962 : parser::
     475             : commit(
     476             :     std::size_t n)
     477             : {
     478             :     // Can't commit after eof
     479       42962 :     if(got_eof_)
     480           0 :         detail::throw_logic_error();
     481             : 
     482       42962 :     switch(st_)
     483             :     {
     484           0 :     default:
     485             :     case state::reset:
     486             :         // reset must be called first
     487           0 :         detail::throw_logic_error();
     488             : 
     489           0 :     case state::start:
     490             :         // forgot to call start()
     491           0 :         detail::throw_logic_error();
     492             : 
     493       40397 :     case state::header:
     494       40397 :         fb_.commit(n);
     495       40397 :         break;
     496             : 
     497        2565 :     case state::body:
     498        2565 :         if(! is_plain())
     499             :         {
     500             :             // buffered payload
     501           0 :             cb0_.commit(n);
     502           0 :             break;
     503             :         }
     504             : 
     505             :         // plain payload
     506             : 
     507        2565 :         if(how_ == how::in_place)
     508             :         {
     509        1191 :             cb0_.commit(n);
     510        1191 :             break;
     511             :         }
     512             : 
     513        1374 :         if(how_ == how::dynamic)
     514             :         {
     515        1374 :             if(dyn_->size() < dyn_->max_size())
     516             :             {
     517        1066 :                 BOOST_ASSERT(body_avail_ == 0);
     518        1066 :                 BOOST_ASSERT(
     519             :                     body_buf_->size() == 0);
     520        1066 :                 dyn_->commit(n);
     521             :             }
     522             :             else
     523             :             {
     524         308 :                 body_buf_->commit(n);
     525         308 :                 body_avail_ += n;
     526             :             }
     527        1374 :             body_total_ += n;
     528        1374 :             if(h_.md.payload == payload::size)
     529             :             {
     530         615 :                 BOOST_ASSERT(
     531             :                     n <= payload_remain_);
     532         615 :                 payload_remain_ -= n;
     533         615 :                 if(payload_remain_ == 0)
     534         585 :                     st_ = state::complete;
     535             :             }
     536        1374 :             break;
     537             :         }
     538           0 :         if(how_ == how::sink)
     539             :         {
     540           0 :             cb0_.commit(n);
     541           0 :             break;
     542             :         }
     543           0 :         if(how_ == how::pull)
     544             :         {
     545             :             // VFALCO TODO
     546           0 :             detail::throw_logic_error();
     547             :         }
     548           0 :         break;
     549             : 
     550           0 :     case state::body_set:
     551           0 :         BOOST_ASSERT(n == 0);
     552           0 :         break;
     553             : 
     554           0 :     case state::complete:
     555             :         // intended no-op
     556           0 :         break;
     557             :     }
     558       42962 : }
     559             : 
     560             : void
     561        4356 : parser::
     562             : commit_eof()
     563             : {
     564        4356 :     switch(st_)
     565             :     {
     566           0 :     default:
     567             :     case state::reset:
     568             :         // reset must be called first
     569           0 :         detail::throw_logic_error();
     570             : 
     571           0 :     case state::start:
     572             :         // forgot to call prepare()
     573           0 :         detail::throw_logic_error();
     574             : 
     575           0 :     case state::header:
     576           0 :         got_eof_ = true;
     577           0 :         break;
     578             : 
     579        4356 :     case state::body:
     580        4356 :         got_eof_ = true;
     581        4356 :         break;
     582             : 
     583           0 :     case state::body_set:
     584           0 :         got_eof_ = true;
     585           0 :         break;
     586             : 
     587           0 :     case state::complete:
     588             :         // can't commit eof when complete
     589           0 :         detail::throw_logic_error();
     590             :     }
     591        4356 : }
     592             : 
     593             : //-----------------------------------------------
     594             : 
     595             : // process input data then
     596             : // eof if input data runs out.
     597             : void
     598       60007 : parser::
     599             : parse(
     600             :     system::error_code& ec)
     601             : {
     602       60007 :     ec = {};
     603       60007 :     switch(st_)
     604             :     {
     605           0 :     default:
     606             :     case state::reset:
     607             :         // reset must be called first
     608           0 :         detail::throw_logic_error();
     609             : 
     610           0 :     case state::start:
     611             :         // start must be called first
     612           0 :         detail::throw_logic_error();
     613             : 
     614       40397 :     case state::header:
     615             :     {
     616       40397 :         BOOST_ASSERT(h_.buf == static_cast<
     617             :             void const*>(ws_.data()));
     618       40397 :         BOOST_ASSERT(h_.cbuf == static_cast<
     619             :             void const*>(ws_.data()));
     620       40397 :         auto const new_size = fb_.size();
     621       40397 :         h_.parse(new_size, svc_.cfg.headers, ec);
     622       40397 :         if(ec == condition::need_more_input)
     623             :         {
     624       26117 :             if(! got_eof_)
     625             :             {
     626             :                 // headers incomplete
     627       26117 :                 return;
     628             :             }
     629           0 :             if(h_.size == 0)
     630             :             {
     631             :                 // stream closed cleanly
     632           0 :                 ec = BOOST_HTTP_PROTO_ERR(
     633             :                     error::end_of_stream);
     634           0 :                 return;
     635             :             }
     636             : 
     637             :             // stream closed with a
     638             :             // partial message received
     639           0 :             ec = BOOST_HTTP_PROTO_ERR(
     640             :                 error::incomplete);
     641           0 :             return;
     642             :         }
     643       14280 :         if(ec.failed())
     644             :         {
     645             :             // other error,
     646             :             //
     647             :             // VFALCO map this to a bad
     648             :             // request or bad response error?
     649             :             //
     650         128 :             st_ = state::reset; // unrecoverable
     651         128 :             return;
     652             :         }
     653             : 
     654             :         // headers are complete
     655       14152 :         on_headers(ec);
     656       14152 :         if(ec.failed())
     657           0 :             return;
     658       14152 :         if(st_ == state::complete)
     659        2084 :             break;
     660             :         BOOST_FALLTHROUGH;
     661             :     }
     662             : 
     663             :     case state::body:
     664             :     {
     665       12068 :     do_body:
     666       18404 :         BOOST_ASSERT(st_ == state::body);
     667       18404 :         BOOST_ASSERT(
     668             :             h_.md.payload != payload::none);
     669       18404 :         BOOST_ASSERT(
     670             :             h_.md.payload != payload::error);
     671       18404 :         if(h_.md.payload == payload::chunked)
     672             :         {
     673             :             // VFALCO parse chunked
     674           0 :             detail::throw_logic_error();
     675             :         }
     676       18404 :         else if(filt_)
     677             :         {
     678             :             // VFALCO TODO apply filter
     679           0 :             detail::throw_logic_error();
     680             :         }
     681             : 
     682       18404 :         if(how_ == how::in_place)
     683             :         {
     684       15570 :             if(h_.md.payload == payload::size)
     685             :             {
     686        8061 :                 if(cb0_.size() <
     687        8061 :                     h_.md.payload_size)
     688             :                 {
     689        1200 :                     body_avail_ = cb0_.size();
     690        1200 :                     if(cb0_.capacity() == 0)
     691             :                     {
     692             :                         // in_place buffer limit
     693           0 :                         ec = BOOST_HTTP_PROTO_ERR(
     694             :                             error::in_place_overflow);
     695           0 :                         return;
     696             :                     }
     697        2400 :                     ec = BOOST_HTTP_PROTO_ERR(
     698             :                         error::need_data);
     699        1200 :                     return;
     700             :                 }
     701        6861 :                 body_avail_ = h_.md.payload_size;
     702        6861 :                 st_ = state::complete;
     703        6861 :                 break;
     704             :             }
     705        7509 :             body_avail_ = cb0_.size();
     706        7509 :             if(body_avail_ > svc_.cfg.body_limit)
     707             :             {
     708           0 :                 ec = BOOST_HTTP_PROTO_ERR(
     709             :                     error::body_too_large);
     710           0 :                 st_ = state::reset; // unrecoverable
     711           0 :                 return;
     712             :             }
     713        7509 :             if( h_.md.payload == payload::chunked ||
     714        7509 :                 ! got_eof_)
     715             :             {
     716       10396 :                 ec = BOOST_HTTP_PROTO_ERR(
     717             :                     error::need_data);
     718        5198 :                 return;
     719             :             }
     720        2311 :             st_ = state::complete;
     721        2311 :             break;
     722             :         }
     723             : 
     724        2834 :         if(how_ == how::dynamic)
     725             :         {
     726             :             // state already updated in commit
     727        2834 :             if(h_.md.payload == payload::size)
     728             :             {
     729          30 :                 BOOST_ASSERT(body_avail_ == 0);
     730          30 :                 BOOST_ASSERT(body_total_ <
     731             :                     h_.md.payload_size);
     732          30 :                 BOOST_ASSERT(
     733             :                     payload_remain_ > 0);
     734          30 :                 if(got_eof_)
     735             :                 {
     736           0 :                     ec = BOOST_HTTP_PROTO_ERR(
     737             :                         error::incomplete);
     738           0 :                     return;
     739             :                 }
     740          30 :                 return;
     741             :             }
     742        2804 :             BOOST_ASSERT(
     743             :                 h_.md.payload == payload::to_eof);
     744        4912 :             if( dyn_->size() == dyn_->max_size() &&
     745        2108 :                 body_avail_ > 0)
     746             :             {
     747        1722 :                 ec = BOOST_HTTP_PROTO_ERR(
     748             :                     error::buffer_overflow);
     749         861 :                 st_ = state::reset; // unrecoverable
     750         861 :                 return;
     751             :             }
     752        1943 :             if(got_eof_)
     753             :             {
     754        1450 :                 BOOST_ASSERT(body_avail_ == 0);
     755        1450 :                 st_ = state::complete;
     756        1450 :                 break;
     757             :             }
     758         493 :             BOOST_ASSERT(body_avail_ == 0);
     759         493 :             break;
     760             :         }
     761             : 
     762             :         // VFALCO TODO
     763           0 :         detail::throw_logic_error();
     764             :     }
     765             : 
     766           0 :     case state::body_set:
     767             :     {
     768             :         // transfer in_place data into set body
     769             : 
     770             :         // this is handled in on_set_body
     771           0 :         BOOST_ASSERT(how_ != how::dynamic);
     772             : 
     773           0 :         if(how_ == how::sink)
     774             :         {
     775           0 :             auto n = body_buf_->size();
     776           0 :             if(h_.md.payload == payload::size)
     777             :             {
     778             :                 // sink_->size_hint(h_.md.payload_size, ec);
     779             : 
     780           0 :                 if(n < h_.md.payload_size)
     781             :                 {
     782           0 :                     auto rv = sink_->write(
     783           0 :                         body_buf_->data(), false);
     784           0 :                     BOOST_ASSERT(rv.ec.failed() ||
     785             :                         rv.bytes == body_buf_->size());
     786           0 :                     BOOST_ASSERT(
     787             :                         rv.bytes >= body_avail_);
     788           0 :                     BOOST_ASSERT(
     789             :                         rv.bytes < payload_remain_);
     790           0 :                     body_buf_->consume(rv.bytes);
     791           0 :                     body_avail_ -= rv.bytes;
     792           0 :                     body_total_ += rv.bytes;
     793           0 :                     payload_remain_ -= rv.bytes;
     794           0 :                     if(rv.ec.failed())
     795             :                     {
     796           0 :                         ec = rv.ec;
     797           0 :                         st_ = state::reset; // unrecoverable
     798           0 :                         return;
     799             :                     }
     800           0 :                     st_ = state::body;
     801           0 :                     goto do_body;
     802             :                 }
     803             : 
     804           0 :                 n = h_.md.payload_size;
     805             :             }
     806             :             // complete
     807           0 :             BOOST_ASSERT(body_buf_ == &cb0_);
     808           0 :             auto rv = sink_->write(
     809           0 :                 body_buf_->data(), true);
     810           0 :             BOOST_ASSERT(rv.ec.failed() ||
     811             :                 rv.bytes == body_buf_->size());
     812           0 :             body_buf_->consume(rv.bytes);
     813           0 :             if(rv.ec.failed())
     814             :             {
     815           0 :                 ec = rv.ec;
     816           0 :                 st_ = state::reset; // unrecoverable
     817           0 :                 return;
     818             :             }
     819           0 :             st_ = state::complete;
     820           0 :             return;
     821             :         }
     822             : 
     823             :         // VFALCO TODO
     824           0 :         detail::throw_logic_error();
     825             :     }
     826             : 
     827       13274 :     case state::complete:
     828             :     {
     829             :         // This is a no-op except when set_body
     830             :         // was called and we have in-place data.
     831       13274 :         switch(how_)
     832             :         {
     833        6775 :         default:
     834             :         case how::in_place:
     835        6775 :             break;
     836             : 
     837        6499 :         case how::dynamic:
     838             :         {
     839        6499 :             if(body_buf_->size() == 0)
     840        6499 :                 break;
     841           0 :             BOOST_ASSERT(dyn_->size() == 0);
     842           0 :             auto n = buffers::buffer_copy(
     843           0 :                 dyn_->prepare(
     844           0 :                     body_buf_->size()),
     845           0 :                 body_buf_->data());
     846           0 :             body_buf_->consume(n);
     847           0 :             break;
     848             :         }
     849             : 
     850           0 :         case how::sink:
     851             :         {
     852           0 :             if(body_buf_->size() == 0)
     853           0 :                 break;
     854           0 :             auto rv = sink_->write(
     855           0 :                 body_buf_->data(), false);
     856           0 :             body_buf_->consume(rv.bytes);
     857           0 :             if(rv.ec.failed())
     858             :             {
     859           0 :                 ec = rv.ec;
     860           0 :                 st_ = state::reset; // unrecoverable
     861           0 :                 return;
     862             :             }
     863           0 :             break;
     864             :         }
     865             : 
     866           0 :         case how::pull:
     867             :             // VFALCO TODO
     868           0 :             detail::throw_logic_error();
     869             :         }
     870             :     }
     871             :     }
     872             : }
     873             : 
     874             : //------------------------------------------------
     875             : 
     876             : auto
     877           0 : parser::
     878             : pull_some() ->
     879             :     const_buffers_type
     880             : {
     881           0 :     return {};
     882             : }
     883             : 
     884             : core::string_view
     885       29135 : parser::
     886             : body() const noexcept
     887             : {
     888       29135 :     switch(st_)
     889             :     {
     890        5792 :     default:
     891             :     case state::reset:
     892             :     case state::start:
     893             :     case state::header:
     894             :     case state::body:
     895             :     case state::body_set:
     896             :         // not complete
     897        5792 :         return {};
     898             : 
     899       23343 :     case state::complete:
     900       23343 :         if(how_ != how::in_place)
     901             :         {
     902             :             // not in_place
     903        9793 :             return {};
     904             :         }
     905       13550 :         auto cbp = body_buf_->data();
     906       13550 :         BOOST_ASSERT(cbp[1].size() == 0);
     907       13550 :         BOOST_ASSERT(cbp[0].size() >= body_avail_);
     908       13550 :         return core::string_view(
     909             :             static_cast<char const*>(
     910       13550 :                 cbp[0].data()),
     911       27100 :             body_avail_);
     912             :     }
     913             : }
     914             : 
     915             : core::string_view
     916           0 : parser::
     917             : release_buffered_data() noexcept
     918             : {
     919           0 :     return {};
     920             : }
     921             : 
     922             : //------------------------------------------------
     923             : //
     924             : // Implementation
     925             : //
     926             : //------------------------------------------------
     927             : 
     928             : auto
     929          55 : parser::
     930             : safe_get_header() const ->
     931             :     detail::header const*
     932             : {
     933          55 :     switch(st_)
     934             :     {
     935           0 :     default:
     936             :     case state::start:
     937             :     case state::header:
     938             :         // Headers not received yet
     939           0 :         detail::throw_logic_error();
     940             : 
     941          55 :     case state::body:
     942             :     case state::complete:
     943             :         // VFALCO check if headers are discarded?
     944          55 :         break;
     945             :     }
     946          55 :     return &h_;
     947             : }
     948             : 
     949             : // return true if payload is plain
     950             : bool
     951       23973 : parser::
     952             : is_plain() const noexcept
     953             : {
     954             :     return
     955       47946 :         h_.md.payload != payload::chunked &&
     956       47946 :         ! filt_;
     957             : }
     958             : 
     959             : // Called immediately after
     960             : // complete headers are received
     961             : void
     962       14152 : parser::
     963             : on_headers(
     964             :     system::error_code& ec)
     965             : {
     966             :     auto const overread =
     967       14152 :         fb_.size() - h_.size;
     968       14152 :     BOOST_ASSERT(
     969             :         overread <= svc_.max_overread());
     970             : 
     971             :     // reserve headers + table
     972       14152 :     ws_.reserve_front(h_.size);
     973       14152 :     ws_.reserve_back(h_.table_space());
     974             : 
     975             :     // no payload
     976       14152 :     if( h_.md.payload == payload::none ||
     977       12068 :         head_response_)
     978             :     {
     979             :         // set cb0_ to overread
     980        4168 :         cb0_ = {
     981        2084 :             ws_.data(),
     982        2084 :             fb_.capacity() - h_.size,
     983             :             overread };
     984        2084 :         body_avail_ = 0;
     985        2084 :         body_total_ = 0;
     986        2084 :         body_buf_ = &cb0_;
     987        2084 :         st_ = state::complete;
     988        2084 :         return;
     989             :     }
     990             : 
     991             :     // metadata error
     992       12068 :     if(h_.md.payload == payload::error)
     993             :     {
     994             :         // VFALCO This needs looking at
     995           0 :         ec = BOOST_HTTP_PROTO_ERR(
     996             :             error::bad_payload);
     997           0 :         st_ = state::reset; // unrecoverable
     998           0 :         return;
     999             :     }
    1000             : 
    1001             :     // calculate filter
    1002       12068 :     filt_ = nullptr;
    1003             : 
    1004             :     // plain payload
    1005       12068 :     if(is_plain())
    1006             :     {
    1007             :         // payload size is known
    1008       12068 :         if(h_.md.payload == payload::size)
    1009             :         {
    1010        7446 :             if(h_.md.payload_size >
    1011        7446 :                 svc_.cfg.body_limit)
    1012             :             {
    1013           0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1014             :                     error::body_too_large);
    1015           0 :                 st_ = state::reset; // unrecoverable
    1016           0 :                 return;
    1017             :             }
    1018             :             auto n0 =
    1019        7446 :                 fb_.capacity() - h_.size +
    1020        7446 :                 svc_.cfg.min_buffer +
    1021        7446 :                 svc_.max_codec;
    1022             :             // limit the capacity of cb0_ so
    1023             :             // that going over max_overread
    1024             :             // is impossible.
    1025       14892 :             if( n0 > h_.md.payload_size &&
    1026        7446 :                 n0 - h_.md.payload_size >=
    1027        7446 :                     svc_.max_overread())
    1028        7446 :                 n0 =
    1029        7446 :                     h_.md.payload_size +
    1030        7446 :                     svc_.max_overread();
    1031        7446 :             BOOST_ASSERT(n0 <= ws_.size());
    1032        7446 :             cb0_ = {
    1033        7446 :                 ws_.data(),
    1034             :                 n0,
    1035             :                 overread };
    1036        7446 :             body_buf_ = &cb0_;
    1037        7446 :             body_avail_ = cb0_.size();
    1038        7446 :             if( body_avail_ >= h_.md.payload_size)
    1039        6276 :                 body_avail_ = h_.md.payload_size;
    1040        7446 :             body_total_ = body_avail_;
    1041        7446 :             payload_remain_ =
    1042        7446 :                 h_.md.payload_size - body_total_;
    1043        7446 :             st_ = state::body;
    1044        7446 :             return;
    1045             :         }
    1046             : 
    1047             :         // payload to eof
    1048             :         // overread is not applicable
    1049             :         auto const n0 =
    1050        4622 :             fb_.capacity() - h_.size +
    1051        4622 :             svc_.cfg.min_buffer +
    1052        4622 :             svc_.max_codec;
    1053        4622 :         BOOST_ASSERT(n0 <= ws_.size());
    1054        4622 :         cb0_ = {
    1055        4622 :             ws_.data(),
    1056             :             n0,
    1057             :             overread };
    1058        4622 :         body_buf_ = &cb0_;
    1059        4622 :         body_avail_ = cb0_.size();
    1060        4622 :         body_total_ = body_avail_;
    1061        4622 :         st_ = state::body;
    1062        4622 :         return;
    1063             :     }
    1064             : 
    1065             :     // buffered payload
    1066             :     auto const n0 =
    1067           0 :         fb_.capacity() - h_.size;
    1068           0 :     BOOST_ASSERT(
    1069             :         n0 <= svc_.max_overread());
    1070           0 :     auto n1 = svc_.cfg.min_buffer;
    1071           0 :     if(! filt_)
    1072           0 :         n1 += svc_.max_codec;
    1073           0 :     BOOST_ASSERT(
    1074             :         n0 + n1 <= ws_.size());
    1075           0 :     cb0_ = {
    1076           0 :         ws_.data(),
    1077             :         n0,
    1078             :         overread };
    1079           0 :     cb1_ = {
    1080           0 :         ws_.data() + n0,
    1081             :         n1 };
    1082           0 :     body_buf_ = &cb1_;
    1083           0 :     body_avail_ = 0;
    1084           0 :     body_total_ = 0;
    1085           0 :     st_ = state::body;
    1086           0 :     return;
    1087             : }
    1088             : 
    1089             : // Called at the end of set_body
    1090             : void
    1091        6775 : parser::
    1092             : on_set_body()
    1093             : {
    1094             :     // This function is called after all
    1095             :     // limit checking and calculation of
    1096             :     // chunked or filter.
    1097             : 
    1098        6775 :     BOOST_ASSERT(got_header());
    1099             : 
    1100             :     // VFALCO review this hack
    1101        6775 :     if(! is_plain())
    1102         741 :         return;
    1103             : 
    1104             :     // plain payload
    1105             : 
    1106        6034 :     if(how_ == how::dynamic)
    1107             :     {
    1108        6034 :         if(h_.md.payload == payload::none)
    1109             :         {
    1110           0 :             st_ = state::complete;
    1111           0 :             return;
    1112             :         }
    1113        6034 :         auto n = body_avail_;
    1114        6034 :         BOOST_ASSERT(n <= body_buf_->size());
    1115             :         auto const space_left =
    1116        6034 :             dyn_->max_size() - dyn_->size();
    1117        6034 :         if( n > space_left)
    1118         630 :             n = space_left;
    1119        6034 :         if(h_.md.payload == payload::size)
    1120             :         {
    1121        3723 :             if( n > h_.md.payload_size)
    1122           0 :                 n = h_.md.payload_size;
    1123             :             // reserve space
    1124        3723 :             if(space_left >= h_.md.payload_size)
    1125        3723 :                 dyn_->prepare(h_.md.payload_size);
    1126             :             else
    1127           0 :                 dyn_->prepare(space_left);
    1128             :         }
    1129        6034 :         dyn_->commit(
    1130             :             buffers::buffer_copy(
    1131        6034 :                 dyn_->prepare(n),
    1132        6034 :                 body_buf_->data()));
    1133        6034 :         body_buf_->consume(n);
    1134        6034 :         body_avail_ -= n;
    1135             :         // VFALCO maybe update payload_remain_ here
    1136        6034 :         if( h_.md.payload == payload::size)
    1137             :         {
    1138        3723 :             if(n < h_.md.payload_size)
    1139             :             {
    1140         585 :                 BOOST_ASSERT(
    1141             :                     payload_remain_ > 0);
    1142         585 :                 return;
    1143             :             }
    1144        3138 :             BOOST_ASSERT(
    1145             :                 n == h_.md.payload_size);
    1146             :             // complete
    1147        3138 :             BOOST_ASSERT(
    1148             :                 payload_remain_ == 0);
    1149        3138 :             BOOST_ASSERT(body_avail_ == 0);
    1150        3138 :             BOOST_ASSERT(body_total_ == n);
    1151        3138 :             st_ = state::complete;
    1152        3138 :             return;
    1153             :         }
    1154             : 
    1155        2311 :         st_ = state::body;
    1156        2311 :         return;
    1157             :     }
    1158             : 
    1159           0 :     if(how_ == how::sink)
    1160             :     {
    1161             :         // handle this in prepare() where
    1162             :         // we can return an error_code.
    1163           0 :         st_ = state::body_set;
    1164           0 :         return;
    1165             :     }
    1166             : 
    1167             :     // VFALCO TODO
    1168           0 :     st_ = state::body_set;
    1169           0 :     detail::throw_logic_error();
    1170             : }
    1171             : 
    1172             : } // http_proto
    1173             : } // boost
    1174             : 
    1175             : #endif

Generated by: LCOV version 1.15