LCOV - code coverage report
Current view: top level - libs/http/src/server/detail - route_rule.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 19.2 % 78 15
Test Date: 2026-01-20 00:11:34 Functions: 16.7 % 6 1

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

Generated by: LCOV version 2.3