GCC Code Coverage Report


Directory: ./
File: libs/http/include/boost/http/server/router.hpp
Date: 2026-01-20 00:11:35
Exec Total Coverage
Lines: 72 75 96.0%
Functions: 162 179 90.5%
Branches: 31 31 100.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/http
8 //
9
10 #ifndef BOOST_HTTP_SERVER_ROUTER_HPP
11 #define BOOST_HTTP_SERVER_ROUTER_HPP
12
13 #include <boost/http/detail/config.hpp>
14 #include <boost/http/server/router_types.hpp>
15 #include <boost/http/server/detail/router_base.hpp>
16 #include <boost/http/method.hpp>
17 #include <boost/url/url_view.hpp>
18 #include <boost/mp11/algorithm.hpp>
19 #include <boost/capy/task.hpp>
20 #include <boost/assert.hpp>
21 #include <exception>
22 #include <string_view>
23 #include <type_traits>
24
25 namespace boost {
26 namespace http {
27
28 template<class> class router;
29
30 /** Configuration options for HTTP 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 102 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
60 @return A reference to `*this` for chaining.
61 */
62 router_options&
63 merge_params(
64 bool value) noexcept
65 {
66 v_ = (v_ & ~1) | (value ? 1 : 0);
67 return *this;
68 }
69
70 /** Set whether pattern matching is case-sensitive.
71
72 When this option is not set explicitly, the value is inherited
73 from the parent router or defaults to `false` if there is no parent.
74
75 @par Example
76 @code
77 router r( router_options()
78 .case_sensitive( true )
79 .strict( true ) );
80 @endcode
81
82 @param value `true` to perform case-sensitive path matching.
83
84 @return A reference to `*this` for chaining.
85 */
86 router_options&
87 4 case_sensitive(
88 bool value) noexcept
89 {
90
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(value)
91 3 v_ = (v_ & ~6) | 2;
92 else
93 1 v_ = (v_ & ~6) | 4;
94 4 return *this;
95 }
96
97 /** Set whether pattern matching is strict.
98
99 When this option is not set explicitly, the value is inherited
100 from the parent router or defaults to `false` if there is no parent.
101 Strict matching treats a trailing slash as significant:
102 the pattern `"/api"` matches `"/api"` but not `"/api/"`.
103 When strict matching is disabled, these paths are treated
104 as equivalent.
105
106 @par Example
107 @code
108 router r( router_options()
109 .strict( true )
110 .case_sensitive( false ) );
111 @endcode
112
113 @param value `true` to enable strict path matching.
114
115 @return A reference to `*this` for chaining.
116 */
117 router_options&
118 strict(
119 bool value) noexcept
120 {
121 if(value)
122 v_ = (v_ & ~24) | 8;
123 else
124 v_ = (v_ & ~24) | 16;
125 return *this;
126 }
127
128 private:
129 template<class> friend class router;
130 unsigned int v_ = 0;
131 };
132
133 //-----------------------------------------------
134
135 /** A container for HTTP route handlers.
136
137 `router` objects store and dispatch route handlers based on the
138 HTTP method and path of an incoming request. Routes are added with a
139 path pattern, method, and an associated handler, and the router is then
140 used to dispatch the appropriate handler.
141
142 Patterns used to create route definitions have percent-decoding applied
143 when handlers are mounted. A literal "%2F" in the pattern string is
144 indistinguishable from a literal '/'. For example, "/x%2Fz" is the
145 same as "/x/z" when used as a pattern.
146
147 @par Example
148 @code
149 using router_type = router<route_params>;
150 router_type router;
151 router.get( "/hello",
152 []( route_params& p )
153 {
154 p.res.status( status::ok );
155 p.res.set_body( "Hello, world!" );
156 return route::send;
157 } );
158 @endcode
159
160 Router objects are lightweight, shared references to their contents.
161 Copies of a router obtained through construction, conversion, or
162 assignment do not create new instances; they all refer to the same
163 underlying data.
164
165 @par Handlers
166
167 Regular handlers are invoked for matching routes and have this
168 equivalent signature:
169 @code
170 route_result handler( Params& p )
171 @endcode
172
173 The return value is a @ref route_result used to indicate the desired
174 action through @ref route enum values, or to indicate that a failure
175 occurred. Failures are represented by error codes for which
176 `system::error_code::failed()` returns `true`.
177
178 When a failing error code is produced and remains unhandled, the
179 router enters error-dispatching mode. In this mode, only error
180 handlers are invoked. Error handlers are registered globally or
181 for specific paths and execute in the order of registration whenever
182 a failing error code is present in the response.
183
184 Error handlers have this equivalent signature:
185 @code
186 route_result error_handler( Params& p, system::error_code ec )
187 @endcode
188
189 Each error handler may return any failing @ref system::error_code,
190 which is equivalent to calling:
191 @code
192 p.next( ec ); // with ec.failed() == true
193 @endcode
194
195 Returning @ref route::next indicates that control should proceed to
196 the next matching error handler. Returning a different failing code
197 replaces the current error and continues dispatch in error mode using
198 that new code. Error handlers are invoked until one returns a result
199 other than @ref route::next.
200
201 Exception handlers have this equivalent signature:
202 @code
203 route_result exception_handler( Params& p, E ex )
204 @endcode
205
206 Where `E` is the type of exception caught. Handlers installed for an
207 exception of type `E` will also be called when the exception type is
208 a derived class of `E`. Exception handlers are invoked in the order
209 of registration whenever an exception is present in the request.
210
211 The prefix match is not strict: middleware attached to `"/api"`
212 will also match `"/api/users"` and `"/api/data"`. When registered
213 before route handlers for the same prefix, middleware runs before
214 those routes. This is analogous to `app.use( path, ... )` in
215 Express.js.
216
217 @par Thread Safety
218
219 Member functions marked `const` such as @ref dispatch and @ref resume
220 may be called concurrently on routers that refer to the same data.
221 Modification of routers through calls to non-`const` member functions
222 is not thread-safe and must not be performed concurrently with any
223 other member function.
224
225 @par Nesting Depth
226
227 Routers may be nested to a maximum depth of `max_path_depth` (16 levels).
228 Exceeding this limit throws `std::length_error` when the nested router
229 is added via @ref use. This limit ensures that @ref flat_router dispatch
230 never overflows its fixed-size tracking arrays.
231
232 @par Constraints
233
234 `Params` must be publicly derived from @ref route_params_base.
235
236 @tparam Params The type of the parameters object passed to handlers.
237 */
238 template<class P>
239 class router : public detail::router_base
240 {
241 static_assert(std::derived_from<P, route_params_base>);
242
243 template<class T>
244 static inline constexpr char handler_kind =
245 []() -> char
246 {
247 if constexpr (detail::returns_route_task<T, P&>)
248 {
249 return is_plain;
250 }
251 else if constexpr (detail::returns_route_task<
252 T, P&, system::error_code>)
253 {
254 return is_error;
255 }
256 else if constexpr(
257 std::is_base_of_v<router_base, T> &&
258 std::is_convertible_v<T const volatile*,
259 router_base const volatile*> &&
260 std::is_constructible_v<T, router<P>>)
261 {
262 return is_router;
263 }
264 else if constexpr (detail::returns_route_task<
265 T, P&, std::exception_ptr>)
266 {
267 return is_exception;
268 }
269 else
270 {
271 return is_invalid;
272 }
273 }();
274
275 template<class... Ts>
276 static inline constexpr bool handler_crvals =
277 ((!std::is_lvalue_reference_v<Ts> ||
278 std::is_const_v<std::remove_reference_t<Ts>> ||
279 std::is_function_v<std::remove_reference_t<Ts>>) && ...);
280
281 template<char Mask, class... Ts>
282 static inline constexpr bool handler_check =
283 (((handler_kind<Ts> & Mask) != 0) && ...);
284
285 template<class H>
286 struct handler_impl : handler
287 {
288 std::decay_t<H> h;
289
290 template<class H_>
291 258 explicit handler_impl(H_ h_)
292 : handler(handler_kind<H>)
293 258 , h(std::forward<H_>(h_))
294 {
295 258 }
296
297 142 auto invoke(route_params_base& rp) const ->
298 capy::task<route_result> override
299 {
300 if constexpr (detail::returns_route_task<H, P&>)
301 {
302 118 return h(static_cast<P&>(rp));
303 }
304 else if constexpr (detail::returns_route_task<
305 H, P&, system::error_code>)
306 {
307 18 return h(static_cast<P&>(rp), rp.ec_);
308 }
309 else if constexpr (detail::returns_route_task<
310 H, P&, std::exception_ptr>)
311 {
312
1/1
✓ Branch 2 taken 3 times.
6 return h(static_cast<P&>(rp), rp.ep_);
313 }
314 else
315 {
316 // impossible with flat router
317 std::terminate();
318 }
319 }
320
321 detail::router_base*
322 580 get_router() noexcept override
323 {
324 if constexpr (std::is_base_of_v<
325 detail::router_base, std::decay_t<H>>)
326 580 return &h;
327 else
328 return nullptr;
329 }
330 };
331
332 template<class H>
333 258 static handler_ptr make_handler(H&& h)
334 {
335
1/1
✓ Branch 2 taken 129 times.
258 return std::make_unique<handler_impl<H>>(std::forward<H>(h));
336 }
337
338 template<std::size_t N>
339 struct handlers_impl : handlers
340 {
341 handler_ptr v[N];
342
343 template<class... HN>
344 238 explicit handlers_impl(HN&&... hn)
345 {
346 238 p = v;
347 238 n = sizeof...(HN);
348
1/1
✓ Branch 2 taken 112 times.
238 assign<0>(std::forward<HN>(hn)...);
349 238 }
350
351 private:
352 template<std::size_t I, class H1, class... HN>
353 258 void assign(H1&& h1, HN&&... hn)
354 {
355
1/1
✓ Branch 2 taken 129 times.
258 v[I] = make_handler(std::forward<H1>(h1));
356 258 assign<I+1>(std::forward<HN>(hn)...);
357 258 }
358
359 template<std::size_t>
360 235 void assign(int = 0)
361 {
362 235 }
363 };
364
365 template<class... HN>
366 238 static auto make_handlers(HN&&... hn)
367 {
368 return handlers_impl<sizeof...(HN)>(
369 238 std::forward<HN>(hn)...);
370 }
371
372 public:
373 /** The type of params used in handlers.
374 */
375 using params_type = P;
376
377 /** A fluent interface for defining handlers on a specific route.
378
379 This type represents a single route within the router and
380 provides a chainable API for registering handlers associated
381 with particular HTTP methods or for all methods collectively.
382
383 Typical usage registers one or more handlers for a route:
384 @code
385 router.route( "/users/:id" )
386 .get( show_user )
387 .put( update_user )
388 .all( log_access );
389 @endcode
390
391 Each call appends handlers in registration order.
392 */
393 class fluent_route;
394
395 router(router const&) = delete;
396 router& operator=(router const&) = delete;
397
398 /** Constructor.
399
400 Creates an empty router with the specified configuration.
401 Routers constructed with default options inherit the values
402 of @ref router_options::case_sensitive and
403 @ref router_options::strict from the parent router, or default
404 to `false` if there is no parent. The value of
405 @ref router_options::merge_params defaults to `false` and
406 is never inherited.
407
408 @param options The configuration options to use.
409 */
410 explicit
411 102 router(
412 router_options options = {})
413 102 : router_base(options.v_)
414 {
415 102 }
416
417 /** Construct a router from another router with compatible types.
418
419 This constructs a router that shares the same underlying routing
420 state as another router whose params type is a base class of `Params`.
421
422 The resulting router participates in shared ownership of the
423 implementation; copying the router does not duplicate routes or
424 handlers, and changes visible through one router are visible
425 through all routers that share the same underlying state.
426
427 @par Constraints
428
429 `Params` must be derived from `OtherParams`.
430
431 @param other The router to construct from.
432
433 @tparam OtherParams The params type of the source router.
434 */
435 template<class OtherP>
436 requires std::derived_from<OtherP, P>
437 80 router(
438 router<OtherP>&& other) noexcept
439 80 : router_base(std::move(other))
440 {
441 80 }
442
443 /** Add middleware handlers for a path prefix.
444
445 Each handler registered with this function participates in the
446 routing and error-dispatch process for requests whose path begins
447 with the specified prefix, as described in the @ref router
448 class documentation. Handlers execute in the order they are added
449 and may return @ref route::next to transfer control to the
450 subsequent handler in the chain.
451
452 @par Example
453 @code
454 router.use( "/api",
455 []( route_params& p )
456 {
457 if( ! authenticate( p ) )
458 {
459 p.res.status( 401 );
460 p.res.set_body( "Unauthorized" );
461 return route::send;
462 }
463 return route::next;
464 },
465 []( route_params& p )
466 {
467 p.res.set_header( "X-Powered-By", "MyServer" );
468 return route::next;
469 } );
470 @endcode
471
472 @par Preconditions
473
474 @p pattern must be a valid path prefix; it may be empty to
475 indicate the root scope.
476
477 @param pattern The pattern to match.
478
479 @param h1 The first handler to add.
480
481 @param hn Additional handlers to add, invoked after @p h1 in
482 registration order.
483 */
484 template<class H1, class... HN>
485 174 void use(
486 std::string_view pattern,
487 H1&& h1, HN&&... hn)
488 {
489 static_assert(handler_crvals<H1, HN...>,
490 "pass handlers by value or std::move()");
491 static_assert(! handler_check<8, H1, HN...>,
492 "cannot use exception handlers here");
493 static_assert(handler_check<7, H1, HN...>,
494 "invalid handler signature");
495
2/2
✓ Branch 2 taken 82 times.
✓ Branch 5 taken 2 times.
174 add_impl(pattern, make_handlers(
496 std::forward<H1>(h1), std::forward<HN>(hn)...));
497 172 }
498
499 /** Add global middleware handlers.
500
501 Each handler registered with this function participates in the
502 routing and error-dispatch process as described in the
503 @ref router class documentation. Handlers execute in the
504 order they are added and may return @ref route::next to transfer
505 control to the next handler in the chain.
506
507 This is equivalent to writing:
508 @code
509 use( "/", h1, hn... );
510 @endcode
511
512 @par Example
513 @code
514 router.use(
515 []( Params& p )
516 {
517 p.res.erase( "X-Powered-By" );
518 return route::next;
519 } );
520 @endcode
521
522 @par Constraints
523
524 @p h1 must not be convertible to @ref std::string_view.
525
526 @param h1 The first handler to add.
527
528 @param hn Additional handlers to add, invoked after @p h1 in
529 registration order.
530 */
531 template<class H1, class... HN>
532 39 void use(H1&& h1, HN&&... hn)
533 requires (!std::convertible_to<H1, std::string_view>)
534 {
535 static_assert(handler_crvals<H1, HN...>,
536 "pass handlers by value or std::move()");
537 static_assert(! handler_check<8, H1, HN...>,
538 "cannot use exception handlers here");
539 static_assert(handler_check<7, H1, HN...>,
540 "invalid handler signature");
541
1/1
✓ Branch 3 taken 15 times.
39 use(std::string_view(),
542 std::forward<H1>(h1), std::forward<HN>(hn)...);
543 39 }
544
545 /** Add exception handlers for a route pattern.
546
547 Registers one or more exception handlers that will be invoked
548 when an exception is thrown during request processing for routes
549 matching the specified pattern.
550
551 Handlers are invoked in the order provided until one handles
552 the exception.
553
554 @par Example
555 @code
556 app.except( "/api*",
557 []( route_params& p, std::exception const& ex )
558 {
559 p.res.set_status( 500 );
560 return route::send;
561 } );
562 @endcode
563
564 @param pattern The route pattern to match, or empty to match
565 all routes.
566
567 @param h1 The first exception handler.
568
569 @param hn Additional exception handlers.
570 */
571 template<class H1, class... HN>
572 4 void except(
573 std::string_view pattern,
574 H1&& h1, HN&&... hn)
575 {
576 static_assert(handler_crvals<H1, HN...>,
577 "pass handlers by value or std::move()");
578 static_assert(handler_check<8, H1, HN...>,
579 "only exception handlers are allowed here");
580
2/2
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
4 add_impl(pattern, make_handlers(
581 std::forward<H1>(h1), std::forward<HN>(hn)...));
582 4 }
583
584 /** Add global exception handlers.
585
586 Registers one or more exception handlers that will be invoked
587 when an exception is thrown during request processing for any
588 route.
589
590 Equivalent to calling `except( "", h1, hn... )`.
591
592 @par Example
593 @code
594 app.except(
595 []( route_params& p, std::exception const& ex )
596 {
597 p.res.set_status( 500 );
598 return route::send;
599 } );
600 @endcode
601
602 @param h1 The first exception handler.
603
604 @param hn Additional exception handlers.
605 */
606 template<class H1, class... HN>
607 4 void except(H1&& h1, HN&&... hn)
608 requires (!std::convertible_to<H1, std::string_view>)
609 {
610 static_assert(handler_crvals<H1, HN...>,
611 "pass handlers by value or std::move()");
612 static_assert(handler_check<8, H1, HN...>,
613 "only exception handlers are allowed here");
614
1/1
✓ Branch 3 taken 1 times.
4 except(std::string_view(),
615 std::forward<H1>(h1), std::forward<HN>(hn)...);
616 4 }
617
618 /** Add handlers for all HTTP methods matching a path pattern.
619
620 This registers regular handlers for the specified path pattern,
621 participating in dispatch as described in the @ref router
622 class documentation. Handlers run when the route matches,
623 regardless of HTTP method, and execute in registration order.
624 Error handlers and routers cannot be passed here. A new route
625 object is created even if the pattern already exists.
626
627 @par Example
628 @code
629 router.route( "/status" )
630 .add( method::head, check_headers )
631 .add( method::get, send_status )
632 .all( log_access );
633 @endcode
634
635 @par Preconditions
636
637 @p pattern must be a valid path pattern; it must not be empty.
638
639 @param pattern The path pattern to match.
640
641 @param h1 The first handler to add.
642
643 @param hn Additional handlers to add, invoked after @p h1 in
644 registration order.
645 */
646 template<class H1, class... HN>
647 9 void all(
648 std::string_view pattern,
649 H1&& h1, HN&&... hn)
650 {
651 static_assert(handler_crvals<H1, HN...>,
652 "pass handlers by value or std::move()");
653 static_assert(handler_check<1, H1, HN...>,
654 "only normal route handlers are allowed here");
655
2/2
✓ Branch 1 taken 6 times.
✓ Branch 5 taken 6 times.
9 this->route(pattern).all(
656 std::forward<H1>(h1), std::forward<HN>(hn)...);
657 9 }
658
659 /** Add route handlers for a method and pattern.
660
661 This registers regular handlers for the specified HTTP verb and
662 path pattern, participating in dispatch as described in the
663 @ref router class documentation. Error handlers and
664 routers cannot be passed here.
665
666 @param verb The known HTTP method to match.
667
668 @param pattern The path pattern to match.
669
670 @param h1 The first handler to add.
671
672 @param hn Additional handlers to add, invoked after @p h1 in
673 registration order.
674 */
675 template<class H1, class... HN>
676 12 void add(
677 http::method verb,
678 std::string_view pattern,
679 H1&& h1, HN&&... hn)
680 {
681 static_assert(handler_crvals<H1, HN...>,
682 "pass handlers by value or std::move()");
683 static_assert(handler_check<1, H1, HN...>,
684 "only normal route handlers are allowed here");
685
2/2
✓ Branch 1 taken 12 times.
✓ Branch 5 taken 12 times.
12 this->route(pattern).add(verb,
686 std::forward<H1>(h1), std::forward<HN>(hn)...);
687 12 }
688
689 /** Add route handlers for a method string and pattern.
690
691 This registers regular handlers for the specified HTTP verb and
692 path pattern, participating in dispatch as described in the
693 @ref router class documentation. Error handlers and
694 routers cannot be passed here.
695
696 @param verb The HTTP method string to match.
697
698 @param pattern The path pattern to match.
699
700 @param h1 The first handler to add.
701
702 @param hn Additional handlers to add, invoked after @p h1 in
703 registration order.
704 */
705 template<class H1, class... HN>
706 2 void add(
707 std::string_view verb,
708 std::string_view pattern,
709 H1&& h1, HN&&... hn)
710 {
711 static_assert(handler_crvals<H1, HN...>,
712 "pass handlers by value or std::move()");
713 static_assert(handler_check<1, H1, HN...>,
714 "only normal route handlers are allowed here");
715
2/2
✓ Branch 1 taken 2 times.
✓ Branch 5 taken 2 times.
2 this->route(pattern).add(verb,
716 std::forward<H1>(h1), std::forward<HN>(hn)...);
717 2 }
718
719 /** Return a fluent route for the specified path pattern.
720
721 Adds a new route to the router for the given pattern.
722 A new route object is always created, even if another
723 route with the same pattern already exists. The returned
724 @ref fluent_route reference allows method-specific handler
725 registration (such as GET or POST) or catch-all handlers
726 with @ref fluent_route::all.
727
728 @param pattern The path expression to match against request
729 targets. This may include parameters or wildcards following
730 the router's pattern syntax. May not be empty.
731
732 @return A fluent route interface for chaining handler
733 registrations.
734 */
735 auto
736 27 route(
737 std::string_view pattern) -> fluent_route
738 {
739
1/1
✓ Branch 1 taken 27 times.
27 return fluent_route(*this, pattern);
740 }
741 };
742
743 template<class P>
744 class router<P>::
745 fluent_route
746 {
747 public:
748 fluent_route(fluent_route const&) = default;
749
750 /** Add handlers that apply to all HTTP methods.
751
752 This registers regular handlers that run for any request matching
753 the route's pattern, regardless of HTTP method. Handlers are
754 appended to the route's handler sequence and are invoked in
755 registration order whenever a preceding handler returns
756 @ref route::next. Error handlers and routers cannot be passed here.
757
758 This function returns a @ref fluent_route, allowing additional
759 method registrations to be chained. For example:
760 @code
761 router.route( "/resource" )
762 .all( log_request )
763 .add( method::get, show_resource )
764 .add( method::post, update_resource );
765 @endcode
766
767 @param h1 The first handler to add.
768
769 @param hn Additional handlers to add, invoked after @p h1 in
770 registration order.
771
772 @return A reference to `*this` for chained registrations.
773 */
774 template<class H1, class... HN>
775 9 auto all(
776 H1&& h1, HN&&... hn) ->
777 fluent_route
778 {
779 static_assert(handler_check<1, H1, HN...>);
780
3/3
✓ Branch 2 taken 6 times.
✓ Branch 6 taken 6 times.
✓ Branch 9 taken 6 times.
9 owner_.add_impl(owner_.get_layer(layer_idx_), std::string_view{},
781 owner_.make_handlers(
782 std::forward<H1>(h1), std::forward<HN>(hn)...));
783 9 return *this;
784 }
785
786 /** Add handlers for a specific HTTP method.
787
788 This registers regular handlers for the given method on the
789 current route, participating in dispatch as described in the
790 @ref router class documentation. Handlers are appended
791 to the route's handler sequence and invoked in registration
792 order whenever a preceding handler returns @ref route::next.
793 Error handlers and routers cannot be passed here.
794
795 @param verb The HTTP method to match.
796
797 @param h1 The first handler to add.
798
799 @param hn Additional handlers to add, invoked after @p h1 in
800 registration order.
801
802 @return A reference to `*this` for chained registrations.
803 */
804 template<class H1, class... HN>
805 43 auto add(
806 http::method verb,
807 H1&& h1, HN&&... hn) ->
808 fluent_route
809 {
810 static_assert(handler_check<1, H1, HN...>);
811
6/6
✓ Branch 2 taken 21 times.
✓ Branch 5 taken 21 times.
✓ Branch 8 taken 21 times.
✓ Branch 3 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
43 owner_.add_impl(owner_.get_layer(layer_idx_), verb, owner_.make_handlers(
812 std::forward<H1>(h1), std::forward<HN>(hn)...));
813 43 return *this;
814 }
815
816 /** Add handlers for a method string.
817
818 This registers regular handlers for the given HTTP method string
819 on the current route, participating in dispatch as described in
820 the @ref router class documentation. This overload is
821 intended for methods not represented by @ref http::method.
822 Handlers are appended to the route's handler sequence and invoked
823 in registration order whenever a preceding handler returns
824 @ref route::next. Error handlers and routers cannot be passed here.
825
826 @param verb The HTTP method string to match.
827
828 @param h1 The first handler to add.
829
830 @param hn Additional handlers to add, invoked after @p h1 in
831 registration order.
832
833 @return A reference to `*this` for chained registrations.
834 */
835 template<class H1, class... HN>
836 2 auto add(
837 std::string_view verb,
838 H1&& h1, HN&&... hn) ->
839 fluent_route
840 {
841 static_assert(handler_check<1, H1, HN...>);
842
3/3
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
2 owner_.add_impl(owner_.get_layer(layer_idx_), verb, owner_.make_handlers(
843 std::forward<H1>(h1), std::forward<HN>(hn)...));
844 2 return *this;
845 }
846
847 private:
848 friend class router;
849 27 fluent_route(
850 router& owner,
851 std::string_view pattern)
852 27 : layer_idx_(owner.new_layer_idx(pattern))
853 27 , owner_(owner)
854 {
855 27 }
856
857 std::size_t layer_idx_;
858 router& owner_;
859 };
860
861 } // http
862 } // boost
863
864 #endif
865