LCOV - code coverage report
Current view: top level - http_proto/detail/impl - header.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 538 577 93.2 %
Date: 2023-01-26 23:44:13 Functions: 45 54 83.3 %

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

Generated by: LCOV version 1.15