Line data Source code
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 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 129 : explicit handler_impl(H_ h_)
292 : : handler(handler_kind<H>)
293 129 : , h(std::forward<H_>(h_))
294 : {
295 129 : }
296 :
297 71 : 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 59 : return h(static_cast<P&>(rp));
303 : }
304 : else if constexpr (detail::returns_route_task<
305 : H, P&, system::error_code>)
306 : {
307 9 : 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 3 : return h(static_cast<P&>(rp), rp.ep_);
313 : }
314 : else
315 : {
316 : // impossible with flat router
317 0 : std::terminate();
318 : }
319 : }
320 :
321 : detail::router_base*
322 290 : get_router() noexcept override
323 : {
324 : if constexpr (std::is_base_of_v<
325 : detail::router_base, std::decay_t<H>>)
326 290 : return &h;
327 : else
328 0 : return nullptr;
329 : }
330 : };
331 :
332 : template<class H>
333 129 : static handler_ptr make_handler(H&& h)
334 : {
335 129 : 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 119 : explicit handlers_impl(HN&&... hn)
345 0 : {
346 119 : p = v;
347 119 : n = sizeof...(HN);
348 119 : assign<0>(std::forward<HN>(hn)...);
349 119 : }
350 :
351 : private:
352 : template<std::size_t I, class H1, class... HN>
353 129 : void assign(H1&& h1, HN&&... hn)
354 : {
355 129 : v[I] = make_handler(std::forward<H1>(h1));
356 129 : assign<I+1>(std::forward<HN>(hn)...);
357 129 : }
358 :
359 : template<std::size_t>
360 119 : void assign(int = 0)
361 : {
362 119 : }
363 : };
364 :
365 : template<class... HN>
366 119 : static auto make_handlers(HN&&... hn)
367 : {
368 : return handlers_impl<sizeof...(HN)>(
369 119 : 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 87 : 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 87 : add_impl(pattern, make_handlers(
496 : std::forward<H1>(h1), std::forward<HN>(hn)...));
497 86 : }
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 20 : 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 20 : use(std::string_view(),
542 : std::forward<H1>(h1), std::forward<HN>(hn)...);
543 20 : }
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 2 : 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 : add_impl(pattern, make_handlers(
581 : std::forward<H1>(h1), std::forward<HN>(hn)...));
582 2 : }
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 2 : 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 2 : except(std::string_view(),
615 : std::forward<H1>(h1), std::forward<HN>(hn)...);
616 2 : }
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 6 : 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 6 : this->route(pattern).all(
656 : std::forward<H1>(h1), std::forward<HN>(hn)...);
657 6 : }
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 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 : 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 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 6 : auto all(
776 : H1&& h1, HN&&... hn) ->
777 : fluent_route
778 : {
779 : static_assert(handler_check<1, H1, HN...>);
780 6 : 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 6 : 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 22 : auto add(
806 : http::method verb,
807 : H1&& h1, HN&&... hn) ->
808 : fluent_route
809 : {
810 : static_assert(handler_check<1, H1, HN...>);
811 22 : owner_.add_impl(owner_.get_layer(layer_idx_), verb, owner_.make_handlers(
812 : std::forward<H1>(h1), std::forward<HN>(hn)...));
813 22 : 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 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
|