GCC Code Coverage Report


Directory: libs/beast2/
File: src/server/route_rule.hpp
Date: 2025-11-13 15:50:44
Exec Total Coverage
Lines: 50 113 44.2%
Functions: 9 16 56.2%
Branches: 12 80 15.0%

Line Branch Exec Source
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
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 39 times.
48 if(p_)
38
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
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
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 }())
74 9 , n_(s.size())
75 {
76 9 }
77
78 9 stable_chars(
79 char const* it, char const* end)
80
1/2
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
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 operator()(char ch) const noexcept
164 {
165 return ch >= 0x20 && ch <= 0x7E && ch != ')';
166 }
167 };
168
169 struct ident_char
170 {
171 constexpr
172 bool
173 operator()(char ch) const noexcept
174 {
175 return
176 (ch >= 'a' && ch <= 'z') ||
177 (ch >= '0' && ch <= '9') ||
178 (ch >= 'A' && ch <= 'Z') ||
179 (ch == '_');
180 }
181 };
182
183 constexpr struct
184 {
185 // empty for no constraint
186 using value_type = core::string_view;
187
188 auto
189 parse(
190 char const*& it,
191 char const* end) const noexcept ->
192 system::result<value_type>
193 {
194 if(it == end || *it != '(')
195 return "";
196 if(it == end)
197 BOOST_BEAST2_RETURN_EC(
198 grammar::error::syntax);
199 auto it0 = it;
200 it = grammar::find_if_not(
201 it, end, constraint_char{});
202 if(it - it0 <= 1)
203 {
204 // too small
205 it = it0;
206 BOOST_BEAST2_RETURN_EC(
207 grammar::error::syntax);
208 }
209 if(it == end)
210 {
211 it = it0;
212 BOOST_BEAST2_RETURN_EC(
213 grammar::error::syntax);
214 }
215 if(*it != ')')
216 {
217 it0 = it;
218 BOOST_BEAST2_RETURN_EC(
219 grammar::error::syntax);
220 }
221 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 parse(
231 char const*& it,
232 char const* end) const noexcept ->
233 system::result<value_type>
234 {
235 if(it == end)
236 BOOST_BEAST2_RETURN_EC(
237 grammar::error::syntax);
238 if(! grammar::alpha_chars(*it))
239 BOOST_BEAST2_RETURN_EC(
240 grammar::error::syntax);
241 auto it0 = it++;
242 it = grammar::find_if_not(
243 it, end, ident_char{});
244 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 parse(
268 char const*& it,
269 char const* end) const noexcept ->
270 system::result<value_type>
271 {
272 if(it == end)
273 BOOST_BEAST2_RETURN_EC(
274 grammar::error::syntax);
275 if(*it != ':' && *it != '*')
276 BOOST_BEAST2_RETURN_EC(
277 grammar::error::mismatch);
278 value_type v;
279 v.ptype = *it++;
280 {
281 // param-name
282 auto rv = grammar::parse(
283 it, end, param_name_rule);
284 if(rv.has_error())
285 return rv.error();
286 v.name = rv.value();
287 }
288 {
289 // constraint
290 auto rv = grammar::parse(
291 it, end, constraint_rule);
292 if( rv.has_error())
293 return rv.error();
294 v.constraint = rv.value();
295 }
296 // modifier
297 if( it != end && (
298 *it == '?' || *it == '*' || *it == '+'))
299 v.modifier = *it++;
300 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
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
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
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 9 times.
26 while(it != end)
334 {
335
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if( *it == ':' ||
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 *it == '*')
337 {
338 auto const it2 = it;
339 auto rv1 = urls::grammar::parse(
340 core::string_view(it, end),
341 param_segment_rule);
342 if(rv1.has_error())
343 return rv1.error();
344 route_seg rs = rv1.value();
345 rs.prefix = { it2, it1 };
346 rv.segs.push_back(rs);
347 it1 = it;
348 continue;
349 }
350 17 ++it;
351 }
352
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if(it1 != it)
353 {
354 9 route_seg rs;
355 9 rs.prefix = core::string_view(it1, end);
356
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
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
378