LCOV - code coverage report
Current view: top level - http_proto/impl - parser.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 102 163 62.6 %
Date: 2023-02-10 23:49:12 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/buffers/buffer_copy.hpp>
      18             : #include <boost/url/grammar/ci_string.hpp>
      19             : #include <boost/assert.hpp>
      20             : #include <boost/none.hpp>
      21             : #include <memory>
      22             : 
      23             : namespace boost {
      24             : namespace http_proto {
      25             : 
      26             : /*
      27             :     Parser design:
      28             : 
      29             :     The usage of the parser is thus:
      30             : 
      31             :     pr.reset(); // prepare for a new stream
      32             : 
      33             :     pr.start(); // prepare for a new message
      34             :     pr.start_head_response(); // new message with no payload
      35             : 
      36             :     read_header( ..., pr );
      37             :         do
      38             :         {
      39             :             read_some(..., pr );
      40             :         }
      41             :         while(! got_header());
      42             : 
      43             :     pr.set_body( ... );
      44             :         // invalidates the headers? yes.
      45             : 
      46             :     read_body( ..., pr );
      47             :         while(! (pr.flags() &
      48             :             parser::is_done_bit ) );
      49             :         {
      50             :             read_some(..., pr );
      51             :         }
      52             : 
      53             :     If these are called out of order, an
      54             :     exception is thrown.
      55             : 
      56             :     Every call to `prepare` must be
      57             :     followed by a call to commit, reset,
      58             :     or the destructor.
      59             : */
      60             : //------------------------------------------------
      61             : 
      62         745 : parser::
      63             : parser(
      64             :     detail::kind k,
      65         745 :     config_base const& cfg)
      66             :     : cfg_(cfg)
      67         745 :     , h_(detail::empty{k})
      68             : {
      69         745 : }
      70             : 
      71             : void
      72         745 : parser::
      73             : construct(
      74             :     std::size_t extra_buffer_size)
      75             : {
      76             :     // headers_limit too large
      77         745 :     if( cfg_.headers_limit >
      78             :         BOOST_HTTP_PROTO_MAX_HEADER)
      79           0 :         detail::throw_invalid_argument();
      80             : 
      81             :     // start_line_limit too large
      82         745 :     if( cfg_.start_line_limit >=
      83         745 :         cfg_.headers_limit)
      84           0 :         detail::throw_invalid_argument();
      85             : 
      86             :     // field_size_limit too large
      87         745 :     if( cfg_.field_size_limit >=
      88         745 :         cfg_.headers_limit)
      89           0 :         detail::throw_invalid_argument();
      90             : 
      91             :     // fields_limit too large
      92         745 :     if( cfg_.fields_limit >
      93         745 :         cfg_.headers_limit / 4)
      94           0 :         detail::throw_invalid_argument();
      95             : 
      96             :     // largest space needed
      97             :     auto const bytes_needed =
      98         745 :         detail::header::bytes_needed(
      99             :             cfg_.headers_limit,
     100             :             cfg_.fields_limit);
     101             : 
     102             :     // prevent overflow
     103         745 :     if(extra_buffer_size >
     104         745 :             std::size_t(-1) - bytes_needed)
     105           0 :         detail::throw_invalid_argument();
     106             : 
     107             :     // allocate max headers plus extra
     108         745 :     ws_.allocate(
     109             :         bytes_needed +
     110             :         extra_buffer_size);
     111             : 
     112         745 :     h_.cap = bytes_needed;
     113             : 
     114         745 :     reset();
     115         745 : }
     116             : 
     117             : //------------------------------------------------
     118             : //
     119             : // Special Members
     120             : //
     121             : //------------------------------------------------
     122             : 
     123         745 : parser::
     124        2980 : ~parser()
     125             : {
     126         745 : }
     127             : 
     128             : parser::
     129             : parser(
     130             :     parser&&) noexcept = default;
     131             : 
     132             : //------------------------------------------------
     133             : //
     134             : // Observers
     135             : //
     136             : //------------------------------------------------
     137             : 
     138             : string_view
     139           0 : parser::
     140             : body() const noexcept
     141             : {
     142             : #if 0
     143             :     // VFALCO How about some
     144             :     // asserts or exceptions?
     145             :     if(! m_.got_chunked)
     146             :         return string_view(
     147             :             h_.buf + h_.size,
     148             :             m_.n_payload);
     149             :     return string_view(
     150             :         h_.buf +
     151             :             h_.size +
     152             :             m_.n_chunk,
     153             :         m_.n_payload);
     154             : #else
     155           0 :     return {};
     156             : #endif
     157             : }
     158             : 
     159             : //------------------------------------------------
     160             : //
     161             : // Modifiers
     162             : //
     163             : //------------------------------------------------
     164             : 
     165             : // prepare for a new stream
     166             : void
     167         745 : parser::
     168             : reset() noexcept
     169             : {
     170         745 :     st_ = state::need_start;
     171         745 :     got_eof_ = false;
     172         745 : }
     173             : 
     174             : void
     175         770 : parser::
     176             : start_impl(
     177             :     bool head_response)
     178             : {
     179         770 :     std::size_t initial_size = 0;
     180         770 :     switch(st_)
     181             :     {
     182         737 :     default:
     183             :     case state::need_start:
     184         737 :         BOOST_ASSERT(h_.size == 0);
     185         737 :         BOOST_ASSERT(h_buf_.size() == 0);
     186         737 :         BOOST_ASSERT(! got_eof_);
     187         737 :         break;
     188             : 
     189           0 :     case state::headers:
     190             :         // Can't call start twice.
     191           0 :         detail::throw_logic_error();
     192             : 
     193           0 :     case state::headers_done:
     194             :     case state::body:
     195             :         // Can't call start with
     196             :         // an incomplete message.
     197           0 :         detail::throw_logic_error();
     198             : 
     199          33 :     case state::complete:
     200          33 :         if(h_buf_.size() > 0)
     201             :         {
     202             :             // headers with no body
     203          33 :             BOOST_ASSERT(h_.size > 0);
     204          33 :             h_buf_.consume(h_.size);
     205          33 :             initial_size = h_buf_.size();
     206             :             // move unused octets to front
     207          33 :             buffers::buffer_copy(
     208          33 :                 buffers::mutable_buffer(
     209             :                     ws_.data(),
     210             :                     initial_size),
     211          66 :                 h_buf_.data());
     212             :         }
     213             :         else
     214             :         {
     215             :             // leftover data after body
     216             :         }
     217          33 :         break;
     218             :     }
     219             : 
     220         770 :     ws_.clear();
     221             : 
     222             :     // set up header read buffer
     223         770 :     h_buf_ = {
     224             :         ws_.data(),
     225             :         cfg_.headers_limit,
     226             :         initial_size };
     227             : 
     228             :     // reset the header but
     229             :     // preserve the capacity
     230         770 :     auto const cap = h_.cap;
     231        1540 :     h_ = detail::header(
     232         770 :         detail::empty{h_.kind});
     233         770 :     h_.buf = reinterpret_cast<
     234         770 :         char*>(ws_.data());
     235         770 :     h_.cbuf = h_.buf;
     236         770 :     h_.cap = cap;
     237             : 
     238         770 :     cfg_impl_ = {};
     239         770 :     cfg_impl_.headers_limit = cfg_.headers_limit;
     240         770 :     cfg_impl_.start_line_limit = cfg_.start_line_limit;
     241         770 :     cfg_impl_.field_size_limit = cfg_.field_size_limit;
     242         770 :     cfg_impl_.fields_limit = cfg_.fields_limit;
     243             : 
     244         770 :     st_ = state::headers;
     245             : 
     246         770 :     BOOST_ASSERT(! head_response ||
     247             :         h_.kind == detail::kind::response);
     248         770 :     head_response_ = head_response;
     249         770 : }
     250             : 
     251             : auto
     252        3185 : parser::
     253             : prepare() ->
     254             :     mutable_buffers_type
     255             : {
     256        3185 :     switch(st_)
     257             :     {
     258           0 :     default:
     259             :     case state::need_start:
     260             :         // start must be called once
     261             :         // before calling prepare.
     262           0 :         detail::throw_logic_error();
     263             : 
     264        3185 :     case state::headers:
     265             :         // fill up to headers_limit
     266             :         return {
     267        3185 :             h_buf_.prepare(
     268        3185 :                 cfg_.headers_limit -
     269        3185 :                 h_buf_.size()),
     270        3185 :             buffers::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