LCOV - code coverage report
Current view: top level - libs/http_proto/src/rfc/detail/rules.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 91.4 % 185 169
Test Date: 2024-07-12 15:42:45 Functions: 90.9 % 11 10

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/http_proto
       8              : //
       9              : 
      10              : #include <boost/http_proto/rfc/detail/rules.hpp>
      11              : 
      12              : #include <boost/http_proto/error.hpp>
      13              : #include <boost/http_proto/detail/config.hpp>
      14              : #include <boost/http_proto/rfc/token_rule.hpp>
      15              : 
      16              : #include <boost/core/detail/string_view.hpp>
      17              : #include <boost/url/grammar/delim_rule.hpp>
      18              : #include <boost/url/grammar/digit_chars.hpp>
      19              : #include <boost/url/grammar/error.hpp>
      20              : #include <boost/url/grammar/hexdig_chars.hpp>
      21              : #include <boost/url/grammar/lut_chars.hpp>
      22              : #include <boost/url/grammar/parse.hpp>
      23              : #include <boost/url/grammar/tuple_rule.hpp>
      24              : 
      25              : #include "rules.hpp"
      26              : 
      27              : namespace boost {
      28              : namespace http_proto {
      29              : namespace detail {
      30              : 
      31              : auto
      32         6287 : crlf_rule_t::
      33              : parse(
      34              :     char const*& it,
      35              :     char const* end) const noexcept ->
      36              :         system::result<value_type>
      37              : {
      38         6287 :     if(it == end)
      39         1006 :         return grammar::error::need_more;
      40         5281 :     if(*it != '\r')
      41           31 :         return grammar::error::mismatch;
      42         5250 :     ++it;
      43         5250 :     if(it == end)
      44          163 :         return grammar::error::need_more;
      45         5087 :     if(*it != '\n')
      46           52 :         return grammar::error::mismatch;
      47         5035 :     ++it;
      48         5035 :     return {};
      49              : }
      50              : 
      51              : //------------------------------------------------
      52              : 
      53              : auto
      54         3594 : version_rule_t::
      55              : parse(
      56              :     char const*& it,
      57              :     char const* end) const noexcept ->
      58              :         system::result<value_type>
      59              : {
      60         3594 :     value_type v = 0;
      61         3594 :     if(it == end)
      62              :     {
      63              :         // expected "HTTP/"
      64          171 :         BOOST_HTTP_PROTO_RETURN_EC(
      65              :             grammar::error::need_more);
      66              :     }
      67         3423 :     if(end - it >= 5)
      68              :     {
      69         2883 :         if(std::memcmp(
      70              :             it, "HTTP/", 5) != 0)
      71              :         {
      72            0 :             BOOST_HTTP_PROTO_RETURN_EC(
      73              :                 grammar::error::mismatch);
      74              :         }
      75         2883 :         it += 5;
      76              :     }
      77         3423 :     if(it == end)
      78              :     {
      79              :         // expected DIGIT
      80           90 :         BOOST_HTTP_PROTO_RETURN_EC(
      81              :             grammar::error::need_more);
      82              :     }
      83         3333 :     if(! grammar::digit_chars(*it))
      84              :     {
      85              :         // expected DIGIT
      86          540 :         BOOST_HTTP_PROTO_RETURN_EC(
      87              :             grammar::error::need_more);
      88              :     }
      89         2793 :     v = 10 * (*it++ - '0');
      90         2793 :     if(it == end)
      91              :     {
      92              :         // expected "."
      93          234 :         BOOST_HTTP_PROTO_RETURN_EC(
      94              :             grammar::error::need_more);
      95              :     }
      96         2559 :     if(*it != '.')
      97              :     {
      98              :         // expected "."
      99            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     100              :             grammar::error::need_more);
     101              :     }
     102         2559 :     ++it;
     103         2559 :     if(it == end)
     104              :     {
     105              :         // expected DIGIT
     106           89 :         BOOST_HTTP_PROTO_RETURN_EC(
     107              :             grammar::error::need_more);
     108              :     }
     109         2470 :     if(! grammar::digit_chars(*it))
     110              :     {
     111              :         // expected DIGIT
     112            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     113              :             grammar::error::need_more);
     114              :     }
     115         2470 :     v += *it++ - '0';
     116         2470 :     return v;
     117              : }
     118              : 
     119              : //------------------------------------------------
     120              : 
     121              : auto
     122          608 : status_code_rule_t::
     123              : parse(
     124              :     char const*& it,
     125              :     char const* end) const noexcept ->
     126              :         system::result<value_type>
     127              : {
     128              :     auto const dig =
     129         1773 :         [](char c) -> int
     130              :         {
     131         1773 :             unsigned char uc(c - '0');
     132         1773 :             if(uc > 9)
     133            0 :                 return -1;
     134         1773 :             return uc;
     135              :         };
     136              : 
     137          608 :     if(it == end)
     138              :     {
     139              :         // end
     140            9 :         BOOST_HTTP_PROTO_RETURN_EC(
     141              :             grammar::error::need_more);
     142              :     }
     143          599 :     auto it0 = it;
     144          599 :     int v = dig(*it);
     145          599 :     if(v == -1)
     146              :     {
     147              :         // expected DIGIT
     148            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     149              :             grammar::error::mismatch);
     150              :     }
     151          599 :     value_type t;
     152          599 :     t.v = 100 * v;
     153          599 :     ++it;
     154          599 :     if(it == end)
     155              :     {
     156              :         // end
     157            8 :         BOOST_HTTP_PROTO_RETURN_EC(
     158              :             grammar::error::need_more);
     159              :     }
     160          591 :     v = dig(*it);
     161          591 :     if(v == -1)
     162              :     {
     163              :         // expected DIGIT
     164            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     165              :             grammar::error::mismatch);
     166              :     }
     167          591 :     t.v = t.v + (10 * v);
     168          591 :     ++it;
     169          591 :     if(it == end)
     170              :     {
     171              :         // end
     172            8 :         BOOST_HTTP_PROTO_RETURN_EC(
     173              :             grammar::error::need_more);
     174              :     }
     175          583 :     v = dig(*it);
     176          583 :     if(v == -1)
     177              :     {
     178              :         // expected DIGIT
     179            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     180              :             grammar::error::need_more);
     181              :     }
     182          583 :     t.v = t.v + v;
     183          583 :     ++it;
     184              : 
     185          583 :     t.s = core::string_view(it0, it - it0);
     186          583 :     t.st = int_to_status(t.v);
     187          583 :     return t;
     188              : }
     189              : 
     190              : //------------------------------------------------
     191              : 
     192              : auto
     193         4841 : field_name_rule_t::
     194              : parse(
     195              :     char const*& it,
     196              :     char const* end) const noexcept ->
     197              :         system::result<value_type>
     198              : {
     199         4841 :     if( it == end )
     200            1 :         BOOST_HTTP_PROTO_RETURN_EC(
     201              :             grammar::error::need_more);
     202              : 
     203         4840 :     value_type v;
     204              : 
     205         4840 :     auto begin = it;
     206         4840 :     auto rv = grammar::parse(
     207              :         it, end, token_rule);
     208         4840 :     if( rv.has_error() || (it != end) )
     209              :     {
     210         4237 :         if( it != begin )
     211              :         {
     212         4172 :             v = core::string_view(begin, it - begin);
     213         4172 :             return v;
     214              :         }
     215           65 :         return error::bad_field_name;
     216              :     }
     217              : 
     218          603 :     v = core::string_view(begin, end - begin);
     219          603 :     return v;
     220              : }
     221              : 
     222              : auto
     223         4419 : field_value_rule_t::
     224              : parse(
     225              :     char const*& it,
     226              :     char const* end) const noexcept ->
     227              :         system::result<value_type>
     228              : {
     229         4419 :     value_type v;
     230         4419 :     if( it == end )
     231              :     {
     232          199 :         v.value = core::string_view(it, 0);
     233          199 :         return v;
     234              :     }
     235              : 
     236              :     // field-line     = field-name ":" OWS field-value OWS
     237              :     // field-value    = *field-content
     238              :     // field-content  = field-vchar
     239              :     //                  [ 1*( SP / HTAB / field-vchar ) field-vchar ]
     240              :     // field-vchar    = VCHAR / obs-text
     241              :     // obs-text       = %x80-FF
     242              :     // VCHAR          = %x21-7E
     243              :     //                       ; visible (printing) characters
     244              : 
     245        15877 :     auto is_field_vchar = [](unsigned char ch)
     246              :     {
     247        15877 :       return (ch >= 0x21 && ch <= 0x7e) || ch >= 0x80;
     248              :     };
     249              : 
     250         4220 :     char const* s0 = nullptr;
     251         4220 :     char const* s1 = nullptr;
     252              : 
     253         4220 :     bool has_crlf = false;
     254         4220 :     bool has_obs_fold = false;
     255              : 
     256        26910 :     while( it < end )
     257              :     {
     258        25952 :         auto ch = *it;
     259        25952 :         if( ws(ch) )
     260              :         {
     261         6131 :             ++it;
     262         6131 :             continue;
     263              :         }
     264              : 
     265        19821 :         if( ch == '\r' )
     266              :         {
     267              :             // too short to know if we have a potential obs-fold
     268              :             // occurrence
     269         3944 :             if( end - it < 2 )
     270          200 :                 BOOST_HTTP_PROTO_RETURN_EC(
     271              :                     grammar::error::need_more);
     272              : 
     273         3744 :             if( it[1] != '\n' )
     274           53 :                 goto done;
     275              : 
     276         3691 :             if( end - it < 3 )
     277          171 :                 BOOST_HTTP_PROTO_RETURN_EC(
     278              :                     grammar::error::need_more);
     279              : 
     280         3520 :             if(! ws(it[2]) )
     281              :             {
     282         2804 :                 has_crlf = true;
     283         2804 :                 goto done;
     284              :             }
     285              : 
     286          716 :             has_obs_fold = true;
     287          716 :             it = it + 3;
     288          716 :             continue;
     289          716 :         }
     290              : 
     291        15877 :         if(! is_field_vchar(ch) )
     292              :         {
     293           34 :             goto done;
     294              :         }
     295              : 
     296        15843 :         if(! s0 )
     297         3595 :             s0 = it;
     298              : 
     299        15843 :         ++it;
     300        15843 :         s1 = it;
     301              :     }
     302              : 
     303          958 : done:
     304              :     // later routines wind up doing pointer
     305              :     // subtraction using the .data() member
     306              :     // of the value so we need a valid 0-len range
     307         3849 :     if(! s0 )
     308              :     {
     309          462 :         s0 = it;
     310          462 :         s1 = s0;
     311              :     }
     312              : 
     313         3849 :     v.value = core::string_view(s0, s1 - s0);
     314         3849 :     v.has_crlf = has_crlf;
     315         3849 :     v.has_obs_fold = has_obs_fold;
     316         3849 :     return v;
     317              : }
     318              : 
     319              : auto
     320         6957 : field_rule_t::
     321              : parse(
     322              :     char const*& it,
     323              :     char const* end) const noexcept ->
     324              :         system::result<value_type>
     325              : {
     326         6957 :     if(it == end)
     327              :     {
     328          197 :         BOOST_HTTP_PROTO_RETURN_EC(
     329              :             grammar::error::need_more);
     330              :     }
     331              :     // check for leading CRLF
     332         6760 :     if(it[0] == '\r')
     333              :     {
     334         2224 :         ++it;
     335         2224 :         if(it == end)
     336              :         {
     337          134 :             BOOST_HTTP_PROTO_RETURN_EC(
     338              :                 grammar::error::need_more);
     339              :         }
     340         2090 :         if(*it != '\n')
     341              :         {
     342           21 :             BOOST_HTTP_PROTO_RETURN_EC(
     343              :                 grammar::error::mismatch);
     344              :         }
     345              :         // end of fields
     346         2069 :         ++it;
     347         2069 :         BOOST_HTTP_PROTO_RETURN_EC(
     348              :             grammar::error::end_of_range);
     349              :     }
     350              : 
     351         4536 :     value_type v;
     352              :     auto rv = grammar::parse(
     353         4536 :         it, end, grammar::tuple_rule(
     354              :             field_name_rule,
     355         4536 :             grammar::delim_rule(':'),
     356              :             field_value_rule,
     357         4536 :             crlf_rule));
     358              : 
     359         4536 :     if( rv.has_error() )
     360         1739 :         return rv.error();
     361              : 
     362         2797 :     auto val = rv.value();
     363         2797 :     v.name = std::get<0>(val);
     364         2797 :     v.value = std::get<2>(val).value;
     365         2797 :     v.has_obs_fold = std::get<2>(val).has_obs_fold;
     366              : 
     367         2797 :     return v;
     368              : }
     369              : 
     370              : //------------------------------------------------
     371              : 
     372              : void
     373          241 : remove_obs_fold(
     374              :     char* it,
     375              :     char const* const end) noexcept
     376              : {
     377         2247 :     while(it != end)
     378              :     {
     379         2224 :         if(*it != '\r')
     380              :         {
     381         1628 :             ++it;
     382         1628 :             continue;
     383              :         }
     384          596 :         if(end - it < 3)
     385          218 :             break;
     386          378 :         BOOST_ASSERT(it[1] == '\n');
     387          756 :         if( it[1] == '\n' &&
     388          378 :             ws(it[2]))
     389              :         {
     390          375 :             it[0] = ' ';
     391          375 :             it[1] = ' ';
     392          375 :             it += 3;
     393              :         }
     394              :         else
     395              :         {
     396            3 :             ++it;
     397              :         }
     398              :     }
     399          241 : }
     400              : 
     401              : //------------------------------------------------
     402              : 
     403              : system::result<hex_rule_t::value_type>
     404          244 : hex_rule_t::
     405              : parse(
     406              :     char const*& it,
     407              :     char const* end) const noexcept
     408              : {
     409          244 :     value_type v;
     410              : 
     411              :     constexpr
     412          244 :     auto const max_size =
     413              :         (std::numeric_limits<std::size_t>::max)() >> 4;
     414              : 
     415          244 :     auto const begin = it;
     416          497 :     for( ; it < end; ++it )
     417              :     {
     418          493 :         auto n = grammar::hexdig_value(*it);
     419          493 :         if(n < 0)
     420              :         {
     421          239 :             if( it == begin )
     422           84 :                 BOOST_HTTP_PROTO_RETURN_EC(
     423              :                     grammar::error::mismatch);
     424              : 
     425          155 :             return v;
     426              :         }
     427              : 
     428              :         // we know that so long as we can shift the current
     429              :         // value up 4 bits, we'll be able to fit the hex
     430              :         // digit we've just parsed
     431          254 :         if( v.v > max_size )
     432            1 :             BOOST_HTTP_PROTO_RETURN_EC(
     433              :                 grammar::error::invalid);
     434              : 
     435          253 :         v.v = (v.v << 4) | (std::size_t)n;
     436              :     }
     437            4 :     return v;
     438              : }
     439              : 
     440              : system::result<void>
     441            0 : last_chunk_rule_t::
     442              : parse(
     443              :     char const*& it,
     444              :     char const* end) const noexcept
     445              : {
     446              :     constexpr
     447            0 :     std::size_t const last_chunk_len = 5; // 0\r\n\r\n
     448              : 
     449            0 :     if( static_cast<std::size_t>(end - it) <
     450              :         last_chunk_len )
     451            0 :         BOOST_HTTP_PROTO_RETURN_EC(
     452              :             grammar::error::need_more);
     453              : 
     454            0 :     core::string_view str(it, end - it);
     455            0 :     if( str != "0\r\n\r\n" )
     456            0 :         BOOST_HTTP_PROTO_RETURN_EC(grammar::error::invalid);
     457              : 
     458            0 :     it += last_chunk_len;
     459              : 
     460            0 :     return {};
     461              : }
     462              : 
     463              : } // detail
     464              : } // http_proto
     465              : } // boost
        

Generated by: LCOV version 2.1