LCOV - code coverage report
Current view: top level - src/detail - header.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 92.1 % 611 563 48
Test Date: 2026-06-13 19:44:58 Functions: 82.5 % 57 47 10

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

Generated by: LCOV version 2.3