GCC Code Coverage Report


Directory: ./
File: libs/beast2/src/server/route_rule.hpp
Date: 2025-12-24 17:07:59
Exec Total Coverage
Lines: 0 34 0.0%
Functions: 0 4 0.0%
Branches: 0 36 0.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_string
30 {
31 char const* p_ = 0;
32 std::size_t n_ = 0;
33
34 public:
35 ~stable_string()
36 {
37 if(p_)
38 delete[] p_;
39 }
40
41 stable_string() = default;
42
43 stable_string(
44 stable_string&& other) noexcept
45 : p_(other.p_)
46 , n_(other.n_)
47 {
48 other.p_ = nullptr;
49 other.n_ = 0;
50 }
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 stable_string(
66 core::string_view s)
67 : p_(
68 [&]
69 {
70 auto p =new char[s.size()];
71 std::memcpy(p, s.data(), s.size());
72 return p;
73 }())
74 , n_(s.size())
75 {
76 }
77
78 stable_string(
79 char const* it, char const* end)
80 : stable_string(core::string_view(it, end))
81 {
82 }
83
84 char const* data() const noexcept
85 {
86 return p_;
87 }
88
89 std::size_t size() const noexcept
90 {
91 return n_;
92 }
93
94 operator core::string_view() const noexcept
95 {
96 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 char term; // param terminator or NULL
266 };
267
268 struct param_segment_rule_t
269 {
270 using value_type = route_seg;
271
272 auto
273 parse(
274 char const*& it,
275 char const* end) const noexcept ->
276 system::result<value_type>
277 {
278 if(it == end)
279 BOOST_BEAST2_RETURN_EC(
280 grammar::error::syntax);
281 if(*it != ':' && *it != '*')
282 BOOST_BEAST2_RETURN_EC(
283 grammar::error::mismatch);
284 value_type v;
285 v.ptype = *it++;
286 {
287 // param-name
288 auto rv = grammar::parse(
289 it, end, param_name_rule);
290 if(rv.has_error())
291 return rv.error();
292 v.name = rv.value();
293 }
294 {
295 // constraint
296 auto rv = grammar::parse(
297 it, end, constraint_rule);
298 if( rv.has_error())
299 return rv.error();
300 v.constraint = rv.value();
301 }
302 // modifier
303 if( it != end && (
304 *it == '?' || *it == '*' || *it == '+'))
305 v.modifier = *it++;
306 return v;
307 }
308 };
309
310 constexpr param_segment_rule_t param_segment_rule{};
311
312 //------------------------------------------------
313
314 constexpr token_rule<unreserved_char> literal_segment_rule{};
315
316 //------------------------------------------------
317
318 struct path_rule_t
319 {
320 struct value_type
321 {
322 std::vector<route_seg> segs;
323 };
324
325 auto
326 parse(
327 char const*& it0,
328 char const* const end) const ->
329 system::result<value_type>
330 {
331 value_type rv;
332 auto it = it0;
333 auto it1 = it;
334 while(it != end)
335 {
336 if( *it == ':' ||
337 *it == '*')
338 {
339 auto const it2 = it;
340 auto rv1 = urls::grammar::parse(
341 core::string_view(it, end),
342 param_segment_rule);
343 if(rv1.has_error())
344 return rv1.error();
345 route_seg rs = rv1.value();
346 rs.prefix = { it2, it1 };
347 if(it != end)
348 {
349 if( *it == ':' ||
350 *it == '*')
351 {
352 // can't have ":id:id"
353 return grammar::error::syntax;
354 }
355 }
356 rv.segs.push_back(rs);
357 it1 = it;
358 continue;
359 }
360 ++it;
361 }
362 if(it1 != it)
363 {
364 route_seg rs;
365 rs.prefix = core::string_view(it1, end);
366 rv.segs.push_back(rs);
367 }
368 it0 = it0 + (it - it1);
369 // gcc 7 bug workaround
370 return system::result<value_type>(std::move(rv));
371 }
372 };
373
374 constexpr path_rule_t path_rule{};
375
376 struct route_match
377 {
378 using iterator = urls::segments_encoded_view::iterator;
379
380 urls::segments_encoded_view base;
381 urls::segments_encoded_view path;
382 };
383
384 } // beast2
385 } // boost
386
387 #endif
388