LCOV - code coverage report
Current view: top level - http_proto/impl - parser.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 109 205 53.2 %
Date: 2023-02-25 19:48:06 Functions: 10 18 55.6 %

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3             : //
       4             : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5             : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // Official repository: https://github.com/CPPAlliance/http_proto
       8             : //
       9             : 
      10             : #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      11             : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      12             : 
      13             : #include <boost/http_proto/parser.hpp>
      14             : #include <boost/http_proto/context.hpp>
      15             : #include <boost/http_proto/error.hpp>
      16             : #include <boost/http_proto/service/zlib_service.hpp>
      17             : #include <boost/http_proto/detail/except.hpp>
      18             : #include <boost/buffers/buffer_copy.hpp>
      19             : #include <boost/url/grammar/ci_string.hpp>
      20             : #include <boost/assert.hpp>
      21             : #include <boost/none.hpp>
      22             : #include <memory>
      23             : 
      24             : namespace boost {
      25             : namespace http_proto {
      26             : 
      27             : //------------------------------------------------
      28             : /*
      29             :     Four body styles for `parser`
      30             :         * Specify a DynamicBuffer
      31             :         * Specify a Sink
      32             :         * Read from a parser::stream
      33             :         * in-place
      34             : 
      35             : Buffer Usage
      36             : 
      37             : |                                               | begin
      38             : | H |   p   |                               | f | read headers
      39             : | H |   p   |                           | T | f | set T body
      40             : | H |   p   |                       | C | T | f | make codec C
      41             : | H |   p           |       b       | C | T | f | decode p into b
      42             : | H |       p       |       b       | C | T | f | read/parse loop
      43             : | H |                                   | T | f | destroy codec
      44             : | H |                                   | T | f | finished
      45             : 
      46             : H   headers
      47             : C   codec
      48             : T   body
      49             : f   table
      50             : p   partial payload
      51             : b   body data
      52             : 
      53             : - We can compact the headers:
      54             :     move the table downwards to
      55             :     squeeze out the unused space
      56             : 
      57             : */
      58             : //------------------------------------------------
      59             : 
      60             : struct parser_service
      61             :     : service
      62             : {
      63             :     parser::config_base cfg;
      64             :     std::size_t space_needed = 0;
      65             :     zlib::deflate_decoder_service const*
      66             :         deflate_svc = nullptr;
      67             : 
      68             :     parser_service(
      69             :         context& ctx,
      70             :         parser::config_base const& cfg_);
      71             : };
      72             : 
      73           6 : parser_service::
      74             : parser_service(
      75             :     context& ctx,
      76           6 :     parser::config_base const& cfg_)
      77           6 :         : cfg(cfg_)
      78             : {
      79             : /*
      80             :     | fb |      cb0      |      cb1     | C | T | f |
      81             : 
      82             :     fb  flat_buffer         headers.max_size
      83             :     cb0 circular_buffer     min_in_place_body
      84             :     cb1 circular_buffer     min_in_place_body
      85             :     C   codec               max_codec
      86             :     T   body                max_type_erase
      87             :     f   table               max_table_space
      88             : 
      89             : */
      90             :     // validate
      91             :     //if(cfg.min_prepare > cfg.max_prepare)
      92             :         //detail::throw_invalid_argument();
      93             : 
      94             :     // fb, f
      95           6 :     space_needed +=
      96           6 :         cfg.headers.valid_space_needed();
      97             : 
      98             :     // cb0, cb1
      99           6 :     space_needed +=
     100           6 :         2 * cfg.min_in_place_body;
     101             : 
     102             :     // T
     103           6 :     space_needed += cfg.max_type_erase;
     104             : 
     105             :     // max(C...)
     106           6 :     std::size_t C = 0;
     107             :     {
     108           6 :         if(cfg.apply_deflate_decoder)
     109             :         {
     110           0 :             deflate_svc = &ctx.get_service<
     111           0 :                 zlib::deflate_decoder_service>();
     112             :             auto const n = 
     113           0 :                 deflate_svc->space_needed();
     114           0 :             if( C < n)
     115           0 :                 C = n;
     116             :         }
     117             :     }
     118           6 :     space_needed += C;
     119           6 : }
     120             : 
     121             : void
     122           6 : install_parser_service(
     123             :     context& ctx,
     124             :     parser::config_base const& cfg)
     125             : {
     126             :     ctx.make_service<
     127           6 :         parser_service>(cfg);
     128           6 : }
     129             : 
     130             : //------------------------------------------------
     131             : 
     132         736 : parser::
     133             : parser(
     134             :     context& ctx,
     135         736 :     detail::kind k)
     136             :     : ctx_(ctx)
     137             :     , svc_(ctx.get_service<
     138        1472 :         parser_service>())
     139         736 :     , h_(detail::empty{k})
     140             : {
     141         736 :     auto const n =
     142         736 :         svc_.space_needed;
     143         736 :     ws_.allocate(n);
     144         736 :     h_.cap = n;
     145         736 :     reset();
     146         736 : }
     147             : 
     148             : //------------------------------------------------
     149             : //
     150             : // Special Members
     151             : //
     152             : //------------------------------------------------
     153             : 
     154         736 : parser::
     155         736 : ~parser()
     156             : {
     157         736 : }
     158             : 
     159             : parser::
     160             : parser(
     161             :     parser&&) noexcept = default;
     162             : 
     163             : //------------------------------------------------
     164             : //
     165             : // Modifiers
     166             : //
     167             : //------------------------------------------------
     168             : 
     169             : // prepare for a new stream
     170             : void
     171         736 : parser::
     172             : reset() noexcept
     173             : {
     174         736 :     ws_.clear();
     175         736 :     st_ = state::need_start;
     176         736 :     body_ = body::in_place;
     177         736 :     head_response_ = false;
     178         736 :     got_eof_ = false;
     179         736 : }
     180             : 
     181             : void
     182         767 : parser::
     183             : start_impl(
     184             :     bool head_response)
     185             : {
     186         767 :     std::size_t leftover = 0;
     187         767 :     switch(st_)
     188             :     {
     189         734 :     default:
     190             :     case state::need_start:
     191         734 :         BOOST_ASSERT(h_.size == 0);
     192         734 :         BOOST_ASSERT(fb_.size() == 0);
     193         734 :         BOOST_ASSERT(! got_eof_);
     194         734 :         break;
     195             : 
     196           0 :     case state::headers:
     197             :         // can't call start() twice
     198           0 :         detail::throw_logic_error();
     199             : 
     200           0 :     case state::headers_done:
     201             :     case state::body:
     202             :         // previous message was unfinished
     203           0 :         detail::throw_logic_error();
     204             : 
     205          33 :     case state::complete:
     206          33 :         if(fb_.size() > 0)
     207             :         {
     208             :             // headers with no body
     209          33 :             BOOST_ASSERT(h_.size > 0);
     210          33 :             fb_.consume(h_.size);
     211          33 :             leftover = fb_.size();
     212             :             // move unused octets to front
     213          33 :             buffers::buffer_copy(
     214          33 :                 buffers::mutable_buffer(
     215             :                     ws_.data(),
     216             :                     leftover),
     217          66 :                 fb_.data());
     218             :         }
     219             :         else
     220             :         {
     221             :             // leftover data after body
     222             :         }
     223          33 :         break;
     224             :     }
     225             : /*
     226             : | fb |      cb0      |      cb1     | C | T | f |
     227             : */
     228             :     // start with fb+cb0
     229         767 :     fb_ = {
     230             :         ws_.data(), // VFALCO this might need an offset
     231         767 :         svc_.cfg.headers.max_size +
     232         767 :             svc_.cfg.min_in_place_body,
     233             :         leftover };
     234             : 
     235        1534 :     h_ = detail::header(
     236         767 :         detail::empty{h_.kind});
     237         767 :     h_.buf = reinterpret_cast<
     238         767 :         char*>(ws_.data());
     239         767 :     h_.cbuf = h_.buf;
     240         767 :     h_.cap = ws_.size();
     241             : 
     242         767 :     st_ = state::headers;
     243         767 :     body_ = body::in_place;
     244             : 
     245         767 :     BOOST_ASSERT(! head_response ||
     246             :         h_.kind == detail::kind::response);
     247         767 :     head_response_ = head_response;
     248         767 : }
     249             : 
     250             : auto
     251        3185 : parser::
     252             : prepare() ->
     253             :     mutable_buffers_type
     254             : {
     255        3185 :     switch(st_)
     256             :     {
     257           0 :     default:
     258             :     case state::need_start:
     259             :         // forgot to call start()
     260           0 :         detail::throw_logic_error();
     261             : 
     262        3185 :     case state::headers:
     263             :     {
     264        3185 :         BOOST_ASSERT(h_.size <
     265             :             svc_.cfg.headers.max_size);
     266        3185 :         auto n = fb_.capacity() - fb_.size();
     267        3185 :         if( n > svc_.cfg.max_prepare)
     268           0 :             n = svc_.cfg.max_prepare;
     269             :         return {
     270        3185 :             fb_.prepare(n),
     271        3185 :             buffers::mutable_buffer{} };
     272             :     }
     273             : 
     274           0 :     case state::headers_done:
     275             :     {
     276             :         // reserve headers
     277           0 :         ws_.reserve_front(h_.size);
     278             : 
     279             :         // reserve table at the back
     280           0 :         ws_.reserve_back(h_.table_space());
     281             : 
     282           0 :         st_ = state::body;
     283             :         // VFALCO set up body buffer
     284             :         BOOST_FALLTHROUGH;
     285             :     }
     286             : 
     287           0 :     case state::body:
     288             :     {
     289             :         //if(body_ == body::dynamic)
     290           0 :         auto n = cb0_.capacity() -
     291           0 :             cb0_.size();
     292           0 :         if( n > svc_.cfg.max_prepare)
     293           0 :             n = svc_.cfg.max_prepare;
     294           0 :         return cb0_.prepare(n);
     295             :     }
     296             : 
     297           0 :     case state::complete:
     298             :         // forgot to call start()
     299           0 :         detail::throw_logic_error();
     300             :     }
     301             : }
     302             : 
     303             : void
     304        3185 : parser::
     305             : commit(
     306             :     std::size_t n)
     307             : {
     308             :     // Can't commit after eof
     309        3185 :     if(got_eof_)
     310           0 :         detail::throw_logic_error();
     311             : 
     312        3185 :     switch(st_)
     313             :     {
     314           0 :     default:
     315             :     case state::need_start:
     316             :         // forgot to call start()
     317           0 :         detail::throw_logic_error();
     318             : 
     319        3185 :     case state::headers:
     320        3185 :         fb_.commit(n);
     321        3185 :         break;
     322             : 
     323           0 :     case state::headers_done:
     324             :         // forgot to call prepare()
     325           0 :         detail::throw_logic_error();
     326             : 
     327           0 :     case state::body:
     328           0 :         cb0_.commit(n);
     329           0 :         break;
     330             : 
     331           0 :     case state::complete:
     332             :         // forgot to call start()
     333           0 :         detail::throw_logic_error();
     334             :     }
     335        3185 : }
     336             : 
     337             : void
     338           0 : parser::
     339             : commit_eof()
     340             : {
     341           0 :     switch(st_)
     342             :     {
     343           0 :     default:
     344             :     case state::need_start:
     345             :         // forgot to call prepare()
     346           0 :         detail::throw_logic_error();
     347             : 
     348           0 :     case state::headers:
     349           0 :         got_eof_ = true;
     350           0 :         break;
     351             : 
     352           0 :     case state::headers_done:
     353             :         // forgot to call prepare()
     354           0 :         detail::throw_logic_error();
     355             : 
     356           0 :     case state::body:
     357           0 :         got_eof_ = true;
     358           0 :         break;
     359             : 
     360           0 :     case state::complete:
     361             :         // Can't commit eof when
     362             :         // message is complete.
     363           0 :         detail::throw_logic_error();
     364             :     }
     365           0 : }
     366             : 
     367             : //-----------------------------------------------
     368             : 
     369             : // process input data then
     370             : // eof if input data runs out.
     371             : void
     372        3185 : parser::
     373             : parse(
     374             :     error_code& ec)
     375             : {
     376        3185 :     switch(st_)
     377             :     {
     378           0 :     default:
     379             :     case state::need_start:
     380             :         // forgot to call start()
     381           0 :         detail::throw_logic_error();
     382             : 
     383        3185 :     case state::headers:
     384             :     {
     385        3185 :         BOOST_ASSERT(h_.buf == ws_.data());
     386        3185 :         BOOST_ASSERT(h_.cbuf == ws_.data());
     387        3185 :         auto const new_size = fb_.size();
     388        3185 :         h_.parse(new_size, svc_.cfg.headers, ec);
     389        3185 :         if(! ec.failed())
     390             :         {
     391         584 :             if( h_.md.payload != payload::none &&
     392           3 :                 ! head_response_)
     393             :             {
     394           3 :                 if(h_.md.payload == payload::size)
     395           2 :                     remain_ = h_.md.payload_size;
     396             : 
     397             :                 // Deliver headers to caller
     398           3 :                 st_ = state::headers_done;
     399           3 :                 break;
     400             :             }
     401             :             // no payload
     402         581 :             st_ = state::complete;
     403         581 :             break;
     404             :         }
     405        2601 :         if(ec == grammar::error::need_more)
     406             :         {
     407        2473 :             if(! got_eof_)
     408        2473 :                 break;
     409           0 :             if(h_.size > 0)
     410             :             {
     411             :                 // Connection closed before
     412             :                 // message is complete.
     413           0 :                 ec = BOOST_HTTP_PROTO_ERR(
     414             :                     error::incomplete);
     415           0 :                 return;
     416             :             }
     417             : 
     418             :             // Connection closed
     419             :             // cleanly.
     420           0 :             ec = BOOST_HTTP_PROTO_ERR(
     421             :                 error::end_of_stream);
     422           0 :             return;
     423             :         }
     424         128 :         BOOST_ASSERT(ec.failed());
     425         128 :         return;
     426             :     }
     427             : 
     428           0 :     case state::headers_done:
     429             :     {
     430             :         // This is a no-op
     431             :         // VFALCO Is this right?
     432           0 :         ec = {};
     433           0 :         break;
     434             :     }
     435             : 
     436           0 :     case state::body:
     437             :     {
     438           0 :         parse_body(ec);
     439           0 :         if(ec.failed())
     440           0 :             return;
     441           0 :         st_ = state::complete;
     442           0 :         break;
     443             :     }
     444             : 
     445           0 :     case state::complete:
     446           0 :         break;
     447             :     }
     448             : }
     449             : 
     450             : //------------------------------------------------
     451             : 
     452             : auto
     453           0 : parser::
     454             : get_stream() ->
     455             :     stream
     456             : {
     457             :     // body type already chosen
     458           0 :     if(body_ != body::in_place)
     459           0 :         detail::throw_logic_error();
     460             : 
     461           0 :     body_ = body::stream;
     462           0 :     return stream(*this);
     463             : }
     464             : 
     465             : //------------------------------------------------
     466             : 
     467             : string_view
     468           0 : parser::
     469             : release_buffered_data() noexcept
     470             : {
     471           0 :     return {};
     472             : }
     473             : 
     474             : //------------------------------------------------
     475             : //
     476             : // Implementation
     477             : //
     478             : //------------------------------------------------
     479             : 
     480             : auto
     481          37 : parser::
     482             : safe_get_header() const ->
     483             :     detail::header const*
     484             : {
     485          37 :     switch(st_)
     486             :     {
     487           0 :     default:
     488             :     case state::need_start:
     489             :     case state::headers:
     490             :         // Headers not received yet
     491           0 :         detail::throw_logic_error();
     492             : 
     493           3 :     case state::headers_done:
     494           3 :         break;
     495             : 
     496           0 :     case state::body:
     497             :         // Headers received and discarded
     498           0 :         detail::throw_logic_error();
     499             : 
     500          34 :     case state::complete:
     501             :         // VFALCO Could be OK
     502          34 :         break;
     503             :     }
     504          37 :     return &h_;
     505             : }
     506             : 
     507             : void
     508           0 : parser::
     509             : on_set_body()
     510             : {
     511           0 : }
     512             : 
     513             : void
     514           0 : parser::
     515             : parse_body(
     516             :     error_code& ec)
     517             : {
     518           0 :     if(body_ == body::in_place)
     519             :     {
     520           0 :         ec = {};
     521           0 :         return;
     522             :     }
     523           0 :     if(body_ == body::dynamic)
     524             :     {
     525           0 :         ec = {};
     526           0 :         return;
     527             :     }
     528           0 :     if(body_ == body::sink)
     529             :     {
     530           0 :         ec = {};
     531           0 :         return;
     532             :     }
     533           0 :     if(body_ == body::stream)
     534             :     {
     535           0 :         ec = {};
     536           0 :         return;
     537             :     }
     538             : }
     539             : 
     540             : void
     541           0 : parser::
     542             : parse_chunk(
     543             :     error_code& ec)
     544             : {
     545           0 :     ec = {};
     546           0 : }
     547             : 
     548             : } // http_proto
     549             : } // boost
     550             : 
     551             : #endif

Generated by: LCOV version 1.15