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: 97.4 % 115 112
Test Date: 2025-12-01 15:47:26 Functions: 93.1 % 218 203

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

Generated by: LCOV version 2.1