LCOV - code coverage report
Current view: top level - http_proto/impl - parser.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 101 163 62.0 %
Date: 2023-02-02 18:17:21 Functions: 10 17 58.8 %

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3             : //
       4             : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5             : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // Official repository: https://github.com/CPPAlliance/http_proto
       8             : //
       9             : 
      10             : #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      11             : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
      12             : 
      13             : #include <boost/http_proto/error.hpp>
      14             : #include <boost/http_proto/parser.hpp>
      15             : #include <boost/http_proto/detail/codec.hpp>
      16             : #include <boost/http_proto/detail/except.hpp>
      17             : #include <boost/url/grammar/ci_string.hpp>
      18             : #include <boost/assert.hpp>
      19             : #include <boost/none.hpp>
      20             : #include <memory>
      21             : 
      22             : namespace boost {
      23             : namespace http_proto {
      24             : 
      25             : /*
      26             :     Parser design:
      27             : 
      28             :     The usage of the parser is thus:
      29             : 
      30             :     pr.reset(); // prepare for a new stream
      31             : 
      32             :     pr.start(); // prepare for a new message
      33             :     pr.start_head_response(); // new message with no payload
      34             : 
      35             :     read_header( ..., pr );
      36             :         do
      37             :         {
      38             :             read_some(..., pr );
      39             :         }
      40             :         while(! got_header());
      41             : 
      42             :     pr.set_body( ... );
      43             :         // invalidates the headers? yes.
      44             : 
      45             :     read_body( ..., pr );
      46             :         while(! (pr.flags() &
      47             :             parser::is_done_bit ) );
      48             :         {
      49             :             read_some(..., pr );
      50             :         }
      51             : 
      52             :     If these are called out of order, an
      53             :     exception is thrown.
      54             : 
      55             :     Every call to `prepare` must be
      56             :     followed by a call to commit, reset,
      57             :     or the destructor.
      58             : */
      59             : //------------------------------------------------
      60             : 
      61         745 : parser::
      62             : parser(
      63             :     detail::kind k,
      64         745 :     config_base const& cfg)
      65             :     : cfg_(cfg)
      66         745 :     , h_(detail::empty{k})
      67             : {
      68         745 : }
      69             : 
      70             : void
      71         745 : parser::
      72             : construct(
      73             :     std::size_t extra_buffer_size)
      74             : {
      75             :     // headers_limit too large
      76         745 :     if( cfg_.headers_limit >
      77             :         BOOST_HTTP_PROTO_MAX_HEADER)
      78           0 :         detail::throw_invalid_argument();
      79             : 
      80             :     // start_line_limit too large
      81         745 :     if( cfg_.start_line_limit >=
      82         745 :         cfg_.headers_limit)
      83           0 :         detail::throw_invalid_argument();
      84             : 
      85             :     // field_size_limit too large
      86         745 :     if( cfg_.field_size_limit >=
      87         745 :         cfg_.headers_limit)
      88           0 :         detail::throw_invalid_argument();
      89             : 
      90             :     // fields_limit too large
      91         745 :     if( cfg_.fields_limit >
      92         745 :         cfg_.headers_limit / 4)
      93           0 :         detail::throw_invalid_argument();
      94             : 
      95             :     // largest space needed
      96             :     auto const bytes_needed =
      97         745 :         detail::header::bytes_needed(
      98             :             cfg_.headers_limit,
      99             :             cfg_.fields_limit);
     100             : 
     101             :     // prevent overflow
     102         745 :     if(extra_buffer_size >
     103         745 :             std::size_t(-1) - bytes_needed)
     104           0 :         detail::throw_invalid_argument();
     105             : 
     106             :     // allocate max headers plus extra
     107        1490 :     ws_ = detail::workspace(
     108             :         bytes_needed +
     109         745 :         extra_buffer_size);
     110             : 
     111         745 :     h_.cap = bytes_needed;
     112             : 
     113         745 :     reset();
     114         745 : }
     115             : 
     116             : //------------------------------------------------
     117             : //
     118             : // Special Members
     119             : //
     120             : //------------------------------------------------
     121             : 
     122         745 : parser::
     123        2980 : ~parser()
     124             : {
     125         745 : }
     126             : 
     127             : parser::
     128             : parser(
     129             :     parser&&) noexcept = default;
     130             : 
     131             : //------------------------------------------------
     132             : //
     133             : // Observers
     134             : //
     135             : //------------------------------------------------
     136             : 
     137             : string_view
     138           0 : parser::
     139             : body() const noexcept
     140             : {
     141             : #if 0
     142             :     // VFALCO How about some
     143             :     // asserts or exceptions?
     144             :     if(! m_.got_chunked)
     145             :         return string_view(
     146             :             h_.buf + h_.size,
     147             :             m_.n_payload);
     148             :     return string_view(
     149             :         h_.buf +
     150             :             h_.size +
     151             :             m_.n_chunk,
     152             :         m_.n_payload);
     153             : #else
     154           0 :     return {};
     155             : #endif
     156             : }
     157             : 
     158             : //------------------------------------------------
     159             : //
     160             : // Modifiers
     161             : //
     162             : //------------------------------------------------
     163             : 
     164             : // prepare for a new stream
     165             : void
     166         745 : parser::
     167             : reset() noexcept
     168             : {
     169         745 :     st_ = state::need_start;
     170         745 :     got_eof_ = false;
     171         745 : }
     172             : 
     173             : void
     174         770 : parser::
     175             : start_impl(
     176             :     bool head_response)
     177             : {
     178         770 :     std::size_t initial_size = 0;
     179         770 :     switch(st_)
     180             :     {
     181         737 :     default:
     182             :     case state::need_start:
     183         737 :         BOOST_ASSERT(h_.size == 0);
     184         737 :         BOOST_ASSERT(h_buf_.size() == 0);
     185         737 :         BOOST_ASSERT(! got_eof_);
     186         737 :         break;
     187             : 
     188           0 :     case state::headers:
     189             :         // Can't call start twice.
     190           0 :         detail::throw_logic_error();
     191             : 
     192           0 :     case state::headers_done:
     193             :     case state::body:
     194             :         // Can't call start with
     195             :         // an incomplete message.
     196           0 :         detail::throw_logic_error();
     197             : 
     198          33 :     case state::complete:
     199          33 :         if(h_buf_.size() > 0)
     200             :         {
     201             :             // headers with no body
     202          33 :             BOOST_ASSERT(h_.size > 0);
     203          33 :             h_buf_.consume(h_.size);
     204          33 :             initial_size = h_buf_.size();
     205             :             // move unused octets to front
     206          33 :             buffer_copy(
     207          33 :                 mutable_buffer(
     208             :                     ws_.data(),
     209             :                     initial_size),
     210          66 :                 h_buf_.data());
     211             :         }
     212             :         else
     213             :         {
     214             :             // leftover data after body
     215             :         }
     216          33 :         break;
     217             :     }
     218             : 
     219         770 :     ws_.clear();
     220             : 
     221             :     // set up header read buffer
     222         770 :     h_buf_ = {
     223             :         ws_.data(),
     224             :         cfg_.headers_limit,
     225             :         initial_size };
     226             : 
     227             :     // reset the header but
     228             :     // preserve the capacity
     229         770 :     auto const cap = h_.cap;
     230        1540 :     h_ = detail::header(
     231         770 :         detail::empty{h_.kind});
     232         770 :     h_.buf = reinterpret_cast<
     233         770 :         char*>(ws_.data());
     234         770 :     h_.cbuf = h_.buf;
     235         770 :     h_.cap = cap;
     236             : 
     237         770 :     cfg_impl_ = {};
     238         770 :     cfg_impl_.headers_limit = cfg_.headers_limit;
     239         770 :     cfg_impl_.start_line_limit = cfg_.start_line_limit;
     240         770 :     cfg_impl_.field_size_limit = cfg_.field_size_limit;
     241         770 :     cfg_impl_.fields_limit = cfg_.fields_limit;
     242             : 
     243         770 :     st_ = state::headers;
     244             : 
     245         770 :     BOOST_ASSERT(! head_response ||
     246             :         h_.kind == detail::kind::response);
     247         770 :     head_response_ = head_response;
     248         770 : }
     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             :         // start must be called once
     260             :         // before calling prepare.
     261           0 :         if(st_ == state::need_start)
     262           0 :             detail::throw_logic_error();
     263             : 
     264             :     case state::headers:
     265             :         // fill up to headers_limit
     266             :         return {
     267             :             h_buf_.prepare(
     268        3185 :                 cfg_.headers_limit -
     269        3185 :                 h_buf_.size()),
     270        3185 :             mutable_buffer{} };
     271             : 
     272           0 :     case state::headers_done:
     273             :     {
     274             :         // discard headers and move
     275             :         // any leftover stream data.
     276           0 :         std::memmove(
     277             :             ws_.data(),
     278           0 :             h_.cbuf + h_.size,
     279           0 :             h_buf_.size() - h_.size);
     280           0 :         st_ = state::body;
     281             :         // VFALCO set up body buffer
     282             :         BOOST_FALLTHROUGH;
     283             :     }
     284             : 
     285           0 :     case state::body:
     286             :     {
     287           0 :         return {};
     288             :     }
     289             : 
     290           0 :     case state::complete:
     291             :         // Can't call `prepare` again after
     292             :         // a complete message is parsed,
     293             :         // call `start` first.
     294           0 :         detail::throw_logic_error();
     295             :     }
     296             : }
     297             : 
     298             : void
     299        3185 : parser::
     300             : commit(
     301             :     std::size_t n)
     302             : {
     303             :     // Can't commit after eof
     304        3185 :     if(got_eof_)
     305           0 :         detail::throw_logic_error();
     306             : 
     307        3185 :     switch(st_)
     308             :     {
     309        3185 :     default:
     310             :     case state::need_start:
     311             :     case state::headers:
     312        3185 :         h_buf_.commit(n);
     313        3185 :         break;
     314             : 
     315           0 :     case state::headers_done:
     316             :     case state::body:
     317             :     case state::complete:
     318           0 :         break;
     319             :     }
     320        3185 : }
     321             : 
     322             : void
     323           0 : parser::
     324             : commit_eof()
     325             : {
     326           0 :     switch(st_)
     327             :     {
     328           0 :     default:
     329             :     case state::need_start:
     330             :         // Can't commit eof
     331             :         // before calling start.
     332           0 :         detail::throw_logic_error();
     333             : 
     334           0 :     case state::headers:
     335             :     case state::headers_done:
     336             :     case state::body:
     337           0 :         got_eof_ = true;
     338           0 :         break;
     339             : 
     340           0 :     case state::complete:
     341             :         // Can't commit eof when
     342             :         // message is complete.
     343           0 :         detail::throw_logic_error();
     344             :     }
     345           0 : }
     346             : 
     347             : // process input data then
     348             : // eof if input data runs out.
     349             : void
     350        3185 : parser::
     351             : parse(
     352             :     error_code& ec)
     353             : {
     354        3185 :     switch(st_)
     355             :     {
     356           0 :     default:
     357             :     case state::need_start:
     358             :         // You must call start before
     359             :         // calling parse on a new message.
     360           0 :         detail::throw_logic_error();
     361             : 
     362        3185 :     case state::headers:
     363             :     {
     364        3185 :         BOOST_ASSERT(h_.buf == ws_.data());
     365        3185 :         BOOST_ASSERT(h_.cbuf == ws_.data());
     366        3185 :         auto const new_size = h_buf_.size();
     367        3185 :         h_.parse(cfg_impl_, new_size, ec);
     368        3185 :         if(! ec.failed())
     369             :         {
     370         584 :             if( h_.md.payload != payload::none &&
     371           3 :                 ! head_response_)
     372             :             {
     373             :                 // Deliver headers to caller
     374           3 :                 st_ = state::headers_done;
     375           3 :                 break;
     376             :             }
     377             :             // no payload
     378         581 :             st_ = state::complete;
     379         581 :             break;
     380             :         }
     381        5074 :         if( ec == grammar::error::need_more &&
     382        2473 :             got_eof_)
     383             :         {
     384           0 :             if(h_.size > 0)
     385             :             {
     386             :                 // Connection closed before
     387             :                 // message is complete.
     388           0 :                 ec = BOOST_HTTP_PROTO_ERR(
     389             :                     error::incomplete);
     390             : 
     391           0 :                 return;
     392             :             }
     393             : 
     394             :             // Connection closed
     395             :             // cleanly.
     396           0 :             ec = BOOST_HTTP_PROTO_ERR(
     397             :                 error::end_of_stream);
     398             : 
     399           0 :             return;
     400             :         }
     401        2601 :         return;
     402             :     }
     403             : 
     404           0 :     case state::headers_done:
     405             :     {
     406             :         // This is a no-op
     407           0 :         ec = {};
     408           0 :         break;
     409             :     }
     410             : 
     411           0 :     case state::body:
     412             :     {
     413           0 :         parse_body(ec);
     414           0 :         if(ec.failed())
     415           0 :             return;
     416           0 :         st_ = state::complete;
     417           0 :         break;
     418             :     }
     419             :     }
     420             : }
     421             : 
     422             : //------------------------------------------------
     423             : 
     424             : string_view
     425           0 : parser::
     426             : release_buffered_data() noexcept
     427             : {
     428           0 :     return {};
     429             : }
     430             : 
     431             : //------------------------------------------------
     432             : //
     433             : // Implementation
     434             : //
     435             : //------------------------------------------------
     436             : 
     437             : void
     438           2 : parser::
     439             : apply_param(
     440             :     config_base const& cfg) noexcept
     441             : {
     442           2 :     cfg_ = cfg;
     443           2 : }
     444             : 
     445             : //------------------------------------------------
     446             : 
     447             : auto
     448          37 : parser::
     449             : safe_get_header() const ->
     450             :     detail::header const*
     451             : {
     452          37 :     switch(st_)
     453             :     {
     454           0 :     default:
     455             :     case state::need_start:
     456             :     case state::headers:
     457             :         // Headers not received yet
     458           0 :         detail::throw_logic_error();
     459             : 
     460           3 :     case state::headers_done:
     461           3 :         break;
     462             : 
     463           0 :     case state::body:
     464             :         // Headers received and discarded
     465           0 :         detail::throw_logic_error();
     466             : 
     467          34 :     case state::complete:
     468             :         // VFALCO Could be OK
     469          34 :         break;
     470             :     }
     471          37 :     return &h_;
     472             : }
     473             : 
     474             : void
     475           0 : parser::
     476             : parse_body(
     477             :     error_code& ec)
     478             : {
     479             :     (void)ec;
     480           0 : return;
     481             : // VFALCO TODO
     482             : #if 0
     483             :     BOOST_ASSERT(st_ == state::body);
     484             : 
     485             :     if(h_.kind == detail::kind::request)
     486             :     {
     487             :         // https://tools.ietf.org/html/rfc7230#section-3.3
     488             :         if(m_.skip_body)
     489             :             return;
     490             :         if(m_.content_len.has_value())
     491             :         {
     492             :             if(*m_.content_len > cfg_.body_too_large)
     493             :             {
     494             :                 ec = error::body_too_large;
     495             :                 return;
     496             :             }
     497             :             if(*m_.content_len == 0)
     498             :                 return;
     499             :         }
     500             :         else if(m_.got_chunked)
     501             :         {
     502             :             // VFALCO TODO
     503             :             return;
     504             :         }
     505             :         else
     506             :         {
     507             :             // Content-Length: 0
     508             :             return;
     509             :         }
     510             :     }
     511             :     else
     512             :     {
     513             :         BOOST_ASSERT(h_.kind ==
     514             :             detail::kind::response);
     515             : 
     516             :         // https://tools.ietf.org/html/rfc7230#section-3.3
     517             :         if((h_.res.status_int /  100 == 1) || // 1xx e.g. Continue
     518             :             h_.res.status_int == 204 ||       // No Content
     519             :             h_.res.status_int == 304)         // Not Modified
     520             :         {
     521             :             // Content-Length may be present, but we
     522             :             // treat the message as not having a body.
     523             :         }
     524             :         else if(m_.content_len.has_value())
     525             :         {
     526             :             if(*m_.content_len > 0)
     527             :             {
     528             :                 if(*m_.content_len > cfg_.body_too_large)
     529             :                 {
     530             :                     ec = error::body_too_large;
     531             :                     return;
     532             :                 }
     533             :             }
     534             :         }
     535             :         else
     536             :         {
     537             :             // No Content-Length
     538             :             return;
     539             :         }
     540             :     }
     541             : 
     542             :     auto avail = committed_ - size_;
     543             :     if(m_.content_len.has_value())
     544             :     {
     545             :         // known payload length
     546             :         BOOST_ASSERT(! m_.got_chunked);
     547             :         BOOST_ASSERT(m_.n_remain > 0);
     548             :         BOOST_ASSERT(m_.content_len <
     549             :             cfg_.body_too_large);
     550             :         if(avail == 0)
     551             :         {
     552             :             if(! got_eof_)
     553             :             {
     554             :                 ec = grammar::error::need_more;
     555             :                 return;
     556             :             }
     557             :             ec = error::need_more;
     558             :             return;
     559             :         }
     560             :         if( avail > m_.n_remain)
     561             :             avail = static_cast<
     562             :                 std::size_t>(m_.n_remain);
     563             :         size_ += avail;
     564             :         m_.payload_seen += avail;
     565             :         m_.n_payload += avail;
     566             :         m_.n_remain -= avail;
     567             :         if(m_.n_remain > 0)
     568             :         {
     569             :             ec = {};
     570             :             return;
     571             :         }
     572             :         st_ = state::complete;
     573             :         ec = error::end_of_message;
     574             :         return;
     575             :     }
     576             : 
     577             :     if(! m_.got_chunked)
     578             :     {
     579             :         // end of body indicated by EOF
     580             :         if(avail > 0)
     581             :         {
     582             :             if(avail > (std::size_t(
     583             :                 -1) - m_.n_payload))
     584             :             {
     585             :                 // overflow size_t
     586             :                 // VFALCO revisit this
     587             :                 ec = error::numeric_overflow;
     588             :                 return;
     589             :             }
     590             :             size_ += avail;
     591             :             m_.n_payload += avail;
     592             :             ec = {};
     593             :             return;
     594             :         }
     595             :         if(! got_eof_)
     596             :         {
     597             :             ec = grammar::error::need_more;
     598             :             return;
     599             :         }
     600             :         st_ = state::complete;
     601             :         ec = error::end_of_message;
     602             :         return;
     603             :     }
     604             : #if 0
     605             :     if(m_.payload_left == 0)
     606             :     {
     607             :         // start of chunk
     608             :         bnf::chunk_part p;
     609             :         auto it = p.parse(
     610             :             h_.buf + size_,
     611             :             h_.buf + (
     612             :                 committed_ - size_),
     613             :             ec);
     614             :         if(ec)
     615             :             return;
     616             :         auto const v =
     617             :             p.value();
     618             :         m_.chunk.size = v.size;
     619             :         m_.chunk.ext = v.ext;
     620             :         m_.chunk.trailer = v.trailer;
     621             :         m_.chunk.fresh = true;
     622             :         m_.payload_left =
     623             :             v.size - v.data.size();
     624             :     }
     625             :     else
     626             :     {
     627             :         // continuation of chunk
     628             : 
     629             :     }
     630             : #endif
     631             : #endif
     632             : }
     633             : 
     634             : void
     635           0 : parser::
     636             : parse_chunk(
     637             :     error_code& ec)
     638             : {
     639             :     (void)ec;
     640             : #if 0
     641             :     switch(st_)
     642             :     {
     643             :     case state::start_line_line:
     644             :     case state::header_fields:
     645             :         parse_header(ec);
     646             :         if(ec.failed())
     647             :             return;
     648             :         BOOST_ASSERT(st_ >
     649             :             state::header_fields);
     650             :         break;
     651             :     case state::body:
     652             :         if(! m_.got_chunked)
     653             :             return parse_body(ec);
     654             :         break;
     655             :     case state::complete:
     656             :         ec = error::end_of_message;
     657             :         if(! got_eof_)
     658             :             return;
     659             :         st_ = state::end_of_stream;
     660             :         return;
     661             :     case state::end_of_stream:
     662             :         ec = error::end_of_stream;
     663             :         return;
     664             :     }
     665             : 
     666             :     auto const avail = committed_ - size_;
     667             :     auto const start = h_.buf + size_;
     668             :     if(m_.payload_left == 0)
     669             :     {
     670             :         // start of chunk
     671             :         // VFALCO What about chunk_part_next?
     672             :         BOOST_ASSERT(
     673             :             size_ == m_.header_size);
     674             :         bnf::chunk_part p;
     675             :         auto it = p.parse(start,
     676             :             h_.buf + (
     677             :                 committed_ - size_), ec);
     678             :         BOOST_ASSERT(it == start);
     679             :         if(ec)
     680             :             return;
     681             :         auto const v = p.value();
     682             :         m_.chunk.size = v.size;
     683             :         m_.chunk.ext = v.ext;
     684             :         m_.chunk.fresh = true;
     685             :         if(v.size > 0)
     686             :         {
     687             :             // chunk
     688             :             m_.chunk.trailer = {};
     689             :             m_.payload_left =
     690             :                 v.size - v.data.size();
     691             :             size_ += it - start; // excludes CRLF
     692             :             return;
     693             :         }
     694             :         // last-chunk
     695             :         BOOST_ASSERT(
     696             :             v.data.empty());
     697             :         m_.chunk.trailer =
     698             :             v.trailer;
     699             :         m_.body = {};
     700             :         size_ += it - start; // excludes CRLF
     701             :         st_ = state::complete;
     702             :     }
     703             :     else
     704             :     {
     705             :         // continuation of chunk
     706             : 
     707             :     }
     708             : #endif
     709           0 : }
     710             : 
     711             : } // http_proto
     712             : } // boost
     713             : 
     714             : #endif

Generated by: LCOV version 1.15