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.8 % 172 163
Test Date: 2025-12-02 19:05:12 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/server/route_handler.hpp>
      17              : #include <boost/http_proto/method.hpp>
      18              : #include <boost/url/url_view.hpp>
      19              : #include <boost/capy/detail/call_traits.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 http_proto {
      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          144 :     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 http_proto::basic_router;
     144              :     using opt_flags = unsigned int;
     145              : 
     146              :     struct BOOST_HTTP_PROTO_DECL any_handler
     147              :     {
     148          348 :         virtual ~any_handler() = default;
     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_HTTP_PROTO_DECL ~any_router();
     168              :     BOOST_HTTP_PROTO_DECL any_router(opt_flags);
     169              :     BOOST_HTTP_PROTO_DECL any_router(any_router&&) noexcept;
     170              :     BOOST_HTTP_PROTO_DECL any_router(any_router const&) noexcept;
     171              :     BOOST_HTTP_PROTO_DECL any_router& operator=(any_router&&) noexcept;
     172              :     BOOST_HTTP_PROTO_DECL any_router& operator=(any_router const&) noexcept;
     173              :     BOOST_HTTP_PROTO_DECL std::size_t count() const noexcept;
     174              :     BOOST_HTTP_PROTO_DECL layer& new_layer(core::string_view pattern);
     175              :     BOOST_HTTP_PROTO_DECL void add_impl(core::string_view, handler_list const&);
     176              :     BOOST_HTTP_PROTO_DECL void add_impl(layer&,
     177              :         http_proto::method, handler_list const&);
     178              :     BOOST_HTTP_PROTO_DECL void add_impl(layer&,
     179              :         core::string_view, handler_list const&);
     180              :     BOOST_HTTP_PROTO_DECL route_result resume_impl(
     181              :         basic_request&, basic_response&, route_result ec) const;
     182              :     BOOST_HTTP_PROTO_DECL route_result dispatch_impl(http_proto::method,
     183              :         core::string_view, urls::url_view const&,
     184              :             basic_request&, basic_response&) const;
     185              :     BOOST_HTTP_PROTO_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 route::send;
     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              :     template<class H, class = void>
     359              :     struct except_type : std::false_type {};
     360              : 
     361              :     template<class H>
     362              :     struct except_type<H, typename std::enable_if<
     363              :         capy::detail::call_traits<H>{} && (
     364              :         capy::detail::type_list_size<typename
     365              :             capy::detail::call_traits<H>::arg_types>{} == 3) &&
     366              :         std::is_convertible<Req&, typename capy::detail::type_at<0, typename
     367              :             capy::detail::call_traits<H>::arg_types>::type>::value &&
     368              :         std::is_convertible<Res&, typename capy::detail::type_at<1, typename
     369              :             capy::detail::call_traits<H>::arg_types>::type>{}
     370              :             >::type>
     371              :         : std::true_type
     372              :     {
     373              :         using type = typename std::decay<typename
     374              :             capy::detail::type_at<2,typename
     375              :                 capy::detail::call_traits<H>::arg_types>::type>::type;
     376              :     };
     377              : 
     378              :     template<int, class... Hs>
     379              :     struct except_types;
     380              : 
     381              :     template<int N>
     382              :     struct except_types<N> : std::true_type {};
     383              : 
     384              :     template<int N, class H1, class... HN>
     385              :     struct except_types<N, H1, HN...> : std::integral_constant<bool,
     386              :         except_type<H1>{} && except_types<N, HN...>{}>
     387              :     {};
     388              : 
     389              : public:
     390              :     /** The type of request object used in handlers
     391              :     */
     392              :     using request_type = Req;
     393              : 
     394              :     /** The type of response object used in handlers
     395              :     */
     396              :     using response_type = Res;
     397              : 
     398              :     /** A fluent interface for defining handlers on a specific route.
     399              : 
     400              :         This type represents a single route within the router and
     401              :         provides a chainable API for registering handlers associated
     402              :         with particular HTTP methods or for all methods collectively.
     403              : 
     404              :         Typical usage registers one or more handlers for a route:
     405              :         @code
     406              :         router.route("/users/:id")
     407              :             .get(show_user)
     408              :             .put(update_user)
     409              :             .all(log_access);
     410              :         @endcode
     411              : 
     412              :         Each call appends handlers in registration order.
     413              :     */
     414              :     class fluent_route;
     415              : 
     416              :     /** Constructor
     417              : 
     418              :         Creates an empty router with the specified configuration.
     419              :         Routers constructed with default options inherit the values
     420              :         of @ref router_options::case_sensitive and
     421              :         @ref router_options::strict from the parent router, or default
     422              :         to `false` if there is no parent. The value of
     423              :         @ref router_options::merge_params defaults to `false` and
     424              :         is never inherited.
     425              : 
     426              :         @param options The configuration options to use.
     427              :     */
     428              :     explicit
     429          144 :     basic_router(router_options options = {})
     430          144 :         : any_router(options.v_)
     431              :     {
     432          144 :     }
     433              : 
     434              :     /** Construct a router from another router with compatible types.
     435              : 
     436              :         This constructs a router that shares the same underlying routing
     437              :         state as another router whose request and response types are base
     438              :         classes of `Req` and `Res`, respectively.
     439              : 
     440              :         The resulting router participates in shared ownership of the
     441              :         implementation; copying the router does not duplicate routes or
     442              :         handlers, and changes visible through one router are visible
     443              :         through all routers that share the same underlying state.
     444              : 
     445              :         @par Constraints
     446              :         `Req` must be derived from `OtherReq`, and `Res` must be
     447              :         derived from `OtherRes`.
     448              : 
     449              :         @tparam OtherReq The request type of the source router.
     450              :         @tparam OtherRes The response type of the source router.
     451              :         @param other The router to copy.
     452              :     */
     453              :     template<
     454              :         class OtherReq, class OtherRes,
     455              :         class = typename std::enable_if<
     456              :             detail::derived_from<Req, OtherReq>::value &&
     457              :             detail::derived_from<Res, OtherRes>::value>::type
     458              :     >
     459              :     basic_router(
     460              :         basic_router<OtherReq, OtherRes> const& other)
     461              :         : any_router(other)
     462              :     {
     463              :     }
     464              : 
     465              :     /** Add one or more middleware handlers for a path prefix.
     466              : 
     467              :         Each handler registered with this function participates in the
     468              :         routing and error-dispatch process for requests whose path begins
     469              :         with the specified prefix, as described in the @ref basic_router
     470              :         class documentation. Handlers execute in the order they are added
     471              :         and may return @ref route::next to transfer control to the
     472              :         subsequent handler in the chain.
     473              : 
     474              :         @par Example
     475              :         @code
     476              :         router.use("/api",
     477              :             [](Request& req, Response& res)
     478              :             {
     479              :                 if (!authenticate(req))
     480              :                 {
     481              :                     res.status(401);
     482              :                     res.set_body("Unauthorized");
     483              :                     return route::send;
     484              :                 }
     485              :                 return route::next;
     486              :             },
     487              :             [](Request&, Response& res)
     488              :             {
     489              :                 res.set_header("X-Powered-By", "MyServer");
     490              :                 return route::next;
     491              :             });
     492              :         @endcode
     493              : 
     494              :         @par Preconditions
     495              :         @p `pattern` must be a valid path prefix; it may be empty to
     496              :             indicate the root scope.
     497              : 
     498              :         @param pattern The pattern to match.
     499              :         @param h1 The first handler to add.
     500              :         @param hn Additional handlers to add, invoked after @p h1 in
     501              :             registration order.
     502              :     */
     503              :     template<class H1, class... HN>
     504          192 :     void use(
     505              :         core::string_view pattern,
     506              :         H1&& h1, HN... hn)
     507              :     {
     508              :         // If you get a compile error on this line it means that
     509              :         // one or more of the provided types is not a valid handler,
     510              :         // error handler, or router.
     511              :         BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
     512          192 :         add_impl(pattern, make_handler_list(
     513              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
     514          192 :     }
     515              : 
     516              :     /** Add one or more global middleware handlers.
     517              :         
     518              :         Each handler registered with this function participates in the
     519              :         routing and error-dispatch process as described in the
     520              :         @ref basic_router class documentation. Handlers execute in the
     521              :         order they are added and may return @ref route::next to transfer
     522              :         control to the next handler in the chain.
     523              : 
     524              :         This is equivalent to writing:
     525              :         @code
     526              :         use( "/", h1, hn... );
     527              :         @endcode
     528              : 
     529              :         @par Example
     530              :         @code
     531              :         router.use(
     532              :             [](Request&, Response& res)
     533              :             {
     534              :                 res.message.erase("X-Powered-By");
     535              :                 return route::next;
     536              :             });
     537              :         @endcode
     538              : 
     539              :         @par Constraints
     540              :         @li `h1` must not be convertible to @ref core::string_view.
     541              : 
     542              :         @param h1 The first handler to add.
     543              :         @param hn Additional handlers to add, invoked after @p h1 in
     544              :             registration order.
     545              :     */
     546              :     template<class H1, class... HN
     547              :         , class = typename std::enable_if<
     548              :             ! std::is_convertible<H1, core::string_view>::value>::type>
     549          108 :     void use(H1&& h1, HN&&... hn)
     550              :     {
     551              :         // If you get a compile error on this line it means that
     552              :         // one or more of the provided types is not a valid handler,
     553              :         // error handler, or router.
     554              :         BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
     555          108 :         use(core::string_view(),
     556              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     557          108 :     }
     558              : 
     559              :     /** Add a global exception handler.
     560              :     */
     561              :     template<class H1, class... HN>
     562           11 :     void except(
     563              :         core::string_view pattern,
     564              :         H1&& h1, HN... hn)
     565              :     {
     566              :         // If you get a compile error on this line it means that one or
     567              :         // more of the provided types is not a valid exception handler
     568              :         BOOST_CORE_STATIC_ASSERT(except_types<0, H1, HN...>::value);
     569           11 :         add_impl(pattern, make_except_list(
     570              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
     571           11 :     }
     572              : 
     573              :     template<class H1, class... HN
     574              :         , class = typename std::enable_if<
     575              :             ! std::is_convertible<H1, core::string_view>::value>::type>
     576           11 :     void except(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<0, H1, HN...>::value);
     581           11 :         except(core::string_view(),
     582              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     583           11 :     }
     584              : 
     585              :     /** Add handlers for all HTTP methods matching a path pattern.
     586              :         
     587              :         This registers regular handlers for the specified path pattern,
     588              :         participating in dispatch as described in the @ref basic_router
     589              :         class documentation. Handlers run when the route matches,
     590              :         regardless of HTTP method, and execute in registration order.
     591              :         Error handlers and routers cannot be passed here. A new route
     592              :         object is created even if the pattern already exists.
     593              : 
     594              :         @code
     595              :         router.route("/status")
     596              :             .head(check_headers)
     597              :             .get(send_status)
     598              :             .all(log_access);
     599              :         @endcode
     600              : 
     601              :         @par Preconditions
     602              :         @p `pattern` must be a valid path pattern; it must not be empty.
     603              : 
     604              :         @param pattern The path pattern to match.
     605              :         @param h1 The first handler to add.
     606              :         @param hn Additional handlers to add, invoked after @p h1 in
     607              :             registration order.
     608              :     */
     609              :     template<class H1, class... HN>
     610           12 :     void all(
     611              :         core::string_view pattern,
     612              :         H1&& h1, HN&&... hn)
     613              :     {
     614              :         // If you get a compile error on this line it means that
     615              :         // one or more of the provided types is not a valid handler.
     616              :         // Error handlers and routers cannot be passed here.
     617              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
     618           12 :         this->route(pattern).all(
     619              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     620           11 :     }
     621              : 
     622              :     /** Add one or more route handlers for a method and pattern.
     623              :         
     624              :         This registers regular handlers for the specified HTTP verb and
     625              :         path pattern, participating in dispatch as described in the
     626              :         @ref basic_router class documentation. Error handlers and
     627              :         routers cannot be passed here.
     628              : 
     629              :         @param verb The known HTTP method to match.
     630              :         @param pattern The path pattern to match.
     631              :         @param h1 The first handler to add.
     632              :         @param hn Additional handlers to add, invoked after @p h1 in
     633              :             registration order.
     634              :     */
     635              :     template<class H1, class... HN>
     636           21 :     void add(
     637              :         http_proto::method verb,
     638              :         core::string_view pattern,
     639              :         H1&& h1, HN&&... hn)
     640              :     {
     641              :         // If you get a compile error on this line it means that
     642              :         // one or more of the provided types is not a valid handler.
     643              :         // Error handlers and routers cannot be passed here.
     644              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
     645           21 :         this->route(pattern).add(verb,
     646              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     647           21 :     }
     648              : 
     649              :     /** Add one or more route handlers for a method and pattern.
     650              :         
     651              :         This registers regular handlers for the specified HTTP verb and
     652              :         path pattern, participating in dispatch as described in the
     653              :         @ref basic_router class documentation. Error handlers and
     654              :         routers cannot be passed here.
     655              : 
     656              :         @param verb The HTTP method string to match.
     657              :         @param pattern The path pattern to match.
     658              :         @param h1 The first handler to add.
     659              :         @param hn Additional handlers to add, invoked after @p h1 in
     660              :             registration order.
     661              :     */
     662              :     template<class H1, class... HN>
     663            2 :     void add(
     664              :         core::string_view verb,
     665              :         core::string_view pattern,
     666              :         H1&& h1, HN&&... hn)
     667              :     {
     668              :         // If you get a compile error on this line it means that
     669              :         // one or more of the provided types is not a valid handler.
     670              :         // Error handlers and routers cannot be passed here.
     671              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
     672            2 :         this->route(pattern).add(verb,
     673              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     674            2 :     }
     675              : 
     676              :     /** Return a fluent route for the specified path pattern.
     677              : 
     678              :         Adds a new route to the router for the given pattern.
     679              :         A new route object is always created, even if another
     680              :         route with the same pattern already exists. The returned
     681              :         @ref fluent_route reference allows method-specific handler
     682              :         registration (such as GET or POST) or catch-all handlers
     683              :         with @ref fluent_route::all.
     684              : 
     685              :         @param pattern The path expression to match against request
     686              :         targets. This may include parameters or wildcards following
     687              :         the router's pattern syntax. May not be empty.
     688              :         @return A fluent route interface for chaining handler registrations.
     689              :     */
     690              :     auto
     691           63 :     route(
     692              :         core::string_view pattern) -> fluent_route
     693              :     {
     694           63 :         return fluent_route(*this, pattern);
     695              :     }
     696              : 
     697              :     //--------------------------------------------
     698              : 
     699              :     /** Dispatch a request to the appropriate handler.
     700              :         
     701              :         This runs the routing and error-dispatch logic for the given HTTP
     702              :         method and target URL, as described in the @ref basic_router class
     703              :         documentation.
     704              : 
     705              :         @par Thread Safety
     706              :         This function may be called concurrently on the same object along
     707              :         with other `const` member functions. Each concurrent invocation
     708              :         must use distinct request and response objects.
     709              : 
     710              :         @param verb The HTTP method to match. This must not be
     711              :             @ref http_proto::method::unknown.
     712              :         @param url The full request target used for route matching.
     713              :         @param req The request to pass to handlers.
     714              :         @param res The response to pass to handlers.
     715              :         @return The @ref route_result describing how routing completed.
     716              :         @throws std::invalid_argument If @p verb is
     717              :             @ref http_proto::method::unknown.
     718              :     */
     719              :     auto
     720          148 :     dispatch(
     721              :         http_proto::method verb,
     722              :         urls::url_view const& url,
     723              :         Req& req, Res& res) const ->
     724              :             route_result
     725              :     {
     726          148 :         if(verb == http_proto::method::unknown)
     727            1 :             detail::throw_invalid_argument();
     728          291 :         return dispatch_impl(verb,
     729          288 :             core::string_view(), url, req, res);
     730              :     }
     731              : 
     732              :     /** Dispatch a request to the appropriate handler using a method string.
     733              :         
     734              :         This runs the routing and error-dispatch logic for the given HTTP
     735              :         method string and target URL, as described in the @ref basic_router
     736              :         class documentation. This overload is intended for method tokens
     737              :         that are not represented by @ref http_proto::method.
     738              : 
     739              :         @par Thread Safety
     740              :         This function may be called concurrently on the same object along
     741              :         with other `const` member functions. Each concurrent invocation
     742              :         must use distinct request and response objects.
     743              : 
     744              :         @param verb The HTTP method string to match. This must not be empty.
     745              :         @param url The full request target used for route matching.
     746              :         @param req The request to pass to handlers.
     747              :         @param res The response to pass to handlers.
     748              :         @return The @ref route_result describing how routing completed.
     749              :         @throws std::invalid_argument If @p verb is empty.
     750              :     */
     751              :     auto
     752           34 :     dispatch(
     753              :         core::string_view verb,
     754              :         urls::url_view const& url,
     755              :         Req& req, Res& res) ->
     756              :             route_result
     757              :     {
     758              :         // verb cannot be empty
     759           34 :         if(verb.empty())
     760            1 :             detail::throw_invalid_argument();
     761           33 :         return dispatch_impl(
     762              :             http_proto::method::unknown,
     763           33 :                 verb, url, req, res);
     764              :     }
     765              : 
     766              :     /** Resume dispatch after a detached handler.
     767              :         
     768              :         This continues routing after a previous call to @ref dispatch
     769              :         returned @ref route::detach. It recreates the routing state and
     770              :         resumes as if the handler that detached had instead returned
     771              :         the specified @p ec from its body. The regular routing and
     772              :         error-dispatch logic then proceeds as described in the
     773              :         @ref basic_router class documentation. For example, if @p ec is
     774              :         @ref route::next, the next matching handlers are invoked.
     775              : 
     776              :         @par Thread Safety
     777              :         This function may be called concurrently on the same object along
     778              :         with other `const` member functions. Each concurrent invocation
     779              :         must use distinct request and response objects.
     780              : 
     781              :         @param req The request to pass to handlers.
     782              :         @param res The response to pass to handlers.
     783              :         @param rv The @ref route_result to resume with, as if returned
     784              :             by the detached handler.
     785              :         @return The @ref route_result describing how routing completed.
     786              :     */
     787              :     auto
     788            9 :     resume(
     789              :         Req& req, Res& res,
     790              :         route_result const& rv) const ->
     791              :             route_result
     792              :     {
     793            9 :          return resume_impl(req, res, rv);
     794              :     }
     795              : 
     796              : private:
     797              :     struct undo_resume
     798              :     {
     799              :         std::size_t& resume;
     800              :         bool undo_ = true;
     801          232 :         ~undo_resume()
     802              :         {
     803          232 :             if(undo_)
     804          220 :                 resume = 0;
     805          232 :         }
     806          232 :         undo_resume(
     807              :             std::size_t& resume_) noexcept
     808          232 :             : resume(resume_)
     809              :         {
     810          232 :         }
     811           12 :         void cancel() noexcept
     812              :         {
     813           12 :             undo_ = false;
     814           12 :         }
     815              :     };
     816              : 
     817              :     // wrapper for route handlers
     818              :     template<
     819              :         class H,
     820              :         class Ty = handler_type<typename
     821              :         std::decay<H>::type > >
     822              :     struct handler_impl : any_handler
     823              :     {
     824              :         typename std::decay<H>::type h;
     825              : 
     826              :         template<class... Args>
     827          336 :         explicit handler_impl(Args&&... args)
     828          336 :             : h(std::forward<Args>(args)...)
     829              :         {
     830          336 :         }
     831              : 
     832              :         std::size_t
     833          141 :         count() const noexcept override
     834              :         {
     835          141 :             return count(Ty{});
     836              :         }
     837              : 
     838              :         route_result
     839          266 :         invoke(
     840              :             basic_request& req,
     841              :             basic_response& res) const override
     842              :         {
     843          526 :             return invoke(
     844              :                 static_cast<Req&>(req),
     845          351 :                 static_cast<Res&>(res), Ty{});
     846              :         }
     847              : 
     848              :     private:
     849              :         std::size_t count(
     850              :             std::integral_constant<int, 0>) = delete;
     851              : 
     852          131 :         std::size_t count(
     853              :             std::integral_constant<int, 1>) const noexcept
     854              :         {
     855          131 :             return 1;
     856              :         }
     857              : 
     858            6 :         std::size_t count(
     859              :             std::integral_constant<int, 2>) const noexcept
     860              :         {
     861            6 :             return 1;
     862              :         }
     863              : 
     864            4 :         std::size_t count(
     865              :             std::integral_constant<int, 4>) const noexcept
     866              :         {
     867            4 :             return 1 + h.count();
     868              :         }
     869              : 
     870              :         route_result invoke(Req&, Res&,
     871              :             std::integral_constant<int, 0>) const = delete;
     872              : 
     873              :         // (Req, Res)
     874          203 :         route_result invoke(Req& req, Res& res,
     875              :             std::integral_constant<int, 1>) const
     876              :         {
     877          203 :             auto& res_ = static_cast<
     878              :                 basic_response&>(res);
     879          203 :             if( res_.ec_.failed() ||
     880              :                 res_.ep_)
     881           12 :                 return http_proto::route::next;
     882              :             // avoid racing on res_.resume_
     883          191 :             undo_resume u(res_.resume_);
     884          191 :             res_.resume_ = res_.pos_;
     885          191 :             auto rv = h(req, res);
     886          185 :             if(rv == http_proto::route::detach)
     887              :             {
     888           12 :                 u.cancel();
     889           12 :                 return rv;
     890              :             }
     891          173 :             return rv;
     892          191 :         }
     893              : 
     894              :         // (Req&, Res&, error_code)
     895              :         route_result
     896           46 :         invoke(Req& req, Res& res,
     897              :             std::integral_constant<int, 2>) const
     898              :         {
     899           46 :             auto& res_ = static_cast<
     900              :                 basic_response&>(res);
     901           46 :             if(! res_.ec_.failed())
     902            8 :                 return http_proto::route::next;
     903              :             // avoid racing on res.resume_
     904           38 :             res_.resume_ = res_.pos_;
     905           38 :             undo_resume u(res_.resume_);
     906           38 :             auto rv = h(req, res, res_.ec_);
     907           38 :             if(rv == http_proto::route::detach)
     908              :             {
     909            0 :                 u.cancel();
     910            0 :                 return rv;
     911              :             }
     912           38 :             return rv;
     913           38 :         }
     914              : 
     915              :         // any_router
     916           17 :         route_result invoke(Req& req, Res& res,
     917              :             std::integral_constant<int, 4>) const
     918              :         {
     919           17 :             auto const& res_ = static_cast<
     920              :                 basic_response const&>(res);
     921           32 :             if( res_.resume_ > 0 ||
     922           15 :                 (   ! res_.ec_.failed() &&
     923           14 :                     ! res_.ep_))
     924           16 :                 return h.dispatch_impl(req, res);
     925            1 :             return http_proto::route::next;
     926              :         }
     927              :     };
     928              : 
     929              :     template<class H, class E = typename
     930              :         except_type<typename std::decay<H>::type>::type>
     931              :     struct except_impl : any_handler
     932              :     {
     933              :         typename std::decay<H>::type h;
     934              : 
     935              :         template<class... Args>
     936           12 :         explicit except_impl(Args&&... args)
     937           12 :             : h(std::forward<Args>(args)...)
     938              :         {
     939           12 :         }
     940              : 
     941              :         std::size_t
     942            0 :         count() const noexcept override
     943              :         {
     944            0 :             return 1;
     945              :         }
     946              : 
     947              :         route_result
     948           12 :         invoke(Req& req, Res& res) const override
     949              :         {
     950              :         #ifndef BOOST_NO_EXCEPTIONS
     951           12 :             auto& res_ = static_cast<
     952              :                 basic_response&>(res);
     953           24 :             if( ! res_.ec_.failed() &&
     954           12 :                 ! res_.ep_)
     955            6 :                 return http_proto::route::next;
     956            6 :             volatile int dummy = sizeof(E);
     957            6 :             (void)(dummy);
     958              :             try
     959              :             {
     960           12 :                 std::rethrow_exception(res_.ep_);
     961              :             }
     962            9 :             catch(E const& ex)
     963              :             {
     964              :                 // avoid racing on res.resume_
     965            3 :                 res_.resume_ = res_.pos_;
     966            3 :                 undo_resume u(res_.resume_);
     967              :                 // VFALCO What if h throws?
     968            3 :                 auto rv = h(req, res, ex);
     969            3 :                 if(rv == http_proto::route::detach)
     970              :                 {
     971            0 :                     u.cancel();
     972            0 :                     return rv;
     973              :                 }
     974            3 :                 return rv;
     975            3 :             }
     976            3 :             catch(...)
     977              :             {
     978            3 :                 res_.ep_ = std::current_exception();
     979            3 :                 return http_proto::route::next;
     980              :             }
     981              :         #else
     982              :             (void)req;
     983              :             (void)res;
     984              :             return http_proto::route::next;
     985              :         #endif
     986              :         }
     987              :     };
     988              : 
     989              :     template<std::size_t N>
     990              :     struct handler_list_impl : handler_list
     991              :     {
     992              :         template<class... HN>
     993          283 :         explicit handler_list_impl(HN&&... hn)
     994            0 :         {
     995          283 :             n = sizeof...(HN);
     996          283 :             p = v;
     997          283 :             assign<0>(std::forward<HN>(hn)...);
     998          283 :         }
     999              : 
    1000              :         // exception handlers
    1001              :         template<class... HN>
    1002           11 :         explicit handler_list_impl(int, HN&&... hn)
    1003            0 :         {
    1004           11 :             n = sizeof...(HN);
    1005           11 :             p = v;
    1006           11 :             assign<0>(0, std::forward<HN>(hn)...);
    1007           11 :         }
    1008              : 
    1009              :     private:
    1010              :         template<std::size_t I, class H1, class... HN>
    1011          336 :         void assign(H1&& h1, HN&&... hn)
    1012              :         {
    1013          336 :             v[I] = handler_ptr(new handler_impl<H1>(
    1014              :                 std::forward<H1>(h1)));
    1015          336 :             assign<I+1>(std::forward<HN>(hn)...);
    1016          336 :         }
    1017              : 
    1018              :         // exception handlers
    1019              :         template<std::size_t I, class H1, class... HN>
    1020           12 :         void assign(int, H1&& h1, HN&&... hn)
    1021              :         {
    1022           12 :             v[I] = handler_ptr(new except_impl<H1>(
    1023              :                 std::forward<H1>(h1)));
    1024           12 :             assign<I+1>(0, std::forward<HN>(hn)...);
    1025           12 :         }
    1026              : 
    1027              :         template<std::size_t>
    1028          294 :         void assign(int = 0)
    1029              :         {
    1030          294 :         }
    1031              : 
    1032              :         handler_ptr v[N];
    1033              :     };
    1034              : 
    1035              :     template<class... HN>
    1036              :     static auto
    1037          283 :     make_handler_list(HN&&... hn) ->
    1038              :         handler_list_impl<sizeof...(HN)>
    1039              :     {
    1040              :         return handler_list_impl<sizeof...(HN)>(
    1041          283 :             std::forward<HN>(hn)...);
    1042              :     }
    1043              : 
    1044              :     template<class... HN>
    1045              :     static auto
    1046           11 :     make_except_list(HN&&... hn) ->
    1047              :         handler_list_impl<sizeof...(HN)>
    1048              :     {
    1049              :         return handler_list_impl<sizeof...(HN)>(
    1050           11 :             0, std::forward<HN>(hn)...);
    1051              :     }
    1052              : 
    1053              :     void append(layer& e,
    1054              :         http_proto::method verb,
    1055              :         handler_list const& handlers)
    1056              :     {
    1057              :         add_impl(e, verb, handlers);
    1058              :     }
    1059              : };
    1060              : 
    1061              : //-----------------------------------------------
    1062              : 
    1063              : template<class Req, class Res>
    1064              : class basic_router<Req, Res>::
    1065              :     fluent_route
    1066              : {
    1067              : public:
    1068              :     fluent_route(fluent_route const&) = default;
    1069              : 
    1070              :     /** Add handlers that apply to all HTTP methods.
    1071              :         
    1072              :         This registers regular handlers that run for any request matching
    1073              :         the route's pattern, regardless of HTTP method. Handlers are
    1074              :         appended to the route's handler sequence and are invoked in
    1075              :         registration order whenever a preceding handler returns
    1076              :         @ref route::next. Error handlers and routers cannot be passed here.
    1077              : 
    1078              :         This function returns a @ref fluent_route, allowing additional
    1079              :         method registrations to be chained. For example:
    1080              :         @code
    1081              :         router.route("/resource")
    1082              :             .all(log_request)
    1083              :             .get(show_resource)
    1084              :             .post(update_resource);
    1085              :         @endcode
    1086              : 
    1087              :         @param h1 The first handler to add.
    1088              :         @param hn Additional handlers to add, invoked after @p h1 in
    1089              :             registration order.
    1090              :         @return The @ref fluent_route for further chained registrations.
    1091              :     */
    1092              :     template<class H1, class... HN>
    1093           14 :     auto all(
    1094              :         H1&& h1, HN&&... hn) ->
    1095              :             fluent_route
    1096              :     {
    1097              :         // If you get a compile error on this line it means that
    1098              :         // one or more of the provided types is not a valid handler.
    1099              :         // Error handlers and routers cannot be passed here.
    1100              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1101           14 :         owner_.add_impl(e_, core::string_view(), make_handler_list(
    1102              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1103           14 :         return *this;
    1104              :     }
    1105              : 
    1106              :     /** Add handlers for a specific HTTP method.
    1107              :         
    1108              :         This registers regular handlers for the given method on the
    1109              :         current route, participating in dispatch as described in the
    1110              :         @ref basic_router class documentation. Handlers are appended
    1111              :         to the route's handler sequence and invoked in registration
    1112              :         order whenever a preceding handler returns @ref route::next.
    1113              :         Error handlers and routers cannot be passed here.
    1114              : 
    1115              :         @param verb The HTTP method to match.
    1116              :         @param h1 The first handler to add.
    1117              :         @param hn Additional handlers to add, invoked after @p h1 in
    1118              :             registration order.
    1119              :         @return The @ref fluent_route for further chained registrations.
    1120              :     */
    1121              :     template<class H1, class... HN>
    1122           68 :     auto add(
    1123              :         http_proto::method verb,
    1124              :         H1&& h1, HN&&... hn) ->
    1125              :             fluent_route
    1126              :     {
    1127              :         // If you get a compile error on this line it means that
    1128              :         // one or more of the provided types is not a valid handler.
    1129              :         // Error handlers and routers cannot be passed here.
    1130              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1131           68 :         owner_.add_impl(e_, verb, make_handler_list(
    1132              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1133           67 :         return *this;
    1134              :     }
    1135              : 
    1136              :     /** Add handlers for a method name.
    1137              :         
    1138              :         This registers regular handlers for the given HTTP method string
    1139              :         on the current route, participating in dispatch as described in
    1140              :         the @ref basic_router class documentation. This overload is
    1141              :         intended for methods not represented by @ref http_proto::method.
    1142              :         Handlers are appended to the route's handler sequence and invoked
    1143              :         in registration order whenever a preceding handler returns
    1144              :         @ref route::next.
    1145              : 
    1146              :         @par Constraints
    1147              :         @li Each handler must be a regular handler; error handlers and
    1148              :             routers cannot be passed.
    1149              : 
    1150              :         @param verb The HTTP method string to match.
    1151              :         @param h1 The first handler to add.
    1152              :         @param hn Additional handlers to add, invoked after @p h1 in
    1153              :             registration order.
    1154              :         @return The @ref fluent_route for further chained registrations.
    1155              :     */
    1156              :     template<class H1, class... HN>
    1157            9 :     auto add(
    1158              :         core::string_view verb,
    1159              :         H1&& h1, HN&&... hn) ->
    1160              :             fluent_route
    1161              :     {
    1162              :         // If you get a compile error on this line it means that
    1163              :         // one or more of the provided types is not a valid handler.
    1164              :         // Error handlers and routers cannot be passed here.
    1165              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1166            9 :         owner_.add_impl(e_, verb, make_handler_list(
    1167              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1168            9 :         return *this;
    1169              :     }
    1170              : 
    1171              : private:
    1172              :     friend class basic_router;
    1173           63 :     fluent_route(
    1174              :         basic_router& owner,
    1175              :         core::string_view pattern)
    1176           63 :         : e_(owner.new_layer(pattern))
    1177           62 :         , owner_(owner)
    1178              :     {
    1179           62 :     }
    1180              : 
    1181              :     layer& e_;
    1182              :     basic_router& owner_;
    1183              : };
    1184              : 
    1185              : } // http_proto
    1186              : } // boost
    1187              : 
    1188              : #endif
        

Generated by: LCOV version 2.1