LCOV - code coverage report
Current view: top level - boost/beast2/server - basic_router.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 96.5 % 113 109
Test Date: 2025-11-20 15:35:53 Functions: 93.2 % 222 207

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

Generated by: LCOV version 2.1