LCOV - code coverage report
Current view: top level - libs/http_proto/src - parser.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 438 580 75.5 %
Date: 2024-01-17 18:52:26 Functions: 26 33 78.8 %

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

Generated by: LCOV version 1.15