GCC Code Coverage Report


Directory: ./
File: libs/http_proto/include/boost/http_proto/server/basic_router.hpp
Date: 2025-12-04 09:48:34
Exec Total Coverage
Lines: 157 166 94.6%
Functions: 243 263 92.4%
Branches: 72 82 87.8%

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