LCOV - code coverage report
Current view: top level - http_proto/impl - parser.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 105 171 61.4 %
Date: 2023-01-15 07:18:31 Functions: 11 20 55.0 %

          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/url/grammar/ci_string.hpp>
      16             : #include <boost/assert.hpp>
      17             : #include <boost/none.hpp>
      18             : #include <memory>
      19             : 
      20             : namespace boost {
      21             : namespace http_proto {
      22             : 
      23             : //------------------------------------------------
      24             : 
      25         707 : parser::
      26             : parser(
      27             :     detail::kind k,
      28             :     config const& cfg,
      29         707 :     std::size_t buffer_bytes)
      30             :     : cfg_(cfg)
      31             :     , h_(detail::empty{k})
      32             :     , committed_(0)
      33             :     , state_(state::empty)
      34             :     , got_eof_(false)
      35         707 :     , m_{}
      36             : {
      37             :     // buffer must be large enough to
      38             :     // hold a complete header.
      39         707 :     if( buffer_bytes <
      40         707 :         cfg.max_header_size)
      41         707 :         buffer_bytes =
      42             :             cfg.max_header_size;
      43             : 
      44         707 :     h_.buf = new char[buffer_bytes];
      45         707 :     h_.cbuf = h_.buf;
      46         707 :     h_.cap = buffer_bytes;
      47         707 :     h_.size = 0;
      48         707 :     h_.prefix = 0;
      49         707 : }
      50             : 
      51             : //------------------------------------------------
      52             : //
      53             : // Special Members
      54             : //
      55             : //------------------------------------------------
      56             : 
      57         707 : parser::
      58         707 : ~parser()
      59             : {
      60         707 :     delete[] h_.buf;
      61         707 : }
      62             : 
      63             : //------------------------------------------------
      64             : //
      65             : // Observers
      66             : //
      67             : //------------------------------------------------
      68             : 
      69             : string_view
      70           0 : parser::
      71             : body() const noexcept
      72             : {
      73             :     // VFALCO How about some
      74             :     // asserts or exceptions?
      75           0 :     if(! m_.got_chunked)
      76           0 :         return string_view(
      77           0 :             h_.buf + h_.size,
      78           0 :             m_.n_payload);
      79           0 :     return string_view(
      80           0 :         h_.buf +
      81           0 :             h_.size +
      82           0 :             m_.n_chunk,
      83           0 :         m_.n_payload);
      84             : }
      85             : 
      86             : //------------------------------------------------
      87             : //
      88             : // Input
      89             : //
      90             : //------------------------------------------------
      91             : 
      92             : void
      93           0 : parser::
      94             : clear() noexcept
      95             : {
      96           0 :     reset();
      97           0 : }
      98             : 
      99             : void
     100           0 : parser::
     101             : reset()
     102             : {
     103           0 :     if(got_eof_)
     104             :     {
     105             :         // new connection, throw
     106             :         // out all previous state.
     107           0 :         committed_ = 0;
     108           0 :         got_eof_ = false;
     109             :     }
     110             :     else
     111             :     {
     112             :         // Throwing out partial data
     113             :         // will desync the HTTP stream
     114             :         //BOOST_ASSERT(is_complete());
     115           0 :         if( committed_ > h_.size &&
     116           0 :             h_.size > 0)
     117             :         {
     118             :             // move unused octets to front
     119           0 :             std::memcpy(
     120           0 :                 h_.buf,
     121           0 :                 h_.buf + h_.size,
     122           0 :                 committed_ - h_.size);
     123           0 :             committed_ -= h_.size;
     124           0 :             h_.size = 0;
     125             :         }
     126             :         else
     127             :         {
     128           0 :             committed_ = 0;
     129             :         }
     130             :     }
     131             : 
     132             :     // reset the header but
     133             :     // preserve the capacity
     134             :     detail::header h(
     135           0 :         detail::empty{h_.kind});
     136           0 :     h.assign_to(h_);
     137             : 
     138           0 :     m_ = message{};
     139           0 :     state_ = state::empty;
     140           0 : }
     141             : 
     142             : mutable_buffers
     143        2969 : parser::
     144             : prepare()
     145             : {
     146             :     // Can't commit bytes after eof
     147        2969 :     BOOST_ASSERT(! got_eof_);
     148             : 
     149        5938 :     mb_ = {
     150        2969 :         h_.buf + committed_,
     151        2969 :         h_.cap - committed_ };
     152        2969 :     return { &mb_, 1 };
     153             : }
     154             : 
     155             : void
     156        2969 : parser::
     157             : commit(
     158             :     std::size_t n,
     159             :     error_code& ec)
     160             : {
     161        2969 :     BOOST_ASSERT(! got_eof_);
     162        2969 :     BOOST_ASSERT(
     163             :         n <= h_.cap - committed_);
     164        2969 :     committed_ += n;
     165        2969 :     parse(ec);
     166        2969 : }
     167             : 
     168             : void
     169           0 : parser::
     170             : commit_eof(
     171             :     error_code &ec)
     172             : {
     173           0 :     got_eof_ = true;
     174           0 :     parse(ec);
     175           0 : }
     176             : 
     177             : //------------------------------------------------
     178             : //
     179             : //
     180             : //
     181             : //------------------------------------------------
     182             : 
     183             : void
     184           0 : parser::
     185             : discard_header() noexcept
     186             : {
     187           0 : }
     188             : 
     189             : // discard all of the body,
     190             : // but don't discard chunk-ext
     191             : void
     192           0 : parser::
     193             : discard_body() noexcept
     194             : {
     195           0 : }
     196             : 
     197             : // discard all of the payload
     198             : // including any chunk-ext
     199             : void
     200           0 : parser::
     201             : discard_chunk() noexcept
     202             : {
     203           0 : }
     204             : 
     205             : //------------------------------------------------
     206             : //
     207             : // Implementation
     208             : //
     209             : //------------------------------------------------
     210             : 
     211             : void
     212        2969 : parser::
     213             : parse(
     214             :     error_code& ec)
     215             : {
     216        2969 :     ec.clear();
     217        3548 :     while(state_ != state::complete)
     218             :     {
     219        2969 :         switch(state_)
     220             :         {
     221         707 :         case state::empty:
     222         707 :             if(got_eof_)
     223             :             {
     224           0 :                 BOOST_ASSERT(h_.size == 0);
     225           0 :                 ec = error::end_of_stream;
     226           0 :                 return;
     227             :             }
     228         707 :             state_ = state::start_line;
     229             :             BOOST_FALLTHROUGH;
     230             : 
     231        1922 :         case state::start_line:
     232        1922 :             parse_start_line(ec);
     233        1922 :             if(ec.failed())
     234        1215 :                 return;
     235         707 :             state_ = state::header_fields;
     236             :             BOOST_FALLTHROUGH;
     237             : 
     238        1754 :         case state::header_fields:
     239        1754 :             parse_fields(ec);
     240        1754 :             if(ec.failed())
     241        1175 :                 return;
     242         579 :             state_ = state::body;
     243             :             BOOST_FALLTHROUGH;
     244             : 
     245         579 :         case state::body:
     246         579 :             parse_body(ec);
     247         579 :             if(ec.failed())
     248           0 :                 return;
     249         579 :             state_ = state::complete;
     250             :             BOOST_FALLTHROUGH;
     251             : 
     252         579 :         default:
     253         579 :             break;
     254             :         }
     255             :     }
     256             : }
     257             : 
     258             : void
     259        1922 : parser::
     260             : parse_start_line(
     261             :     error_code& ec)
     262             : {
     263        1922 :     BOOST_ASSERT(h_.size == 0);
     264        1922 :     std::size_t const new_size =
     265        1922 :         committed_ <= cfg_.max_header_size
     266        1922 :         ? committed_
     267             :         : cfg_.max_header_size;
     268             :     string_view s(
     269        1922 :         this->h_.buf, new_size);
     270             :     auto rv =
     271        1922 :         detail::parse_start_line(h_, s);
     272        1922 :     if(rv == grammar::error::need_more)
     273             :     {
     274        1215 :         if( new_size <
     275        1215 :             cfg_.max_header_size)
     276             :         {
     277        1215 :             ec = rv.error();
     278        1215 :             return;
     279             :         }
     280           0 :         ec = error::header_too_large;
     281           0 :         return;
     282             :     }
     283         707 :     if(! rv)
     284             :     {
     285           0 :         ec = rv.error();
     286           0 :         return;
     287             :     }
     288         707 :     ec = {};
     289             : }
     290             : 
     291             : void
     292        1754 : parser::
     293             : parse_fields(
     294             :     error_code& ec)
     295             : {
     296        1754 :     std::size_t const new_size =
     297        1754 :         committed_ <= cfg_.max_header_size
     298        1754 :         ? committed_
     299             :         : cfg_.max_header_size;
     300             :     field id;
     301        1754 :     string_view v;
     302             :     for(;;)
     303             :     {
     304        2351 :         auto const size0 = h_.size;
     305        2351 :         auto got_one = detail::parse_field(
     306        2351 :             h_, new_size, id, v, ec);
     307        2351 :         if(ec == grammar::error::need_more)
     308             :         {
     309        1047 :             if(new_size >=
     310        1047 :                     cfg_.max_header_size)
     311           0 :                 ec = error::header_too_large;
     312             :         }
     313        2351 :         if(! got_one)
     314        1754 :             return;
     315         597 :         if(h_.count > cfg_.max_field_count)
     316             :         {
     317           0 :             ec = error::too_many_fields;
     318           0 :             return;
     319             :         }
     320         597 :         if(h_.size >
     321         597 :             cfg_.max_field_size + size0)
     322             :         {
     323           0 :             ec = error::field_too_large;
     324           0 :             return;
     325             :         }
     326         597 :         switch(id)
     327             :         {
     328          32 :         case field::connection:
     329             :         case field::proxy_connection:
     330          32 :             do_connection(v, ec);
     331          32 :             if(ec.failed())
     332           0 :                 return;
     333          32 :             break;
     334          31 :         case field::content_length:
     335          31 :             do_content_length(v, ec);
     336          31 :             if(ec.failed())
     337           0 :                 return;
     338          31 :             break;
     339           1 :         case field::transfer_encoding:
     340           1 :             do_transfer_encoding(v, ec);
     341           1 :             if(ec.failed())
     342           0 :                 return;
     343           1 :             break;
     344           0 :         case field::upgrade:
     345           0 :             do_upgrade(v, ec);
     346           0 :             if(ec.failed())
     347           0 :                 return;
     348           0 :             break;
     349         533 :         default:
     350         533 :             break;
     351             :         }
     352         597 :     }
     353             : }
     354             : 
     355             : void
     356         579 : parser::
     357             : parse_body(
     358             :     error_code& ec)
     359             : {
     360             :     (void)ec;
     361         579 : return;
     362             : // VFALCO TODO
     363             : #if 0
     364             :     BOOST_ASSERT(state_ == state::body);
     365             : 
     366             :     if(h_.kind == detail::kind::request)
     367             :     {
     368             :         // https://tools.ietf.org/html/rfc7230#section-3.3
     369             :         if(m_.skip_body)
     370             :             return;
     371             :         if(m_.content_len.has_value())
     372             :         {
     373             :             if(*m_.content_len > cfg_.body_too_large)
     374             :             {
     375             :                 ec = error::body_too_large;
     376             :                 return;
     377             :             }
     378             :             if(*m_.content_len == 0)
     379             :                 return;
     380             :         }
     381             :         else if(m_.got_chunked)
     382             :         {
     383             :             // VFALCO TODO
     384             :             return;
     385             :         }
     386             :         else
     387             :         {
     388             :             // Content-Length: 0
     389             :             return;
     390             :         }
     391             :     }
     392             :     else
     393             :     {
     394             :         BOOST_ASSERT(h_.kind ==
     395             :             detail::kind::response);
     396             : 
     397             :         // https://tools.ietf.org/html/rfc7230#section-3.3
     398             :         if((h_.res.status_int /  100 == 1) || // 1xx e.g. Continue
     399             :             h_.res.status_int == 204 ||       // No Content
     400             :             h_.res.status_int == 304)         // Not Modified
     401             :         {
     402             :             // Content-Length may be present, but we
     403             :             // treat the message as not having a body.
     404             :         }
     405             :         else if(m_.content_len.has_value())
     406             :         {
     407             :             if(*m_.content_len > 0)
     408             :             {
     409             :                 if(*m_.content_len > cfg_.body_too_large)
     410             :                 {
     411             :                     ec = error::body_too_large;
     412             :                     return;
     413             :                 }
     414             :             }
     415             :         }
     416             :         else
     417             :         {
     418             :             // No Content-Length
     419             :             return;
     420             :         }
     421             :     }
     422             : 
     423             :     auto avail = committed_ - size_;
     424             :     if(m_.content_len.has_value())
     425             :     {
     426             :         // known payload length
     427             :         BOOST_ASSERT(! m_.got_chunked);
     428             :         BOOST_ASSERT(m_.n_remain > 0);
     429             :         BOOST_ASSERT(m_.content_len <
     430             :             cfg_.body_too_large);
     431             :         if(avail == 0)
     432             :         {
     433             :             if(! got_eof_)
     434             :             {
     435             :                 ec = grammar::error::need_more;
     436             :                 return;
     437             :             }
     438             :             ec = error::need_more;
     439             :             return;
     440             :         }
     441             :         if( avail > m_.n_remain)
     442             :             avail = static_cast<
     443             :                 std::size_t>(m_.n_remain);
     444             :         size_ += avail;
     445             :         m_.payload_seen += avail;
     446             :         m_.n_payload += avail;
     447             :         m_.n_remain -= avail;
     448             :         if(m_.n_remain > 0)
     449             :         {
     450             :             ec = {};
     451             :             return;
     452             :         }
     453             :         state_ = state::complete;
     454             :         ec = error::end_of_message;
     455             :         return;
     456             :     }
     457             : 
     458             :     if(! m_.got_chunked)
     459             :     {
     460             :         // end of body indicated by EOF
     461             :         if(avail > 0)
     462             :         {
     463             :             if(avail > (std::size_t(
     464             :                 -1) - m_.n_payload))
     465             :             {
     466             :                 // overflow size_t
     467             :                 // VFALCO revisit this
     468             :                 ec = error::numeric_overflow;
     469             :                 return;
     470             :             }
     471             :             size_ += avail;
     472             :             m_.n_payload += avail;
     473             :             ec = {};
     474             :             return;
     475             :         }
     476             :         if(! got_eof_)
     477             :         {
     478             :             ec = grammar::error::need_more;
     479             :             return;
     480             :         }
     481             :         state_ = state::complete;
     482             :         ec = error::end_of_message;
     483             :         return;
     484             :     }
     485             : #if 0
     486             :     if(m_.payload_left == 0)
     487             :     {
     488             :         // start of chunk
     489             :         bnf::chunk_part p;
     490             :         auto it = p.parse(
     491             :             h_.buf + size_,
     492             :             h_.buf + (
     493             :                 committed_ - size_),
     494             :             ec);
     495             :         if(ec)
     496             :             return;
     497             :         auto const v =
     498             :             p.value();
     499             :         m_.chunk.size = v.size;
     500             :         m_.chunk.ext = v.ext;
     501             :         m_.chunk.trailer = v.trailer;
     502             :         m_.chunk.fresh = true;
     503             :         m_.payload_left =
     504             :             v.size - v.data.size();
     505             :     }
     506             :     else
     507             :     {
     508             :         // continuation of chunk
     509             : 
     510             :     }
     511             : #endif
     512             : #endif
     513             : }
     514             : 
     515             : void
     516           0 : parser::
     517             : parse_chunk(
     518             :     error_code& ec)
     519             : {
     520             :     (void)ec;
     521             : #if 0
     522             :     switch(state_)
     523             :     {
     524             :     case state::start_line:
     525             :     case state::header_fields:
     526             :         parse_header(ec);
     527             :         if(ec.failed())
     528             :             return;
     529             :         BOOST_ASSERT(state_ >
     530             :             state::header_fields);
     531             :         break;
     532             :     case state::body:
     533             :         if(! m_.got_chunked)
     534             :             return parse_body(ec);
     535             :         break;
     536             :     case state::complete:
     537             :         ec = error::end_of_message;
     538             :         if(! got_eof_)
     539             :             return;
     540             :         state_ = state::end_of_stream;
     541             :         return;
     542             :     case state::end_of_stream:
     543             :         ec = error::end_of_stream;
     544             :         return;
     545             :     }
     546             : 
     547             :     auto const avail = committed_ - size_;
     548             :     auto const start = h_.buf + size_;
     549             :     if(m_.payload_left == 0)
     550             :     {
     551             :         // start of chunk
     552             :         // VFALCO What about chunk_part_next?
     553             :         BOOST_ASSERT(
     554             :             size_ == m_.header_size);
     555             :         bnf::chunk_part p;
     556             :         auto it = p.parse(start,
     557             :             h_.buf + (
     558             :                 committed_ - size_), ec);
     559             :         BOOST_ASSERT(it == start);
     560             :         if(ec)
     561             :             return;
     562             :         auto const v = p.value();
     563             :         m_.chunk.size = v.size;
     564             :         m_.chunk.ext = v.ext;
     565             :         m_.chunk.fresh = true;
     566             :         if(v.size > 0)
     567             :         {
     568             :             // chunk
     569             :             m_.chunk.trailer = {};
     570             :             m_.payload_left =
     571             :                 v.size - v.data.size();
     572             :             size_ += it - start; // excludes CRLF
     573             :             return;
     574             :         }
     575             :         // last-chunk
     576             :         BOOST_ASSERT(
     577             :             v.data.empty());
     578             :         m_.chunk.trailer =
     579             :             v.trailer;
     580             :         m_.body = {};
     581             :         size_ += it - start; // excludes CRLF
     582             :         state_ = state::complete;
     583             :     }
     584             :     else
     585             :     {
     586             :         // continuation of chunk
     587             : 
     588             :     }
     589             : #endif
     590           0 : }
     591             : 
     592             : //------------------------------------------------
     593             : 
     594             : // https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
     595             : void
     596          32 : parser::
     597             : do_connection(
     598             :     string_view s, error_code& ec)
     599             : {
     600             :     (void)ec;
     601             :     (void)s;
     602             : #if 0
     603             :     for(auto v : bnf::range<bnf::connection>(s))
     604             :     {
     605             :         if(grammar::ci_is_equal(v, "close"))
     606             :         {
     607             :             m_.got_close = true;
     608             :         }
     609             :         else if(grammar::ci_is_equal(v, "keep-alive"))
     610             :         {
     611             :             m_.got_keep_alive = true;
     612             :         }
     613             :         else if(grammar::ci_is_equal(v, "upgrade"))
     614             :         {
     615             :             m_.got_upgrade = true;
     616             :         }
     617             :     }
     618             : #endif
     619          32 : }
     620             : 
     621             : void
     622          31 : parser::
     623             : do_content_length(
     624             :     string_view s,
     625             :     error_code& ec)
     626             : {
     627             :     (void)ec;
     628             :     (void)s;
     629             : #if 0
     630             :     // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     631             :     // Content-Length can be a comma separated
     632             :     // list, with all the same values, as well
     633             :     // as having multiple Content-Length fields
     634             :     // with the same number. We handle these here.
     635             :     auto list = bnf::range<
     636             :         bnf::list_of_one_or_more<
     637             :             bnf::dec_number>>(s);
     638             :     auto it = list.begin(ec);
     639             :     if(ec.failed())
     640             :     {
     641             :         ec = error::bad_content_length;
     642             :         return;
     643             :     }
     644             :     BOOST_ASSERT(it != list.end());
     645             :     auto v = *it;
     646             :     if(! m_.content_len.has_value())
     647             :     {
     648             :         if(v > cfg_.max_body_size)
     649             :         {
     650             :             ec = error::body_too_large;
     651             :             return;
     652             :         }
     653             :         m_.content_len = v;
     654             :     }
     655             :     else if(*m_.content_len != v)
     656             :     {
     657             :         // differing values
     658             :         ec = error::bad_content_length;
     659             :         return;
     660             :     }
     661             :     for(;;)
     662             :     {
     663             :         it.increment(ec);
     664             :         if(ec.failed())
     665             :         {
     666             :             ec = error::bad_content_length;
     667             :             return;
     668             :         }
     669             :         if(it == list.end())
     670             :             break;
     671             :         v = *it;
     672             :         if(m_.content_len != v)
     673             :         {
     674             :             // differing lengths
     675             :             ec = error::bad_content_length;
     676             :             return;
     677             :         }
     678             :     }
     679             :     if(! m_.skip_body)
     680             :         m_.n_remain = *m_.content_len;
     681             : #endif
     682          31 : }
     683             : 
     684             : void
     685           1 : parser::
     686             : do_transfer_encoding(
     687             :     string_view s, error_code& ec)
     688             : {
     689             :     (void)s;
     690             :     (void)ec;
     691             : #if 0
     692             :     using namespace detail::string_literals;
     693             : 
     694             :     bnf::range<bnf::transfer_encoding> te(s);
     695             :     auto const end = te.end();
     696             :     auto it = te.begin(ec);
     697             :     if(ec.failed())
     698             :     {
     699             :         // expected at least one encoding
     700             :         ec = error::bad_transfer_encoding;
     701             :         return;
     702             :     }
     703             :     BOOST_ASSERT(it != end);
     704             :     // handle multiple
     705             :     // Transfer-Encoding header lines
     706             :     m_.got_chunked = false;
     707             :     // get last encoding
     708             :     for(;;)
     709             :     {
     710             :         auto prev = it++;
     711             :         if(it == end)
     712             :         {
     713             :             if(grammar::ci_is_equal(
     714             :                 prev->name, "chunked"))
     715             :                 m_.got_chunked = true;
     716             :             break;
     717             :         }
     718             :     }
     719             : #endif
     720           1 : }
     721             : 
     722             : void
     723           0 : parser::
     724             : do_upgrade(
     725             :     string_view s, error_code& ec)
     726             : {
     727             :     (void)s;
     728             :     (void)ec;
     729           0 : }
     730             : 
     731             : } // http_proto
     732             : } // boost
     733             : 
     734             : #endif

Generated by: LCOV version 1.15