LCOV - code coverage report
Current view: top level - http_proto/detail/impl - header.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 546 581 94.0 %
Date: 2023-12-23 23:40:21 Functions: 47 56 83.9 %

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

Generated by: LCOV version 1.15