LCOV - code coverage report
Current view: top level - libs/http_proto/src/detail/header.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.3 % 611 564
Test Date: 2025-10-12 23:51:56 Functions: 82.5 % 57 47

            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_proto
       9              : //
      10              : 
      11              : #include "src/rfc/detail/rules.hpp"
      12              : #include "src/rfc/detail/transfer_coding_rule.hpp"
      13              : 
      14              : #include <boost/http_proto/detail/header.hpp>
      15              : #include <boost/http_proto/field.hpp>
      16              : #include <boost/http_proto/header_limits.hpp>
      17              : #include <boost/http_proto/rfc/list_rule.hpp>
      18              : #include <boost/http_proto/rfc/token_rule.hpp>
      19              : #include <boost/http_proto/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_proto {
      33              : namespace detail {
      34              : 
      35              : //------------------------------------------------
      36              : 
      37              : auto
      38           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_proto::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_proto::status::ok }
     108              : {
     109              : }
     110              : 
     111              : //------------------------------------------------
     112              : 
     113              : header const*
     114         2360 : 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         2360 :     return &h[k];
     122              : }
     123              : 
     124        11902 : header::
     125        11902 : header(empty v) noexcept
     126        11902 :     : kind(v.param)
     127              : {
     128        11902 : }
     129              : 
     130         1289 : header::
     131         1289 : header(detail::kind k) noexcept
     132         1289 :     : header(*get_default(k))
     133              : {
     134         1289 : }
     135              : 
     136              : void
     137           78 : header::
     138              : swap(header& h) noexcept
     139              : {
     140           78 :     std::swap(cbuf, h.cbuf);
     141           78 :     std::swap(buf, h.buf);
     142           78 :     std::swap(cap, h.cap);
     143           78 :     std::swap(size, h.size);
     144           78 :     std::swap(count, h.count);
     145           78 :     std::swap(prefix, h.prefix);
     146           78 :     std::swap(version, h.version);
     147           78 :     std::swap(md, h.md);
     148           78 :     switch(kind)
     149              :     {
     150           15 :     default:
     151              :     case detail::kind::fields:
     152           15 :         break;
     153           55 :     case detail::kind::request:
     154           55 :         std::swap(
     155           55 :             req.method_len, h.req.method_len);
     156           55 :         std::swap(
     157           55 :             req.target_len, h.req.target_len);
     158           55 :         std::swap(req.method, h.req.method);
     159           55 :         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           78 : }
     167              : 
     168              : /*  References:
     169              : 
     170              :     6.3.  Persistence
     171              :     https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
     172              : */
     173              : bool
     174           22 : header::
     175              : keep_alive() const noexcept
     176              : {
     177           22 :     if(md.payload == payload::error)
     178            1 :         return false;
     179           21 :     if( version ==
     180              :         http_proto::version::http_1_1)
     181              :     {
     182           13 :         if(md.connection.close)
     183            3 :             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         1994 : 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         1994 :     if(size < 19)
     214         1241 :         size = 19;
     215              : 
     216              :     // align size up to alignof(entry)
     217         1994 :     size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
     218              : 
     219         1994 :     return size + count * sizeof(entry);
     220              : }
     221              : 
     222              : std::size_t
     223        10006 : header::
     224              : table_space(
     225              :     std::size_t count) noexcept
     226              : {
     227              :     return count *
     228        10006 :         sizeof(header::entry);
     229              : }
     230              : 
     231              : std::size_t
     232        10006 : header::
     233              : table_space() const noexcept
     234              : {
     235        10006 :     return table_space(count);
     236              : }
     237              : 
     238              : auto
     239         2599 : header::
     240              : tab() const noexcept ->
     241              :     table
     242              : {
     243         2599 :     BOOST_ASSERT(cap > 0);
     244         2599 :     BOOST_ASSERT(buf != nullptr);
     245         2599 :     return table(buf + cap);
     246              : }
     247              : 
     248              : auto
     249          702 : header::
     250              : tab_() const noexcept ->
     251              :     entry*
     252              : {
     253              :     return reinterpret_cast<
     254          702 :         entry*>(buf + cap);
     255              : }
     256              : 
     257              : // return true if header cbuf is a default
     258              : bool
     259           41 : header::
     260              : is_default() const noexcept
     261              : {
     262           41 :     return buf == nullptr;
     263              : }
     264              : 
     265              : std::size_t
     266          136 : header::
     267              : find(
     268              :     field id) const noexcept
     269              : {
     270          136 :     if(count == 0)
     271           59 :         return 0;
     272           77 :     std::size_t i = 0;
     273           77 :     auto const* p = &tab()[0];
     274          120 :     while(i < count)
     275              :     {
     276           91 :         if(p->id == id)
     277           48 :             break;
     278           43 :         ++i;
     279           43 :         --p;
     280              :     }
     281           77 :     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         1116 : 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         1116 :     if(n == 0)
     315         1096 :         return;
     316              : 
     317           20 :     std::memcpy(
     318              :         reinterpret_cast<
     319           20 :             entry*>(dest) - n,
     320              :         reinterpret_cast<
     321              :             entry const*>(
     322           20 :                 cbuf + cap) - n,
     323              :         n * sizeof(entry));
     324              : }
     325              : 
     326              : void
     327         1116 : header::
     328              : copy_table(
     329              :     void* dest) const noexcept
     330              : {
     331         1116 :     copy_table(dest, count);
     332         1116 : }
     333              : 
     334              : // assign all the members but
     335              : // preserve the allocated memory
     336              : void
     337         1095 : header::
     338              : assign_to(
     339              :     header& dest) const noexcept
     340              : {
     341         1095 :     auto const buf_ = dest.buf;
     342         1095 :     auto const cbuf_ = dest.cbuf;
     343         1095 :     auto const cap_ = dest.cap;
     344         1095 :     dest = *this;
     345         1095 :     dest.buf = buf_;
     346         1095 :     dest.cbuf = cbuf_;
     347         1095 :     dest.cap = cap_;
     348         1095 : }
     349              : 
     350              : //------------------------------------------------
     351              : //
     352              : // Metadata
     353              : //
     354              : //------------------------------------------------
     355              : 
     356              : std::size_t
     357            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           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        10610 : 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        10610 :     update_payload();
     419        10610 : }
     420              : 
     421              : // called after a field is inserted
     422              : void
     423        11780 : header::
     424              : on_insert(
     425              :     field id,
     426              :     core::string_view v)
     427              : {
     428        11780 :     if(kind == detail::kind::fields)
     429          483 :         return;
     430        11297 :     switch(id)
     431              :     {
     432          129 :     case field::content_encoding:
     433          129 :         return on_insert_content_encoding(v);
     434         4877 :     case field::content_length:
     435         4877 :         return on_insert_content_length(v);
     436          136 :     case field::connection:
     437          136 :         return on_insert_connection(v);
     438           47 :     case field::expect:
     439           47 :         return on_insert_expect(v);
     440         4229 :     case field::transfer_encoding:
     441         4229 :         return on_insert_transfer_encoding(v);
     442           24 :     case field::upgrade:
     443           24 :         return on_insert_upgrade(v);
     444         1855 :     default:
     445         1855 :         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            0 :     case field::content_encoding:
     461            0 :         return on_erase_content_encoding();
     462            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          143 : header::
     482              : on_insert_connection(
     483              :     core::string_view v)
     484              : {
     485          143 :     ++md.connection.count;
     486          143 :     if(md.connection.ec.failed())
     487            5 :         return;
     488              :     auto rv = grammar::parse(
     489          142 :         v, list_rule(token_rule, 1));
     490          142 :     if(! rv)
     491              :     {
     492            4 :         md.connection.ec =
     493            8 :             BOOST_HTTP_PROTO_ERR(
     494              :                 error::bad_connection);
     495            4 :         return;
     496              :     }
     497          138 :     md.connection.ec = {};
     498          287 :     for(auto t : *rv)
     499              :     {
     500          149 :         if(grammar::ci_is_equal(
     501              :                 t, "close"))
     502           96 :             md.connection.close = true;
     503           53 :         else if(grammar::ci_is_equal(
     504              :                 t, "keep-alive"))
     505           26 :             md.connection.keep_alive = true;
     506           27 :         else if(grammar::ci_is_equal(
     507              :                 t, "upgrade"))
     508           20 :             md.connection.upgrade = true;
     509              :     }
     510          142 : }
     511              : 
     512              : void
     513         4878 : 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         4878 :     ++md.content_length.count;
     523         4878 :     if(md.content_length.ec.failed())
     524         4695 :         return;
     525              :     auto rv =
     526         4876 :         grammar::parse(v, num_rule);
     527         4876 :     if(! rv)
     528              :     {
     529              :         // parse failure
     530            5 :         md.content_length.ec =
     531           10 :             BOOST_HTTP_PROTO_ERR(
     532              :             error::bad_content_length);
     533            5 :         md.content_length.value = 0;
     534            5 :         update_payload();
     535            5 :         return;
     536              :     }
     537         4871 :     if(md.content_length.count == 1)
     538              :     {
     539              :         // one value
     540         4681 :         md.content_length.ec = {};
     541         4681 :         md.content_length.value = *rv;
     542         4681 :         update_payload();
     543         4681 :         return;
     544              :     }
     545          190 :     if(*rv == md.content_length.value)
     546              :     {
     547              :         // ok: duplicate value
     548            7 :         return;
     549              :     }
     550              :     // bad: different values
     551          183 :     md.content_length.ec =
     552          366 :         BOOST_HTTP_PROTO_ERR(
     553              :             error::multiple_content_length);
     554          183 :     md.content_length.value = 0;
     555          183 :     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.failed())
     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_PROTO_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         4231 : header::
     585              : on_insert_transfer_encoding(
     586              :     core::string_view v)
     587              : {
     588         4231 :     ++md.transfer_encoding.count;
     589         4231 :     if(md.transfer_encoding.ec.failed())
     590         4223 :         return;
     591              : 
     592              :     auto rv = grammar::parse(
     593         4230 :         v, list_rule(transfer_coding_rule, 1));
     594         4230 :     if(! rv)
     595              :     {
     596              :         // parse error
     597            4 :         goto error;
     598              :     }
     599         8455 :     for(auto t : *rv)
     600              :     {
     601         4233 :         if(! md.transfer_encoding.is_chunked)
     602              :         {
     603         4229 :             if(t.id == transfer_coding_rule_t::chunked)
     604         4210 :                 md.transfer_encoding.is_chunked = true;
     605         4229 :             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         8463 :     }
     615         4222 :     update_payload();
     616         4222 :     return;
     617              : 
     618            8 : error:
     619            8 :     md.transfer_encoding.ec =
     620           16 :         BOOST_HTTP_PROTO_ERR(
     621              :             error::bad_transfer_encoding);
     622            8 :     md.transfer_encoding.is_chunked = false;
     623            8 :     update_payload();
     624         4230 : }
     625              : 
     626              : void
     627          129 : header::
     628              : on_insert_content_encoding(
     629              :     core::string_view v)
     630              : {
     631          129 :     ++md.content_encoding.count;
     632          129 :     if(md.content_encoding.ec.failed())
     633            3 :         return;
     634              : 
     635              :     auto rv = grammar::parse(
     636          129 :         v, list_rule(token_rule, 1));
     637          129 :     if(!rv)
     638              :     {
     639            1 :         md.content_encoding.ec =
     640            2 :             BOOST_HTTP_PROTO_ERR(
     641              :                 error::bad_content_encoding);
     642            1 :         md.content_encoding.coding =
     643              :             content_coding::unknown;
     644            1 :         return;
     645              :     }
     646              : 
     647          128 :     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          252 :     if(grammar::ci_is_equal(
     655          252 :         *rv->begin(), "deflate"))
     656              :     {
     657           62 :         md.content_encoding.coding =
     658              :             content_coding::deflate;
     659              :     }
     660          128 :     else if(grammar::ci_is_equal(
     661          128 :         *rv->begin(), "gzip"))
     662              :     {
     663           64 :         md.content_encoding.coding =
     664              :             content_coding::gzip;
     665              :     }
     666            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          129 : }
     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.failed())
     686            5 :         return;
     687           25 :     if( version !=
     688              :         http_proto::version::http_1_1)
     689              :     {
     690            1 :         md.upgrade.ec =
     691            2 :             BOOST_HTTP_PROTO_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_PROTO_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.failed())
     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.failed())
     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            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            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        19719 : header::
     953              : update_payload() noexcept
     954              : {
     955        19719 :     BOOST_ASSERT(kind !=
     956              :         detail::kind::fields);
     957        19719 :     if(md.payload_override)
     958              :     {
     959              :         // e.g. response to
     960              :         // a HEAD request
     961            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        19719 :     if(md.content_length.ec.failed())
     971              :     {
     972              :         // invalid Content-Length
     973          188 :         md.payload = payload::error;
     974          188 :         md.payload_size = 0;
     975          188 :         return;
     976              :     }
     977        19531 :     if(md.transfer_encoding.ec.failed())
     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        19523 :     if( md.content_length.count > 0 &&
     991         4685 :         md.transfer_encoding.count > 0)
     992              :     {
     993            3 :         md.payload = payload::error;
     994            3 :         md.payload_size = 0;
     995            3 :         return;
     996              :     }
     997              : 
     998        19520 :     if(kind == detail::kind::response)
     999         1310 :         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        18210 :     if(md.content_length.count > 0)
    1011              :     {
    1012         4420 :         if(md.content_length.value > 0)
    1013              :         {
    1014              :             // non-zero Content-Length
    1015         4393 :             md.payload = payload::size;
    1016         4393 :             md.payload_size = md.content_length.value;
    1017         4393 :             return;
    1018              :         }
    1019              :         // Content-Length: 0
    1020           27 :         md.payload = payload::none;
    1021           27 :         md.payload_size = 0;
    1022           27 :         return;
    1023              :     }
    1024        13790 :     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         9778 :     md.payload = payload::none;
    1033         9778 :     md.payload_size = 0;
    1034         9778 :     return;
    1035              : 
    1036              :     //--------------------------------------------
    1037         1310 : do_response:
    1038              : 
    1039         1310 :     if( res.status_int /  100 == 1 ||   // 1xx e.g. Continue
    1040         1299 :         res.status_int == 204 ||        // No Content
    1041         1297 :         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           15 :         md.payload = payload::none;
    1049           15 :         md.payload_size = 0;
    1050           15 :         return;
    1051              :     }
    1052         1295 :     if(md.content_length.count > 0)
    1053              :     {
    1054          259 :         if(md.content_length.value > 0)
    1055              :         {
    1056              :             // Content-Length > 0
    1057          237 :             md.payload = payload::size;
    1058          237 :             md.payload_size = md.content_length.value;
    1059          237 :             return;
    1060              :         }
    1061              :         // Content-Length: 0
    1062           22 :         md.payload = payload::none;
    1063           22 :         md.payload_size = 0;
    1064           22 :         return;
    1065              :     }
    1066         1036 :     if(md.transfer_encoding.is_chunked)
    1067              :     {
    1068              :         // chunked
    1069          193 :         md.payload = payload::chunked;
    1070          193 :         md.payload_size = 0;
    1071          193 :         return;
    1072              :     }
    1073              : 
    1074              :     // eof needed
    1075          843 :     md.payload = payload::to_eof;
    1076          843 :     md.payload_size = 0;
    1077              : }
    1078              : 
    1079              : //------------------------------------------------
    1080              : 
    1081              : std::size_t
    1082          546 : header::
    1083              : count_crlf(
    1084              :     core::string_view s) noexcept
    1085              : {
    1086          546 :     auto it = s.data();
    1087          546 :     auto len = s.size();
    1088          546 :     std::size_t n = 0;
    1089        18941 :     while(len >= 2)
    1090              :     {
    1091        18395 :         if( it[0] == '\r' &&
    1092         1737 :             it[1] != '\r')
    1093              :         {
    1094         1737 :             if(it[1] == '\n')
    1095         1737 :                 n++;
    1096         1737 :             it += 2;
    1097         1737 :             len -= 2;
    1098              :         }
    1099              :         else
    1100              :         {
    1101        16658 :             it++;
    1102        16658 :             len--;
    1103              :         }
    1104              :     }
    1105          546 :     return n;
    1106              : }
    1107              : 
    1108              : static
    1109              : void
    1110        14379 : 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        14379 :     BOOST_ASSERT(h.size == 0);
    1117        14379 :     BOOST_ASSERT(h.prefix == 0);
    1118        14379 :     BOOST_ASSERT(h.cbuf != nullptr);
    1119        14379 :     BOOST_ASSERT(
    1120              :         h.kind != detail::kind::fields);
    1121              : 
    1122        14379 :     auto const it0 = h.cbuf;
    1123        14379 :     auto const end = it0 + new_size;
    1124        14379 :     char const* it = it0;
    1125        14379 :     if( new_size > lim.max_start_line)
    1126           36 :         new_size = lim.max_start_line;
    1127        14379 :     if(h.kind == detail::kind::request)
    1128              :     {
    1129              :         auto rv = grammar::parse(
    1130        12432 :             it, end, request_line_rule);
    1131        12432 :         if(! rv)
    1132              :         {
    1133         2693 :             ec = rv.error();
    1134         5386 :             if( ec == grammar::error::need_more &&
    1135         2693 :                 new_size == lim.max_start_line)
    1136            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1137              :                     error::start_line_limit);
    1138         2693 :             return;
    1139              :         }
    1140              :         // method
    1141         9739 :         auto sm = std::get<0>(*rv);
    1142         9739 :         h.req.method = string_to_method(sm);
    1143         9739 :         h.req.method_len =
    1144         9739 :             static_cast<header::offset_type>(sm.size());
    1145              :         // target
    1146         9739 :         auto st = std::get<1>(*rv);
    1147         9739 :         h.req.target_len =
    1148         9739 :             static_cast<header::offset_type>(st.size());
    1149              :         // version
    1150         9739 :         switch(std::get<2>(*rv))
    1151              :         {
    1152           25 :         case 10:
    1153           25 :             h.version =
    1154              :                 http_proto::version::http_1_0;
    1155           25 :             break;
    1156         9714 :         case 11:
    1157         9714 :             h.version =
    1158              :                 http_proto::version::http_1_1;
    1159         9714 :             break;
    1160            0 :         default:
    1161              :         {
    1162            0 :             ec = BOOST_HTTP_PROTO_ERR(
    1163              :                 error::bad_version);
    1164            0 :             return;
    1165              :         }
    1166              :         }
    1167              :     }
    1168              :     else
    1169              :     {
    1170              :         auto rv = grammar::parse(
    1171         1947 :             it, end, status_line_rule);
    1172         1947 :         if(! rv)
    1173              :         {
    1174         1112 :             ec = rv.error();
    1175         2224 :             if( ec == grammar::error::need_more &&
    1176         1112 :                 new_size == lim.max_start_line)
    1177            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1178              :                     error::start_line_limit);
    1179         1112 :             return;
    1180              :         }
    1181              :         // version
    1182          835 :         switch(std::get<0>(*rv))
    1183              :         {
    1184            4 :         case 10:
    1185            4 :             h.version =
    1186              :                 http_proto::version::http_1_0;
    1187            4 :             break;
    1188          831 :         case 11:
    1189          831 :             h.version =
    1190              :                 http_proto::version::http_1_1;
    1191          831 :             break;
    1192            0 :         default:
    1193              :         {
    1194            0 :             ec = BOOST_HTTP_PROTO_ERR(
    1195              :                 error::bad_version);
    1196            0 :             return;
    1197              :         }
    1198              :         }
    1199              :         // status-code
    1200          835 :         h.res.status_int =
    1201              :             static_cast<unsigned short>(
    1202          835 :                 std::get<1>(*rv).v);
    1203          835 :         h.res.status = std::get<1>(*rv).st;
    1204              :     }
    1205        10574 :     h.prefix = static_cast<header::offset_type>(it - it0);
    1206        10574 :     h.size = h.prefix;
    1207        10574 :     h.on_start_line();
    1208              : }
    1209              : 
    1210              : // returns: true if we added a field
    1211              : static
    1212              : void
    1213        24856 : parse_field(
    1214              :     header& h,
    1215              :     header_limits const& lim,
    1216              :     std::size_t new_size,
    1217              :     system::error_code& ec) noexcept
    1218              : {
    1219        24856 :     if( new_size > lim.max_field)
    1220           96 :         new_size = lim.max_field;
    1221        24856 :     auto const it0 = h.cbuf + h.size;
    1222        24856 :     auto const end = h.cbuf + new_size;
    1223        24856 :     char const* it = it0;
    1224        24856 :     auto rv = grammar::parse(
    1225              :         it, end, field_rule);
    1226        24856 :     if(rv.has_error())
    1227              :     {
    1228        13388 :         ec = rv.error();
    1229        13388 :         if(ec == grammar::error::end_of_range)
    1230              :         {
    1231              :             // final CRLF
    1232        10549 :             h.size = static_cast<
    1233        10549 :                 header::offset_type>(it - h.cbuf);
    1234        13388 :             return;
    1235              :         }
    1236         5419 :         if( ec == grammar::error::need_more &&
    1237         2580 :             new_size == lim.max_field)
    1238              :         {
    1239            0 :             ec = BOOST_HTTP_PROTO_ERR(
    1240              :                 error::field_size_limit);
    1241              :         }
    1242         2839 :         return;
    1243              :     }
    1244        11468 :     if(h.count >= lim.max_fields)
    1245              :     {
    1246            0 :         ec = BOOST_HTTP_PROTO_ERR(
    1247              :             error::fields_limit);
    1248            0 :         return;
    1249              :     }
    1250        11468 :     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        11468 :     auto id = string_to_field(rv->name)
    1257        11468 :         .value_or(header::unknown_field);
    1258        11468 :     h.size = static_cast<header::offset_type>(it - h.cbuf);
    1259              : 
    1260              :     // add field table entry
    1261        11468 :     if(h.buf != nullptr)
    1262              :     {
    1263        22936 :         auto& e = header::table(
    1264        11468 :             h.buf + h.cap)[h.count];
    1265        11468 :         auto const base =
    1266        11468 :             h.buf + h.prefix;
    1267        11468 :         e.np = static_cast<header::offset_type>(
    1268        11468 :             rv->name.data() - base);
    1269        11468 :         e.nn = static_cast<header::offset_type>(
    1270        11468 :             rv->name.size());
    1271        11468 :         e.vp = static_cast<header::offset_type>(
    1272        11468 :             rv->value.data() - base);
    1273        11468 :         e.vn = static_cast<header::offset_type>(
    1274        11468 :             rv->value.size());
    1275        11468 :         e.id = id;
    1276              :     }
    1277        11468 :     ++h.count;
    1278        11468 :     h.on_insert(id, rv->value);
    1279        11468 :     ec = {};
    1280              : }
    1281              : 
    1282              : void
    1283        17193 : header::
    1284              : parse(
    1285              :     std::size_t new_size,
    1286              :     header_limits const& lim,
    1287              :     system::error_code& ec) noexcept
    1288              : {
    1289        17193 :     if( new_size > lim.max_size)
    1290           36 :         new_size = lim.max_size;
    1291        17193 :     if( this->prefix == 0 &&
    1292        14613 :         this->kind !=
    1293              :             detail::kind::fields)
    1294              :     {
    1295        14379 :         parse_start_line(
    1296              :             *this, lim, new_size, ec);
    1297        14379 :         if(ec.failed())
    1298              :         {
    1299         7610 :             if( ec == grammar::error::need_more &&
    1300         3805 :                 new_size == lim.max_fields)
    1301              :             {
    1302            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1303              :                     error::headers_limit);
    1304              :             }
    1305         3805 :             return;
    1306              :         }
    1307              :     }
    1308              :     for(;;)
    1309              :     {
    1310        24856 :         parse_field(
    1311              :             *this, lim, new_size, ec);
    1312        24856 :         if(ec.failed())
    1313              :         {
    1314        15968 :             if( ec == grammar::error::need_more &&
    1315         2580 :                 new_size == lim.max_size)
    1316              :             {
    1317            0 :                 ec = BOOST_HTTP_PROTO_ERR(
    1318              :                     error::headers_limit);
    1319            0 :                 return;
    1320              :             }
    1321        13388 :             break;
    1322              :         }
    1323        11468 :     }
    1324        13388 :     if(ec == grammar::error::end_of_range)
    1325        10549 :         ec = {};
    1326              : }
    1327              : 
    1328              : } // detail
    1329              : } // http_proto
    1330              : } // boost
        

Generated by: LCOV version 2.1