LCOV - code coverage report
Current view: top level - src/detail - header.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 92.9 % 631 586 45
Test Date: 2026-08-26 15:14:03 Functions: 95.5 % 44 42 2

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

Generated by: LCOV version 2.3