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: 44.2 % 113 50
Test Date: 2025-11-13 15:50:43 Functions: 56.2 % 16 9

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