GCC Code Coverage Report


Directory: ./
File: libs/beast2/src/server/route_rule.hpp
Date: 2025-11-20 15:35:53
Exec Total Coverage
Lines: 40 103 38.8%
Functions: 8 15 53.3%
Branches: 10 72 13.9%

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_string
30 {
31 char const* p_ = 0;
32 std::size_t n_ = 0;
33
34 public:
35 396 ~stable_string()
36 {
37
2/2
✓ Branch 0 taken 247 times.
✓ Branch 1 taken 149 times.
396 if(p_)
38
1/2
✓ Branch 0 taken 247 times.
✗ Branch 1 not taken.
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
1/1
✓ Branch 1 taken 247 times.
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 operator()(char ch) const noexcept
169 {
170 return ch >= 0x20 && ch <= 0x7E && ch != ')';
171 }
172 };
173
174 struct ident_char
175 {
176 constexpr
177 bool
178 operator()(char ch) const noexcept
179 {
180 return
181 (ch >= 'a' && ch <= 'z') ||
182 (ch >= '0' && ch <= '9') ||
183 (ch >= 'A' && ch <= 'Z') ||
184 (ch == '_');
185 }
186 };
187
188 constexpr struct
189 {
190 // empty for no constraint
191 using value_type = core::string_view;
192
193 auto
194 parse(
195 char const*& it,
196 char const* end) const noexcept ->
197 system::result<value_type>
198 {
199 if(it == end || *it != '(')
200 return "";
201 if(it == end)
202 BOOST_BEAST2_RETURN_EC(
203 grammar::error::syntax);
204 auto it0 = it;
205 it = grammar::find_if_not(
206 it, end, constraint_char{});
207 if(it - it0 <= 1)
208 {
209 // too small
210 it = it0;
211 BOOST_BEAST2_RETURN_EC(
212 grammar::error::syntax);
213 }
214 if(it == end)
215 {
216 it = it0;
217 BOOST_BEAST2_RETURN_EC(
218 grammar::error::syntax);
219 }
220 if(*it != ')')
221 {
222 it0 = it;
223 BOOST_BEAST2_RETURN_EC(
224 grammar::error::syntax);
225 }
226 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 parse(
236 char const*& it,
237 char const* end) const noexcept ->
238 system::result<value_type>
239 {
240 if(it == end)
241 BOOST_BEAST2_RETURN_EC(
242 grammar::error::syntax);
243 if(! grammar::alpha_chars(*it))
244 BOOST_BEAST2_RETURN_EC(
245 grammar::error::syntax);
246 auto it0 = it++;
247 it = grammar::find_if_not(
248 it, end, ident_char{});
249 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 parse(
273 char const*& it,
274 char const* end) const noexcept ->
275 system::result<value_type>
276 {
277 if(it == end)
278 BOOST_BEAST2_RETURN_EC(
279 grammar::error::syntax);
280 if(*it != ':' && *it != '*')
281 BOOST_BEAST2_RETURN_EC(
282 grammar::error::mismatch);
283 value_type v;
284 v.ptype = *it++;
285 {
286 // param-name
287 auto rv = grammar::parse(
288 it, end, param_name_rule);
289 if(rv.has_error())
290 return rv.error();
291 v.name = rv.value();
292 }
293 {
294 // constraint
295 auto rv = grammar::parse(
296 it, end, constraint_rule);
297 if( rv.has_error())
298 return rv.error();
299 v.constraint = rv.value();
300 }
301 // modifier
302 if( it != end && (
303 *it == '?' || *it == '*' || *it == '+'))
304 v.modifier = *it++;
305 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
2/2
✓ Branch 0 taken 385 times.
✓ Branch 1 taken 116 times.
501 while(it != end)
334 {
335
1/2
✓ Branch 0 taken 385 times.
✗ Branch 1 not taken.
385 if( *it == ':' ||
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 385 times.
385 *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 385 ++it;
351 }
352
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 if(it1 != it)
353 {
354 116 route_seg rs;
355 116 rs.prefix = core::string_view(it1, end);
356
1/1
✓ Branch 1 taken 116 times.
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
378