GCC Code Coverage Report


Directory: ./
File: libs/beast2/include/boost/beast2/server/basic_router.hpp
Date: 2025-11-20 15:35:53
Exec Total Coverage
Lines: 109 113 96.5%
Functions: 207 222 93.2%
Branches: 48 53 90.6%

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_BASIC_ROUTER_HPP
11 #define BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
12
13 #include <boost/beast2/detail/config.hpp>
14 #include <boost/beast2/detail/call_traits.hpp>
15 #include <boost/beast2/detail/type_traits.hpp>
16 #include <boost/beast2/server/router_types.hpp>
17 #include <boost/beast2/server/route_handler.hpp>
18 #include <boost/http_proto/method.hpp>
19 #include <boost/url/url_view.hpp>
20 #include <boost/core/detail/string_view.hpp>
21 #include <boost/core/detail/static_assert.hpp>
22 #include <type_traits>
23
24 namespace boost {
25 namespace beast2 {
26
27 template<class, class>
28 class basic_router;
29
30 /** Configuration options for routers.
31 */
32 struct router_options
33 {
34 /** Constructor
35
36 Routers constructed with default options inherit the values of
37 @ref case_sensitive and @ref strict from the parent router.
38 If there is no parent, both default to `false`.
39 The value of @ref merge_params always defaults to `false`
40 and is never inherited.
41 */
42 136 router_options() = default;
43
44 /** Set whether to merge parameters from parent routers.
45
46 This setting controls whether route parameters defined on parent
47 routers are made available in nested routers. It is not inherited
48 and always defaults to `false`.
49
50 @par Example
51 @code
52 router r(router_options()
53 .merge_params(true)
54 .case_sensitive(true)
55 .strict(false));
56 @endcode
57
58 @param value `true` to merge parameters from parent routers.
59 @return A reference to `*this` for chaining.
60 */
61 router_options&
62 1 merge_params(
63 bool value) noexcept
64 {
65
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 v_ = (v_ & ~1) | (value ? 1 : 0);
66 1 return *this;
67 }
68
69 /** Set whether pattern matching is case-sensitive.
70
71 When this option is not set explicitly, the value is inherited
72 from the parent router or defaults to `false` if there is no parent.
73
74 @par Example
75 @code
76 router r(router_options()
77 .case_sensitive(true)
78 .strict(true));
79 @endcode
80
81 @param value `true` to perform case-sensitive path matching.
82 @return A reference to `*this` for chaining.
83 */
84 router_options&
85 7 case_sensitive(
86 bool value) noexcept
87 {
88
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if(value)
89 5 v_ = (v_ & ~6) | 2;
90 else
91 2 v_ = (v_ & ~6) | 4;
92 7 return *this;
93 }
94
95 /** Set whether pattern matching is strict.
96
97 When this option is not set explicitly, the value is inherited
98 from the parent router or defaults to `false` if there is no parent.
99 Strict matching treats a trailing slash as significant:
100 the pattern `"/api"` matches `"/api"` but not `"/api/"`.
101 When strict matching is disabled, these paths are treated
102 as equivalent.
103
104 @par Example
105 @code
106 router r(router_options()
107 .strict(true)
108 .case_sensitive(false));
109 @endcode
110
111 @param value `true` to enable strict path matching.
112 @return A reference to `*this` for chaining.
113 */
114 router_options&
115 1 strict(
116 bool value) noexcept
117 {
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(value)
119 v_ = (v_ & ~24) | 8;
120 else
121 1 v_ = (v_ & ~24) | 16;
122 1 return *this;
123 }
124
125 private:
126 template<class, class> friend class basic_router;
127 unsigned int v_ = 0;
128 };
129
130 //-----------------------------------------------
131
132 namespace detail {
133
134 class any_router;
135
136 //-----------------------------------------------
137
138 // implementation for all routers
139 class any_router
140 {
141 private:
142 template<class, class>
143 friend class beast2::basic_router;
144 using opt_flags = unsigned int;
145
146 struct BOOST_SYMBOL_VISIBLE any_handler
147 {
148 BOOST_BEAST2_DECL virtual ~any_handler();
149 virtual std::size_t count() const noexcept = 0;
150 virtual route_result invoke(
151 basic_request&, basic_response&) const = 0;
152 };
153
154 using handler_ptr = std::unique_ptr<any_handler>;
155
156 struct handler_list
157 {
158 std::size_t n;
159 handler_ptr* p;
160 };
161
162 using match_result = basic_request::match_result;
163 struct matcher;
164 struct layer;
165 struct impl;
166
167 BOOST_BEAST2_DECL ~any_router();
168 BOOST_BEAST2_DECL any_router(opt_flags);
169 BOOST_BEAST2_DECL any_router(any_router&&) noexcept;
170 BOOST_BEAST2_DECL any_router(any_router const&) noexcept;
171 BOOST_BEAST2_DECL any_router& operator=(any_router&&) noexcept;
172 BOOST_BEAST2_DECL any_router& operator=(any_router const&) noexcept;
173 BOOST_BEAST2_DECL std::size_t count() const noexcept;
174 BOOST_BEAST2_DECL layer& new_layer(core::string_view pattern);
175 BOOST_BEAST2_DECL void add_impl(core::string_view, handler_list const&);
176 BOOST_BEAST2_DECL void add_impl(layer&,
177 http_proto::method, handler_list const&);
178 BOOST_BEAST2_DECL void add_impl(layer&,
179 core::string_view, handler_list const&);
180 BOOST_BEAST2_DECL route_result resume_impl(
181 basic_request&, basic_response&, route_result const& ec) const;
182 BOOST_BEAST2_DECL route_result dispatch_impl(http_proto::method,
183 core::string_view, urls::url_view const&,
184 basic_request&, basic_response&) const;
185 BOOST_BEAST2_DECL route_result dispatch_impl(
186 basic_request&, basic_response&) const;
187 route_result do_dispatch(basic_request&, basic_response&) const;
188
189 impl* impl_ = nullptr;
190 };
191
192 } // detail
193
194 //-----------------------------------------------
195
196 /** A container for HTTP route handlers.
197
198 `basic_router` objects store and dispatch route handlers based on the
199 HTTP method and path of an incoming request. Routes are added with a
200 path pattern and an associated handler, and the router is then used to
201 dispatch the appropriate handler.
202
203 Patterns used to create route definitions have percent-decoding applied
204 when handlers are mounted. A literal "%2F" in the pattern string is
205 indistinguishable from a literal '/'. For example, "/x%2Fz" is the
206 same as "/x/z" when used as a pattern.
207
208 @par Example
209 @code
210 using router_type = basic_router<Req, Res>;
211 router_type router;
212 router.get("/hello",
213 [](Req& req, Res& res)
214 {
215 res.status(http_proto::status::ok);
216 res.set_body("Hello, world!");
217 return system::error_code{};
218 });
219 @endcode
220
221 Router objects are lightweight, shared references to their contents.
222 Copies of a router obtained through construction, conversion, or
223 assignment do not create new instances; they all refer to the same
224 underlying data.
225
226 @par Handlers
227 Regular handlers are invoked for matching routes and have this
228 equivalent signature:
229 @code
230 route_result handler( Req& req, Res& res )
231 @endcode
232
233 The return value is a @ref route_result used to indicate the desired
234 action through @ref route enum values, or to indicate that a failure
235 occurred. Failures are represented by error codes for which
236 `system::error_code::failed()` returns `true`.
237
238 When a failing error code is produced and remains unhandled, the
239 router enters error-dispatching mode. In this mode, only error
240 handlers are invoked. Error handlers are registered globally or
241 for specific paths and execute in the order of registration whenever
242 a failing error code is present in the response.
243
244 Error handlers have this equivalent signature:
245 @code
246 route_result error_handler( Req& req, Res& res, system::error_code ec )
247 @endcode
248
249 Each error handler may return any failing @ref system::error_code,
250 which is equivalent to calling:
251 @code
252 res.next(ec); // with ec.failed() == true
253 @endcode
254 Returning @ref route::next indicates that control should proceed to
255 the next matching error handler. Returning a different failing code
256 replaces the current error and continues dispatch in error mode using
257 that new code. Error handlers are invoked until one returns a result
258 other than @ref route::next.
259
260 @par Handler requirements
261 Regular handlers must be callable with:
262 @code
263 route_result( Req&, Res& )
264 @endcode
265
266 Error handlers must be callable with:
267 @code
268 route_result( Req&, Res&, system::error_code )
269 @endcode
270 Error handlers are invoked only when the response has a failing
271 error code, and execute in sequence until one returns a result
272 other than @ref route::next.
273
274 The prefix match is not strict: middleware attached to `"/api"`
275 will also match `"/api/users"` and `"/api/data"`. When registered
276 before route handlers for the same prefix, middleware runs before
277 those routes. This is analogous to `app.use(path, ...)` in
278 Express.js.
279
280 @par Thread Safety
281 Member functions marked `const` such as @ref dispatch and @ref resume
282 may be called concurrently on routers that refer to the same data.
283 Modification of routers through calls to non-`const` member functions
284 is not thread-safe and must not be performed concurrently with any
285 other member function.
286
287 @par Constraints
288 `Req` must be publicly derived from @ref basic_request.
289 `Res` must be publicly derived from @ref basic_response.
290
291 @tparam Req The type of request object.
292 @tparam Res The type of response object.
293 */
294 template<class Req, class Res>
295 class basic_router : public detail::any_router
296 {
297 // Req must be publicly derived from basic_request
298 BOOST_CORE_STATIC_ASSERT(
299 detail::derived_from<basic_request, Req>::value);
300
301 // Res must be publicly derived from basic_response
302 BOOST_CORE_STATIC_ASSERT(
303 detail::derived_from<basic_response, Res>::value);
304
305 // 0 = unrecognized
306 // 1 = normal handler (Req&, Res&)
307 // 2 = error handler (Req&, Res&, error_code)
308 // 4 = basic_router<Req, Res>
309
310 template<class T, class = void>
311 struct handler_type
312 : std::integral_constant<int, 0>
313 {
314 };
315
316 // route_result( Req&, Res& ) const
317 template<class T>
318 struct handler_type<T, typename
319 std::enable_if<std::is_convertible<
320 decltype(std::declval<T const&>()(
321 std::declval<Req&>(),
322 std::declval<Res&>())),
323 route_result>::value
324 >::type> : std::integral_constant<int, 1> {};
325
326 // route_result( Req&, Res&, system::error_code const& ) const
327 template<class T>
328 struct handler_type<T, typename
329 std::enable_if<std::is_convertible<
330 decltype(std::declval<T const&>()(
331 std::declval<Req&>(),
332 std::declval<Res&>(),
333 std::declval<system::error_code const&>())),
334 route_result>::value
335 >::type> : std::integral_constant<int, 2> {};
336
337 // basic_router<Req, Res>
338 template<class T>
339 struct handler_type<T, typename
340 std::enable_if<
341 std::is_base_of<any_router, T>::value &&
342 std::is_convertible<T const volatile*,
343 any_router const volatile*>::value &&
344 std::is_constructible<T, basic_router<Req, Res>>::value
345 >::type> : std::integral_constant<int, 4> {};
346
347 template<std::size_t Mask, class... Ts>
348 struct handler_check : std::true_type {};
349
350 template<std::size_t Mask, class T0, class... Ts>
351 struct handler_check<Mask, T0, Ts...>
352 : std::conditional<
353 ( (handler_type<T0>::value & Mask) != 0 ),
354 handler_check<Mask, Ts...>,
355 std::false_type
356 >::type {};
357
358 public:
359 /** The type of request object used in handlers
360 */
361 using request_type = Req;
362
363 /** The type of response object used in handlers
364 */
365 using response_type = Res;
366
367 /** A fluent interface for defining handlers on a specific route.
368
369 This type represents a single route within the router and
370 provides a chainable API for registering handlers associated
371 with particular HTTP methods or for all methods collectively.
372
373 Typical usage registers one or more handlers for a route:
374 @code
375 router.route("/users/:id")
376 .get(show_user)
377 .put(update_user)
378 .all(log_access);
379 @endcode
380
381 Each call appends handlers in registration order.
382 */
383 class fluent_route;
384
385 /** Constructor
386
387 Creates an empty router with the specified configuration.
388 Routers constructed with default options inherit the values
389 of @ref router_options::case_sensitive and
390 @ref router_options::strict from the parent router, or default
391 to `false` if there is no parent. The value of
392 @ref router_options::merge_params defaults to `false` and
393 is never inherited.
394
395 @param options The configuration options to use.
396 */
397 explicit
398 136 basic_router(router_options options = {})
399 136 : any_router(options.v_)
400 {
401 136 }
402
403 /** Construct a router from another router with compatible types.
404
405 This constructs a router that shares the same underlying routing
406 state as another router whose request and response types are base
407 classes of `Req` and `Res`, respectively.
408
409 The resulting router participates in shared ownership of the
410 implementation; copying the router does not duplicate routes or
411 handlers, and changes visible through one router are visible
412 through all routers that share the same underlying state.
413
414 @par Constraints
415 `Req` must be derived from `OtherReq`, and `Res` must be
416 derived from `OtherRes`.
417
418 @tparam OtherReq The request type of the source router.
419 @tparam OtherRes The response type of the source router.
420 @param other The router to copy.
421 */
422 template<
423 class OtherReq, class OtherRes,
424 class = typename std::enable_if<
425 detail::derived_from<Req, OtherReq>::value &&
426 detail::derived_from<Res, OtherRes>::value>::type
427 >
428 basic_router(
429 basic_router<OtherReq, OtherRes> const& other)
430 : any_router(other)
431 {
432 }
433
434 /** Add one or more middleware handlers for a path prefix.
435
436 Each handler registered with this function participates in the
437 routing and error-dispatch process for requests whose path begins
438 with the specified prefix, as described in the @ref basic_router
439 class documentation. Handlers execute in the order they are added
440 and may return @ref route::next to transfer control to the
441 subsequent handler in the chain.
442
443 @par Example
444 @code
445 router.use("/api",
446 [](Request& req, Response& res)
447 {
448 if (!authenticate(req))
449 {
450 res.status(401);
451 res.set_body("Unauthorized");
452 return route::send;
453 }
454 return route::next;
455 },
456 [](Request&, Response& res)
457 {
458 res.set_header("X-Powered-By", "MyServer");
459 return route::next;
460 });
461 @endcode
462
463 @par Preconditions
464 @p `pattern` must be a valid path prefix; it may be empty to
465 indicate the root scope.
466
467 @param pattern The pattern to match.
468 @param h1 The first handler to add.
469 @param hn Additional handlers to add, invoked after @p h1 in
470 registration order.
471 */
472 template<class H1, class... HN>
473 369 void use(
474 core::string_view pattern,
475 H1&& h1, HN... hn)
476 {
477 // If you get a compile error on this line it means that
478 // one or more of the provided types is not a valid handler,
479 // error handler, or router.
480 BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
481
3/3
✓ Branch 2 taken 158 times.
✓ Branch 11 taken 1 times.
✓ Branch 5 taken 3 times.
369 add_impl(pattern, make_handler_list(
482 std::forward<H1>(h1), std::forward<HN>(hn)...));
483 369 }
484
485 /** Add one or more global middleware handlers.
486
487 Each handler registered with this function participates in the
488 routing and error-dispatch process as described in the
489 @ref basic_router class documentation. Handlers execute in the
490 order they are added and may return @ref route::next to transfer
491 control to the next handler in the chain.
492
493 This is equivalent to writing:
494 @code
495 use( "/", h1, hn... );
496 @endcode
497
498 @par Example
499 @code
500 router.use(
501 [](Request&, Response& res)
502 {
503 res.message.erase("X-Powered-By");
504 return route::next;
505 });
506 @endcode
507
508 @par Constraints
509 @li `h1` must not be convertible to @ref core::string_view.
510
511 @param h1 The first handler to add.
512 @param hn Additional handlers to add, invoked after @p h1 in
513 registration order.
514 */
515 template<class H1, class... HN
516 , class = typename std::enable_if<
517 ! std::is_convertible<H1, core::string_view>::value>::type>
518 200 void use(H1&& h1, HN&&... hn)
519 {
520 // If you get a compile error on this line it means that
521 // one or more of the provided types is not a valid handler,
522 // error handler, or router.
523 BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
524
6/6
✓ Branch 9 taken 2 times.
✓ Branch 7 taken 4 times.
✓ Branch 5 taken 8 times.
✓ Branch 3 taken 85 times.
✓ Branch 17 taken 1 times.
✓ Branch 15 taken 1 times.
200 use(core::string_view(),
525 std::forward<H1>(h1), std::forward<HN>(hn)...);
526 200 }
527
528 /** Add handlers for all HTTP methods matching a path pattern.
529
530 This registers regular handlers for the specified path pattern,
531 participating in dispatch as described in the @ref basic_router
532 class documentation. Handlers run when the route matches,
533 regardless of HTTP method, and execute in registration order.
534 Error handlers and routers cannot be passed here. A new route
535 object is created even if the pattern already exists.
536
537 @code
538 router.route("/status")
539 .head(check_headers)
540 .get(send_status)
541 .all(log_access);
542 @endcode
543
544 @par Preconditions
545 @p `pattern` must be a valid path pattern; it must not be empty.
546
547 @param pattern The path pattern to match.
548 @param h1 The first handler to add.
549 @param hn Additional handlers to add, invoked after @p h1 in
550 registration order.
551 */
552 template<class H1, class... HN>
553 12 void all(
554 core::string_view pattern,
555 H1&& h1, HN&&... hn)
556 {
557 // If you get a compile error on this line it means that
558 // one or more of the provided types is not a valid handler.
559 // Error handlers and routers cannot be passed here.
560 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
561
2/2
✓ Branch 1 taken 11 times.
✓ Branch 5 taken 11 times.
12 this->route(pattern).all(
562 std::forward<H1>(h1), std::forward<HN>(hn)...);
563 11 }
564
565 /** Add one or more route handlers for a method and pattern.
566
567 This registers regular handlers for the specified HTTP verb and
568 path pattern, participating in dispatch as described in the
569 @ref basic_router class documentation. Error handlers and
570 routers cannot be passed here.
571
572 @param verb The known HTTP method to match.
573 @param pattern The path pattern to match.
574 @param h1 The first handler to add.
575 @param hn Additional handlers to add, invoked after @p h1 in
576 registration order.
577 */
578 template<class H1, class... HN>
579 42 void add(
580 http_proto::method verb,
581 core::string_view pattern,
582 H1&& h1, HN&&... hn)
583 {
584 // If you get a compile error on this line it means that
585 // one or more of the provided types is not a valid handler.
586 // Error handlers and routers cannot be passed here.
587 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
588
2/2
✓ Branch 1 taken 21 times.
✓ Branch 5 taken 19 times.
42 this->route(pattern).add(verb,
589 std::forward<H1>(h1), std::forward<HN>(hn)...);
590 42 }
591
592 /** Add one or more route handlers for a method and pattern.
593
594 This registers regular handlers for the specified HTTP verb and
595 path pattern, participating in dispatch as described in the
596 @ref basic_router class documentation. Error handlers and
597 routers cannot be passed here.
598
599 @param verb The HTTP method string to match.
600 @param pattern The path pattern to match.
601 @param h1 The first handler to add.
602 @param hn Additional handlers to add, invoked after @p h1 in
603 registration order.
604 */
605 template<class H1, class... HN>
606 2 void add(
607 core::string_view verb,
608 core::string_view pattern,
609 H1&& h1, HN&&... hn)
610 {
611 // If you get a compile error on this line it means that
612 // one or more of the provided types is not a valid handler.
613 // Error handlers and routers cannot be passed here.
614 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
615
2/2
✓ Branch 1 taken 2 times.
✓ Branch 5 taken 2 times.
2 this->route(pattern).add(verb,
616 std::forward<H1>(h1), std::forward<HN>(hn)...);
617 2 }
618
619 /** Return a fluent route for the specified path pattern.
620
621 Adds a new route to the router for the given pattern.
622 A new route object is always created, even if another
623 route with the same pattern already exists. The returned
624 @ref fluent_route reference allows method-specific handler
625 registration (such as GET or POST) or catch-all handlers
626 with @ref fluent_route::all.
627
628 @param pattern The path expression to match against request
629 targets. This may include parameters or wildcards following
630 the router's pattern syntax. May not be empty.
631 @return A fluent route interface for chaining handler registrations.
632 */
633 auto
634 63 route(
635 core::string_view pattern) -> fluent_route
636 {
637
1/1
✓ Branch 1 taken 62 times.
63 return fluent_route(*this, pattern);
638 }
639
640 //--------------------------------------------
641
642 /** Dispatch a request to the appropriate handler.
643
644 This runs the routing and error-dispatch logic for the given HTTP
645 method and target URL, as described in the @ref basic_router class
646 documentation.
647
648 @par Thread Safety
649 This function may be called concurrently on the same object along
650 with other `const` member functions. Each concurrent invocation
651 must use distinct request and response objects.
652
653 @param verb The HTTP method to match. This must not be
654 @ref http_proto::method::unknown.
655 @param url The full request target used for route matching.
656 @param req The request to pass to handlers.
657 @param res The response to pass to handlers.
658 @return The @ref route_result describing how routing completed.
659 @throws std::invalid_argument If @p verb is
660 @ref http_proto::method::unknown.
661 */
662 auto
663 141 dispatch(
664 http_proto::method verb,
665 urls::url_view const& url,
666 Req& req, Res& res) const ->
667 route_result
668 {
669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 if(verb == http_proto::method::unknown)
670 detail::throw_invalid_argument();
671
1/1
✓ Branch 2 taken 138 times.
279 return dispatch_impl(verb,
672 276 core::string_view(), url, req, res);
673 }
674
675 /** Dispatch a request to the appropriate handler using a method string.
676
677 This runs the routing and error-dispatch logic for the given HTTP
678 method string and target URL, as described in the @ref basic_router
679 class documentation. This overload is intended for method tokens
680 that are not represented by @ref http_proto::method.
681
682 @par Thread Safety
683 This function may be called concurrently on the same object along
684 with other `const` member functions. Each concurrent invocation
685 must use distinct request and response objects.
686
687 @param verb The HTTP method string to match. This must not be empty.
688 @param url The full request target used for route matching.
689 @param req The request to pass to handlers.
690 @param res The response to pass to handlers.
691 @return The @ref route_result describing how routing completed.
692 @throws std::invalid_argument If @p verb is empty.
693 */
694 auto
695 34 dispatch(
696 core::string_view verb,
697 urls::url_view const& url,
698 Req& req, Res& res) ->
699 route_result
700 {
701 // verb cannot be empty
702
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 33 times.
34 if(verb.empty())
703 1 detail::throw_invalid_argument();
704 33 return dispatch_impl(
705 http_proto::method::unknown,
706 33 verb, url, req, res);
707 }
708
709 /** Resume dispatch after a detached handler.
710
711 This continues routing after a previous call to @ref dispatch
712 returned @ref route::detach. It recreates the routing state and
713 resumes as if the handler that detached had instead returned
714 the specified @p ec from its body. The regular routing and
715 error-dispatch logic then proceeds as described in the
716 @ref basic_router class documentation. For example, if @p ec is
717 @ref route::next, the next matching handlers are invoked.
718
719 @par Thread Safety
720 This function may be called concurrently on the same object along
721 with other `const` member functions. Each concurrent invocation
722 must use distinct request and response objects.
723
724 @param req The request to pass to handlers.
725 @param res The response to pass to handlers.
726 @param rv The @ref route_result to resume with, as if returned
727 by the detached handler.
728 @return The @ref route_result describing how routing completed.
729 */
730 auto
731 9 resume(
732 Req& req, Res& res,
733 route_result const& rv) const ->
734 route_result
735 {
736 9 return resume_impl(req, res, rv);
737 }
738
739 private:
740 // wrapper for route handlers
741 template<class H>
742 struct handler_impl : any_handler
743 {
744 typename std::decay<H>::type h;
745
746 template<class... Args>
747 657 explicit handler_impl(Args&&... args)
748 657 : h(std::forward<Args>(args)...)
749 {
750 657 }
751
752 std::size_t
753 282 count() const noexcept override
754 {
755 564 return count(
756 282 handler_type<decltype(h)>{});
757 }
758
759 route_result
760 521 invoke(
761 basic_request& req,
762 basic_response& res) const override
763 {
764
1/1
✓ Branch 1 taken 92 times.
1042 return invoke(
765 static_cast<Req&>(req),
766 static_cast<Res&>(res),
767 704 handler_type<decltype(h)>{});
768 }
769
770 private:
771 std::size_t count(
772 std::integral_constant<int, 0>) = delete;
773
774 262 std::size_t count(
775 std::integral_constant<int, 1>) const noexcept
776 {
777 262 return 1;
778 }
779
780 6 std::size_t count(
781 std::integral_constant<int, 2>) const noexcept
782 {
783 6 return 1;
784 }
785
786 4 std::size_t count(
787 std::integral_constant<int, 4>) const noexcept
788 {
789 4 return 1 + h.count();
790 }
791
792 route_result invoke(Req&, Res&,
793 std::integral_constant<int, 0>) const = delete;
794
795 // (Req, Res)
796 395 route_result invoke(Req& req, Res& res,
797 std::integral_constant<int, 1>) const
798 {
799 395 auto const& ec = static_cast<
800 basic_response const&>(res).ec_;
801
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 186 times.
395 if(ec.failed())
802 24 return beast2::route::next;
803 // avoid racing on res.resume_
804 371 res.resume_ = res.pos_;
805
1/1
✓ Branch 1 taken 29 times.
371 auto rv = h(req, res);
806
3/4
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 160 times.
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
371 if(rv == beast2::route::detach)
807 24 return rv;
808 347 res.resume_ = 0; // revert
809 347 return rv;
810 }
811
812 // (Req&, Res&, error_code)
813 route_result
814 46 invoke(Req& req, Res& res,
815 std::integral_constant<int, 2>) const
816 {
817 46 auto const& ec = static_cast<
818 basic_response const&>(res).ec_;
819
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 38 times.
46 if(! ec.failed())
820 8 return beast2::route::next;
821 // avoid racing on res.resume_
822 38 res.resume_ = res.pos_;
823
1/1
✓ Branch 1 taken 38 times.
38 auto rv = h(req, res, ec);
824
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
38 if(rv == beast2::route::detach)
825 return rv;
826 38 res.resume_ = 0; // revert
827 38 return rv;
828 }
829
830 // any_router
831 17 route_result invoke(Req& req, Res& res,
832 std::integral_constant<int, 4>) const
833 {
834 17 auto const& ec = static_cast<
835 basic_response const&>(res).ec_;
836
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 times.
17 if(! ec.failed())
837 16 return h.dispatch_impl(req, res);
838 1 return beast2::route::next;
839 }
840 };
841
842 template<std::size_t N>
843 struct handler_list_impl : handler_list
844 {
845 template<class... HN>
846 551 explicit handler_list_impl(HN&&... hn)
847 {
848 551 n = sizeof...(HN);
849 551 p = v;
850
2/2
✓ Branch 2 taken 246 times.
✓ Branch 8 taken 1 times.
551 assign<0>(std::forward<HN>(hn)...);
851 551 }
852
853 private:
854 template<std::size_t I, class H1, class... HN>
855 657 void assign(H1&& h1, HN&&... hn)
856 {
857
1/1
✓ Branch 1 taken 329 times.
657 v[I] = handler_ptr(new handler_impl<H1>(
858 std::forward<H1>(h1)));
859 657 assign<I+1>(std::forward<HN>(hn)...);
860 657 }
861
862 template<std::size_t>
863 551 void assign()
864 {
865 551 }
866
867 handler_ptr v[N];
868 };
869
870 template<class... HN>
871 static auto
872 551 make_handler_list(HN&&... hn) ->
873 handler_list_impl<sizeof...(HN)>
874 {
875 return handler_list_impl<sizeof...(HN)>(
876 551 std::forward<HN>(hn)...);
877 }
878
879 void append(layer& e,
880 http_proto::method verb,
881 handler_list const& handlers)
882 {
883 add_impl(e, verb, handlers);
884 }
885 };
886
887 //-----------------------------------------------
888
889 template<class Req, class Res>
890 class basic_router<Req, Res>::
891 fluent_route
892 {
893 public:
894 fluent_route(fluent_route const&) = default;
895
896 /** Add handlers that apply to all HTTP methods.
897
898 This registers regular handlers that run for any request matching
899 the route's pattern, regardless of HTTP method. Handlers are
900 appended to the route's handler sequence and are invoked in
901 registration order whenever a preceding handler returns
902 @ref route::next. Error handlers and routers cannot be passed here.
903
904 This function returns a @ref fluent_route, allowing additional
905 method registrations to be chained. For example:
906 @code
907 router.route("/resource")
908 .all(log_request)
909 .get(show_resource)
910 .post(update_resource);
911 @endcode
912
913 @param h1 The first handler to add.
914 @param hn Additional handlers to add, invoked after @p h1 in
915 registration order.
916 @return The @ref fluent_route for further chained registrations.
917 */
918 template<class H1, class... HN>
919 14 auto all(
920 H1&& h1, HN&&... hn) ->
921 fluent_route
922 {
923 // If you get a compile error on this line it means that
924 // one or more of the provided types is not a valid handler.
925 // Error handlers and routers cannot be passed here.
926 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
927
2/2
✓ Branch 2 taken 14 times.
✓ Branch 6 taken 14 times.
14 owner_.add_impl(e_, core::string_view(), make_handler_list(
928 std::forward<H1>(h1), std::forward<HN>(hn)...));
929 14 return *this;
930 }
931
932 /** Add handlers for a specific HTTP method.
933
934 This registers regular handlers for the given method on the
935 current route, participating in dispatch as described in the
936 @ref basic_router class documentation. Handlers are appended
937 to the route's handler sequence and invoked in registration
938 order whenever a preceding handler returns @ref route::next.
939 Error handlers and routers cannot be passed here.
940
941 @param verb The HTTP method to match.
942 @param h1 The first handler to add.
943 @param hn Additional handlers to add, invoked after @p h1 in
944 registration order.
945 @return The @ref fluent_route for further chained registrations.
946 */
947 template<class H1, class... HN>
948 135 auto add(
949 http_proto::method verb,
950 H1&& h1, HN&&... hn) ->
951 fluent_route
952 {
953 // If you get a compile error on this line it means that
954 // one or more of the provided types is not a valid handler.
955 // Error handlers and routers cannot be passed here.
956 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
957
4/4
✓ Branch 2 taken 65 times.
✓ Branch 5 taken 64 times.
✓ Branch 3 taken 1 times.
✓ Branch 6 taken 1 times.
135 owner_.add_impl(e_, verb, make_handler_list(
958 std::forward<H1>(h1), std::forward<HN>(hn)...));
959 133 return *this;
960 }
961
962 /** Add handlers for a method name.
963
964 This registers regular handlers for the given HTTP method string
965 on the current route, participating in dispatch as described in
966 the @ref basic_router class documentation. This overload is
967 intended for methods not represented by @ref http_proto::method.
968 Handlers are appended to the route's handler sequence and invoked
969 in registration order whenever a preceding handler returns
970 @ref route::next.
971
972 @par Constraints
973 @li Each handler must be a regular handler; error handlers and
974 routers cannot be passed.
975
976 @param verb The HTTP method string to match.
977 @param h1 The first handler to add.
978 @param hn Additional handlers to add, invoked after @p h1 in
979 registration order.
980 @return The @ref fluent_route for further chained registrations.
981 */
982 template<class H1, class... HN>
983 9 auto add(
984 core::string_view verb,
985 H1&& h1, HN&&... hn) ->
986 fluent_route
987 {
988 // If you get a compile error on this line it means that
989 // one or more of the provided types is not a valid handler.
990 // Error handlers and routers cannot be passed here.
991 BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
992
2/2
✓ Branch 2 taken 9 times.
✓ Branch 5 taken 9 times.
9 owner_.add_impl(e_, verb, make_handler_list(
993 std::forward<H1>(h1), std::forward<HN>(hn)...));
994 9 return *this;
995 }
996
997 private:
998 friend class basic_router;
999 63 fluent_route(
1000 basic_router& owner,
1001 core::string_view pattern)
1002 63 : e_(owner.new_layer(pattern))
1003 62 , owner_(owner)
1004 {
1005 62 }
1006
1007 layer& e_;
1008 basic_router& owner_;
1009 };
1010
1011 } // beast2
1012 } // boost
1013
1014 #endif
1015