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

Generated by: LCOV version 2.1