LCOV - code coverage report
Current view: top level - boost/http_proto/server - basic_router.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.6 % 166 157
Test Date: 2025-12-04 09:48:33 Functions: 92.0 % 264 243

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

Generated by: LCOV version 2.1