LCOV - code coverage report
Current view: top level - src/detail - header.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 92.7 % 614 569 45
Test Date: 2026-07-22 18:52:55 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            4872 : 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            4872 :     return &h[k];
     122                 : }
     123                 : 
     124           13370 : header::
     125           13370 : header(empty v) noexcept
     126           13370 :     : kind(v.param)
     127                 : {
     128           13370 : }
     129                 : 
     130            2679 : header::
     131            2679 : header(detail::kind k) noexcept
     132            2679 :     : header(*get_default(k))
     133                 : {
     134            2679 : }
     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            3156 : 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            3156 :     if(size < 19)
     214            2388 :         size = 19;
     215                 : 
     216                 :     // align size up to alignof(entry)
     217            3156 :     size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
     218                 : 
     219            3156 :     return size + count * sizeof(entry);
     220                 : }
     221                 : 
     222                 : std::size_t
     223            9585 : header::
     224                 : table_space(
     225                 :     std::size_t count) noexcept
     226                 : {
     227                 :     return count *
     228            9585 :         sizeof(header::entry);
     229                 : }
     230                 : 
     231                 : std::size_t
     232            9585 : header::
     233                 : table_space() const noexcept
     234                 : {
     235            9585 :     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            2267 : 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            2267 :     if(n == 0)
     315            2246 :         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            2267 : header::
     328                 : copy_table(
     329                 :     void* dest) const noexcept
     330                 : {
     331            2267 :     copy_table(dest, count);
     332            2267 : }
     333                 : 
     334                 : // assign all the members but
     335                 : // preserve the allocated memory
     336                 : void
     337            2217 : header::
     338                 : assign_to(
     339                 :     header& dest) const noexcept
     340                 : {
     341            2217 :     auto const buf_ = dest.buf;
     342            2217 :     auto const cbuf_ = dest.cbuf;
     343            2217 :     auto const cap_ = dest.cap;
     344            2217 :     dest = *this;
     345            2217 :     dest.buf = buf_;
     346            2217 :     dest.cbuf = cbuf_;
     347            2217 :     dest.cap = cap_;
     348            2217 : }
     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           10668 : 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           10668 :     update_payload();
     419           10668 : }
     420                 : 
     421                 : // called after a field is inserted
     422                 : void
     423           11031 : header::
     424                 : on_insert(
     425                 :     field id,
     426                 :     core::string_view v)
     427                 : {
     428           11031 :     if(kind == detail::kind::fields)
     429             482 :         return;
     430           10549 :     switch(id)
     431                 :     {
     432               7 :     case field::content_encoding:
     433               7 :         return on_insert_content_encoding(v);
     434            4557 :     case field::content_length:
     435            4557 :         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            4558 : 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            4558 :     ++md.content_length.count;
     523            4558 :     if(md.content_length.ec)
     524            4495 :         return;
     525                 :     auto rv =
     526            4556 :         grammar::parse(v, num_rule);
     527            4556 :     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            4551 :     if(md.content_length.count == 1)
     538                 :     {
     539                 :         // one value
     540            4481 :         md.content_length.ec = {};
     541            4481 :         md.content_length.value = *rv;
     542            4481 :         update_payload();
     543            4481 :         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               7 : header::
     628                 : on_insert_content_encoding(
     629                 :     core::string_view v)
     630                 : {
     631               7 :     ++md.content_encoding.count;
     632               7 :     if(md.content_encoding.ec)
     633               3 :         return;
     634                 : 
     635                 :     auto rv = grammar::parse(
     636               7 :         v, list_rule(token_rule, 1));
     637               7 :     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               6 :     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               8 :     if(grammar::ci_is_equal(
     655               8 :         *rv->begin(), "deflate"))
     656                 :     {
     657 MIS           0 :         md.content_encoding.coding =
     658                 :             content_coding::deflate;
     659                 :     }
     660 HIT           8 :     else if(grammar::ci_is_equal(
     661               8 :         *rv->begin(), "gzip"))
     662                 :     {
     663               2 :         md.content_encoding.coding =
     664                 :             content_coding::gzip;
     665                 :     }
     666               4 :     else if(grammar::ci_is_equal(
     667               4 :         *rv->begin(), "br"))
     668                 :     {
     669               1 :         md.content_encoding.coding =
     670                 :             content_coding::br;
     671                 :     }
     672               2 :     else if(grammar::ci_is_equal(
     673               2 :         *rv->begin(), "zstd"))
     674                 :     {
     675               1 :         md.content_encoding.coding =
     676                 :             content_coding::zstd;
     677                 :     }
     678                 :     else
     679                 :     {
     680 MIS           0 :         md.content_encoding.coding =
     681                 :             content_coding::unknown;
     682                 :     }
     683 HIT           7 : }
     684                 : 
     685                 : void
     686              26 : header::
     687                 : on_insert_upgrade(
     688                 :     core::string_view v)
     689                 : {
     690              26 :     ++md.upgrade.count;
     691              26 :     if(md.upgrade.ec)
     692               5 :         return;
     693              25 :     if( version !=
     694                 :         http::version::http_1_1)
     695                 :     {
     696               1 :         md.upgrade.ec =
     697               2 :             BOOST_HTTP_ERR(
     698                 :                 error::bad_upgrade);
     699               1 :         md.upgrade.websocket = false;
     700               1 :         return;
     701                 :     }
     702                 :     auto rv = grammar::parse(
     703              24 :         v, upgrade_rule);
     704              24 :     if(! rv)
     705                 :     {
     706               3 :         md.upgrade.ec =
     707               6 :             BOOST_HTTP_ERR(
     708                 :                 error::bad_upgrade);
     709               3 :         md.upgrade.websocket = false;
     710               3 :         return;
     711                 :     }
     712              21 :     if(! md.upgrade.websocket)
     713                 :     {
     714              23 :         for(auto t : *rv)
     715                 :         {
     716              16 :             if( grammar::ci_is_equal(
     717              26 :                     t.name, "websocket") &&
     718              10 :                 t.version.empty())
     719                 :             {
     720               9 :                 md.upgrade.websocket = true;
     721               9 :                 break;
     722                 :             }
     723                 :         }
     724                 :     }
     725              24 : }
     726                 : 
     727                 : //------------------------------------------------
     728                 : 
     729                 : void
     730               9 : header::
     731                 : on_erase_connection()
     732                 : {
     733               9 :     BOOST_ASSERT(
     734                 :         md.connection.count > 0);
     735                 :     // reset and re-insert
     736               9 :     auto n = md.connection.count - 1;
     737               9 :     auto const p = cbuf + prefix;
     738               9 :     auto const* e = &tab()[0];
     739               9 :     md.connection = {};
     740              17 :     while(n > 0)
     741                 :     {
     742               8 :         if(e->id == field::connection)
     743                 :         {
     744               7 :             on_insert_connection(
     745                 :                 core::string_view(
     746               7 :                     p + e->vp, e->vn));
     747               7 :             --n;
     748                 :         }
     749               8 :         --e;
     750                 :     }
     751               9 : }
     752                 : 
     753                 : void
     754               4 : header::
     755                 : on_erase_content_length()
     756                 : {
     757               4 :     BOOST_ASSERT(
     758                 :         md.content_length.count > 0);
     759               4 :     --md.content_length.count;
     760               4 :     if(md.content_length.count == 0)
     761                 :     {
     762                 :         // no Content-Length
     763               1 :         md.content_length = {};
     764               1 :         update_payload();
     765               1 :         return;
     766                 :     }
     767               3 :     if(! md.content_length.ec)
     768                 :     {
     769                 :         // removing a duplicate value
     770               2 :         return;
     771                 :     }
     772                 :     // reset and re-insert
     773               1 :     auto n = md.content_length.count;
     774               1 :     auto const p = cbuf + prefix;
     775               1 :     auto const* e = &tab()[0];
     776               1 :     md.content_length = {};
     777               2 :     while(n > 0)
     778                 :     {
     779               1 :         if(e->id == field::content_length)
     780                 :         {
     781               1 :             on_insert_content_length(
     782                 :                 core::string_view(
     783               1 :                     p + e->vp, e->vn));
     784               1 :             --n;
     785                 :         }
     786               1 :         --e;
     787                 :     }
     788               1 :     update_payload();
     789                 : }
     790                 : 
     791                 : void
     792              10 : header::
     793                 : on_erase_expect()
     794                 : {
     795              10 :     BOOST_ASSERT(
     796                 :         md.expect.count > 0);
     797              10 :     --md.expect.count;
     798              10 :     if(kind != detail::kind::request)
     799               1 :         return;
     800               9 :     if(md.expect.count == 0)
     801                 :     {
     802                 :         // no Expect
     803               3 :         md.expect = {};
     804               3 :         return;
     805                 :     }
     806                 :     // VFALCO This should be uncommented
     807                 :     // if we want to allow multiple Expect
     808                 :     // fields with the value 100-continue
     809                 :     /*
     810                 :     if(! md.expect.ec)
     811                 :         return;
     812                 :     */
     813                 :     // reset and re-insert
     814               6 :     auto n = md.expect.count;
     815               6 :     auto const p = cbuf + prefix;
     816               6 :     auto const* e = &tab()[0];
     817               6 :     md.expect = {};
     818              18 :     while(n > 0)
     819                 :     {
     820              12 :         if(e->id == field::expect)
     821                 :         {
     822               6 :             on_insert_expect(
     823                 :                 core::string_view(
     824               6 :                     p + e->vp, e->vn));
     825               6 :             --n;
     826                 :         }
     827              12 :         --e;
     828                 :     }
     829                 : }
     830                 : 
     831                 : void
     832               4 : header::
     833                 : on_erase_transfer_encoding()
     834                 : {
     835               4 :     BOOST_ASSERT(
     836                 :         md.transfer_encoding.count > 0);
     837                 :     // reset and re-insert
     838               4 :     auto n = md.transfer_encoding.count - 1;
     839               4 :     auto const p = cbuf + prefix;
     840               4 :     auto const* e = &tab()[0];
     841               4 :     md.transfer_encoding = {};
     842               7 :     while(n > 0)
     843                 :     {
     844               3 :         if(e->id == field::transfer_encoding)
     845                 :         {
     846               2 :             on_insert_transfer_encoding(
     847                 :                 core::string_view(
     848               2 :                     p + e->vp, e->vn));
     849               2 :             --n;
     850                 :         }
     851               3 :         --e;
     852                 :     }
     853               4 : }
     854                 : 
     855                 : void
     856 MIS           0 : header::
     857                 : on_erase_content_encoding()
     858                 : {
     859               0 :     BOOST_ASSERT(
     860                 :         md.content_encoding.count > 0);
     861               0 :     --md.content_encoding.count;
     862               0 :     if(md.content_encoding.count == 0)
     863                 :     {
     864                 :         // no Content-Encoding
     865               0 :         md.content_encoding = {};
     866               0 :         return;
     867                 :     }
     868                 :     // re-insert everything
     869               0 :     --md.content_encoding.count;
     870                 :     // TODO
     871                 :     // on_insert_content_encoding();
     872                 : }
     873                 : 
     874                 : // called when Upgrade is erased
     875                 : void
     876 HIT           4 : header::
     877                 : on_erase_upgrade()
     878                 : {
     879               4 :     BOOST_ASSERT(
     880                 :         md.upgrade.count > 0);
     881               4 :     --md.upgrade.count;
     882               4 :     if(md.upgrade.count == 0)
     883                 :     {
     884                 :         // no Upgrade
     885               2 :         md.upgrade = {};
     886               2 :         return;
     887                 :     }
     888                 :     // reset and re-insert
     889               2 :     auto n = md.upgrade.count;
     890               2 :     auto const p = cbuf + prefix;
     891               2 :     auto const* e = &tab()[0];
     892               2 :     md.upgrade = {};
     893               4 :     while(n > 0)
     894                 :     {
     895               2 :         if(e->id == field::upgrade)
     896               2 :             on_insert_upgrade(
     897                 :                 core::string_view(
     898               2 :                     p + e->vp, e->vn));
     899               2 :         --n;
     900               2 :         --e;
     901                 :     }
     902                 : }
     903                 : 
     904                 : //------------------------------------------------
     905                 : 
     906                 : // called when all fields with id are removed
     907                 : void
     908              72 : header::
     909                 : on_erase_all(
     910                 :     field id)
     911                 : {
     912              72 :     if(kind == detail::kind::fields)
     913              21 :         return;
     914              51 :     switch(id)
     915                 :     {
     916               3 :     case field::connection:
     917               3 :         md.connection = {};
     918               3 :         return;
     919                 : 
     920               2 :     case field::content_length:
     921               2 :         md.content_length = {};
     922               2 :         update_payload();
     923               2 :         return;
     924                 : 
     925               5 :     case field::expect:
     926               5 :         md.expect = {};
     927               5 :         update_payload();
     928               5 :         return;
     929                 : 
     930               1 :     case field::transfer_encoding:
     931               1 :         md.transfer_encoding = {};
     932               1 :         update_payload();
     933               1 :         return;
     934                 : 
     935               1 :     case field::upgrade:
     936               1 :         md.upgrade = {};
     937               1 :         return;
     938                 : 
     939              39 :     default:
     940              39 :         break;
     941                 :     }
     942                 : }
     943                 : 
     944                 : //------------------------------------------------
     945                 : 
     946                 : /*  References:
     947                 : 
     948                 :     3.3.  Message Body
     949                 :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
     950                 : 
     951                 :     3.3.1.  Transfer-Encoding
     952                 :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
     953                 : 
     954                 :     3.3.2.  Content-Length
     955                 :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     956                 : */
     957                 : void
     958           19641 : header::
     959                 : update_payload() noexcept
     960                 : {
     961           19641 :     BOOST_ASSERT(kind !=
     962                 :         detail::kind::fields);
     963           19641 :     if(md.payload_override)
     964                 :     {
     965                 :         // e.g. response to
     966                 :         // a HEAD request
     967 MIS           0 :         return;
     968                 :     }
     969                 : 
     970                 : /*  If there is an error in either Content-Length
     971                 :     or Transfer-Encoding, then the payload is
     972                 :     undefined. Clients should probably close the
     973                 :     connection. Servers can send a Bad Request
     974                 :     and avoid reading any payload bytes.
     975                 : */
     976 HIT       19641 :     if(md.content_length.ec)
     977                 :     {
     978                 :         // invalid Content-Length
     979              68 :         md.payload = payload::error;
     980              68 :         md.payload_size = 0;
     981              68 :         return;
     982                 :     }
     983           19573 :     if(md.transfer_encoding.ec)
     984                 :     {
     985                 :         // invalid Transfer-Encoding
     986               8 :         md.payload = payload::error;
     987               8 :         md.payload_size = 0;
     988               8 :         return;
     989                 :     }
     990                 : 
     991                 : /*  A sender MUST NOT send a Content-Length
     992                 :     header field in any message that contains
     993                 :     a Transfer-Encoding header field.
     994                 :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     995                 : */
     996           19565 :     if( md.content_length.count > 0 &&
     997            4485 :         md.transfer_encoding.count > 0)
     998                 :     {
     999               3 :         md.payload = payload::error;
    1000               3 :         md.payload_size = 0;
    1001               3 :         return;
    1002                 :     }
    1003                 : 
    1004           19562 :     if(kind == detail::kind::response)
    1005            1924 :         goto do_response;
    1006                 : 
    1007                 :     //--------------------------------------------
    1008                 : 
    1009                 : /*  The presence of a message body in a
    1010                 :     request is signaled by a Content-Length
    1011                 :     or Transfer-Encoding header field. Request
    1012                 :     message framing is independent of method
    1013                 :     semantics, even if the method does not
    1014                 :     define any use for a message body.
    1015                 : */
    1016           17638 :     if(md.content_length.count > 0)
    1017                 :     {
    1018            4177 :         if(md.content_length.value > 0)
    1019                 :         {
    1020                 :             // non-zero Content-Length
    1021            4150 :             md.payload = payload::size;
    1022            4150 :             md.payload_size = md.content_length.value;
    1023            4150 :             return;
    1024                 :         }
    1025                 :         // Content-Length: 0
    1026              27 :         md.payload = payload::none;
    1027              27 :         md.payload_size = 0;
    1028              27 :         return;
    1029                 :     }
    1030           13461 :     if(md.transfer_encoding.is_chunked)
    1031                 :     {
    1032                 :         // chunked
    1033            4012 :         md.payload = payload::chunked;
    1034            4012 :         md.payload_size = 0;
    1035            4012 :         return;
    1036                 :     }
    1037                 :     // no payload
    1038            9449 :     md.payload = payload::none;
    1039            9449 :     md.payload_size = 0;
    1040            9449 :     return;
    1041                 : 
    1042                 :     //--------------------------------------------
    1043            1924 : do_response:
    1044                 : 
    1045            1924 :     if( res.status_int /  100 == 1 ||   // 1xx e.g. Continue
    1046            1913 :         res.status_int == 204 ||        // No Content
    1047            1910 :         res.status_int == 304)          // Not Modified
    1048                 :     {
    1049                 :     /*  The correctness of any Content-Length
    1050                 :         here is defined by the particular
    1051                 :         resource, and cannot be determined
    1052                 :         here. In any case there is no payload.
    1053                 :     */
    1054              16 :         md.payload = payload::none;
    1055              16 :         md.payload_size = 0;
    1056              16 :         return;
    1057                 :     }
    1058            1908 :     if(md.content_length.count > 0)
    1059                 :     {
    1060             302 :         if(md.content_length.value > 0)
    1061                 :         {
    1062                 :             // Content-Length > 0
    1063             283 :             md.payload = payload::size;
    1064             283 :             md.payload_size = md.content_length.value;
    1065             283 :             return;
    1066                 :         }
    1067                 :         // Content-Length: 0
    1068              19 :         md.payload = payload::none;
    1069              19 :         md.payload_size = 0;
    1070              19 :         return;
    1071                 :     }
    1072            1606 :     if(md.transfer_encoding.is_chunked)
    1073                 :     {
    1074                 :         // chunked
    1075             377 :         md.payload = payload::chunked;
    1076             377 :         md.payload_size = 0;
    1077             377 :         return;
    1078                 :     }
    1079                 : 
    1080                 :     // eof needed
    1081            1229 :     md.payload = payload::to_eof;
    1082            1229 :     md.payload_size = 0;
    1083                 : }
    1084                 : 
    1085                 : //------------------------------------------------
    1086                 : 
    1087                 : std::size_t
    1088             549 : header::
    1089                 : count_crlf(
    1090                 :     core::string_view s) noexcept
    1091                 : {
    1092             549 :     auto it = s.data();
    1093             549 :     auto len = s.size();
    1094             549 :     std::size_t n = 0;
    1095           19120 :     while(len >= 2)
    1096                 :     {
    1097           18571 :         if( it[0] == '\r' &&
    1098            1749 :             it[1] != '\r')
    1099                 :         {
    1100            1749 :             if(it[1] == '\n')
    1101            1749 :                 n++;
    1102            1749 :             it += 2;
    1103            1749 :             len -= 2;
    1104                 :         }
    1105                 :         else
    1106                 :         {
    1107           16822 :             it++;
    1108           16822 :             len--;
    1109                 :         }
    1110                 :     }
    1111             549 :     return n;
    1112                 : }
    1113                 : 
    1114                 : static
    1115                 : void
    1116           29145 : parse_start_line(
    1117                 :     header& h,
    1118                 :     header_limits const& lim,
    1119                 :     std::size_t new_size,
    1120                 :     system::error_code& ec) noexcept
    1121                 : {
    1122           29145 :     BOOST_ASSERT(h.size == 0);
    1123           29145 :     BOOST_ASSERT(h.prefix == 0);
    1124           29145 :     BOOST_ASSERT(h.cbuf != nullptr);
    1125           29145 :     BOOST_ASSERT(
    1126                 :         h.kind != detail::kind::fields);
    1127                 : 
    1128           29145 :     auto const it0 = h.cbuf;
    1129           29145 :     auto const end = it0 + new_size;
    1130           29145 :     char const* it = it0;
    1131           29145 :     if( new_size > lim.max_start_line)
    1132              10 :         new_size = lim.max_start_line;
    1133           29145 :     if(h.kind == detail::kind::request)
    1134                 :     {
    1135                 :         auto rv = grammar::parse(
    1136           11375 :             it, end, request_line_rule);
    1137           11375 :         if(! rv)
    1138                 :         {
    1139            1991 :             ec = rv.error();
    1140            3982 :             if( ec == grammar::error::need_more &&
    1141            1991 :                 new_size == lim.max_start_line)
    1142 MIS           0 :                 ec = BOOST_HTTP_ERR(
    1143                 :                     error::start_line_limit);
    1144 HIT        1991 :             return;
    1145                 :         }
    1146                 :         // method
    1147            9384 :         auto sm = std::get<0>(*rv);
    1148            9384 :         h.req.method = string_to_method(sm);
    1149            9384 :         h.req.method_len =
    1150            9384 :             static_cast<header::offset_type>(sm.size());
    1151                 :         // target
    1152            9384 :         auto st = std::get<1>(*rv);
    1153            9384 :         h.req.target_len =
    1154            9384 :             static_cast<header::offset_type>(st.size());
    1155                 :         // version
    1156            9384 :         switch(std::get<2>(*rv))
    1157                 :         {
    1158              25 :         case 10:
    1159              25 :             h.version =
    1160                 :                 http::version::http_1_0;
    1161              25 :             break;
    1162            9359 :         case 11:
    1163            9359 :             h.version =
    1164                 :                 http::version::http_1_1;
    1165            9359 :             break;
    1166 MIS           0 :         default:
    1167                 :         {
    1168               0 :             ec = BOOST_HTTP_ERR(
    1169                 :                 error::bad_version);
    1170               0 :             return;
    1171                 :         }
    1172                 :         }
    1173                 :     }
    1174                 :     else
    1175                 :     {
    1176                 :         auto rv = grammar::parse(
    1177 HIT       17770 :             it, end, status_line_rule);
    1178           17770 :         if(! rv)
    1179                 :         {
    1180           16554 :             ec = rv.error();
    1181           33108 :             if( ec == grammar::error::need_more &&
    1182           16554 :                 new_size == lim.max_start_line)
    1183 MIS           0 :                 ec = BOOST_HTTP_ERR(
    1184                 :                     error::start_line_limit);
    1185 HIT       16554 :             return;
    1186                 :         }
    1187                 :         // version
    1188            1216 :         switch(std::get<0>(*rv))
    1189                 :         {
    1190               4 :         case 10:
    1191               4 :             h.version =
    1192                 :                 http::version::http_1_0;
    1193               4 :             break;
    1194            1212 :         case 11:
    1195            1212 :             h.version =
    1196                 :                 http::version::http_1_1;
    1197            1212 :             break;
    1198 MIS           0 :         default:
    1199                 :         {
    1200               0 :             ec = BOOST_HTTP_ERR(
    1201                 :                 error::bad_version);
    1202               0 :             return;
    1203                 :         }
    1204                 :         }
    1205                 :         // status-code
    1206 HIT        1216 :         h.res.status_int =
    1207                 :             static_cast<unsigned short>(
    1208            1216 :                 std::get<1>(*rv).v);
    1209            1216 :         h.res.status = std::get<1>(*rv).st;
    1210                 :     }
    1211           10600 :     h.prefix = static_cast<header::offset_type>(it - it0);
    1212           10600 :     h.size = h.prefix;
    1213           10600 :     h.on_start_line();
    1214                 : }
    1215                 : 
    1216                 : // returns: true if we added a field
    1217                 : static
    1218                 : void
    1219           37744 : parse_field(
    1220                 :     header& h,
    1221                 :     header_limits const& lim,
    1222                 :     std::size_t new_size,
    1223                 :     system::error_code& ec) noexcept
    1224                 : {
    1225           37744 :     if( new_size > lim.max_field)
    1226              20 :         new_size = lim.max_field;
    1227           37744 :     auto const it0 = h.cbuf + h.size;
    1228           37744 :     auto const end = h.cbuf + new_size;
    1229           37744 :     char const* it = it0;
    1230           37744 :     auto rv = grammar::parse(
    1231                 :         it, end, field_rule);
    1232           37744 :     if(rv.has_error())
    1233                 :     {
    1234           27030 :         ec = rv.error();
    1235           27030 :         if(ec == grammar::error::end_of_range)
    1236                 :         {
    1237                 :             // final CRLF
    1238           10131 :             h.size = static_cast<
    1239           10131 :                 header::offset_type>(it - h.cbuf);
    1240           27030 :             return;
    1241                 :         }
    1242           33539 :         if( ec == grammar::error::need_more &&
    1243           16640 :             new_size == lim.max_field)
    1244                 :         {
    1245 MIS           0 :             ec = BOOST_HTTP_ERR(
    1246                 :                 error::field_size_limit);
    1247                 :         }
    1248 HIT       16899 :         return;
    1249                 :     }
    1250           10714 :     if(h.count >= lim.max_fields)
    1251                 :     {
    1252 MIS           0 :         ec = BOOST_HTTP_ERR(
    1253                 :             error::fields_limit);
    1254               0 :         return;
    1255                 :     }
    1256 HIT       10714 :     if(rv->has_obs_fold)
    1257                 :     {
    1258                 :         // obs fold not allowed in test views
    1259             210 :         BOOST_ASSERT(h.buf != nullptr);
    1260             210 :         remove_obs_fold(h.buf + h.size, it);
    1261                 :     }
    1262           10714 :     auto id = string_to_field(rv->name)
    1263           10714 :         .value_or(header::unknown_field);
    1264           10714 :     h.size = static_cast<header::offset_type>(it - h.cbuf);
    1265                 : 
    1266                 :     // add field table entry
    1267           10714 :     if(h.buf != nullptr)
    1268                 :     {
    1269           21428 :         auto& e = header::table(
    1270           10714 :             h.buf + h.cap)[h.count];
    1271           10714 :         auto const base =
    1272           10714 :             h.buf + h.prefix;
    1273           10714 :         e.np = static_cast<header::offset_type>(
    1274           10714 :             rv->name.data() - base);
    1275           10714 :         e.nn = static_cast<header::offset_type>(
    1276           10714 :             rv->name.size());
    1277           10714 :         e.vp = static_cast<header::offset_type>(
    1278           10714 :             rv->value.data() - base);
    1279           10714 :         e.vn = static_cast<header::offset_type>(
    1280           10714 :             rv->value.size());
    1281           10714 :         e.id = id;
    1282                 :     }
    1283           10714 :     ++h.count;
    1284           10714 :     h.on_insert(id, rv->value);
    1285           10714 :     ec = {};
    1286                 : }
    1287                 : 
    1288                 : void
    1289           45575 : header::
    1290                 : parse(
    1291                 :     std::size_t new_size,
    1292                 :     header_limits const& lim,
    1293                 :     system::error_code& ec) noexcept
    1294                 : {
    1295           45575 :     if( new_size > lim.max_size)
    1296              10 :         new_size = lim.max_size;
    1297           45575 :     if( this->prefix == 0 &&
    1298           29379 :         this->kind !=
    1299                 :             detail::kind::fields)
    1300                 :     {
    1301           29145 :         parse_start_line(
    1302                 :             *this, lim, new_size, ec);
    1303           29145 :         if(ec)
    1304                 :         {
    1305           37090 :             if( ec == grammar::error::need_more &&
    1306           18545 :                 new_size == lim.max_fields)
    1307                 :             {
    1308 MIS           0 :                 ec = BOOST_HTTP_ERR(
    1309                 :                     error::headers_limit);
    1310                 :             }
    1311 HIT       18545 :             return;
    1312                 :         }
    1313                 :     }
    1314                 :     for(;;)
    1315                 :     {
    1316           37744 :         parse_field(
    1317                 :             *this, lim, new_size, ec);
    1318           37744 :         if(ec)
    1319                 :         {
    1320           43670 :             if( ec == grammar::error::need_more &&
    1321           16640 :                 new_size == lim.max_size)
    1322                 :             {
    1323 MIS           0 :                 ec = BOOST_HTTP_ERR(
    1324                 :                     error::headers_limit);
    1325               0 :                 return;
    1326                 :             }
    1327 HIT       27030 :             break;
    1328                 :         }
    1329           10714 :     }
    1330           27030 :     if(ec == grammar::error::end_of_range)
    1331           10131 :         ec = {};
    1332                 : }
    1333                 : 
    1334                 : } // detail
    1335                 : } // http
    1336                 : } // boost
        

Generated by: LCOV version 2.3