LCOV - code coverage report
Current view: top level - libs/http_proto/src/detail/header.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.0 % 585 550
Test Date: 2024-05-23 18:56:44 Functions: 83.9 % 56 47

            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/detail/align_up.hpp>
      12              : #include <boost/http_proto/field.hpp>
      13              : #include <boost/http_proto/fields_view_base.hpp>
      14              : #include <boost/http_proto/header_limits.hpp>
      15              : #include <boost/http_proto/rfc/list_rule.hpp>
      16              : #include <boost/http_proto/rfc/token_rule.hpp>
      17              : #include <boost/http_proto/rfc/transfer_encoding_rule.hpp>
      18              : #include <boost/http_proto/rfc/upgrade_rule.hpp>
      19              : #include <boost/http_proto/rfc/detail/rules.hpp>
      20              : #include <boost/url/grammar/ci_string.hpp>
      21              : #include <boost/url/grammar/parse.hpp>
      22              : #include <boost/url/grammar/range_rule.hpp>
      23              : #include <boost/url/grammar/recycled.hpp>
      24              : #include <boost/url/grammar/unsigned_rule.hpp>
      25              : #include <boost/assert.hpp>
      26              : #include <boost/assert/source_location.hpp>
      27              : #include <boost/static_assert.hpp>
      28              : #include <string>
      29              : #include <utility>
      30              : 
      31              : namespace boost {
      32              : namespace http_proto {
      33              : namespace detail {
      34              : 
      35              : //------------------------------------------------
      36              : 
      37              : auto
      38          115 : header::
      39              : entry::
      40              : operator+(
      41              :     std::size_t dv) const noexcept ->
      42              :         entry
      43              : {
      44              :     return {
      45              :         static_cast<
      46          115 :             offset_type>(np + dv),
      47          115 :         nn,
      48              :         static_cast<
      49          115 :             offset_type>(vp + dv),
      50          115 :         vn,
      51          115 :         id };
      52              : }
      53              : 
      54              : auto
      55           79 : header::
      56              : entry::
      57              : operator-(
      58              :     std::size_t dv) const noexcept ->
      59              :         entry
      60              : {
      61              :     return {
      62              :         static_cast<
      63           79 :             offset_type>(np - dv),
      64           79 :         nn,
      65              :         static_cast<
      66           79 :             offset_type>(vp - dv),
      67           79 :         vn,
      68           79 :         id };
      69              : }
      70              : 
      71              : //------------------------------------------------
      72              : 
      73              : constexpr
      74              : header::
      75              : header(fields_tag) noexcept
      76              :     : kind(detail::kind::fields)
      77              :     , cbuf("\r\n")
      78              :     , size(2)
      79              :     , fld{}
      80              : {
      81              : }
      82              : 
      83              : constexpr
      84              : header::
      85              : header(request_tag) noexcept
      86              :     : kind(detail::kind::request)
      87              :     , cbuf("GET / HTTP/1.1\r\n\r\n")
      88              :     , size(18)
      89              :     , prefix(16)
      90              :     , req{ 3, 1,
      91              :         http_proto::method::get }
      92              : {
      93              : }
      94              : 
      95              : constexpr
      96              : header::
      97              : header(response_tag) noexcept
      98              :     : kind(detail::kind::response)
      99              :     , cbuf("HTTP/1.1 200 OK\r\n\r\n")
     100              :     , size(19)
     101              :     , prefix(17)
     102              :     , res{ 200,
     103              :         http_proto::status::ok }
     104              : {
     105              : }
     106              : 
     107              : //------------------------------------------------
     108              : 
     109              : header const*
     110          201 : header::
     111              : get_default(detail::kind k) noexcept
     112              : {
     113              :     static constexpr header h[3] = {
     114              :         fields_tag{},
     115              :         request_tag{},
     116              :         response_tag{}};
     117          201 :     return &h[k];
     118              : }
     119              : 
     120         3315 : header::
     121         3315 : header(empty v) noexcept
     122         3315 :     : kind(v.param)
     123              : {
     124         3315 : }
     125              : 
     126          182 : header::
     127          182 : header(detail::kind k) noexcept
     128          182 :     : header(*get_default(k))
     129              : {
     130          182 : }
     131              : 
     132              : void
     133           74 : header::
     134              : swap(header& h) noexcept
     135              : {
     136           74 :     std::swap(cbuf, h.cbuf);
     137           74 :     std::swap(buf, h.buf);
     138           74 :     std::swap(cap, h.cap);
     139           74 :     std::swap(max_cap, h.max_cap);
     140           74 :     std::swap(size, h.size);
     141           74 :     std::swap(count, h.count);
     142           74 :     std::swap(prefix, h.prefix);
     143           74 :     std::swap(version, h.version);
     144           74 :     std::swap(md, h.md);
     145           74 :     switch(kind)
     146              :     {
     147           18 :     default:
     148              :     case detail::kind::fields:
     149           18 :         break;
     150           47 :     case detail::kind::request:
     151           47 :         std::swap(
     152           47 :             req.method_len, h.req.method_len);
     153           47 :         std::swap(
     154           47 :             req.target_len, h.req.target_len);
     155           47 :         std::swap(req.method, h.req.method);
     156           47 :         break;
     157            9 :     case detail::kind::response:
     158            9 :         std::swap(
     159            9 :             res.status_int, h.res.status_int);
     160            9 :         std::swap(res.status, h.res.status);
     161            9 :         break;
     162              :     }
     163           74 : }
     164              : 
     165              : /*  References:
     166              : 
     167              :     6.3.  Persistence
     168              :     https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
     169              : */
     170              : bool
     171           22 : header::
     172              : keep_alive() const noexcept
     173              : {
     174           22 :     if(md.payload == payload::error)
     175            1 :         return false;
     176           21 :     if( version ==
     177              :         http_proto::version::http_1_1)
     178              :     {
     179           13 :         if(md.connection.close)
     180            3 :             return false;
     181              :     }
     182              :     else
     183              :     {
     184            8 :         if(! md.connection.keep_alive)
     185            4 :             return false;
     186              :     }
     187              :     // can't use to_eof in requests
     188           14 :     BOOST_ASSERT(
     189              :         kind != detail::kind::request ||
     190              :         md.payload != payload::to_eof);
     191           14 :     if(md.payload == payload::to_eof)
     192            3 :         return false;
     193           11 :     return true;
     194              : }
     195              : 
     196              : //------------------------------------------------
     197              : 
     198              : // return total bytes needed
     199              : // to store message of `size`
     200              : // bytes and `count` fields.
     201              : std::size_t
     202          833 : header::
     203              : bytes_needed(
     204              :     std::size_t size,
     205              :     std::size_t count) noexcept
     206              : {
     207              :     // make sure `size` is big enough
     208              :     // to hold the largest default buffer:
     209              :     // "HTTP/1.1 200 OK\r\n\r\n"
     210          833 :     if( size < 19)
     211          172 :         size = 19;
     212              :     static constexpr auto A =
     213              :         alignof(header::entry);
     214          833 :     return align_up(size, A) +
     215          833 :             (count * sizeof(
     216          833 :                 header::entry));
     217              : }
     218              : 
     219              : std::size_t
     220         1329 : header::
     221              : table_space(
     222              :     std::size_t count) noexcept
     223              : {
     224              :     return count *
     225         1329 :         sizeof(header::entry);
     226              : }
     227              : 
     228              : std::size_t
     229         1329 : header::
     230              : table_space() const noexcept
     231              : {
     232         1329 :     return table_space(count);
     233              : }
     234              : 
     235              : auto
     236         2500 : header::
     237              : tab() const noexcept ->
     238              :     table
     239              : {
     240         2500 :     BOOST_ASSERT(cap > 0);
     241         2500 :     BOOST_ASSERT(buf != nullptr);
     242         2500 :     return table(buf + cap);
     243              : }
     244              : 
     245              : auto
     246          689 : header::
     247              : tab_() const noexcept ->
     248              :     entry*
     249              : {
     250              :     return reinterpret_cast<
     251          689 :         entry*>(buf + cap);
     252              : }
     253              : 
     254              : // return true if header cbuf is a default
     255              : bool
     256           43 : header::
     257              : is_default() const noexcept
     258              : {
     259           43 :     return buf == nullptr;
     260              : }
     261              : 
     262              : std::size_t
     263           71 : header::
     264              : find(
     265              :     field id) const noexcept
     266              : {
     267           71 :     if(count == 0)
     268            6 :         return 0;
     269           65 :     std::size_t i = 0;
     270           65 :     auto const* p = &tab()[0];
     271           89 :     while(i < count)
     272              :     {
     273           89 :         if(p->id == id)
     274           65 :             break;
     275           24 :         ++i;
     276           24 :         --p;
     277              :     }
     278           65 :     return i;
     279              : }
     280              : 
     281              : std::size_t
     282           20 : header::
     283              : find(
     284              :     core::string_view name) const noexcept
     285              : {
     286           20 :     if(count == 0)
     287            5 :         return 0;
     288           15 :     std::size_t i = 0;
     289           15 :     auto const* p = &tab()[0];
     290           21 :     while(i < count)
     291              :     {
     292              :         core::string_view s(
     293           21 :             cbuf + prefix + p->np,
     294           21 :             p->nn);
     295           21 :         if(grammar::ci_is_equal(s, name))
     296           15 :             break;
     297            6 :         ++i;
     298            6 :         --p;
     299              :     }
     300           15 :     return i;
     301              : }
     302              : 
     303              : void
     304           30 : header::
     305              : copy_table(
     306              :     void* dest,
     307              :     std::size_t n) const noexcept
     308              : {
     309           30 :     std::memcpy(
     310              :         reinterpret_cast<
     311           30 :             entry*>(dest) - n,
     312              :         reinterpret_cast<
     313              :             entry const*>(
     314           30 :                 cbuf + cap) - n,
     315              :         n * sizeof(entry));
     316           30 : }
     317              : 
     318              : void
     319           30 : header::
     320              : copy_table(
     321              :     void* dest) const noexcept
     322              : {
     323           30 :     copy_table(dest, count);
     324           30 : }
     325              : 
     326              : // assign all the members but
     327              : // preserve the allocated memory
     328              : void
     329           30 : header::
     330              : assign_to(
     331              :     header& dest) const noexcept
     332              : {
     333           30 :     auto const buf_ = dest.buf;
     334           30 :     auto const cbuf_ = dest.cbuf;
     335           30 :     auto const cap_ = dest.cap;
     336           30 :     dest = *this;
     337           30 :     dest.buf = buf_;
     338           30 :     dest.cbuf = cbuf_;
     339           30 :     dest.cap = cap_;
     340           30 : }
     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           21 : header::
     375              : is_special(
     376              :     field id) const noexcept
     377              : {
     378           21 :     if(kind == detail::kind::fields)
     379            4 :         return false;
     380           17 :     switch(id)
     381              :     {
     382            9 :     case field::connection:
     383              :     case field::content_length:
     384              :     case field::expect:
     385              :     case field::transfer_encoding:
     386              :     case field::upgrade:
     387            9 :         return true;
     388            8 :     default:
     389            8 :         break;
     390              :     }
     391            8 :     return false;
     392              : }
     393              : 
     394              : //------------------------------------------------
     395              : 
     396              : // called when the start-line changes
     397              : void
     398         2034 : 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         2034 :     update_payload();
     408         2034 : }
     409              : 
     410              : // called after a field is inserted
     411              : void
     412         2916 : header::
     413              : on_insert(
     414              :     field id,
     415              :     core::string_view v)
     416              : {
     417         2916 :     if(kind == detail::kind::fields)
     418          510 :         return;
     419         2406 :     switch(id)
     420              :     {
     421          590 :     case field::content_length:
     422          590 :         return on_insert_content_length(v);
     423          147 :     case field::connection:
     424          147 :         return on_insert_connection(v);
     425           47 :     case field::expect:
     426           47 :         return on_insert_expect(v);
     427           48 :     case field::transfer_encoding:
     428           48 :         return on_insert_transfer_encoding();
     429           24 :     case field::upgrade:
     430           24 :         return on_insert_upgrade(v);
     431         1550 :     default:
     432         1550 :         break;
     433              :     }
     434              : }
     435              : 
     436              : // called when one field is erased
     437              : void
     438           40 : header::
     439              : on_erase(field id)
     440              : {
     441           40 :     if(kind == detail::kind::fields)
     442            3 :         return;
     443           37 :     switch(id)
     444              :     {
     445            9 :     case field::connection:
     446            9 :         return on_erase_connection();
     447            4 :     case field::content_length:
     448            4 :         return on_erase_content_length();
     449           10 :     case field::expect:
     450           10 :         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          151 : header::
     467              : on_insert_connection(
     468              :     core::string_view v)
     469              : {
     470          151 :     ++md.connection.count;
     471          151 :     if(md.connection.ec.failed())
     472            5 :         return;
     473              :     auto rv = grammar::parse(
     474          150 :         v, list_rule(token_rule, 1));
     475          150 :     if(! rv)
     476              :     {
     477            4 :         md.connection.ec =
     478            8 :             BOOST_HTTP_PROTO_ERR(
     479              :                 error::bad_connection);
     480            4 :         return;
     481              :     }
     482          146 :     md.connection.ec = {};
     483          303 :     for(auto t : *rv)
     484              :     {
     485          157 :         if(grammar::ci_is_equal(
     486              :                 t, "close"))
     487          107 :             md.connection.close = true;
     488           50 :         else if(grammar::ci_is_equal(
     489              :                 t, "keep-alive"))
     490           26 :             md.connection.keep_alive = true;
     491           24 :         else if(grammar::ci_is_equal(
     492              :                 t, "upgrade"))
     493           19 :             md.connection.upgrade = true;
     494              :     }
     495          150 : }
     496              : 
     497              : void
     498          591 : 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          591 :     ++md.content_length.count;
     508          591 :     if(md.content_length.ec.failed())
     509          468 :         return;
     510              :     auto rv =
     511          589 :         grammar::parse(v, num_rule);
     512          589 :     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          584 :     if(md.content_length.count == 1)
     523              :     {
     524              :         // one value
     525          454 :         md.content_length.ec = {};
     526          454 :         md.content_length.value = *rv;
     527          454 :         update_payload();
     528          454 :         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           53 : header::
     545              : on_insert_expect(
     546              :     core::string_view v)
     547              : {
     548           53 :     ++md.expect.count;
     549           53 :     if(kind != detail::kind::request)
     550            8 :         return;
     551           45 :     if(md.expect.ec.failed())
     552            4 :         return;
     553              :     // VFALCO Should we allow duplicate
     554              :     // Expect fields that have 100-continue?
     555           73 :     if( md.expect.count > 1 ||
     556           73 :         ! grammar::ci_is_equal(v,
     557              :             "100-continue"))
     558              :     {
     559           19 :         md.expect.ec =
     560           38 :             BOOST_HTTP_PROTO_ERR(
     561              :                 error::bad_expect);
     562           19 :         md.expect.is_100_continue = false;
     563           19 :         return;
     564              :     }
     565           22 :     md.expect.is_100_continue = true;
     566              : }
     567              : 
     568              : void
     569           51 : header::
     570              : on_insert_transfer_encoding()
     571              : {
     572           51 :     ++md.transfer_encoding.count;
     573           51 :     if(md.transfer_encoding.ec.failed())
     574            1 :         return;
     575           50 :     auto const n =
     576              :         md.transfer_encoding.count;
     577           50 :     md.transfer_encoding = {};
     578           50 :     md.transfer_encoding.count = n;
     579           50 :     for(auto s :
     580              :         fields_view_base::subrange(
     581          157 :             this, find(field::transfer_encoding)))
     582              :     {
     583              :         auto rv = grammar::parse(
     584           65 :             s, transfer_encoding_rule);
     585           65 :         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           61 :         md.transfer_encoding.codings += rv->size();
     597          127 :         for(auto t : *rv)
     598              :         {
     599           70 :             if(! md.transfer_encoding.is_chunked)
     600              :             {
     601           66 :                 if(t.id == transfer_coding::chunked)
     602           30 :                     md.transfer_encoding.is_chunked = true;
     603           66 :                 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          135 :         }
     625           65 :     }
     626           42 :     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           24 : }
     670              : 
     671              : //------------------------------------------------
     672              : 
     673              : void
     674            9 : header::
     675              : on_erase_connection()
     676              : {
     677            9 :     BOOST_ASSERT(
     678              :         md.connection.count > 0);
     679              :     // reset and re-insert
     680            9 :     auto n = md.connection.count - 1;
     681            9 :     auto const p = cbuf + prefix;
     682            9 :     auto const* e = &tab()[0];
     683            9 :     md.connection = {};
     684           14 :     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            9 : }
     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           10 : header::
     733              : on_erase_expect()
     734              : {
     735           10 :     BOOST_ASSERT(
     736              :         md.expect.count > 0);
     737           10 :     --md.expect.count;
     738           10 :     if(kind != detail::kind::request)
     739            1 :         return;
     740            9 :     if(md.expect.count == 0)
     741              :     {
     742              :         // no Expect
     743            3 :         md.expect = {};
     744            3 :         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            6 :     auto n = count;
     755            6 :     auto const p = cbuf + prefix;
     756            6 :     auto const* e = &tab()[0];
     757            6 :     md.expect = {};
     758           19 :     while(n > 0)
     759              :     {
     760           13 :         if(e->id == field::expect)
     761            6 :             on_insert_expect(
     762              :                 core::string_view(
     763            6 :                     p + e->vp, e->vn));
     764           13 :         --n;
     765           13 :         --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           60 : header::
     823              : on_erase_all(
     824              :     field id)
     825              : {
     826           60 :     if(kind == detail::kind::fields)
     827           17 :         return;
     828           43 :     switch(id)
     829              :     {
     830            3 :     case field::connection:
     831            3 :         md.connection = {};
     832            3 :         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           31 :     default:
     854           31 :         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         2678 : header::
     873              : update_payload() noexcept
     874              : {
     875         2678 :     BOOST_ASSERT(kind !=
     876              :         detail::kind::fields);
     877         2678 :     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         2678 :     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         2550 :     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         2542 :     if( md.content_length.count > 0 &&
     911          458 :         md.transfer_encoding.count > 0)
     912              :     {
     913            3 :         md.payload = payload::error;
     914            3 :         md.payload_size = 0;
     915            3 :         return;
     916              :     }
     917              : 
     918         2539 :     if(kind == detail::kind::response)
     919          639 :         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         1900 :     if(md.content_length.count > 0)
     931              :     {
     932          297 :         if(md.content_length.value > 0)
     933              :         {
     934              :             // non-zero Content-Length
     935          291 :             md.payload = payload::size;
     936          291 :             md.payload_size = md.content_length.value;
     937          291 :             return;
     938              :         }
     939              :         // Content-Length: 0
     940            6 :         md.payload = payload::none;
     941            6 :         md.payload_size = 0;
     942            6 :         return;
     943              :     }
     944         1603 :     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         1589 :     md.payload = payload::none;
     953         1589 :     md.payload_size = 0;
     954         1589 :     return;
     955              : 
     956              :     //--------------------------------------------
     957          639 : do_response:
     958              : 
     959          639 :     if( res.status_int /  100 == 1 ||   // 1xx e.g. Continue
     960          629 :         res.status_int == 204 ||        // No Content
     961          627 :         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           14 :         md.payload = payload::none;
     969           14 :         md.payload_size = 0;
     970           14 :         return;
     971              :     }
     972          625 :     if(md.content_length.count > 0)
     973              :     {
     974          155 :         if(md.content_length.value > 0)
     975              :         {
     976              :             // Content-Length > 0
     977          142 :             md.payload = payload::size;
     978          142 :             md.payload_size = md.content_length.value;
     979          142 :             return;
     980              :         }
     981              :         // Content-Length: 0
     982           13 :         md.payload = payload::none;
     983           13 :         md.payload_size = 0;
     984           13 :         return;
     985              :     }
     986          470 :     if(md.transfer_encoding.is_chunked)
     987              :     {
     988              :         // chunked
     989            9 :         md.payload = payload::chunked;
     990            9 :         md.payload_size = 0;
     991            9 :         return;
     992              :     }
     993              : 
     994              :     // eof needed
     995          461 :     md.payload = payload::to_eof;
     996          461 :     md.payload_size = 0;
     997              : }
     998              : 
     999              : //------------------------------------------------
    1000              : 
    1001              : std::size_t
    1002          535 : header::
    1003              : count_crlf(
    1004              :     core::string_view s) noexcept
    1005              : {
    1006          535 :     auto it = s.data();
    1007          535 :     auto len = s.size();
    1008          535 :     std::size_t n = 0;
    1009        18834 :     while(len >= 2)
    1010              :     {
    1011        18299 :         if( it[0] == '\r' &&
    1012         1714 :             it[1] != '\r')
    1013              :         {
    1014         1714 :             if(it[1] == '\n')
    1015         1714 :                 n++;
    1016         1714 :             it += 2;
    1017         1714 :             len -= 2;
    1018              :         }
    1019              :         else
    1020              :         {
    1021        16585 :             it++;
    1022        16585 :             len--;
    1023              :         }
    1024              :     }
    1025          535 :     return n;
    1026              : }
    1027              : 
    1028              : static
    1029              : void
    1030         3960 : 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         3960 :     BOOST_ASSERT(h.size == 0);
    1037         3960 :     BOOST_ASSERT(h.prefix == 0);
    1038         3960 :     BOOST_ASSERT(h.cbuf != nullptr);
    1039         3960 :     BOOST_ASSERT(
    1040              :         h.kind != detail::kind::fields);
    1041              : 
    1042         3960 :     auto const it0 = h.cbuf;
    1043         3960 :     auto const end = it0 + new_size;
    1044         3960 :     char const* it = it0;
    1045         3960 :     if( new_size > lim.max_start_line)
    1046            0 :         new_size = lim.max_start_line;
    1047         3960 :     if(h.kind == detail::kind::request)
    1048              :     {
    1049              :         auto rv = grammar::parse(
    1050         3356 :             it, end, request_line_rule);
    1051         3356 :         if(! rv)
    1052              :         {
    1053         1809 :             ec = rv.error();
    1054         3618 :             if( ec == grammar::error::need_more &&
    1055         1809 :                 new_size == lim.max_start_line)
    1056            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1057              :                     error::start_line_limit);
    1058         1809 :             return;
    1059              :         }
    1060              :         // method
    1061         1547 :         auto sm = std::get<0>(*rv);
    1062         1547 :         h.req.method = string_to_method(sm);
    1063         1547 :         h.req.method_len =
    1064         1547 :             static_cast<offset_type>(sm.size());
    1065              :         // target
    1066         1547 :         auto st = std::get<1>(*rv);
    1067         1547 :         h.req.target_len =
    1068         1547 :             static_cast<offset_type>(st.size());
    1069              :         // version
    1070         1547 :         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         1527 :         case 11:
    1077         1527 :             h.version =
    1078              :                 http_proto::version::http_1_1;
    1079         1527 :             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          604 :             it, end, status_line_rule);
    1092          604 :         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          453 :         switch(std::get<0>(*rv))
    1103              :         {
    1104            5 :         case 10:
    1105            5 :             h.version =
    1106              :                 http_proto::version::http_1_0;
    1107            5 :             break;
    1108          448 :         case 11:
    1109          448 :             h.version =
    1110              :                 http_proto::version::http_1_1;
    1111          448 :             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          453 :         h.res.status_int =
    1121              :             static_cast<unsigned short>(
    1122          453 :                 std::get<1>(*rv).v);
    1123          453 :         h.res.status = std::get<1>(*rv).st;
    1124              :     }
    1125         2000 :     h.prefix = static_cast<offset_type>(it - it0);
    1126         2000 :     h.size = h.prefix;
    1127         2000 :     h.on_start_line();
    1128              : }
    1129              : 
    1130              : // returns: true if we added a field
    1131              : static
    1132              : void
    1133         6781 : parse_field(
    1134              :     header& h,
    1135              :     header_limits const& lim,
    1136              :     std::size_t new_size,
    1137              :     system::error_code& ec) noexcept
    1138              : {
    1139         6781 :     if( new_size > lim.max_field)
    1140            0 :         new_size = lim.max_field;
    1141         6781 :     auto const it0 = h.cbuf + h.size;
    1142         6781 :     auto const end = h.cbuf + new_size;
    1143         6781 :     char const* it = it0;
    1144         6781 :     auto rv = grammar::parse(
    1145              :         it, end, field_rule);
    1146         6781 :     if(rv.has_error())
    1147              :     {
    1148         4072 :         ec = rv.error();
    1149         4072 :         if(ec == grammar::error::end_of_range)
    1150              :         {
    1151              :             // final CRLF
    1152         1981 :             h.size = static_cast<
    1153         1981 :                 offset_type>(it - h.cbuf);
    1154         4072 :             return;
    1155              :         }
    1156         3923 :         if( ec == grammar::error::need_more &&
    1157         1832 :             new_size == lim.max_field)
    1158              :         {
    1159            0 :             ec = BOOST_HTTP_PROTO_ERR(
    1160              :                 error::field_size_limit);
    1161              :         }
    1162         2091 :         return;
    1163              :     }
    1164         2709 :     if(h.count >= lim.max_fields)
    1165              :     {
    1166            0 :         ec = BOOST_HTTP_PROTO_ERR(
    1167              :             error::fields_limit);
    1168            0 :         return;
    1169              :     }
    1170         2709 :     if(rv->has_obs_fold)
    1171              :     {
    1172              :         // obs fold not allowed in test views
    1173          210 :         BOOST_ASSERT(h.buf != nullptr);
    1174          210 :         remove_obs_fold(h.buf + h.size, it);
    1175              :     }
    1176         2709 :     auto id = string_to_field(rv->name);
    1177         2709 :     h.size = static_cast<offset_type>(it - h.cbuf);
    1178              : 
    1179              :     // add field table entry
    1180         2709 :     if(h.buf != nullptr)
    1181              :     {
    1182         5418 :         auto& e = header::table(
    1183         2709 :             h.buf + h.cap)[h.count];
    1184         2709 :         auto const base =
    1185         2709 :             h.buf + h.prefix;
    1186         2709 :         e.np = static_cast<offset_type>(
    1187         2709 :             rv->name.data() - base);
    1188         2709 :         e.nn = static_cast<offset_type>(
    1189         2709 :             rv->name.size());
    1190         2709 :         e.vp = static_cast<offset_type>(
    1191         2709 :             rv->value.data() - base);
    1192         2709 :         e.vn = static_cast<offset_type>(
    1193         2709 :             rv->value.size());
    1194         2709 :         e.id = id;
    1195              :     }
    1196         2709 :     ++h.count;
    1197         2709 :     h.on_insert(id, rv->value);
    1198         2709 :     ec = {};
    1199              : }
    1200              : 
    1201              : void
    1202         6032 : header::
    1203              : parse(
    1204              :     std::size_t new_size,
    1205              :     header_limits const& lim,
    1206              :     system::error_code& ec) noexcept
    1207              : {
    1208         6032 :     if( new_size > lim.max_size)
    1209            0 :         new_size = lim.max_size;
    1210         6032 :     if( this->prefix == 0 &&
    1211         4200 :         this->kind !=
    1212              :             detail::kind::fields)
    1213              :     {
    1214         3960 :         parse_start_line(
    1215              :             *this, lim, new_size, ec);
    1216         3960 :         if(ec.failed())
    1217              :         {
    1218         3920 :             if( ec == grammar::error::need_more &&
    1219         1960 :                 new_size == lim.max_fields)
    1220              :             {
    1221            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1222              :                     error::headers_limit);
    1223              :             }
    1224         1960 :             return;
    1225              :         }
    1226              :     }
    1227              :     for(;;)
    1228              :     {
    1229         6781 :         parse_field(
    1230              :             *this, lim, new_size, ec);
    1231         6781 :         if(ec.failed())
    1232              :         {
    1233         5904 :             if( ec == grammar::error::need_more &&
    1234         1832 :                 new_size == lim.max_size)
    1235              :             {
    1236            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1237              :                     error::headers_limit);
    1238            0 :                 return;
    1239              :             }
    1240         4072 :             break;
    1241              :         }
    1242         2709 :     }
    1243         4072 :     if(ec == grammar::error::end_of_range)
    1244         1981 :         ec = {};
    1245              : }
    1246              : 
    1247              : } // detail
    1248              : } // http_proto
    1249              : } // boost
        

Generated by: LCOV version 2.1