LCOV - code coverage report
Current view: top level - libs/http_proto/src/detail - header.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 546 581 94.0 %
Date: 2024-01-15 16:47:12 Functions: 47 56 83.9 %

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3             : //
       4             : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5             : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // Official repository: https://github.com/cppalliance/http_proto
       8             : //
       9             : 
      10             : #include <boost/http_proto/detail/header.hpp>
      11             : #include <boost/http_proto/field.hpp>
      12             : #include <boost/http_proto/fields_view_base.hpp>
      13             : #include <boost/http_proto/header_limits.hpp>
      14             : #include <boost/http_proto/rfc/list_rule.hpp>
      15             : #include <boost/http_proto/rfc/token_rule.hpp>
      16             : #include <boost/http_proto/rfc/transfer_encoding_rule.hpp>
      17             : #include <boost/http_proto/rfc/upgrade_rule.hpp>
      18             : #include <boost/http_proto/rfc/detail/rules.hpp>
      19             : #include <boost/url/grammar/ci_string.hpp>
      20             : #include <boost/url/grammar/parse.hpp>
      21             : #include <boost/url/grammar/range_rule.hpp>
      22             : #include <boost/url/grammar/recycled.hpp>
      23             : #include <boost/url/grammar/unsigned_rule.hpp>
      24             : #include <boost/assert.hpp>
      25             : #include <boost/assert/source_location.hpp>
      26             : #include <boost/static_assert.hpp>
      27             : #include <string>
      28             : #include <utility>
      29             : 
      30             : namespace boost {
      31             : namespace http_proto {
      32             : namespace detail {
      33             : 
      34             : //------------------------------------------------
      35             : 
      36             : auto
      37          41 : header::
      38             : entry::
      39             : operator+(
      40             :     std::size_t dv) const noexcept ->
      41             :         entry
      42             : {
      43             :     return {
      44             :         static_cast<
      45          41 :             offset_type>(np + dv),
      46          41 :         nn,
      47             :         static_cast<
      48          41 :             offset_type>(vp + dv),
      49          41 :         vn,
      50          41 :         id };
      51             : }
      52             : 
      53             : auto
      54          75 : header::
      55             : entry::
      56             : operator-(
      57             :     std::size_t dv) const noexcept ->
      58             :         entry
      59             : {
      60             :     return {
      61             :         static_cast<
      62          75 :             offset_type>(np - dv),
      63          75 :         nn,
      64             :         static_cast<
      65          75 :             offset_type>(vp - dv),
      66          75 :         vn,
      67          75 :         id };
      68             : }
      69             : 
      70             : //------------------------------------------------
      71             : 
      72             : constexpr
      73             : header::
      74             : header(fields_tag) noexcept
      75             :     : kind(detail::kind::fields)
      76             :     , cbuf("\r\n")
      77             :     , size(2)
      78             :     , fld{}
      79             : {
      80             : }
      81             : 
      82             : constexpr
      83             : header::
      84             : header(request_tag) noexcept
      85             :     : kind(detail::kind::request)
      86             :     , cbuf("GET / HTTP/1.1\r\n\r\n")
      87             :     , size(18)
      88             :     , prefix(16)
      89             :     , req{ 3, 1,
      90             :         http_proto::method::get }
      91             : {
      92             : }
      93             : 
      94             : constexpr
      95             : header::
      96             : header(response_tag) noexcept
      97             :     : kind(detail::kind::response)
      98             :     , cbuf("HTTP/1.1 200 OK\r\n\r\n")
      99             :     , size(19)
     100             :     , prefix(17)
     101             :     , res{ 200,
     102             :         http_proto::status::ok }
     103             : {
     104             : }
     105             : 
     106             : //------------------------------------------------
     107             : 
     108             : header const*
     109         105 : header::
     110             : get_default(detail::kind k) noexcept
     111             : {
     112             :     static constexpr header h[3] = {
     113             :         fields_tag{},
     114             :         request_tag{},
     115             :         response_tag{}};
     116         105 :     return &h[k];
     117             : }
     118             : 
     119        2733 : header::
     120        2733 : header(empty v) noexcept
     121        2733 :     : kind(v.param)
     122             : {
     123        2733 : }
     124             : 
     125          87 : header::
     126          87 : header(detail::kind k) noexcept
     127          87 :     : header(*get_default(k))
     128             : {
     129          87 : }
     130             : 
     131             : void
     132          62 : header::
     133             : swap(header& h) noexcept
     134             : {
     135          62 :     std::swap(cbuf, h.cbuf);
     136          62 :     std::swap(buf, h.buf);
     137          62 :     std::swap(cap, h.cap);
     138          62 :     std::swap(size, h.size);
     139          62 :     std::swap(count, h.count);
     140          62 :     std::swap(prefix, h.prefix);
     141          62 :     std::swap(version, h.version);
     142          62 :     std::swap(md, h.md);
     143          62 :     switch(kind)
     144             :     {
     145          16 :     default:
     146             :     case detail::kind::fields:
     147          16 :         break;
     148          45 :     case detail::kind::request:
     149          45 :         std::swap(
     150          45 :             req.method_len, h.req.method_len);
     151          45 :         std::swap(
     152          45 :             req.target_len, h.req.target_len);
     153          45 :         std::swap(req.method, h.req.method);
     154          45 :         break;
     155           1 :     case detail::kind::response:
     156           1 :         std::swap(
     157           1 :             res.status_int, h.res.status_int);
     158           1 :         std::swap(res.status, h.res.status);
     159           1 :         break;
     160             :     }
     161          62 : }
     162             : 
     163             : /*  References:
     164             : 
     165             :     6.3.  Persistence
     166             :     https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
     167             : */
     168             : bool
     169          22 : header::
     170             : keep_alive() const noexcept
     171             : {
     172          22 :     if(md.payload == payload::error)
     173           1 :         return false;
     174          21 :     if( version ==
     175             :         http_proto::version::http_1_1)
     176             :     {
     177          13 :         if(md.connection.close)
     178           3 :             return false;
     179             :     }
     180             :     else
     181             :     {
     182           8 :         if(! md.connection.keep_alive)
     183           4 :             return false;
     184             :     }
     185             :     // can't use to_eof in requests
     186          14 :     BOOST_ASSERT(
     187             :         kind != detail::kind::request ||
     188             :         md.payload != payload::to_eof);
     189          14 :     if(md.payload == payload::to_eof)
     190           3 :         return false;
     191          11 :     return true;
     192             : }
     193             : 
     194             : //------------------------------------------------
     195             : 
     196             : // return total bytes needed
     197             : // to store message of `size`
     198             : // bytes and `count` fields.
     199             : std::size_t
     200         577 : header::
     201             : bytes_needed(
     202             :     std::size_t size,
     203             :     std::size_t count) noexcept
     204             : {
     205             :     // make sure `size` is big enough
     206             :     // to hold the largest default buffer:
     207             :     // "HTTP/1.1 200 OK\r\n\r\n"
     208         577 :     if( size < 19)
     209         137 :         size = 19;
     210             :     static constexpr auto A =
     211             :         alignof(header::entry);
     212             :     // round up to alignof(A)
     213         577 :     return A * (
     214         577 :         (size + A - 1) / A) +
     215         577 :             (count * sizeof(
     216         577 :                 header::entry));
     217             : }
     218             : 
     219             : std::size_t
     220        1207 : header::
     221             : table_space(
     222             :     std::size_t count) noexcept
     223             : {
     224             :     return count *
     225        1207 :         sizeof(header::entry);
     226             : }
     227             : 
     228             : std::size_t
     229        1207 : header::
     230             : table_space() const noexcept
     231             : {
     232        1207 :     return table_space(count);
     233             : }
     234             : 
     235             : auto
     236        2187 : header::
     237             : tab() const noexcept ->
     238             :     table
     239             : {
     240        2187 :     BOOST_ASSERT(cap > 0);
     241        2187 :     BOOST_ASSERT(buf != nullptr);
     242        2187 :     return table(buf + cap);
     243             : }
     244             : 
     245             : auto
     246         363 : header::
     247             : tab_() const noexcept ->
     248             :     entry*
     249             : {
     250             :     return reinterpret_cast<
     251         363 :         entry*>(buf + cap);
     252             : }
     253             : 
     254             : // return true if header cbuf is a default
     255             : bool
     256          27 : header::
     257             : is_default() const noexcept
     258             : {
     259          27 :     return buf == nullptr;
     260             : }
     261             : 
     262             : std::size_t
     263          63 : header::
     264             : find(
     265             :     field id) const noexcept
     266             : {
     267          63 :     if(count == 0)
     268           6 :         return 0;
     269          57 :     std::size_t i = 0;
     270          57 :     auto const* p = &tab()[0];
     271          78 :     while(i < count)
     272             :     {
     273          78 :         if(p->id == id)
     274          57 :             break;
     275          21 :         ++i;
     276          21 :         --p;
     277             :     }
     278          57 :     return i;
     279             : }
     280             : 
     281             : std::size_t
     282          13 : header::
     283             : find(
     284             :     core::string_view name) const noexcept
     285             : {
     286          13 :     if(count == 0)
     287           4 :         return 0;
     288           9 :     std::size_t i = 0;
     289           9 :     auto const* p = &tab()[0];
     290          12 :     while(i < count)
     291             :     {
     292             :         core::string_view s(
     293          12 :             cbuf + prefix + p->np,
     294          12 :             p->nn);
     295          12 :         if(grammar::ci_is_equal(s, name))
     296           9 :             break;
     297           3 :         ++i;
     298           3 :         --p;
     299             :     }
     300           9 :     return i;
     301             : }
     302             : 
     303             : void
     304          16 : header::
     305             : copy_table(
     306             :     void* dest,
     307             :     std::size_t n) const noexcept
     308             : {
     309          16 :     std::memcpy(
     310             :         reinterpret_cast<
     311          16 :             entry*>(dest) - n,
     312             :         reinterpret_cast<
     313             :             entry const*>(
     314          16 :                 cbuf + cap) - n,
     315             :         n * sizeof(entry));
     316          16 : }
     317             : 
     318             : void
     319          16 : header::
     320             : copy_table(
     321             :     void* dest) const noexcept
     322             : {
     323          16 :     copy_table(dest, count);
     324          16 : }
     325             : 
     326             : // assign all the members but
     327             : // preserve the allocated memory
     328             : void
     329          17 : header::
     330             : assign_to(
     331             :     header& dest) const noexcept
     332             : {
     333          17 :     auto const buf_ = dest.buf;
     334          17 :     auto const cbuf_ = dest.cbuf;
     335          17 :     auto const cap_ = dest.cap;
     336          17 :     dest = *this;
     337          17 :     dest.buf = buf_;
     338          17 :     dest.cbuf = cbuf_;
     339          17 :     dest.cap = cap_;
     340          17 : }
     341             : 
     342             : //------------------------------------------------
     343             : //
     344             : // Metadata
     345             : //
     346             : //------------------------------------------------
     347             : 
     348             : std::size_t
     349           0 : header::
     350             : maybe_count(
     351             :     field id) const noexcept
     352             : {
     353           0 :     if(kind == detail::kind::fields)
     354           0 :         return std::size_t(-1);
     355           0 :     switch(id)
     356             :     {
     357           0 :     case field::connection:
     358           0 :         return md.connection.count;
     359           0 :     case field::content_length:
     360           0 :         return md.content_length.count;
     361           0 :     case field::expect:
     362           0 :         return md.expect.count;
     363           0 :     case field::transfer_encoding:
     364           0 :         return md.transfer_encoding.count;
     365           0 :     case field::upgrade:
     366           0 :         return md.upgrade.count;
     367           0 :     default:
     368           0 :         break;
     369             :     }
     370           0 :     return std::size_t(-1);
     371             : }
     372             : 
     373             : bool
     374          17 : header::
     375             : is_special(
     376             :     field id) const noexcept
     377             : {
     378          17 :     if(kind == detail::kind::fields)
     379           4 :         return false;
     380          13 :     switch(id)
     381             :     {
     382           7 :     case field::connection:
     383             :     case field::content_length:
     384             :     case field::expect:
     385             :     case field::transfer_encoding:
     386             :     case field::upgrade:
     387           7 :         return true;
     388           6 :     default:
     389           6 :         break;
     390             :     }
     391           6 :     return false;
     392             : }
     393             : 
     394             : //------------------------------------------------
     395             : 
     396             : // called when the start-line changes
     397             : void
     398        1722 : header::
     399             : on_start_line()
     400             : {
     401             :     // items in both the request-line
     402             :     // and the status-line can affect
     403             :     // the payload, for example whether
     404             :     // or not EOF marks the end of the
     405             :     // payload.
     406             : 
     407        1722 :     update_payload();
     408        1722 : }
     409             : 
     410             : // called after a field is inserted
     411             : void
     412        2563 : header::
     413             : on_insert(
     414             :     field id,
     415             :     core::string_view v)
     416             : {
     417        2563 :     if(kind == detail::kind::fields)
     418         434 :         return;
     419        2129 :     switch(id)
     420             :     {
     421         559 :     case field::content_length:
     422         559 :         return on_insert_content_length(v);
     423         120 :     case field::connection:
     424         120 :         return on_insert_connection(v);
     425          33 :     case field::expect:
     426          33 :         return on_insert_expect(v);
     427          43 :     case field::transfer_encoding:
     428          43 :         return on_insert_transfer_encoding();
     429          24 :     case field::upgrade:
     430          24 :         return on_insert_upgrade(v);
     431        1350 :     default:
     432        1350 :         break;
     433             :     }
     434             : }
     435             : 
     436             : // called when one field is erased
     437             : void
     438          38 : header::
     439             : on_erase(field id)
     440             : {
     441          38 :     if(kind == detail::kind::fields)
     442           3 :         return;
     443          35 :     switch(id)
     444             :     {
     445          11 :     case field::connection:
     446          11 :         return on_erase_connection();
     447           4 :     case field::content_length:
     448           4 :         return on_erase_content_length();
     449           6 :     case field::expect:
     450           6 :         return on_erase_expect();
     451           5 :     case field::transfer_encoding:
     452           5 :         return on_erase_transfer_encoding();
     453           4 :     case field::upgrade:
     454           4 :         return on_erase_upgrade();
     455           5 :     default:
     456           5 :         break;
     457             :     }
     458             : }
     459             : 
     460             : //------------------------------------------------
     461             : 
     462             : /*
     463             :     https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
     464             : */
     465             : void
     466         124 : header::
     467             : on_insert_connection(
     468             :     core::string_view v)
     469             : {
     470         124 :     ++md.connection.count;
     471         124 :     if(md.connection.ec.failed())
     472           5 :         return;
     473             :     auto rv = grammar::parse(
     474         123 :         v, list_rule(token_rule, 1));
     475         123 :     if(! rv)
     476             :     {
     477           4 :         md.connection.ec =
     478           8 :             BOOST_HTTP_PROTO_ERR(
     479             :                 error::bad_connection);
     480           4 :         return;
     481             :     }
     482         119 :     md.connection.ec = {};
     483         249 :     for(auto t : *rv)
     484             :     {
     485         130 :         if(grammar::ci_is_equal(
     486             :                 t, "close"))
     487          82 :             md.connection.close = true;
     488          48 :         else if(grammar::ci_is_equal(
     489             :                 t, "keep-alive"))
     490          24 :             md.connection.keep_alive = true;
     491          24 :         else if(grammar::ci_is_equal(
     492             :                 t, "upgrade"))
     493          19 :             md.connection.upgrade = true;
     494             :     }
     495             : }
     496             : 
     497             : void
     498         560 : header::
     499             : on_insert_content_length(
     500             :     core::string_view v)
     501             : {
     502             :     static
     503             :     constexpr
     504             :     grammar::unsigned_rule<
     505             :         std::uint64_t> num_rule{};
     506             : 
     507         560 :     ++md.content_length.count;
     508         560 :     if(md.content_length.ec.failed())
     509         437 :         return;
     510             :     auto rv =
     511         558 :         grammar::parse(v, num_rule);
     512         558 :     if(! rv)
     513             :     {
     514             :         // parse failure
     515           5 :         md.content_length.ec =
     516          10 :             BOOST_HTTP_PROTO_ERR(
     517             :             error::bad_content_length);
     518           5 :         md.content_length.value = 0;
     519           5 :         update_payload();
     520           5 :         return;
     521             :     }
     522         553 :     if(md.content_length.count == 1)
     523             :     {
     524             :         // one value
     525         423 :         md.content_length.ec = {};
     526         423 :         md.content_length.value = *rv;
     527         423 :         update_payload();
     528         423 :         return;
     529             :     }
     530         130 :     if(*rv == md.content_length.value)
     531             :     {
     532             :         // ok: duplicate value
     533           7 :         return;
     534             :     }
     535             :     // bad: different values
     536         123 :     md.content_length.ec =
     537         246 :         BOOST_HTTP_PROTO_ERR(
     538             :             error::multiple_content_length);
     539         123 :     md.content_length.value = 0;
     540         123 :     update_payload();
     541             : }
     542             : 
     543             : void
     544          36 : header::
     545             : on_insert_expect(
     546             :     core::string_view v)
     547             : {
     548          36 :     ++md.expect.count;
     549          36 :     if(kind != detail::kind::request)
     550           8 :         return;
     551          28 :     if(md.expect.ec.failed())
     552           1 :         return;
     553             :     // VFALCO Should we allow duplicate
     554             :     // Expect fields that have 100-continue?
     555          49 :     if( md.expect.count > 1 ||
     556          49 :         ! grammar::ci_is_equal(v,
     557             :             "100-continue"))
     558             :     {
     559          11 :         md.expect.ec =
     560          22 :             BOOST_HTTP_PROTO_ERR(
     561             :                 error::bad_expect);
     562          11 :         md.expect.is_100_continue = false;
     563          11 :         return;
     564             :     }
     565          16 :     md.expect.is_100_continue = true;
     566             : }
     567             : 
     568             : void
     569          46 : header::
     570             : on_insert_transfer_encoding()
     571             : {
     572          46 :     ++md.transfer_encoding.count;
     573          46 :     if(md.transfer_encoding.ec.failed())
     574           1 :         return;
     575          45 :     auto const n =
     576             :         md.transfer_encoding.count;
     577          45 :     md.transfer_encoding = {};
     578          45 :     md.transfer_encoding.count = n;
     579          52 :     for(auto s :
     580             :         fields_view_base::subrange(
     581         149 :             this, find(field::transfer_encoding)))
     582             :     {
     583             :         auto rv = grammar::parse(
     584          60 :             s, transfer_encoding_rule);
     585          60 :         if(! rv)
     586             :         {
     587             :             // parse error
     588           4 :             md.transfer_encoding.ec =
     589           8 :                 BOOST_HTTP_PROTO_ERR(
     590             :                     error::bad_transfer_encoding);
     591           4 :             md.transfer_encoding.codings = 0;
     592           4 :             md.transfer_encoding.is_chunked = false;
     593           4 :             update_payload();
     594           4 :             return;
     595             :         }
     596          56 :         md.transfer_encoding.codings += rv->size();
     597         117 :         for(auto t : *rv)
     598             :         {
     599          65 :             if(! md.transfer_encoding.is_chunked)
     600             :             {
     601          61 :                 if(t.id == transfer_coding::chunked)
     602          25 :                     md.transfer_encoding.is_chunked = true;
     603          61 :                 continue;
     604             :             }
     605           4 :             if(t.id == transfer_coding::chunked)
     606             :             {
     607             :                 // chunked appears twice
     608           2 :                 md.transfer_encoding.ec =
     609           4 :                     BOOST_HTTP_PROTO_ERR(
     610             :                         error::bad_transfer_encoding);
     611           2 :                 md.transfer_encoding.codings = 0;
     612           2 :                 md.transfer_encoding.is_chunked = false;
     613           2 :                 update_payload();
     614           2 :                 return;
     615             :             }
     616             :             // chunked must be last
     617           2 :             md.transfer_encoding.ec =
     618           4 :                 BOOST_HTTP_PROTO_ERR(
     619             :                     error::bad_transfer_encoding);
     620           2 :             md.transfer_encoding.codings = 0;
     621           2 :             md.transfer_encoding.is_chunked = false;
     622           2 :             update_payload();
     623           2 :             return;
     624             :         }
     625             :     }
     626          37 :     update_payload();
     627             : }
     628             : 
     629             : void
     630          26 : header::
     631             : on_insert_upgrade(
     632             :     core::string_view v)
     633             : {
     634          26 :     ++md.upgrade.count;
     635          26 :     if(md.upgrade.ec.failed())
     636           5 :         return;
     637          25 :     if( version !=
     638             :         http_proto::version::http_1_1)
     639             :     {
     640           1 :         md.upgrade.ec =
     641           2 :             BOOST_HTTP_PROTO_ERR(
     642             :                 error::bad_upgrade);
     643           1 :         md.upgrade.websocket = false;
     644           1 :         return;
     645             :     }
     646             :     auto rv = grammar::parse(
     647          24 :         v, upgrade_rule);
     648          24 :     if(! rv)
     649             :     {
     650           3 :         md.upgrade.ec =
     651           6 :             BOOST_HTTP_PROTO_ERR(
     652             :                 error::bad_upgrade);
     653           3 :         md.upgrade.websocket = false;
     654           3 :         return;
     655             :     }
     656          21 :     if(! md.upgrade.websocket)
     657             :     {
     658          23 :         for(auto t : *rv)
     659             :         {
     660          16 :             if( grammar::ci_is_equal(
     661          26 :                     t.name, "websocket") &&
     662          10 :                 t.version.empty())
     663             :             {
     664           9 :                 md.upgrade.websocket = true;
     665           9 :                 break;
     666             :             }
     667             :         }
     668             :     }
     669             : }
     670             : 
     671             : //------------------------------------------------
     672             : 
     673             : void
     674          11 : header::
     675             : on_erase_connection()
     676             : {
     677          11 :     BOOST_ASSERT(
     678             :         md.connection.count > 0);
     679             :     // reset and re-insert
     680          11 :     auto n = md.connection.count - 1;
     681          11 :     auto const p = cbuf + prefix;
     682          11 :     auto const* e = &tab()[0];
     683          11 :     md.connection = {};
     684          16 :     while(n > 0)
     685             :     {
     686           5 :         if(e->id == field::connection)
     687           4 :             on_insert_connection(
     688             :                 core::string_view(
     689           4 :                     p + e->vp, e->vn));
     690           5 :         --n;
     691           5 :         --e;
     692             :     }
     693          11 : }
     694             : 
     695             : void
     696           4 : header::
     697             : on_erase_content_length()
     698             : {
     699           4 :     BOOST_ASSERT(
     700             :         md.content_length.count > 0);
     701           4 :     --md.content_length.count;
     702           4 :     if(md.content_length.count == 0)
     703             :     {
     704             :         // no Content-Length
     705           1 :         md.content_length = {};
     706           1 :         update_payload();
     707           1 :         return;
     708             :     }
     709           3 :     if(! md.content_length.ec.failed())
     710             :     {
     711             :         // removing a duplicate value
     712           2 :         return;
     713             :     }
     714             :     // reset and re-insert
     715           1 :     auto n = md.content_length.count;
     716           1 :     auto const p = cbuf + prefix;
     717           1 :     auto const* e = &tab()[0];
     718           1 :     md.content_length = {};
     719           2 :     while(n > 0)
     720             :     {
     721           1 :         if(e->id == field::content_length)
     722           1 :             on_insert_content_length(
     723             :                 core::string_view(
     724           1 :                     p + e->vp, e->vn));
     725           1 :         --n;
     726           1 :         --e;
     727             :     }
     728           1 :     update_payload();
     729             : }
     730             : 
     731             : void
     732           6 : header::
     733             : on_erase_expect()
     734             : {
     735           6 :     BOOST_ASSERT(
     736             :         md.expect.count > 0);
     737           6 :     --md.expect.count;
     738           6 :     if(kind != detail::kind::request)
     739           1 :         return;
     740           5 :     if(md.expect.count == 0)
     741             :     {
     742             :         // no Expect
     743           2 :         md.expect = {};
     744           2 :         return;
     745             :     }
     746             :     // VFALCO This should be uncommented
     747             :     // if we want to allow multiple Expect
     748             :     // fields with the value 100-continue
     749             :     /*
     750             :     if(! md.expect.ec.failed())
     751             :         return;
     752             :     */
     753             :     // reset and re-insert
     754           3 :     auto n = md.expect.count;
     755           3 :     auto const p = cbuf + prefix;
     756           3 :     auto const* e = &tab()[0];
     757           3 :     md.expect = {};
     758           6 :     while(n > 0)
     759             :     {
     760           3 :         if(e->id == field::expect)
     761           3 :             on_insert_expect(
     762             :                 core::string_view(
     763           3 :                     p + e->vp, e->vn));
     764           3 :         --n;
     765           3 :         --e;
     766             :     }
     767             : }
     768             : 
     769             : void
     770           5 : header::
     771             : on_erase_transfer_encoding()
     772             : {
     773           5 :     BOOST_ASSERT(
     774             :         md.transfer_encoding.count > 0);
     775           5 :     --md.transfer_encoding.count;
     776           5 :     if(md.transfer_encoding.count == 0)
     777             :     {
     778             :         // no Transfer-Encoding
     779           2 :         md.transfer_encoding = {};
     780           2 :         update_payload();
     781           2 :         return;
     782             :     }
     783             :     // re-insert everything
     784           3 :     --md.transfer_encoding.count;
     785           3 :     on_insert_transfer_encoding();
     786             : }
     787             : 
     788             : // called when Upgrade is erased
     789             : void
     790           4 : header::
     791             : on_erase_upgrade()
     792             : {
     793           4 :     BOOST_ASSERT(
     794             :         md.upgrade.count > 0);
     795           4 :     --md.upgrade.count;
     796           4 :     if(md.upgrade.count == 0)
     797             :     {
     798             :         // no Upgrade
     799           2 :         md.upgrade = {};
     800           2 :         return;
     801             :     }
     802             :     // reset and re-insert
     803           2 :     auto n = md.upgrade.count;
     804           2 :     auto const p = cbuf + prefix;
     805           2 :     auto const* e = &tab()[0];
     806           2 :     md.upgrade = {};
     807           4 :     while(n > 0)
     808             :     {
     809           2 :         if(e->id == field::upgrade)
     810           2 :             on_insert_upgrade(
     811             :                 core::string_view(
     812           2 :                     p + e->vp, e->vn));
     813           2 :         --n;
     814           2 :         --e;
     815             :     }
     816             : }
     817             : 
     818             : //------------------------------------------------
     819             : 
     820             : // called when all fields with id are removed
     821             : void
     822          51 : header::
     823             : on_erase_all(
     824             :     field id)
     825             : {
     826          51 :     if(kind == detail::kind::fields)
     827          14 :         return;
     828          37 :     switch(id)
     829             :     {
     830           1 :     case field::connection:
     831           1 :         md.connection = {};
     832           1 :         return;
     833             : 
     834           2 :     case field::content_length:
     835           2 :         md.content_length = {};
     836           2 :         update_payload();
     837           2 :         return;
     838             : 
     839           5 :     case field::expect:
     840           5 :         md.expect = {};
     841           5 :         update_payload();
     842           5 :         return;
     843             : 
     844           1 :     case field::transfer_encoding:
     845           1 :         md.transfer_encoding = {};
     846           1 :         update_payload();
     847           1 :         return;
     848             : 
     849           1 :     case field::upgrade:
     850           1 :         md.upgrade = {};
     851           1 :         return;
     852             : 
     853          27 :     default:
     854          27 :         break;
     855             :     }
     856             : }
     857             : 
     858             : //------------------------------------------------
     859             : 
     860             : /*  References:
     861             : 
     862             :     3.3.  Message Body
     863             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
     864             : 
     865             :     3.3.1.  Transfer-Encoding
     866             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
     867             : 
     868             :     3.3.2.  Content-Length
     869             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     870             : */
     871             : void
     872        2330 : header::
     873             : update_payload() noexcept
     874             : {
     875        2330 :     BOOST_ASSERT(kind !=
     876             :         detail::kind::fields);
     877        2330 :     if(md.payload_override)
     878             :     {
     879             :         // e.g. response to
     880             :         // a HEAD request
     881           0 :         return;
     882             :     }
     883             : 
     884             : /*  If there is an error in either Content-Length
     885             :     or Transfer-Encoding, then the payload is
     886             :     undefined. Clients should probably close the
     887             :     connection. Servers can send a Bad Request
     888             :     and avoid reading any payload bytes.
     889             : */
     890        2330 :     if(md.content_length.ec.failed())
     891             :     {
     892             :         // invalid Content-Length
     893         128 :         md.payload = payload::error;
     894         128 :         md.payload_size = 0;
     895         128 :         return;
     896             :     }
     897        2202 :     if(md.transfer_encoding.ec.failed())
     898             :     {
     899             :         // invalid Transfer-Encoding
     900           8 :         md.payload = payload::error;
     901           8 :         md.payload_size = 0;
     902           8 :         return;
     903             :     }
     904             : 
     905             : /*  A sender MUST NOT send a Content-Length
     906             :     header field in any message that contains
     907             :     a Transfer-Encoding header field.
     908             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     909             : */
     910        2194 :     if( md.content_length.count > 0 &&
     911         427 :         md.transfer_encoding.count > 0)
     912             :     {
     913           3 :         md.payload = payload::error;
     914           3 :         md.payload_size = 0;
     915           3 :         return;
     916             :     }
     917             : 
     918        2191 :     if(kind == detail::kind::response)
     919         580 :         goto do_response;
     920             : 
     921             :     //--------------------------------------------
     922             : 
     923             : /*  The presence of a message body in a
     924             :     request is signaled by a Content-Length
     925             :     or Transfer-Encoding header field. Request
     926             :     message framing is independent of method
     927             :     semantics, even if the method does not
     928             :     define any use for a message body.
     929             : */
     930        1611 :     if(md.content_length.count > 0)
     931             :     {
     932         281 :         if(md.content_length.value > 0)
     933             :         {
     934             :             // non-zero Content-Length
     935         275 :             md.payload = payload::size;
     936         275 :             md.payload_size = md.content_length.value;
     937         275 :             return;
     938             :         }
     939             :         // Content-Length: 0
     940           6 :         md.payload = payload::none;
     941           6 :         md.payload_size = 0;
     942           6 :         return;
     943             :     }
     944        1330 :     if(md.transfer_encoding.is_chunked)
     945             :     {
     946             :         // chunked
     947          14 :         md.payload = payload::chunked;
     948          14 :         md.payload_size = 0;
     949          14 :         return;
     950             :     }
     951             :     // no payload
     952        1316 :     md.payload = payload::none;
     953        1316 :     md.payload_size = 0;
     954        1316 :     return;
     955             : 
     956             :     //--------------------------------------------
     957         580 : do_response:
     958             : 
     959         580 :     if( res.status_int /  100 == 1 ||   // 1xx e.g. Continue
     960         578 :         res.status_int == 204 ||        // No Content
     961         576 :         res.status_int == 304)          // Not Modified
     962             :     {
     963             :     /*  The correctness of any Content-Length
     964             :         here is defined by the particular
     965             :         resource, and cannot be determined
     966             :         here. In any case there is no payload.
     967             :     */
     968           6 :         md.payload = payload::none;
     969           6 :         md.payload_size = 0;
     970           6 :         return;
     971             :     }
     972         574 :     if(md.content_length.count > 0)
     973             :     {
     974         140 :         if(md.content_length.value > 0)
     975             :         {
     976             :             // Content-Length > 0
     977         129 :             md.payload = payload::size;
     978         129 :             md.payload_size = md.content_length.value;
     979         129 :             return;
     980             :         }
     981             :         // Content-Length: 0
     982          11 :         md.payload = payload::none;
     983          11 :         md.payload_size = 0;
     984          11 :         return;
     985             :     }
     986         434 :     if(md.transfer_encoding.is_chunked)
     987             :     {
     988             :         // chunked
     989           4 :         md.payload = payload::chunked;
     990           4 :         md.payload_size = 0;
     991           4 :         return;
     992             :     }
     993             : 
     994             :     // eof needed
     995         430 :     md.payload = payload::to_eof;
     996         430 :     md.payload_size = 0;
     997             : }
     998             : 
     999             : //------------------------------------------------
    1000             : 
    1001             : std::size_t
    1002         459 : header::
    1003             : count_crlf(
    1004             :     core::string_view s) noexcept
    1005             : {
    1006         459 :     auto it = s.data();
    1007         459 :     auto len = s.size();
    1008         459 :     std::size_t n = 0;
    1009       16563 :     while(len >= 2)
    1010             :     {
    1011       16104 :         if( it[0] == '\r' &&
    1012        1536 :             it[1] != '\r')
    1013             :         {
    1014        1536 :             if(it[1] == '\n')
    1015        1536 :                 n++;
    1016        1536 :             it += 2;
    1017        1536 :             len -= 2;
    1018             :         }
    1019             :         else
    1020             :         {
    1021       14568 :             it++;
    1022       14568 :             len--;
    1023             :         }
    1024             :     }
    1025         459 :     return n;
    1026             : }
    1027             : 
    1028             : static
    1029             : void
    1030        3268 : parse_start_line(
    1031             :     header& h,
    1032             :     header_limits const& lim,
    1033             :     std::size_t new_size,
    1034             :     system::error_code& ec) noexcept
    1035             : {
    1036        3268 :     BOOST_ASSERT(h.size == 0);
    1037        3268 :     BOOST_ASSERT(h.prefix == 0);
    1038        3268 :     BOOST_ASSERT(h.cbuf != nullptr);
    1039        3268 :     BOOST_ASSERT(
    1040             :         h.kind != detail::kind::fields);
    1041             : 
    1042        3268 :     auto const it0 = h.cbuf;
    1043        3268 :     auto const end = it0 + new_size;
    1044        3268 :     char const* it = it0;
    1045        3268 :     if( new_size > lim.max_start_line)
    1046           0 :         new_size = lim.max_start_line;
    1047        3268 :     if(h.kind == detail::kind::request)
    1048             :     {
    1049             :         auto rv = grammar::parse(
    1050        2684 :             it, end, request_line_rule);
    1051        2684 :         if(! rv)
    1052             :         {
    1053        1404 :             ec = rv.error();
    1054        2808 :             if( ec == grammar::error::need_more &&
    1055        1404 :                 new_size == lim.max_start_line)
    1056           0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1057             :                     error::start_line_limit);
    1058        1404 :             return;
    1059             :         }
    1060             :         // method
    1061        1280 :         auto sm = std::get<0>(*rv);
    1062        1280 :         h.req.method = string_to_method(sm);
    1063        1280 :         h.req.method_len =
    1064        1280 :             static_cast<offset_type>(sm.size());
    1065             :         // target
    1066        1280 :         auto st = std::get<1>(*rv);
    1067        1280 :         h.req.target_len =
    1068        1280 :             static_cast<offset_type>(st.size());
    1069             :         // version
    1070        1280 :         switch(std::get<2>(*rv))
    1071             :         {
    1072          20 :         case 10:
    1073          20 :             h.version =
    1074             :                 http_proto::version::http_1_0;
    1075          20 :             break;
    1076        1260 :         case 11:
    1077        1260 :             h.version =
    1078             :                 http_proto::version::http_1_1;
    1079        1260 :             break;
    1080           0 :         default:
    1081             :         {
    1082           0 :             ec = BOOST_HTTP_PROTO_ERR(
    1083             :                 error::bad_version);
    1084           0 :             return;
    1085             :         }
    1086             :         }
    1087             :     }
    1088             :     else
    1089             :     {
    1090             :         auto rv = grammar::parse(
    1091         584 :             it, end, status_line_rule);
    1092         584 :         if(! rv)
    1093             :         {
    1094         151 :             ec = rv.error();
    1095         302 :             if( ec == grammar::error::need_more &&
    1096         151 :                 new_size == lim.max_start_line)
    1097           0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1098             :                     error::start_line_limit);
    1099         151 :             return;
    1100             :         }
    1101             :         // version
    1102         433 :         switch(std::get<0>(*rv))
    1103             :         {
    1104           4 :         case 10:
    1105           4 :             h.version =
    1106             :                 http_proto::version::http_1_0;
    1107           4 :             break;
    1108         429 :         case 11:
    1109         429 :             h.version =
    1110             :                 http_proto::version::http_1_1;
    1111         429 :             break;
    1112           0 :         default:
    1113             :         {
    1114           0 :             ec = BOOST_HTTP_PROTO_ERR(
    1115             :                 error::bad_version);
    1116           0 :             return;
    1117             :         }
    1118             :         }
    1119             :         // status-code
    1120         433 :         h.res.status_int =
    1121             :             static_cast<unsigned short>(
    1122         433 :                 std::get<1>(*rv).v);
    1123         433 :         h.res.status = std::get<1>(*rv).st;
    1124             :     }
    1125        1713 :     h.prefix = static_cast<offset_type>(it - it0);
    1126        1713 :     h.size = h.prefix;
    1127        1713 :     h.on_start_line();
    1128             : }
    1129             : 
    1130             : // returns: true if we added a field
    1131             : static
    1132             : void
    1133        5837 : parse_field(
    1134             :     header& h,
    1135             :     header_limits const& lim,
    1136             :     std::size_t new_size,
    1137             :     system::error_code& ec) noexcept
    1138             : {
    1139        5837 :     if( new_size > lim.max_field)
    1140           0 :         new_size = lim.max_field;
    1141        5837 :     auto const it0 = h.cbuf + h.size;
    1142        5837 :     auto const end = h.cbuf + new_size;
    1143        5837 :     char const* it = it0;
    1144             :     auto rv = grammar::parse(
    1145        5837 :         it, end, field_rule);
    1146        5837 :     if(rv.has_error())
    1147             :     {
    1148        3349 :         ec = rv.error();
    1149        3349 :         if(ec == grammar::error::end_of_range)
    1150             :         {
    1151             :             // final CRLF
    1152        1786 :             h.size = static_cast<
    1153        1786 :                 offset_type>(it - h.cbuf);
    1154        3349 :             return;
    1155             :         }
    1156        2998 :         if( ec == grammar::error::need_more &&
    1157        1435 :             new_size == lim.max_field)
    1158             :         {
    1159           0 :             ec = BOOST_HTTP_PROTO_ERR(
    1160             :                 error::field_size_limit);
    1161             :         }
    1162        1563 :         return;
    1163             :     }
    1164        2488 :     if(h.count >= lim.max_fields)
    1165             :     {
    1166           0 :         ec = BOOST_HTTP_PROTO_ERR(
    1167             :             error::fields_limit);
    1168           0 :         return;
    1169             :     }
    1170        2488 :     if(rv->has_obs_fold)
    1171             :     {
    1172             :         // obs fold not allowed in test views
    1173         137 :         BOOST_ASSERT(h.buf != nullptr);
    1174         137 :         remove_obs_fold(h.buf + h.size, it);
    1175             :     }
    1176        2488 :     auto id = string_to_field(rv->name);
    1177        2488 :     h.size = static_cast<offset_type>(it - h.cbuf);
    1178             : 
    1179             :     // add field table entry
    1180        2488 :     if(h.buf != nullptr)
    1181             :     {
    1182        4976 :         auto& e = header::table(
    1183        2488 :             h.buf + h.cap)[h.count];
    1184        2488 :         auto const base =
    1185        2488 :             h.buf + h.prefix;
    1186        2488 :         e.np = static_cast<offset_type>(
    1187        2488 :             rv->name.data() - base);
    1188        2488 :         e.nn = static_cast<offset_type>(
    1189        2488 :             rv->name.size());
    1190        2488 :         e.vp = static_cast<offset_type>(
    1191        2488 :             rv->value.data() - base);
    1192        2488 :         e.vn = static_cast<offset_type>(
    1193        2488 :             rv->value.size());
    1194        2488 :         e.id = id;
    1195             :     }
    1196        2488 :     ++h.count;
    1197        2488 :     h.on_insert(id, rv->value);
    1198        2488 :     ec = {};
    1199             : }
    1200             : 
    1201             : void
    1202        4904 : header::
    1203             : parse(
    1204             :     std::size_t new_size,
    1205             :     header_limits const& lim,
    1206             :     system::error_code& ec) noexcept
    1207             : {
    1208        4904 :     if( new_size > lim.max_size)
    1209           0 :         new_size = lim.max_size;
    1210        4904 :     if( this->prefix == 0 &&
    1211        3469 :         this->kind !=
    1212             :             detail::kind::fields)
    1213             :     {
    1214        3268 :         parse_start_line(
    1215             :             *this, lim, new_size, ec);
    1216        3268 :         if(ec.failed())
    1217             :         {
    1218        3110 :             if( ec == grammar::error::need_more &&
    1219        1555 :                 new_size == lim.max_fields)
    1220             :             {
    1221           0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1222             :                     error::headers_limit);
    1223             :             }
    1224        1555 :             return;
    1225             :         }
    1226             :     }
    1227             :     for(;;)
    1228             :     {
    1229        5837 :         parse_field(
    1230             :             *this, lim, new_size, ec);
    1231        5837 :         if(ec.failed())
    1232             :         {
    1233        4784 :             if( ec == grammar::error::need_more &&
    1234        1435 :                 new_size == lim.max_size)
    1235             :             {
    1236           0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1237             :                     error::headers_limit);
    1238           0 :                 return;
    1239             :             }
    1240        3349 :             break;
    1241             :         }
    1242        2488 :     }
    1243        3349 :     if(ec == grammar::error::end_of_range)
    1244        1786 :         ec = {};
    1245             : }
    1246             : 
    1247             : } // detail
    1248             : } // http_proto
    1249             : } // boost

Generated by: LCOV version 1.15