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

Generated by: LCOV version 2.1