LCOV - code coverage report
Current view: top level - boost/http/server - router.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 96.0 % 75 72
Test Date: 2026-01-20 00:11:34 Functions: 90.5 % 179 162

            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
        

Generated by: LCOV version 2.3