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: 93.0 % 630 586
Test Date: 2025-06-18 09:40:26 Functions: 83.1 % 59 49

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

Generated by: LCOV version 2.1