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

Generated by: LCOV version 1.15