LCOV - code coverage report
Current view: top level - libs/http_proto/src/parser.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 75.6 % 583 441
Test Date: 2024-05-23 18:56:44 Functions: 78.8 % 33 26

            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         8934 :     max_overread() const noexcept
     123              :     {
     124              :         return
     125         8934 :             cfg.headers.max_size +
     126         8934 :             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         1044 : parser::
     216              : parser(
     217              :     context& ctx,
     218         1044 :     detail::kind k)
     219         1044 :     : ctx_(ctx)
     220         1044 :     , svc_(ctx.get_service<
     221         1044 :         parser_service>())
     222         1044 :     , h_(detail::empty{k})
     223         1044 :     , eb_(nullptr)
     224         2088 :     , st_(state::reset)
     225              : {
     226         1044 :     auto const n =
     227         1044 :         svc_.space_needed;
     228         1044 :     ws_.allocate(n);
     229         1044 :     h_.cap = n;
     230         1044 : }
     231              : 
     232              : //------------------------------------------------
     233              : 
     234         1044 : parser::
     235              : ~parser()
     236              : {
     237         1044 : }
     238              : 
     239              : //------------------------------------------------
     240              : //
     241              : // Modifiers
     242              : //
     243              : //------------------------------------------------
     244              : 
     245              : // prepare for a new stream
     246              : void
     247         1511 : parser::
     248              : reset() noexcept
     249              : {
     250         1511 :     ws_.clear();
     251         1511 :     eb_ = nullptr;
     252         1511 :     st_ = state::start;
     253         1511 :     got_eof_ = false;
     254         1511 : }
     255              : 
     256              : void
     257         1741 : parser::
     258              : start_impl(
     259              :     bool head_response)
     260              : {
     261         1741 :     std::size_t leftover = 0;
     262         1741 :     switch(st_)
     263              :     {
     264            1 :     default:
     265              :     case state::reset:
     266              :         // reset must be called first
     267            1 :         detail::throw_logic_error();
     268              : 
     269         1496 :     case state::start:
     270              :         // reset required on eof
     271         1496 :         if(got_eof_)
     272            0 :             detail::throw_logic_error();
     273         1496 :         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         1736 :     ws_.clear();
     316              : 
     317         3472 :     fb_ = {
     318         1736 :         ws_.data(),
     319         1736 :         svc_.cfg.headers.max_size +
     320         1736 :             svc_.cfg.min_buffer,
     321              :         leftover };
     322         1736 :     BOOST_ASSERT(fb_.capacity() ==
     323              :         svc_.max_overread());
     324              : 
     325         3472 :     h_ = detail::header(
     326         1736 :         detail::empty{h_.kind});
     327         1736 :     h_.buf = reinterpret_cast<
     328         1736 :         char*>(ws_.data());
     329         1736 :     h_.cbuf = h_.buf;
     330         1736 :     h_.cap = ws_.size();
     331              : 
     332         1736 :     BOOST_ASSERT(! head_response ||
     333              :         h_.kind == detail::kind::response);
     334         1736 :     head_response_ = head_response;
     335              : 
     336              :     // begin with in_place mode
     337         1736 :     how_ = how::in_place;
     338         1736 :     st_ = state::header;
     339         1736 :     nprepare_ = 0;
     340         1736 : }
     341              : 
     342              : auto
     343         5549 : parser::
     344              : prepare() ->
     345              :     mutable_buffers_type
     346              : {
     347         5549 :     nprepare_ = 0;
     348              : 
     349         5549 :     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         5486 :     case state::header:
     361              :     {
     362         5486 :         BOOST_ASSERT(h_.size <
     363              :             svc_.cfg.headers.max_size);
     364         5486 :         auto n = fb_.capacity() - fb_.size();
     365         5486 :         BOOST_ASSERT(n <= svc_.max_overread());
     366         5486 :         if( n > svc_.cfg.max_prepare)
     367           29 :             n = svc_.cfg.max_prepare;
     368         5486 :         mbp_[0] = fb_.prepare(n);
     369         5486 :         nprepare_ = n;
     370         5486 :         return mutable_buffers_type(
     371        10972 :             &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         5540 : parser::
     523              : commit(
     524              :     std::size_t n)
     525              : {
     526         5540 :     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         5486 :     case state::header:
     542              :     {
     543         5486 :         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         5485 :         if(got_eof_)
     551              :         {
     552              :             // can't commit after EOF
     553            1 :             detail::throw_logic_error();
     554              :         }
     555              : 
     556         5484 :         nprepare_ = 0; // invalidate
     557         5484 :         fb_.commit(n);
     558         5484 :         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         5533 : }
     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         6462 : parser::
     730              : parse(
     731              :     system::error_code& ec)
     732              : {
     733         6462 :     ec = {};
     734         6462 :     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         5500 :     case state::header:
     746              :     {
     747         5500 :         BOOST_ASSERT(h_.buf == static_cast<
     748              :             void const*>(ws_.data()));
     749         5500 :         BOOST_ASSERT(h_.cbuf == static_cast<
     750              :             void const*>(ws_.data()));
     751         5500 :         auto const new_size = fb_.size();
     752         5500 :         h_.parse(new_size, svc_.cfg.headers, ec);
     753         5500 :         if(ec == condition::need_more_input)
     754              :         {
     755         3792 :             if(! got_eof_)
     756              :             {
     757              :                 // headers incomplete
     758         3774 :                 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         1708 :         if(ec.failed())
     778              :         {
     779              :             // other error,
     780              :             //
     781              :             // VFALCO map this to a bad
     782              :             // request or bad response error?
     783              :             //
     784          259 :             st_ = state::reset; // unrecoverable
     785          259 :             return;
     786              :         }
     787              : 
     788              :         // headers are complete
     789         1449 :         on_headers(ec);
     790         1449 :         if(ec.failed())
     791          120 :             return;
     792         1329 :         if(st_ == state::complete)
     793          844 :             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          314 : parser::
    1098              : safe_get_header() const ->
    1099              :     detail::header const*
    1100              : {
    1101              :     // headers must be received
    1102          628 :     if( ! got_header() ||
    1103          314 :         fb_.size() == 0) // happens on eof
    1104            0 :         detail::throw_logic_error();
    1105              : 
    1106          314 :     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         1449 : parser::
    1124              : on_headers(
    1125              :     system::error_code& ec)
    1126              : {
    1127         1449 :     auto const overread = fb_.size() - h_.size;
    1128         1449 :     BOOST_ASSERT(
    1129              :         overread <= svc_.max_overread());
    1130              : 
    1131              :     // metadata error
    1132         1449 :     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         1329 :     ws_.reserve_front(h_.size);
    1143         1329 :     ws_.reserve_back(h_.table_space());
    1144              : 
    1145              :     // no payload
    1146         1329 :     if( h_.md.payload == payload::none ||
    1147          485 :         head_response_)
    1148              :     {
    1149              :         // set cb0_ to overread
    1150         1688 :         cb0_ = {
    1151          844 :             ws_.data(),
    1152          844 :             fb_.capacity() - h_.size,
    1153              :             overread };
    1154          844 :         body_avail_ = 0;
    1155          844 :         body_total_ = 0;
    1156          844 :         body_buf_ = &cb0_;
    1157          844 :         st_ = state::complete;
    1158          844 :         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 2.1