LCOV - code coverage report
Current view: top level - http_proto/detail/impl - header.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 517 540 95.7 %
Date: 2023-01-15 07:18:31 Functions: 45 46 97.8 %

          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         742 : 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         742 :     return &h[k];
     119             : }
     120             : 
     121         707 : header::
     122         707 : header(empty v) noexcept
     123         707 :     : kind(v.param)
     124             : {
     125         707 : }
     126             : 
     127         724 : header::
     128         724 : header(detail::kind k) noexcept
     129         724 :     : header(*get_default(k))
     130             : {
     131         724 : }
     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        1101 : 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        1101 :     if( size < 19)
     211         201 :         size = 19;
     212             :     static constexpr auto A =
     213             :         alignof(header::entry);
     214             :     // round up to alignof(A)
     215        1101 :     return A * (
     216        1101 :         (size + A - 1) / A) +
     217        1101 :             (count * sizeof(
     218        1101 :                 header::entry));
     219             : }
     220             : 
     221             : auto
     222        3147 : header::
     223             : tab() const noexcept ->
     224             :     table
     225             : {
     226        3147 :     BOOST_ASSERT(cap > 0);
     227        3147 :     BOOST_ASSERT(buf != nullptr);
     228        3147 :     return table(buf + cap);
     229             : }
     230             : 
     231             : auto
     232        2213 : header::
     233             : tab_() const noexcept ->
     234             :     entry*
     235             : {
     236             :     return reinterpret_cast<
     237        2213 :         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         265 : header::
     385             : on_start_line()
     386             : {
     387         265 :     if(kind ==
     388             :         detail::kind::response)
     389             :     {
     390             :         // maybe status_int
     391          74 :         update_payload();
     392             :     }
     393         265 : }
     394             : 
     395             : // called after a field is inserted
     396             : void
     397        1238 : header::
     398             : on_insert(
     399             :     field id,
     400             :     string_view v)
     401             : {
     402        1238 :     if(kind == detail::kind::fields)
     403         233 :         return;
     404        1005 :     switch(id)
     405             :     {
     406         100 :     case field::content_length:
     407         100 :         return on_insert_content_length(v);
     408          97 :     case field::connection:
     409          97 :         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         707 :     default:
     417         707 :         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             : void
     448         101 : header::
     449             : on_insert_connection(
     450             :     string_view v)
     451             : {
     452         101 :     ++md.connection.count;
     453         101 :     if(md.connection.ec.failed())
     454           5 :         return;
     455             :     auto rv = grammar::parse(
     456         100 :         v, list_rule(token_rule, 1));
     457         100 :     if(! rv)
     458             :     {
     459           4 :         md.connection.ec =
     460           8 :             BOOST_HTTP_PROTO_ERR(
     461             :                 error::bad_connection);
     462           4 :         return;
     463             :     }
     464          96 :     md.connection.ec = {};
     465         203 :     for(auto t : *rv)
     466             :     {
     467         107 :         if(grammar::ci_is_equal(
     468             :                 t, "close"))
     469          59 :             md.connection.close = true;
     470          48 :         else if(grammar::ci_is_equal(
     471             :                 t, "keep-alive"))
     472          24 :             md.connection.keep_alive = true;
     473          24 :         else if(grammar::ci_is_equal(
     474             :                 t, "upgrade"))
     475          19 :             md.connection.upgrade = true;
     476             :     }
     477             : }
     478             : 
     479             : void
     480         101 : header::
     481             : on_insert_content_length(
     482             :     string_view v)
     483             : {
     484             :     static
     485             :     constexpr
     486             :     grammar::unsigned_rule<
     487             :         std::uint64_t> num_rule{};
     488             : 
     489         101 :     ++md.content_length.count;
     490         101 :     if(md.content_length.ec.failed())
     491          98 :         return;
     492             :     auto rv =
     493          99 :         grammar::parse(v, num_rule);
     494          99 :     if(! rv)
     495             :     {
     496             :         // parse failure
     497           5 :         md.content_length.ec =
     498          10 :             BOOST_HTTP_PROTO_ERR(
     499             :             error::bad_content_length);
     500           5 :         md.content_length.value = 0;
     501           5 :         update_payload();
     502           5 :         return;
     503             :     }
     504          94 :     if(md.content_length.count == 1)
     505             :     {
     506             :         // one value
     507          84 :         md.content_length.ec = {};
     508          84 :         md.content_length.value = *rv;
     509          84 :         update_payload();
     510          84 :         return;
     511             :     }
     512          10 :     if(*rv == md.content_length.value)
     513             :     {
     514             :         // ok: duplicate value
     515           7 :         return;
     516             :     }
     517             :     // bad: different values
     518           3 :     md.content_length.ec =
     519           6 :         BOOST_HTTP_PROTO_ERR(
     520             :             error::multiple_content_length);
     521           3 :     md.content_length.value = 0;
     522           3 :     update_payload();
     523             : }
     524             : 
     525             : void
     526          36 : header::
     527             : on_insert_expect(
     528             :     string_view v)
     529             : {
     530          36 :     ++md.expect.count;
     531          36 :     if(kind != detail::kind::request)
     532           8 :         return;
     533          28 :     if(md.expect.ec.failed())
     534           1 :         return;
     535             :     // VFALCO Should we allow duplicate
     536             :     // Expect fields that have 100-continue?
     537          49 :     if( md.expect.count > 1 ||
     538          49 :         ! grammar::ci_is_equal(v,
     539             :             "100-continue"))
     540             :     {
     541          11 :         md.expect.ec =
     542          22 :             BOOST_HTTP_PROTO_ERR(
     543             :                 error::bad_expect);
     544          11 :         md.expect.is_100_continue = false;
     545          11 :         return;
     546             :     }
     547          16 :     md.expect.is_100_continue = true;
     548             : }
     549             : 
     550             : void
     551          47 : header::
     552             : on_insert_transfer_encoding()
     553             : {
     554          47 :     ++md.transfer_encoding.count;
     555          47 :     if(md.transfer_encoding.ec.failed())
     556           1 :         return;
     557          46 :     auto const n =
     558             :         md.transfer_encoding.count;
     559          46 :     md.transfer_encoding = {};
     560          46 :     md.transfer_encoding.count = n;
     561          53 :     for(auto s :
     562             :         fields_view_base::subrange(
     563         152 :             this, find(field::transfer_encoding)))
     564             :     {
     565             :         auto rv = grammar::parse(
     566          61 :             s, transfer_encoding_rule);
     567          61 :         if(! rv)
     568             :         {
     569             :             // parse error
     570           4 :             md.transfer_encoding.ec =
     571           8 :                 BOOST_HTTP_PROTO_ERR(
     572             :                     error::bad_transfer_encoding);
     573           4 :             md.transfer_encoding.codings = 0;
     574           4 :             md.transfer_encoding.is_chunked = false;
     575           4 :             update_payload();
     576           4 :             return;
     577             :         }
     578          57 :         md.transfer_encoding.codings += rv->size();
     579         119 :         for(auto t : *rv)
     580             :         {
     581          66 :             if(! md.transfer_encoding.is_chunked)
     582             :             {
     583          62 :                 if(t.id == transfer_coding::chunked)
     584          26 :                     md.transfer_encoding.is_chunked = true;
     585          62 :                 continue;
     586             :             }
     587           4 :             if(t.id == transfer_coding::chunked)
     588             :             {
     589             :                 // chunked appears twice
     590           2 :                 md.transfer_encoding.ec =
     591           4 :                     BOOST_HTTP_PROTO_ERR(
     592             :                         error::bad_transfer_encoding);
     593           2 :                 md.transfer_encoding.codings = 0;
     594           2 :                 md.transfer_encoding.is_chunked = false;
     595           2 :                 update_payload();
     596           2 :                 return;
     597             :             }
     598             :             // chunked must be last
     599           2 :             md.transfer_encoding.ec =
     600           4 :                 BOOST_HTTP_PROTO_ERR(
     601             :                     error::bad_transfer_encoding);
     602           2 :             md.transfer_encoding.codings = 0;
     603           2 :             md.transfer_encoding.is_chunked = false;
     604           2 :             update_payload();
     605           2 :             return;
     606             :         }
     607             :     }
     608          38 :     update_payload();
     609             : }
     610             : 
     611             : void
     612          26 : header::
     613             : on_insert_upgrade(
     614             :     string_view v)
     615             : {
     616          26 :     ++md.upgrade.count;
     617          26 :     if(md.upgrade.ec.failed())
     618           5 :         return;
     619          25 :     if( version !=
     620             :         http_proto::version::http_1_1)
     621             :     {
     622           1 :         md.upgrade.ec =
     623           2 :             BOOST_HTTP_PROTO_ERR(
     624             :                 error::bad_upgrade);
     625           1 :         md.upgrade.websocket = false;
     626           1 :         return;
     627             :     }
     628             :     auto rv = grammar::parse(
     629          24 :         v, upgrade_rule);
     630          24 :     if(! rv)
     631             :     {
     632           3 :         md.upgrade.ec =
     633           6 :             BOOST_HTTP_PROTO_ERR(
     634             :                 error::bad_upgrade);
     635           3 :         md.upgrade.websocket = false;
     636           3 :         return;
     637             :     }
     638          21 :     if(! md.upgrade.websocket)
     639             :     {
     640          23 :         for(auto t : *rv)
     641             :         {
     642          16 :             if( grammar::ci_is_equal(
     643          26 :                     t.name, "websocket") &&
     644          10 :                 t.version.empty())
     645             :             {
     646           9 :                 md.upgrade.websocket = true;
     647           9 :                 break;
     648             :             }
     649             :         }
     650             :     }
     651             : }
     652             : 
     653             : //------------------------------------------------
     654             : 
     655             : void
     656          11 : header::
     657             : on_erase_connection()
     658             : {
     659          11 :     BOOST_ASSERT(
     660             :         md.connection.count > 0);
     661             :     // reset and re-insert
     662          11 :     auto n = md.connection.count - 1;
     663          11 :     auto const p = cbuf + prefix;
     664          11 :     auto const* e = &tab()[0];
     665          11 :     md.connection = {};
     666          16 :     while(n > 0)
     667             :     {
     668           5 :         if(e->id == field::connection)
     669           4 :             on_insert_connection(string_view(
     670           4 :                 p + e->vp, e->vn));
     671           5 :         --n;
     672           5 :         --e;
     673             :     }
     674          11 : }
     675             : 
     676             : void
     677           4 : header::
     678             : on_erase_content_length()
     679             : {
     680           4 :     BOOST_ASSERT(
     681             :         md.content_length.count > 0);
     682           4 :     --md.content_length.count;
     683           4 :     if(md.content_length.count == 0)
     684             :     {
     685             :         // no Content-Length
     686           1 :         md.content_length = {};
     687           1 :         update_payload();
     688           1 :         return;
     689             :     }
     690           3 :     if(! md.content_length.ec.failed())
     691             :     {
     692             :         // removing a duplicate value
     693           2 :         return;
     694             :     }
     695             :     // reset and re-insert
     696           1 :     auto n = md.content_length.count;
     697           1 :     auto const p = cbuf + prefix;
     698           1 :     auto const* e = &tab()[0];
     699           1 :     md.content_length = {};
     700           2 :     while(n > 0)
     701             :     {
     702           1 :         if(e->id == field::content_length)
     703           1 :             on_insert_content_length(
     704           1 :                 string_view(p + e->vp, e->vn));
     705           1 :         --n;
     706           1 :         --e;
     707             :     }
     708           1 :     update_payload();
     709             : }
     710             : 
     711             : void
     712           6 : header::
     713             : on_erase_expect()
     714             : {
     715           6 :     BOOST_ASSERT(
     716             :         md.expect.count > 0);
     717           6 :     --md.expect.count;
     718           6 :     if(kind != detail::kind::request)
     719           1 :         return;
     720           5 :     if(md.expect.count == 0)
     721             :     {
     722             :         // no Expect
     723           2 :         md.expect = {};
     724           2 :         return;
     725             :     }
     726             :     // VFALCO This should be uncommented
     727             :     // if we want to allow multiple Expect
     728             :     // fields with the value 100-continue
     729             :     /*
     730             :     if(! md.expect.ec.failed())
     731             :         return;
     732             :     */
     733             :     // reset and re-insert
     734           3 :     auto n = md.expect.count;
     735           3 :     auto const p = cbuf + prefix;
     736           3 :     auto const* e = &tab()[0];
     737           3 :     md.expect = {};
     738           6 :     while(n > 0)
     739             :     {
     740           3 :         if(e->id == field::expect)
     741           3 :             on_insert_expect(
     742           3 :                 string_view(p + e->vp, e->vn));
     743           3 :         --n;
     744           3 :         --e;
     745             :     }
     746             : }
     747             : 
     748             : void
     749           5 : header::
     750             : on_erase_transfer_encoding()
     751             : {
     752           5 :     BOOST_ASSERT(
     753             :         md.transfer_encoding.count > 0);
     754           5 :     --md.transfer_encoding.count;
     755           5 :     if(md.transfer_encoding.count == 0)
     756             :     {
     757             :         // no Transfer-Encoding
     758           2 :         md.transfer_encoding = {};
     759           2 :         update_payload();
     760           2 :         return;
     761             :     }
     762             :     // re-insert everything
     763           3 :     --md.transfer_encoding.count;
     764           3 :     on_insert_transfer_encoding();
     765             : }
     766             : 
     767             : // called when Upgrade is erased
     768             : void
     769           4 : header::
     770             : on_erase_upgrade()
     771             : {
     772           4 :     BOOST_ASSERT(
     773             :         md.upgrade.count > 0);
     774           4 :     --md.upgrade.count;
     775           4 :     if(md.upgrade.count == 0)
     776             :     {
     777             :         // no Upgrade
     778           2 :         md.upgrade = {};
     779           2 :         return;
     780             :     }
     781             :     // reset and re-insert
     782           2 :     auto n = md.upgrade.count;
     783           2 :     auto const p = cbuf + prefix;
     784           2 :     auto const* e = &tab()[0];
     785           2 :     md.upgrade = {};
     786           4 :     while(n > 0)
     787             :     {
     788           2 :         if(e->id == field::upgrade)
     789           2 :             on_insert_upgrade(string_view(
     790           2 :                 p + e->vp, e->vn));
     791           2 :         --n;
     792           2 :         --e;
     793             :     }
     794             : }
     795             : 
     796             : //------------------------------------------------
     797             : 
     798             : // called when all fields with id are removed
     799             : void
     800          51 : header::
     801             : on_erase_all(
     802             :     field id)
     803             : {
     804          51 :     if(kind == detail::kind::fields)
     805          14 :         return;
     806          37 :     switch(id)
     807             :     {
     808           1 :     case field::connection:
     809           1 :         md.connection = {};
     810           1 :         return;
     811             : 
     812           2 :     case field::content_length:
     813           2 :         md.content_length = {};
     814           2 :         update_payload();
     815           2 :         return;
     816             : 
     817           5 :     case field::expect:
     818           5 :         md.expect = {};
     819           5 :         update_payload();
     820           5 :         return;
     821             : 
     822           1 :     case field::transfer_encoding:
     823           1 :         md.transfer_encoding = {};
     824           1 :         update_payload();
     825           1 :         return;
     826             : 
     827           1 :     case field::upgrade:
     828           1 :         md.upgrade = {};
     829           1 :         return;
     830             : 
     831          27 :     default:
     832          27 :         break;
     833             :     }
     834             : }
     835             : 
     836             : //------------------------------------------------
     837             : 
     838             : /*  References:
     839             : 
     840             :     3.3.  Message Body
     841             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
     842             : 
     843             :     3.3.1.  Transfer-Encoding
     844             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
     845             : 
     846             :     3.3.2.  Content-Length
     847             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     848             : */
     849             : void
     850        1187 : header::
     851             : update_payload() noexcept
     852             : {
     853        1187 :     BOOST_ASSERT(kind !=
     854             :         detail::kind::fields);
     855        1187 :     if(md.manual_payload)
     856             :     {
     857             :         // e.g. response to
     858             :         // a HEAD request
     859           0 :         return;
     860             :     }
     861             : 
     862             : /*  If there is an error in either Content-Length
     863             :     or Transfer-Encoding, then the payload is
     864             :     undefined. Clients should probably close the
     865             :     connection. Servers can send a Bad Request
     866             :     and avoid reading any payload bytes.
     867             : */
     868        1187 :     if(md.content_length.ec.failed())
     869             :     {
     870             :         // invalid Content-Length
     871           8 :         md.payload = payload::error;
     872           8 :         md.payload_size = 0;
     873           8 :         return;
     874             :     }
     875        1179 :     if(md.transfer_encoding.ec.failed())
     876             :     {
     877             :         // invalid Transfer-Encoding
     878           8 :         md.payload = payload::error;
     879           8 :         md.payload_size = 0;
     880           8 :         return;
     881             :     }
     882             : 
     883             : /*  A sender MUST NOT send a Content-Length
     884             :     header field in any message that contains
     885             :     a Transfer-Encoding header field.
     886             :     https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
     887             : */
     888        1171 :     if( md.content_length.count > 0 &&
     889          88 :         md.transfer_encoding.count > 0)
     890             :     {
     891           3 :         md.payload = payload::error;
     892           3 :         md.payload_size = 0;
     893           3 :         return;
     894             :     }
     895             : 
     896        1168 :     if(kind == detail::kind::response)
     897         172 :         goto do_response;
     898             : 
     899             :     //--------------------------------------------
     900             : 
     901             : /*  The presence of a message body in a
     902             :     request is signaled by a Content-Length
     903             :     or Transfer-Encoding header field. Request
     904             :     message framing is independent of method
     905             :     semantics, even if the method does not
     906             :     define any use for a message body.
     907             : */
     908         996 :     if(md.content_length.count > 0)
     909             :     {
     910          65 :         if(md.content_length.value > 0)
     911             :         {
     912             :             // non-zero Content-Length
     913          59 :             md.payload = payload::size;
     914          59 :             md.payload_size = md.content_length.value;
     915          59 :             return;
     916             :         }
     917             :         // Content-Length: 0
     918           6 :         md.payload = payload::none;
     919           6 :         md.payload_size = 0;
     920           6 :         return;
     921             :     }
     922         931 :     if(md.transfer_encoding.is_chunked)
     923             :     {
     924             :         // chunked
     925          15 :         md.payload = payload::chunked;
     926          15 :         md.payload_size = 0;
     927          15 :         return;
     928             :     }
     929             :     // no payload
     930         916 :     md.payload = payload::none;
     931         916 :     md.payload_size = 0;
     932         916 :     return;
     933             : 
     934             :     //--------------------------------------------
     935         172 : do_response:
     936             : 
     937         172 :     if( res.status_int /  100 == 1 ||   // 1xx e.g. Continue
     938         169 :         res.status_int == 204 ||        // No Content
     939         166 :         res.status_int == 304)          // Not Modified
     940             :     {
     941             :     /*  The correctness of any Content-Length
     942             :         here is defined by the particular
     943             :         resource, and cannot be determined
     944             :         here. In any case there is no payload.
     945             :     */
     946           9 :         md.payload = payload::none;
     947           9 :         md.payload_size = 0;
     948           9 :         return;
     949             :     }
     950         163 :     if(md.content_length.count > 0)
     951             :     {
     952          17 :         if(md.content_length.value > 0)
     953             :         {
     954             :             // Content-Length > 0
     955           6 :             md.payload = payload::size;
     956           6 :             md.payload_size = md.content_length.value;
     957           6 :             return;
     958             :         }
     959             :         // Content-Length: 0
     960          11 :         md.payload = payload::none;
     961          11 :         md.payload_size = 0;
     962          11 :         return;
     963             :     }
     964         146 :     if(md.transfer_encoding.is_chunked)
     965             :     {
     966             :         // chunked
     967           4 :         md.payload = payload::chunked;
     968           4 :         md.payload_size = 0;
     969           4 :         return;
     970             :     }
     971             : 
     972             :     // eof needed
     973         142 :     md.payload = payload::to_eof;
     974         142 :     md.payload_size = 0;
     975             : }
     976             : 
     977             : //------------------------------------------------
     978             : 
     979             : static
     980             : result<std::size_t>
     981        2104 : parse_request_line(
     982             :     header& h,
     983             :     string_view s) noexcept
     984             : {
     985        2104 :     auto const it0 = s.data();
     986        2104 :     auto const end = it0 + s.size();
     987        2104 :     char const* it = it0;
     988             :     auto rv = grammar::parse(
     989        2104 :         it, end, request_line_rule);
     990        2104 :     if(! rv)
     991        1215 :         return rv.error();
     992             :     // method
     993         889 :     auto sm = std::get<0>(*rv);
     994         889 :     h.req.method = string_to_method(sm);
     995         889 :     h.req.method_len =
     996         889 :         static_cast<off_t>(sm.size());
     997             :     // target
     998         889 :     auto st = std::get<1>(*rv);
     999         889 :     h.req.target_len =
    1000         889 :         static_cast<off_t>(st.size());
    1001             :     // version
    1002         889 :     switch(std::get<2>(*rv))
    1003             :     {
    1004          20 :     case 10:
    1005          20 :         h.version = version::http_1_0;
    1006          20 :         break;
    1007         869 :     case 11:
    1008         869 :         h.version = version::http_1_1;
    1009         869 :         break;
    1010           0 :     default:
    1011           0 :         return error::bad_version;
    1012             :     }
    1013             : 
    1014         889 :     h.cbuf = s.data();
    1015         889 :     h.prefix =
    1016         889 :         static_cast<off_t>(it - it0);
    1017         889 :     h.size = h.prefix;
    1018         889 :     h.update_payload();
    1019         889 :     return h.prefix;
    1020             : }
    1021             : 
    1022             : static
    1023             : result<std::size_t>
    1024          74 : parse_status_line(
    1025             :     header& h,
    1026             :     string_view s) noexcept
    1027             : {
    1028          74 :     auto const it0 = s.data();
    1029          74 :     auto const end = it0 + s.size();
    1030          74 :     char const* it = it0;
    1031             :     auto rv = grammar::parse(
    1032          74 :         it, end, status_line_rule);
    1033          74 :     if(! rv)
    1034           0 :         return rv.error();
    1035             :     // version
    1036          74 :     switch(std::get<0>(*rv))
    1037             :     {
    1038           4 :     case 10:
    1039           4 :         h.version = version::http_1_0;
    1040           4 :         break;
    1041          70 :     case 11:
    1042          70 :         h.version = version::http_1_1;
    1043          70 :         break;
    1044           0 :     default:
    1045           0 :         return error::bad_version;
    1046             :     }
    1047             :     // status-code
    1048          74 :     h.res.status_int =
    1049             :         static_cast<unsigned short>(
    1050          74 :             std::get<1>(*rv).v);
    1051          74 :     h.res.status = std::get<1>(*rv).st;
    1052             : 
    1053          74 :     h.cbuf = s.data();
    1054          74 :     h.prefix =
    1055          74 :         static_cast<off_t>(it - it0);
    1056          74 :     h.size = h.prefix;
    1057          74 :     h.update_payload();
    1058          74 :     return h.prefix;
    1059             : }
    1060             : 
    1061             : //------------------------------------------------
    1062             : 
    1063             : result<std::size_t>
    1064        2178 : parse_start_line(
    1065             :     header& h,
    1066             :     string_view s) noexcept
    1067             : {
    1068        2178 :     BOOST_ASSERT(! s.empty());
    1069        2178 :     BOOST_ASSERT(h.size == 0);
    1070        2178 :     BOOST_ASSERT(h.prefix == 0);
    1071             : 
    1072             :     // VFALCO do we need a separate
    1073             :     //        limit on start line?
    1074             : 
    1075        2178 :     if(h.kind == detail::kind::request)
    1076        2104 :         return parse_request_line(h, s);
    1077          74 :     return parse_status_line(h, s);
    1078             : }
    1079             : 
    1080             : // returns: true if we added a field
    1081             : bool
    1082        2351 : parse_field(
    1083             :     header& h,
    1084             :     std::size_t new_size,
    1085             :     field& id,
    1086             :     string_view& v,
    1087             :     error_code& ec) noexcept
    1088             : {
    1089        2351 :     auto const it0 = h.cbuf + h.size;
    1090        2351 :     auto const end = h.cbuf + new_size;
    1091        2351 :     char const* it = it0;
    1092             :     auto rv = grammar::parse(
    1093        2351 :         it, end, field_rule);
    1094        2351 :     if(rv.has_error())
    1095             :     {
    1096        1754 :         ec = rv.error();
    1097        1754 :         if(ec == grammar::error::end_of_range)
    1098             :         {
    1099             :             // final CRLF
    1100         579 :             ec.clear();
    1101         579 :             h.size = static_cast<off_t>(
    1102         579 :                 it - h.cbuf);
    1103             :         }
    1104        1754 :         return false;
    1105             :     }
    1106         597 :     if(rv->has_obs_fold)
    1107             :     {
    1108             :         // obs fold not allowed in test views
    1109         137 :         BOOST_ASSERT(h.buf != nullptr);
    1110         137 :         remove_obs_fold(h.buf + h.size, it);
    1111             :     }
    1112         597 :     v = rv->value;
    1113         597 :     id = string_to_field(rv->name);
    1114         597 :     h.size = static_cast<off_t>(
    1115         597 :         it - h.cbuf);
    1116             : 
    1117             :     // add field table entry
    1118         597 :     if(h.buf != nullptr)
    1119             :     {
    1120        1194 :         auto& e = header::table(
    1121         597 :             h.buf + h.cap)[h.count];
    1122         597 :         auto const base =
    1123         597 :             h.buf + h.prefix;
    1124         597 :         e.np = static_cast<off_t>(
    1125         597 :             rv->name.data() - base);
    1126         597 :         e.nn = static_cast<off_t>(
    1127         597 :             rv->name.size());
    1128         597 :         e.vp = static_cast<off_t>(
    1129         597 :             rv->value.data() - base);
    1130         597 :         e.vn = static_cast<off_t>(
    1131         597 :             rv->value.size());
    1132         597 :         e.id = id;
    1133             : 
    1134             :     #if 0
    1135             :         // VFALCO handling zero-length value?
    1136             :         if(fi.value_len > 0)
    1137             :             fi.value_pos = static_cast<
    1138             :                 off_t>(t.v.value.data() - h_.buf);
    1139             :         else
    1140             :             fi.value_pos = 0; // empty string
    1141             :     #endif
    1142             :     }
    1143         597 :     ++h.count;
    1144         597 :     h.on_insert(id, v);
    1145         597 :     return true;
    1146             : }
    1147             : 
    1148             : } // detail
    1149             : } // http_proto
    1150             : } // boost
    1151             : 
    1152             : #endif

Generated by: LCOV version 1.15