LCOV - code coverage report
Current view: top level - libs/beast2/src/server - route_rule.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 38.8 % 103 40
Test Date: 2025-11-20 15:35:53 Functions: 53.3 % 15 8

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/beast2
       8              : //
       9              : 
      10              : #ifndef BOOST_BEAST2_SERVER_ROUTE_RULE_HPP
      11              : #define BOOST_BEAST2_SERVER_ROUTE_RULE_HPP
      12              : 
      13              : #include <boost/beast2/detail/config.hpp>
      14              : #include <boost/url/decode_view.hpp>
      15              : #include <boost/url/segments_encoded_view.hpp>
      16              : #include <boost/url/grammar/alpha_chars.hpp>
      17              : #include <boost/url/grammar/charset.hpp>
      18              : #include <boost/url/grammar/parse.hpp>
      19              : #include <vector>
      20              : 
      21              : namespace boost {
      22              : namespace beast2 {
      23              : 
      24              : namespace grammar = urls::grammar;
      25              : 
      26              : //------------------------------------------------
      27              : 
      28              : // avoids SBO
      29              : class stable_string
      30              : {
      31              :     char const* p_ = 0;
      32              :     std::size_t n_ = 0;
      33              : 
      34              : public:
      35          396 :     ~stable_string()
      36              :     {
      37          396 :         if(p_)
      38          247 :             delete[] p_;
      39          396 :     }
      40              : 
      41              :     stable_string() = default;
      42              : 
      43          149 :     stable_string(
      44              :         stable_string&& other) noexcept
      45          149 :         : p_(other.p_)
      46          149 :         , n_(other.n_)
      47              :     {
      48          149 :         other.p_ = nullptr;
      49          149 :         other.n_ = 0;
      50          149 :     }
      51              : 
      52              :     stable_string& operator=(
      53              :         stable_string&& other) noexcept
      54              :     {
      55              :         auto p = p_;
      56              :         auto n = n_;
      57              :         p_ = other.p_;
      58              :         n_ = other.n_;
      59              :         other.p_ = p;
      60              :         other.n_ = n;
      61              :         return *this;
      62              :     }
      63              : 
      64              :     explicit
      65          247 :     stable_string(
      66              :         core::string_view s)
      67          494 :         : p_(
      68          741 :             [&]
      69              :             {
      70          247 :                 auto p =new char[s.size()];
      71          247 :                 std::memcpy(p, s.data(), s.size());
      72          247 :                 return p;
      73          247 :             }())
      74          247 :         , n_(s.size())
      75              :     {
      76          247 :     }
      77              : 
      78              :     stable_string(
      79              :         char const* it, char const* end)
      80              :         : stable_string(core::string_view(it, end))
      81              :     {
      82              :     }
      83              : 
      84          116 :     char const* data() const noexcept
      85              :     {
      86          116 :         return p_;
      87              :     }
      88              : 
      89          116 :     std::size_t size() const noexcept
      90              :     {
      91          116 :         return n_;
      92              :     }
      93              : 
      94          116 :     operator core::string_view() const noexcept
      95              :     {
      96          116 :         return { data(), size() };
      97              :     }
      98              : };
      99              : 
     100              : //------------------------------------------------
     101              : 
     102              : /** Rule for parsing a non-empty token of chars
     103              : 
     104              :     @par Requires
     105              :     @code
     106              :     std::is_empty<CharSet>::value == true
     107              :     @endcode
     108              : */
     109              : template<class CharSet>
     110              : struct token_rule
     111              : {
     112              :     using value_type = core::string_view;
     113              : 
     114              :     auto
     115              :     parse(
     116              :         char const*& it,
     117              :         char const* end) const noexcept ->
     118              :             system::result<value_type>
     119              :     {
     120              :         static_assert(std::is_empty<CharSet>::value, "");
     121              :         if(it == end)
     122              :             return grammar::error::syntax;
     123              :         auto it1 = grammar::find_if_not(it, end, CharSet{});
     124              :         if(it1 == it)
     125              :             return grammar::error::mismatch;
     126              :         auto s = core::string_view(it, it1);
     127              :         it = it1;
     128              :         return s;
     129              :     }
     130              : };
     131              : 
     132              : //------------------------------------------------
     133              : 
     134              : /*
     135              : route-pattern     =  *( "/" segment ) [ "/" ]
     136              : segment           = literal-segment / param-segment
     137              : literal-segment   = 1*( unreserved-char )
     138              : unreserved-char   = %x21-2F / %x30-3B / %x3D-5A / %x5C-7E  ; all printable except slash
     139              : param-segment     = param-prefix param-name [ constraint ] [ modifier ]
     140              : param-prefix      = ":" / "*"     ; either named param ":" or named wildcard "*"
     141              : param-name        = ident
     142              : constraint        = "(" 1*( constraint-char ) ")"
     143              : modifier          = "?" / "*" / "+"
     144              : ident             = ALPHA *( ALPHA / DIGIT / "_" )
     145              : constraint-char   = %x20-7E except ( ")" )
     146              : */
     147              : 
     148              : //------------------------------------------------
     149              : 
     150              : struct unreserved_char
     151              : {
     152              :     constexpr
     153              :     bool
     154              :     operator()(char ch) const noexcept
     155              :     {
     156              :         return ch != '/' && (
     157              :             (ch >= 0x21 && ch <= 0x2F) ||
     158              :             (ch >= 0x30 && ch <= 0x3B) ||
     159              :             (ch >= 0x3D && ch <= 0x5A) ||
     160              :             (ch >= 0x5C && ch <= 0x7E));
     161              :     }
     162              : };
     163              : 
     164              : struct constraint_char
     165              : {
     166              :     constexpr
     167              :     bool
     168            0 :     operator()(char ch) const noexcept
     169              :     {
     170            0 :         return ch >= 0x20 && ch <= 0x7E && ch != ')';
     171              :     }
     172              : };
     173              : 
     174              : struct ident_char
     175              : {
     176              :     constexpr
     177              :     bool
     178            0 :     operator()(char ch) const noexcept
     179              :     {
     180              :         return
     181            0 :             (ch >= 'a' && ch <= 'z') ||
     182            0 :             (ch >= '0' && ch <= '9') ||
     183            0 :             (ch >= 'A' && ch <= 'Z') ||
     184            0 :             (ch == '_');
     185              :     }
     186              : };
     187              : 
     188              : constexpr struct
     189              : {
     190              :     // empty for no constraint
     191              :     using value_type = core::string_view;
     192              : 
     193              :     auto
     194            0 :     parse(
     195              :         char const*& it,
     196              :         char const* end) const noexcept ->
     197              :             system::result<value_type>
     198              :     {
     199            0 :         if(it == end || *it != '(')
     200            0 :             return "";
     201            0 :         if(it == end)
     202            0 :             BOOST_BEAST2_RETURN_EC(
     203              :                 grammar::error::syntax);
     204            0 :         auto it0 = it;
     205            0 :         it = grammar::find_if_not(
     206            0 :             it, end, constraint_char{});
     207            0 :         if(it - it0 <= 1)
     208              :         {
     209              :             // too small
     210            0 :             it = it0;
     211            0 :             BOOST_BEAST2_RETURN_EC(
     212              :                 grammar::error::syntax);
     213              :         }
     214            0 :         if(it == end)
     215              :         {
     216            0 :             it = it0;
     217            0 :             BOOST_BEAST2_RETURN_EC(
     218              :                 grammar::error::syntax);
     219              :         }
     220            0 :         if(*it != ')')
     221              :         {
     222            0 :             it0 = it;
     223            0 :             BOOST_BEAST2_RETURN_EC(
     224              :                 grammar::error::syntax);
     225              :         }
     226            0 :         return core::string_view(++it0, it++);
     227              :     }
     228              : } constraint_rule{};
     229              : 
     230              : constexpr struct
     231              : {
     232              :     using value_type = core::string_view;
     233              : 
     234              :     auto
     235            0 :     parse(
     236              :         char const*& it,
     237              :         char const* end) const noexcept ->
     238              :             system::result<value_type>
     239              :     {
     240            0 :         if(it == end)
     241            0 :             BOOST_BEAST2_RETURN_EC(
     242              :                 grammar::error::syntax);
     243            0 :         if(! grammar::alpha_chars(*it))
     244            0 :             BOOST_BEAST2_RETURN_EC(
     245              :                 grammar::error::syntax);
     246            0 :         auto it0 = it++;
     247            0 :         it = grammar::find_if_not(
     248            0 :             it, end, ident_char{});
     249            0 :         return core::string_view(it0, it);
     250              :     }
     251              : } param_name_rule{};
     252              : 
     253              : //------------------------------------------------
     254              : 
     255              : /** A unit of matching in a route pattern
     256              : */
     257              : struct route_seg
     258              : {
     259              :     // literal prefix which must match
     260              :     core::string_view prefix;
     261              :     core::string_view name;
     262              :     core::string_view constraint;
     263              :     char ptype = 0; // ':' | '?' | NULL
     264              :     char modifier = 0;
     265              : };
     266              : 
     267              : struct param_segment_rule_t
     268              : {
     269              :     using value_type = route_seg;
     270              : 
     271              :     auto
     272            0 :     parse(
     273              :         char const*& it,
     274              :         char const* end) const noexcept ->
     275              :             system::result<value_type>
     276              :     {
     277            0 :         if(it == end)
     278            0 :             BOOST_BEAST2_RETURN_EC(
     279              :                 grammar::error::syntax);
     280            0 :         if(*it != ':' && *it != '*')
     281            0 :             BOOST_BEAST2_RETURN_EC(
     282              :                 grammar::error::mismatch);
     283            0 :         value_type v;
     284            0 :         v.ptype = *it++;
     285              :         {
     286              :             // param-name
     287            0 :             auto rv = grammar::parse(
     288              :                 it, end, param_name_rule);
     289            0 :             if(rv.has_error())
     290            0 :                 return rv.error();
     291            0 :             v.name = rv.value();
     292              :         }
     293              :         {
     294              :             // constraint
     295            0 :             auto rv = grammar::parse(
     296              :                 it, end, constraint_rule);
     297            0 :             if( rv.has_error())
     298            0 :                 return rv.error();
     299            0 :             v.constraint = rv.value();
     300              :         }
     301              :         // modifier
     302            0 :         if( it != end && (
     303            0 :             *it == '?' || *it == '*' || *it == '+'))
     304            0 :             v.modifier = *it++;
     305            0 :         return v;
     306              :     }
     307              : };
     308              : 
     309              : constexpr param_segment_rule_t param_segment_rule{};
     310              : 
     311              : //------------------------------------------------
     312              : 
     313              : constexpr token_rule<unreserved_char> literal_segment_rule{};
     314              : 
     315              : //------------------------------------------------
     316              : 
     317              : struct path_rule_t
     318              : {
     319              :     struct value_type
     320              :     {
     321              :         std::vector<route_seg> segs;
     322              :     };
     323              : 
     324              :     auto
     325          116 :     parse(
     326              :         char const*& it0,
     327              :         char const* const end) const ->
     328              :             system::result<value_type>
     329              :     {
     330          116 :         value_type rv;
     331          116 :         auto it = it0;
     332          116 :         auto it1 = it;
     333          501 :         while(it != end)
     334              :         {
     335          385 :             if( *it == ':' ||
     336          385 :                 *it == '*')
     337              :             {
     338            0 :                 auto const it2 = it;
     339            0 :                 auto rv1 = urls::grammar::parse(
     340              :                     core::string_view(it, end),
     341              :                     param_segment_rule);
     342            0 :                 if(rv1.has_error())
     343            0 :                     return rv1.error();
     344            0 :                 route_seg rs = rv1.value();
     345            0 :                 rs.prefix = { it2, it1 };
     346            0 :                 rv.segs.push_back(rs);
     347            0 :                 it1 = it;
     348            0 :                 continue;
     349            0 :             }
     350          385 :             ++it;
     351              :         }
     352          116 :         if(it1 != it)
     353              :         {
     354          116 :             route_seg rs;
     355          116 :             rs.prefix = core::string_view(it1, end);
     356          116 :             rv.segs.push_back(rs);
     357              :         }
     358          116 :         it0 = it0 + (it - it1);
     359              :         // gcc 7 bug workaround
     360          116 :         return system::result<value_type>(std::move(rv));
     361          116 :     }
     362              : };
     363              : 
     364              : constexpr path_rule_t path_rule{};
     365              : 
     366              : struct route_match
     367              : {
     368              :     using iterator = urls::segments_encoded_view::iterator;
     369              : 
     370              :     urls::segments_encoded_view base;
     371              :     urls::segments_encoded_view path;
     372              : };
     373              : 
     374              : } // beast2
     375              : } // boost
     376              : 
     377              : #endif
        

Generated by: LCOV version 2.1