LCOV - code coverage report
Current view: top level - boost/beast2/server - basic_router.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.3 % 113 110
Test Date: 2025-11-30 14:55:10 Functions: 93.2 % 222 207

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/beast2
       8              : //
       9              : 
      10              : #ifndef BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
      11              : #define BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
      12              : 
      13              : #include <boost/beast2/detail/config.hpp>
      14              : #include <boost/beast2/detail/call_traits.hpp>
      15              : #include <boost/beast2/detail/type_traits.hpp>
      16              : #include <boost/beast2/server/router_types.hpp>
      17              : #include <boost/beast2/server/route_handler.hpp>
      18              : #include <boost/http_proto/method.hpp>
      19              : #include <boost/url/url_view.hpp>
      20              : #include <boost/core/detail/string_view.hpp>
      21              : #include <boost/core/detail/static_assert.hpp>
      22              : #include <type_traits>
      23              : 
      24              : namespace boost {
      25              : namespace beast2 {
      26              : 
      27              : template<class, class>
      28              : class basic_router;
      29              : 
      30              : /** Configuration options for routers.
      31              : */
      32              : struct router_options
      33              : {
      34              :     /** Constructor
      35              : 
      36              :         Routers constructed with default options inherit the values of
      37              :         @ref case_sensitive and @ref strict from the parent router.
      38              :         If there is no parent, both default to `false`.
      39              :         The value of @ref merge_params always defaults to `false`
      40              :         and is never inherited.
      41              :     */
      42          137 :     router_options() = default;
      43              : 
      44              :     /** Set whether to merge parameters from parent routers.
      45              : 
      46              :         This setting controls whether route parameters defined on parent
      47              :         routers are made available in nested routers. It is not inherited
      48              :         and always defaults to `false`.
      49              : 
      50              :         @par Example
      51              :         @code
      52              :         router r(router_options()
      53              :             .merge_params(true)
      54              :             .case_sensitive(true)
      55              :             .strict(false));
      56              :         @endcode
      57              : 
      58              :         @param value `true` to merge parameters from parent routers.
      59              :         @return A reference to `*this` for chaining.
      60              :     */
      61              :     router_options&
      62            1 :     merge_params(
      63              :         bool value) noexcept
      64              :     {
      65            1 :         v_ = (v_ & ~1) | (value ? 1 : 0);
      66            1 :         return *this;
      67              :     }
      68              : 
      69              :     /** Set whether pattern matching is case-sensitive.
      70              : 
      71              :         When this option is not set explicitly, the value is inherited
      72              :         from the parent router or defaults to `false` if there is no parent.
      73              : 
      74              :         @par Example
      75              :         @code
      76              :         router r(router_options()
      77              :             .case_sensitive(true)
      78              :             .strict(true));
      79              :         @endcode
      80              : 
      81              :         @param value `true` to perform case-sensitive path matching.
      82              :         @return A reference to `*this` for chaining.
      83              :     */
      84              :     router_options&
      85            7 :     case_sensitive(
      86              :         bool value) noexcept
      87              :     {
      88            7 :         if(value)
      89            5 :             v_ = (v_ & ~6) | 2;
      90              :         else
      91            2 :             v_ = (v_ & ~6) | 4;
      92            7 :         return *this;
      93              :     }
      94              : 
      95              :     /** Set whether pattern matching is strict.
      96              : 
      97              :         When this option is not set explicitly, the value is inherited
      98              :         from the parent router or defaults to `false` if there is no parent.
      99              :         Strict matching treats a trailing slash as significant:
     100              :         the pattern `"/api"` matches `"/api"` but not `"/api/"`.
     101              :         When strict matching is disabled, these paths are treated
     102              :         as equivalent.
     103              : 
     104              :         @par Example
     105              :         @code
     106              :         router r(router_options()
     107              :             .strict(true)
     108              :             .case_sensitive(false));
     109              :         @endcode
     110              : 
     111              :         @param value `true` to enable strict path matching.
     112              :         @return A reference to `*this` for chaining.
     113              :     */
     114              :     router_options&
     115            1 :     strict(
     116              :         bool value) noexcept
     117              :     {
     118            1 :         if(value)
     119            0 :             v_ = (v_ & ~24) | 8;
     120              :         else
     121            1 :             v_ = (v_ & ~24) | 16;
     122            1 :         return *this;
     123              :     }
     124              : 
     125              : private:
     126              :     template<class, class> friend class basic_router;
     127              :     unsigned int v_ = 0;
     128              : };
     129              : 
     130              : //-----------------------------------------------
     131              : 
     132              : namespace detail {
     133              : 
     134              : class any_router;
     135              : 
     136              : //-----------------------------------------------
     137              : 
     138              : // implementation for all routers
     139              : class any_router
     140              : {
     141              : private:
     142              :     template<class, class>
     143              :     friend class beast2::basic_router;
     144              :     using opt_flags = unsigned int;
     145              : 
     146              :     struct BOOST_SYMBOL_VISIBLE any_handler
     147              :     {
     148              :         BOOST_BEAST2_DECL virtual ~any_handler();
     149              :         virtual std::size_t count() const noexcept = 0;
     150              :         virtual route_result invoke(
     151              :             basic_request&, basic_response&) const = 0;
     152              :     };
     153              : 
     154              :     using handler_ptr = std::unique_ptr<any_handler>;
     155              : 
     156              :     struct handler_list
     157              :     {
     158              :         std::size_t n;
     159              :         handler_ptr* p;
     160              :     };
     161              : 
     162              :     using match_result = basic_request::match_result;
     163              :     struct matcher;
     164              :     struct layer;
     165              :     struct impl;
     166              : 
     167              :     BOOST_BEAST2_DECL ~any_router();
     168              :     BOOST_BEAST2_DECL any_router(opt_flags);
     169              :     BOOST_BEAST2_DECL any_router(any_router&&) noexcept;
     170              :     BOOST_BEAST2_DECL any_router(any_router const&) noexcept;
     171              :     BOOST_BEAST2_DECL any_router& operator=(any_router&&) noexcept;
     172              :     BOOST_BEAST2_DECL any_router& operator=(any_router const&) noexcept;
     173              :     BOOST_BEAST2_DECL std::size_t count() const noexcept;
     174              :     BOOST_BEAST2_DECL layer& new_layer(core::string_view pattern);
     175              :     BOOST_BEAST2_DECL void add_impl(core::string_view, handler_list const&);
     176              :     BOOST_BEAST2_DECL void add_impl(layer&,
     177              :         http_proto::method, handler_list const&);
     178              :     BOOST_BEAST2_DECL void add_impl(layer&,
     179              :         core::string_view, handler_list const&);
     180              :     BOOST_BEAST2_DECL route_result resume_impl(
     181              :         basic_request&, basic_response&, route_result const& ec) const;
     182              :     BOOST_BEAST2_DECL route_result dispatch_impl(http_proto::method,
     183              :         core::string_view, urls::url_view const&,
     184              :             basic_request&, basic_response&) const;
     185              :     BOOST_BEAST2_DECL route_result dispatch_impl(
     186              :         basic_request&, basic_response&) const;
     187              :     route_result do_dispatch(basic_request&, basic_response&) const;
     188              : 
     189              :     impl* impl_ = nullptr;
     190              : };
     191              : 
     192              : } // detail
     193              : 
     194              : //-----------------------------------------------
     195              : 
     196              : /** A container for HTTP route handlers.
     197              : 
     198              :     The `basic_router` class provides a routing mechanism inspired by
     199              :     Express.js, adapted to idiomatic C++. It stores and dispatches route
     200              :     handlers based on HTTP method and path patterns, enabling modular
     201              :     request processing through middleware chains and method-specific routes.
     202              : 
     203              :     @par Core Concepts
     204              : 
     205              :     Like Express.js Router, `basic_router` allows you to define handlers
     206              :     for specific HTTP methods and URL patterns. However, there are key
     207              :     differences stemming from C++'s synchronous nature versus Node.js's
     208              :     asynchronous I/O model:
     209              : 
     210              :     @li In Express, `res.send()` initiates an asynchronous write and
     211              :         returns immediately. In `basic_router`, handlers prepare the
     212              :         response object completely and return @ref route::send, after
     213              :         which the framework sends the response.
     214              : 
     215              :     @li Express handlers implicitly end the request-response cycle by
     216              :         calling response methods. C++ handlers explicitly return
     217              :         @ref route_result values to control the dispatch flow.
     218              : 
     219              :     @li Express relies on callbacks and promises for asynchronous
     220              :         operations. `basic_router` supports detaching from the routing
     221              :         context for async operations via @ref route::detach.
     222              : 
     223              :     @par Router Structure and Shared Ownership
     224              : 
     225              :     Router objects are lightweight, shared references to their underlying
     226              :     routing state. Copies of a router obtained through construction,
     227              :     conversion, or assignment do not duplicate routes or handlers; all
     228              :     copies refer to the same data. This matches Express.js behavior where
     229              :     router instances can be shared and reused.
     230              : 
     231              :     @code
     232              :     router_type router;
     233              :     router.get("/users", get_users_handler);
     234              : 
     235              :     router_type router_copy = router; // shares same routes
     236              :     router_copy.get("/posts", get_posts_handler); // affects both routers
     237              :     @endcode
     238              : 
     239              :     @par Pattern Matching
     240              : 
     241              :     Path patterns support parameters and follow similar conventions to
     242              :     Express.js routes. Patterns undergo percent-decoding when handlers
     243              :     are mounted, so `"/x%2Fz"` is treated identically to `"/x/z"`.
     244              : 
     245              :     Unlike Express.js string patterns with special characters like `*`
     246              :     and `?`, `basic_router` uses simpler prefix matching for middleware
     247              :     and exact matching for routes. Future versions may expand pattern
     248              :     support.
     249              : 
     250              :     When pattern matching occurs, the matched portion becomes
     251              :     `req.base_path` and the remaining portion becomes `req.path`,
     252              :     analogous to Express's `req.baseUrl` and `req.path`.
     253              : 
     254              :     @code
     255              :     // Pattern: "/api"
     256              :     // Request: "/api/users/123"
     257              :     // During handler execution:
     258              :     //   req.base_path == "/api"
     259              :     //   req.path == "/users/123"
     260              :     @endcode
     261              : 
     262              :     @par Middleware with use()
     263              : 
     264              :     The @ref use function registers middleware handlers that run for
     265              :     requests matching a path prefix, similar to Express's `app.use()`.
     266              :     Middleware executes in registration order and can perform
     267              :     authentication, logging, header manipulation, or other cross-cutting
     268              :     concerns.
     269              : 
     270              :     Unlike routes, middleware uses prefix matching. A middleware mounted
     271              :     at `"/api"` will match `"/api"`, `"/api/"`, `"/api/users"`, and any
     272              :     other path beginning with `"/api"`.
     273              : 
     274              :     @code
     275              :     router.use("/api",
     276              :         [](Request& req, Response& res)
     277              :         {
     278              :             // Authentication middleware
     279              :             if (!is_authenticated(req))
     280              :             {
     281              :                 res.status(http_proto::status::unauthorized);
     282              :                 res.set_body("Authentication required");
     283              :                 return route::send;
     284              :             }
     285              :             return route::next; // continue to next handler
     286              :         },
     287              :         [](Request& req, Response& res)
     288              :         {
     289              :             // Logging middleware
     290              :             log_request(req);
     291              :             return route::next;
     292              :         });
     293              :     @endcode
     294              : 
     295              :     Global middleware (applying to all requests) is registered with
     296              :     `use()` without a path or with `"/"`:
     297              : 
     298              :     @code
     299              :     router.use([](Request& req, Response& res)
     300              :     {
     301              :         res.message.set("X-Powered-By", "Beast2");
     302              :         return route::next;
     303              :     });
     304              :     @endcode
     305              : 
     306              :     @par Routes and HTTP Methods
     307              : 
     308              :     Routes match specific HTTP methods and exact paths. Unlike middleware,
     309              :     routes require complete path matches (unless configured otherwise via
     310              :     @ref router_options::strict). Routes are added via method-specific
     311              :     functions or the fluent @ref route interface.
     312              : 
     313              :     @code
     314              :     // Direct method registration
     315              :     router.get("/users/:id", show_user_handler);
     316              :     router.post("/users", create_user_handler);
     317              :     router.put("/users/:id", update_user_handler);
     318              :     router.delete("/users/:id", delete_user_handler);
     319              : 
     320              :     // Fluent interface for multiple methods on same route
     321              :     router.route("/status")
     322              :         .get([](Request& req, Response& res)
     323              :         {
     324              :             res.set_body("OK");
     325              :             return route::send;
     326              :         })
     327              :         .head([](Request& req, Response& res)
     328              :         {
     329              :             res.status(http_proto::status::ok);
     330              :             return route::send;
     331              :         });
     332              : 
     333              :     // Handler for all methods
     334              :     router.all("/debug", debug_handler);
     335              :     @endcode
     336              : 
     337              :     @par Handler Types and Signatures
     338              : 
     339              :     `basic_router` supports three handler types:
     340              : 
     341              :     @li **Regular handlers**: Process normal requests
     342              :     @code
     343              :     route_result handler( Req& req, Res& res )
     344              :     @endcode
     345              : 
     346              :     @li **Error handlers**: Handle error conditions
     347              :     @code
     348              :     route_result error_handler( Req& req, Res& res, system::error_code ec )
     349              :     @endcode
     350              : 
     351              :     @li **Nested routers**: Sub-routers for modular organization
     352              :     @code
     353              :     router.use("/api", api_router);
     354              :     @endcode
     355              : 
     356              :     @par Return Values and Control Flow
     357              : 
     358              :     Handlers control routing flow by returning @ref route_result values.
     359              :     This explicit control differs from Express.js, where calling response
     360              :     methods implicitly ends processing.
     361              : 
     362              :     @li @ref route::send - Response is ready; framework should send it
     363              :     @li @ref route::next - Continue to next matching handler
     364              :     @li @ref route::next_route - Skip remaining handlers in current route
     365              :     @li @ref route::complete - Response already sent externally
     366              :     @li @ref route::close - Close connection after response
     367              :     @li @ref route::detach - Transfer ownership of connection to handler
     368              : 
     369              :     Express.js comparison:
     370              :     @code
     371              :     // Express.js
     372              :     app.get('/users', (req, res) => {
     373              :         res.json({ users: [] }); // implicitly ends request
     374              :     });
     375              : 
     376              :     // basic_router equivalent
     377              :     router.get("/users", [](Request& req, Response& res)
     378              :     {
     379              :         res.status(http_proto::status::ok);
     380              :         res.set_body(R"({"users":[]})");
     381              :         return route::send; // explicitly indicate response is ready
     382              :     });
     383              :     @endcode
     384              : 
     385              :     The @ref route::next return value is analogous to Express's `next()`
     386              :     function:
     387              : 
     388              :     @code
     389              :     // Express.js
     390              :     app.use((req, res, next) => {
     391              :         console.log('logging');
     392              :         next(); // continue to next handler
     393              :     });
     394              : 
     395              :     // basic_router equivalent
     396              :     router.use([](Request& req, Response& res)
     397              :     {
     398              :         log("processing request");
     399              :         return route::next; // continue to next handler
     400              :     });
     401              :     @endcode
     402              : 
     403              :     @par Error Handling
     404              : 
     405              :     When a handler returns a failing error code (where
     406              :     `system::error_code::failed()` returns `true`), the router enters
     407              :     error-dispatching mode. In this mode, only error handlers are invoked,
     408              :     similar to Express.js error middleware.
     409              : 
     410              :     Error handlers have a three-parameter signature including the error
     411              :     code. They execute in registration order until one returns a value
     412              :     other than @ref route::next.
     413              : 
     414              :     @code
     415              :     router.use("/api",
     416              :         [](Request& req, Response& res)
     417              :         {
     418              :             if (!validate_request(req))
     419              :                 return make_error_code(errc::invalid_argument);
     420              :             return route::next;
     421              :         });
     422              : 
     423              :     // Error handler (registered like middleware)
     424              :     router.use([](Request& req, Response& res, system::error_code ec)
     425              :     {
     426              :         res.status(http_proto::status::internal_server_error);
     427              :         res.set_body("Error: " + ec.message());
     428              :         return route::send;
     429              :     });
     430              :     @endcode
     431              : 
     432              :     Express.js comparison:
     433              :     @code
     434              :     // Express.js error middleware (4 parameters)
     435              :     app.use((err, req, res, next) => {
     436              :         res.status(500).send('Error: ' + err.message);
     437              :     });
     438              :     @endcode
     439              : 
     440              :     @par Execution Order
     441              : 
     442              :     Handlers execute in registration order, forming a chain. This matches
     443              :     Express.js behavior. Middleware registered first executes first;
     444              :     routes are evaluated in definition order.
     445              : 
     446              :     @code
     447              :     router.use(logging_middleware);        // runs first
     448              :     router.use("/api", auth_middleware);   // runs second for /api paths
     449              :     router.get("/api/users", get_users);   // runs third for GET /api/users
     450              :     router.use(error_handler);             // runs if errors occur
     451              :     @endcode
     452              : 
     453              :     @par Nested Routers
     454              : 
     455              :     Routers can be mounted within other routers, enabling modular
     456              :     application structure similar to Express.js Router:
     457              : 
     458              :     @code
     459              :     // Create a sub-router for user-related routes
     460              :     basic_router<Request, Response> users_router;
     461              :     users_router.get("/", list_users);
     462              :     users_router.post("/", create_user);
     463              :     users_router.get("/:id", show_user);
     464              : 
     465              :     // Mount it on the main router
     466              :     router.use("/users", users_router);
     467              : 
     468              :     // These patterns now work:
     469              :     // GET  /users      -> list_users
     470              :     // POST /users      -> create_user
     471              :     // GET  /users/123  -> show_user
     472              :     @endcode
     473              : 
     474              :     When a nested router processes a request, `req.base_path` reflects the
     475              :     cumulative mount path, similar to Express's `req.baseUrl`.
     476              : 
     477              :     @par Router Options
     478              : 
     479              :     The @ref router_options class controls matching behavior:
     480              : 
     481              :     @li **case_sensitive**: Whether path matching is case-sensitive
     482              :         (default: `false`, inherited from parent)
     483              :     @li **strict**: Whether trailing slashes are significant
     484              :         (default: `false`, inherited from parent)
     485              :     @li **merge_params**: Whether to inherit parent route parameters
     486              :         (default: `false`, never inherited)
     487              : 
     488              :     @code
     489              :     router_options opts;
     490              :     opts.case_sensitive(true).strict(true);
     491              :     basic_router<Request, Response> router(opts);
     492              : 
     493              :     // With strict=true:
     494              :     // Pattern "/api" matches "/api" but NOT "/api/"
     495              :     @endcode
     496              : 
     497              :     @par Asynchronous Operations and Detaching
     498              : 
     499              :     Express.js handlers can perform asynchronous I/O using callbacks,
     500              :     promises, or async/await. In `basic_router`, handlers are synchronous
     501              :     by default. For asynchronous operations, handlers can detach from the
     502              :     routing context by returning @ref route::detach, then resume later:
     503              : 
     504              :     @code
     505              :     router.get("/slow", [](Request& req, Response& res)
     506              :     {
     507              :         return res.detach([&req, &res](resumer resume_fn)
     508              :         {
     509              :             // Perform async operation (e.g., with Asio)
     510              :             async_database_query([resume_fn, &res](error_code ec, result r)
     511              :             {
     512              :                 if (ec)
     513              :                 {
     514              :                     resume_fn(ec); // resume with error
     515              :                     return;
     516              :                 }
     517              :                 res.set_body(r.to_json());
     518              :                 resume_fn(route::send); // resume with success
     519              :             });
     520              :         });
     521              :     });
     522              :     @endcode
     523              : 
     524              :     After detaching, the handler must eventually call the @ref resumer
     525              :     function to continue routing. This is conceptually similar to Express's
     526              :     asynchronous model but requires explicit management.
     527              : 
     528              :     @par Complete Example
     529              : 
     530              :     @code
     531              :     #include <boost/beast2/server/basic_router.hpp>
     532              :     #include <boost/beast2/server/route_handler.hpp>
     533              : 
     534              :     using namespace boost::beast2;
     535              : 
     536              :     // Create the router
     537              :     basic_router<Request, Response> router;
     538              : 
     539              :     // Global middleware - runs for all requests
     540              :     router.use([](Request& req, Response& res)
     541              :     {
     542              :         log_request(req.url);
     543              :         return route::next;
     544              :     });
     545              : 
     546              :     // Authentication middleware for /admin routes
     547              :     router.use("/admin", [](Request& req, Response& res)
     548              :     {
     549              :         if (!check_admin_credentials(req))
     550              :         {
     551              :             res.status(http_proto::status::forbidden);
     552              :             res.set_body("Access denied");
     553              :             return route::send;
     554              :         }
     555              :         return route::next;
     556              :     });
     557              : 
     558              :     // Route handlers
     559              :     router.get("/", [](Request& req, Response& res)
     560              :     {
     561              :         res.status(http_proto::status::ok);
     562              :         res.set_body("Welcome!");
     563              :         return route::send;
     564              :     });
     565              : 
     566              :     router.get("/hello/:name", [](Request& req, Response& res)
     567              :     {
     568              :         // In future versions, req.params["name"] would be available
     569              :         res.status(http_proto::status::ok);
     570              :         res.set_body("Hello!");
     571              :         return route::send;
     572              :     });
     573              : 
     574              :     // Multiple handlers for one route
     575              :     router.route("/users/:id")
     576              :         .get([](Request& req, Response& res)
     577              :         {
     578              :             res.set_body(get_user_json(req));
     579              :             return route::send;
     580              :         })
     581              :         .put([](Request& req, Response& res)
     582              :         {
     583              :             update_user(req);
     584              :             res.status(http_proto::status::no_content);
     585              :             return route::send;
     586              :         })
     587              :         .delete([](Request& req, Response& res)
     588              :         {
     589              :             delete_user(req);
     590              :             res.status(http_proto::status::no_content);
     591              :             return route::send;
     592              :         });
     593              : 
     594              :     // Error handler - runs when handlers return failing error codes
     595              :     router.use([](Request& req, Response& res, system::error_code ec)
     596              :     {
     597              :         res.status(http_proto::status::internal_server_error);
     598              :         res.set_body("Internal error: " + ec.message());
     599              :         return route::send;
     600              :     });
     601              : 
     602              :     // Dispatch a request
     603              :     Request req;
     604              :     Response res;
     605              :     auto result = router.dispatch(
     606              :         http_proto::method::get,
     607              :         urls::parse_uri("/hello/world").value(),
     608              :         req, res);
     609              : 
     610              :     if (result == route::send)
     611              :     {
     612              :         // Send response to client
     613              :         send_response(res);
     614              :     }
     615              :     @endcode
     616              : 
     617              :     @par Thread Safety
     618              :     Member functions marked `const` such as @ref dispatch and @ref resume
     619              :     may be called concurrently on routers that refer to the same data.
     620              :     Modification of routers through calls to non-`const` member functions
     621              :     is not thread-safe and must not be performed concurrently with any
     622              :     other member function.
     623              : 
     624              :     @par Constraints
     625              :     `Req` must be publicly derived from @ref basic_request.
     626              :     `Res` must be publicly derived from @ref basic_response.
     627              : 
     628              :     @tparam Req The type of request object.
     629              :     @tparam Res The type of response object.
     630              : 
     631              :     @see router_options, route_result, route, basic_request, basic_response
     632              : */
     633              : template<class Req, class Res>
     634              : class basic_router : public detail::any_router
     635              : {
     636              :     // Req must be publicly derived from basic_request
     637              :     BOOST_CORE_STATIC_ASSERT(
     638              :         detail::derived_from<basic_request, Req>::value);
     639              : 
     640              :     // Res must be publicly derived from basic_response
     641              :     BOOST_CORE_STATIC_ASSERT(
     642              :         detail::derived_from<basic_response, Res>::value);
     643              : 
     644              :     // 0 = unrecognized
     645              :     // 1 = normal handler (Req&, Res&)
     646              :     // 2 = error handler  (Req&, Res&, error_code)
     647              :     // 4 = basic_router<Req, Res>
     648              : 
     649              :     template<class T, class = void>
     650              :     struct handler_type
     651              :         : std::integral_constant<int, 0>
     652              :     {
     653              :     };
     654              : 
     655              :     // route_result( Req&, Res& ) const
     656              :     template<class T>
     657              :     struct handler_type<T, typename
     658              :         std::enable_if<std::is_convertible<
     659              :             decltype(std::declval<T const&>()(
     660              :                 std::declval<Req&>(),
     661              :                 std::declval<Res&>())),
     662              :             route_result>::value
     663              :         >::type> : std::integral_constant<int, 1> {};
     664              : 
     665              :     // route_result( Req&, Res&, system::error_code const& ) const
     666              :     template<class T>
     667              :     struct handler_type<T, typename
     668              :         std::enable_if<std::is_convertible<
     669              :             decltype(std::declval<T const&>()(
     670              :                 std::declval<Req&>(),
     671              :                 std::declval<Res&>(),
     672              :                 std::declval<system::error_code const&>())),
     673              :             route_result>::value
     674              :         >::type> : std::integral_constant<int, 2> {};
     675              : 
     676              :     // basic_router<Req, Res>
     677              :     template<class T>
     678              :     struct handler_type<T, typename
     679              :         std::enable_if<
     680              :             std::is_base_of<any_router, T>::value &&
     681              :             std::is_convertible<T const volatile*,
     682              :                 any_router const volatile*>::value &&
     683              :             std::is_constructible<T, basic_router<Req, Res>>::value
     684              :         >::type> : std::integral_constant<int, 4> {};
     685              : 
     686              :     template<std::size_t Mask, class... Ts>
     687              :     struct handler_check : std::true_type {};
     688              : 
     689              :     template<std::size_t Mask, class T0, class... Ts>
     690              :     struct handler_check<Mask, T0, Ts...>
     691              :         : std::conditional<
     692              :               ( (handler_type<T0>::value & Mask) != 0 ),
     693              :               handler_check<Mask, Ts...>,
     694              :               std::false_type
     695              :           >::type {};
     696              : 
     697              : public:
     698              :     /** The type of request object used in handlers
     699              :     */
     700              :     using request_type = Req;
     701              : 
     702              :     /** The type of response object used in handlers
     703              :     */
     704              :     using response_type = Res;
     705              : 
     706              :     /** A fluent interface for defining handlers on a specific route.
     707              : 
     708              :         This type represents a single route within the router and
     709              :         provides a chainable API for registering handlers associated
     710              :         with particular HTTP methods or for all methods collectively.
     711              : 
     712              :         Typical usage registers one or more handlers for a route:
     713              :         @code
     714              :         router.route("/users/:id")
     715              :             .get(show_user)
     716              :             .put(update_user)
     717              :             .all(log_access);
     718              :         @endcode
     719              : 
     720              :         Each call appends handlers in registration order.
     721              :     */
     722              :     class fluent_route;
     723              : 
     724              :     /** Constructor
     725              : 
     726              :         Creates an empty router with the specified configuration.
     727              :         Routers constructed with default options inherit the values
     728              :         of @ref router_options::case_sensitive and
     729              :         @ref router_options::strict from the parent router, or default
     730              :         to `false` if there is no parent. The value of
     731              :         @ref router_options::merge_params defaults to `false` and
     732              :         is never inherited.
     733              : 
     734              :         @param options The configuration options to use.
     735              :     */
     736              :     explicit
     737          137 :     basic_router(router_options options = {})
     738          137 :         : any_router(options.v_)
     739              :     {
     740          137 :     }
     741              : 
     742              :     /** Construct a router from another router with compatible types.
     743              : 
     744              :         This constructs a router that shares the same underlying routing
     745              :         state as another router whose request and response types are base
     746              :         classes of `Req` and `Res`, respectively.
     747              : 
     748              :         The resulting router participates in shared ownership of the
     749              :         implementation; copying the router does not duplicate routes or
     750              :         handlers, and changes visible through one router are visible
     751              :         through all routers that share the same underlying state.
     752              : 
     753              :         @par Constraints
     754              :         `Req` must be derived from `OtherReq`, and `Res` must be
     755              :         derived from `OtherRes`.
     756              : 
     757              :         @tparam OtherReq The request type of the source router.
     758              :         @tparam OtherRes The response type of the source router.
     759              :         @param other The router to copy.
     760              :     */
     761              :     template<
     762              :         class OtherReq, class OtherRes,
     763              :         class = typename std::enable_if<
     764              :             detail::derived_from<Req, OtherReq>::value &&
     765              :             detail::derived_from<Res, OtherRes>::value>::type
     766              :     >
     767              :     basic_router(
     768              :         basic_router<OtherReq, OtherRes> const& other)
     769              :         : any_router(other)
     770              :     {
     771              :     }
     772              : 
     773              :     /** Add one or more middleware handlers for a path prefix.
     774              : 
     775              :         Each handler registered with this function participates in the
     776              :         routing and error-dispatch process for requests whose path begins
     777              :         with the specified prefix, as described in the @ref basic_router
     778              :         class documentation. Handlers execute in the order they are added
     779              :         and may return @ref route::next to transfer control to the
     780              :         subsequent handler in the chain.
     781              : 
     782              :         @par Example
     783              :         @code
     784              :         router.use("/api",
     785              :             [](Request& req, Response& res)
     786              :             {
     787              :                 if (!authenticate(req))
     788              :                 {
     789              :                     res.status(401);
     790              :                     res.set_body("Unauthorized");
     791              :                     return route::send;
     792              :                 }
     793              :                 return route::next;
     794              :             },
     795              :             [](Request&, Response& res)
     796              :             {
     797              :                 res.set_header("X-Powered-By", "MyServer");
     798              :                 return route::next;
     799              :             });
     800              :         @endcode
     801              : 
     802              :         @par Preconditions
     803              :         @p `pattern` must be a valid path prefix; it may be empty to
     804              :             indicate the root scope.
     805              : 
     806              :         @param pattern The pattern to match.
     807              :         @param h1 The first handler to add.
     808              :         @param hn Additional handlers to add, invoked after @p h1 in
     809              :             registration order.
     810              :     */
     811              :     template<class H1, class... HN>
     812          186 :     void use(
     813              :         core::string_view pattern,
     814              :         H1&& h1, HN... hn)
     815              :     {
     816              :         // If you get a compile error on this line it means that
     817              :         // one or more of the provided types is not a valid handler,
     818              :         // error handler, or router.
     819              :         BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
     820          186 :         add_impl(pattern, make_handler_list(
     821              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
     822          186 :     }
     823              : 
     824              :     /** Add one or more global middleware handlers.
     825              :         
     826              :         Each handler registered with this function participates in the
     827              :         routing and error-dispatch process as described in the
     828              :         @ref basic_router class documentation. Handlers execute in the
     829              :         order they are added and may return @ref route::next to transfer
     830              :         control to the next handler in the chain.
     831              : 
     832              :         This is equivalent to writing:
     833              :         @code
     834              :         use( "/", h1, hn... );
     835              :         @endcode
     836              : 
     837              :         @par Example
     838              :         @code
     839              :         router.use(
     840              :             [](Request&, Response& res)
     841              :             {
     842              :                 res.message.erase("X-Powered-By");
     843              :                 return route::next;
     844              :             });
     845              :         @endcode
     846              : 
     847              :         @par Constraints
     848              :         @li `h1` must not be convertible to @ref core::string_view.
     849              : 
     850              :         @param h1 The first handler to add.
     851              :         @param hn Additional handlers to add, invoked after @p h1 in
     852              :             registration order.
     853              :     */
     854              :     template<class H1, class... HN
     855              :         , class = typename std::enable_if<
     856              :             ! std::is_convertible<H1, core::string_view>::value>::type>
     857          102 :     void use(H1&& h1, HN&&... hn)
     858              :     {
     859              :         // If you get a compile error on this line it means that
     860              :         // one or more of the provided types is not a valid handler,
     861              :         // error handler, or router.
     862              :         BOOST_CORE_STATIC_ASSERT(handler_check<7, H1, HN...>::value);
     863          102 :         use(core::string_view(),
     864              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     865          102 :     }
     866              : 
     867              :     /** Add handlers for all HTTP methods matching a path pattern.
     868              :         
     869              :         This registers regular handlers for the specified path pattern,
     870              :         participating in dispatch as described in the @ref basic_router
     871              :         class documentation. Handlers run when the route matches,
     872              :         regardless of HTTP method, and execute in registration order.
     873              :         Error handlers and routers cannot be passed here. A new route
     874              :         object is created even if the pattern already exists.
     875              : 
     876              :         @code
     877              :         router.route("/status")
     878              :             .head(check_headers)
     879              :             .get(send_status)
     880              :             .all(log_access);
     881              :         @endcode
     882              : 
     883              :         @par Preconditions
     884              :         @p `pattern` must be a valid path pattern; it must not be empty.
     885              : 
     886              :         @param pattern The path pattern to match.
     887              :         @param h1 The first handler to add.
     888              :         @param hn Additional handlers to add, invoked after @p h1 in
     889              :             registration order.
     890              :     */
     891              :     template<class H1, class... HN>
     892           12 :     void all(
     893              :         core::string_view pattern,
     894              :         H1&& h1, HN&&... hn)
     895              :     {
     896              :         // If you get a compile error on this line it means that
     897              :         // one or more of the provided types is not a valid handler.
     898              :         // Error handlers and routers cannot be passed here.
     899              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
     900           12 :         this->route(pattern).all(
     901              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     902           11 :     }
     903              : 
     904              :     /** Add one or more route handlers for a method and pattern.
     905              :         
     906              :         This registers regular handlers for the specified HTTP verb and
     907              :         path pattern, participating in dispatch as described in the
     908              :         @ref basic_router class documentation. Error handlers and
     909              :         routers cannot be passed here.
     910              : 
     911              :         @param verb The known HTTP method to match.
     912              :         @param pattern The path pattern to match.
     913              :         @param h1 The first handler to add.
     914              :         @param hn Additional handlers to add, invoked after @p h1 in
     915              :             registration order.
     916              :     */
     917              :     template<class H1, class... HN>
     918           21 :     void add(
     919              :         http_proto::method verb,
     920              :         core::string_view pattern,
     921              :         H1&& h1, HN&&... hn)
     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           21 :         this->route(pattern).add(verb,
     928              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     929           21 :     }
     930              : 
     931              :     /** Add one or more route handlers for a method and pattern.
     932              :         
     933              :         This registers regular handlers for the specified HTTP verb and
     934              :         path pattern, participating in dispatch as described in the
     935              :         @ref basic_router class documentation. Error handlers and
     936              :         routers cannot be passed here.
     937              : 
     938              :         @param verb The HTTP method string to match.
     939              :         @param pattern The path pattern to match.
     940              :         @param h1 The first handler to add.
     941              :         @param hn Additional handlers to add, invoked after @p h1 in
     942              :             registration order.
     943              :     */
     944              :     template<class H1, class... HN>
     945            2 :     void add(
     946              :         core::string_view verb,
     947              :         core::string_view pattern,
     948              :         H1&& h1, HN&&... hn)
     949              :     {
     950              :         // If you get a compile error on this line it means that
     951              :         // one or more of the provided types is not a valid handler.
     952              :         // Error handlers and routers cannot be passed here.
     953              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
     954            2 :         this->route(pattern).add(verb,
     955              :             std::forward<H1>(h1), std::forward<HN>(hn)...);
     956            2 :     }
     957              : 
     958              :     /** Return a fluent route for the specified path pattern.
     959              : 
     960              :         Adds a new route to the router for the given pattern.
     961              :         A new route object is always created, even if another
     962              :         route with the same pattern already exists. The returned
     963              :         @ref fluent_route reference allows method-specific handler
     964              :         registration (such as GET or POST) or catch-all handlers
     965              :         with @ref fluent_route::all.
     966              : 
     967              :         @param pattern The path expression to match against request
     968              :         targets. This may include parameters or wildcards following
     969              :         the router's pattern syntax. May not be empty.
     970              :         @return A fluent route interface for chaining handler registrations.
     971              :     */
     972              :     auto
     973           63 :     route(
     974              :         core::string_view pattern) -> fluent_route
     975              :     {
     976           63 :         return fluent_route(*this, pattern);
     977              :     }
     978              : 
     979              :     //--------------------------------------------
     980              : 
     981              :     /** Dispatch a request to the appropriate handler.
     982              :         
     983              :         This runs the routing and error-dispatch logic for the given HTTP
     984              :         method and target URL, as described in the @ref basic_router class
     985              :         documentation.
     986              : 
     987              :         @par Thread Safety
     988              :         This function may be called concurrently on the same object along
     989              :         with other `const` member functions. Each concurrent invocation
     990              :         must use distinct request and response objects.
     991              : 
     992              :         @param verb The HTTP method to match. This must not be
     993              :             @ref http_proto::method::unknown.
     994              :         @param url The full request target used for route matching.
     995              :         @param req The request to pass to handlers.
     996              :         @param res The response to pass to handlers.
     997              :         @return The @ref route_result describing how routing completed.
     998              :         @throws std::invalid_argument If @p verb is
     999              :             @ref http_proto::method::unknown.
    1000              :     */
    1001              :     auto
    1002          142 :     dispatch(
    1003              :         http_proto::method verb,
    1004              :         urls::url_view const& url,
    1005              :         Req& req, Res& res) const ->
    1006              :             route_result
    1007              :     {
    1008          142 :         if(verb == http_proto::method::unknown)
    1009            1 :             detail::throw_invalid_argument();
    1010          279 :         return dispatch_impl(verb,
    1011          276 :             core::string_view(), url, req, res);
    1012              :     }
    1013              : 
    1014              :     /** Dispatch a request to the appropriate handler using a method string.
    1015              :         
    1016              :         This runs the routing and error-dispatch logic for the given HTTP
    1017              :         method string and target URL, as described in the @ref basic_router
    1018              :         class documentation. This overload is intended for method tokens
    1019              :         that are not represented by @ref http_proto::method.
    1020              : 
    1021              :         @par Thread Safety
    1022              :         This function may be called concurrently on the same object along
    1023              :         with other `const` member functions. Each concurrent invocation
    1024              :         must use distinct request and response objects.
    1025              : 
    1026              :         @param verb The HTTP method string to match. This must not be empty.
    1027              :         @param url The full request target used for route matching.
    1028              :         @param req The request to pass to handlers.
    1029              :         @param res The response to pass to handlers.
    1030              :         @return The @ref route_result describing how routing completed.
    1031              :         @throws std::invalid_argument If @p verb is empty.
    1032              :     */
    1033              :     auto
    1034           34 :     dispatch(
    1035              :         core::string_view verb,
    1036              :         urls::url_view const& url,
    1037              :         Req& req, Res& res) ->
    1038              :             route_result
    1039              :     {
    1040              :         // verb cannot be empty
    1041           34 :         if(verb.empty())
    1042            1 :             detail::throw_invalid_argument();
    1043           33 :         return dispatch_impl(
    1044              :             http_proto::method::unknown,
    1045           33 :                 verb, url, req, res);
    1046              :     }
    1047              : 
    1048              :     /** Resume dispatch after a detached handler.
    1049              :         
    1050              :         This continues routing after a previous call to @ref dispatch
    1051              :         returned @ref route::detach. It recreates the routing state and
    1052              :         resumes as if the handler that detached had instead returned
    1053              :         the specified @p ec from its body. The regular routing and
    1054              :         error-dispatch logic then proceeds as described in the
    1055              :         @ref basic_router class documentation. For example, if @p ec is
    1056              :         @ref route::next, the next matching handlers are invoked.
    1057              : 
    1058              :         @par Thread Safety
    1059              :         This function may be called concurrently on the same object along
    1060              :         with other `const` member functions. Each concurrent invocation
    1061              :         must use distinct request and response objects.
    1062              : 
    1063              :         @param req The request to pass to handlers.
    1064              :         @param res The response to pass to handlers.
    1065              :         @param rv The @ref route_result to resume with, as if returned
    1066              :             by the detached handler.
    1067              :         @return The @ref route_result describing how routing completed.
    1068              :     */
    1069              :     auto
    1070            9 :     resume(
    1071              :         Req& req, Res& res,
    1072              :         route_result const& rv) const ->
    1073              :             route_result
    1074              :     {
    1075            9 :         return resume_impl(req, res, rv);
    1076              :     }
    1077              : 
    1078              : private:
    1079              :     // wrapper for route handlers
    1080              :     template<class H>
    1081              :     struct handler_impl : any_handler
    1082              :     {
    1083              :         typename std::decay<H>::type h;
    1084              : 
    1085              :         template<class... Args>
    1086          330 :         explicit handler_impl(Args&&... args)
    1087          330 :             : h(std::forward<Args>(args)...)
    1088              :         {
    1089          330 :         }
    1090              : 
    1091              :         std::size_t
    1092          141 :         count() const noexcept override
    1093              :         {
    1094          282 :             return count(
    1095          141 :                 handler_type<decltype(h)>{});
    1096              :         }
    1097              : 
    1098              :         route_result
    1099          261 :         invoke(
    1100              :             basic_request& req,
    1101              :             basic_response& res) const override
    1102              :         {
    1103          522 :             return invoke(
    1104              :                 static_cast<Req&>(req),
    1105              :                 static_cast<Res&>(res),
    1106          353 :                 handler_type<decltype(h)>{});
    1107              :         }
    1108              : 
    1109              :     private:
    1110              :         std::size_t count(
    1111              :             std::integral_constant<int, 0>) = delete;
    1112              : 
    1113          131 :         std::size_t count(
    1114              :             std::integral_constant<int, 1>) const noexcept
    1115              :         {
    1116          131 :             return 1;
    1117              :         }
    1118              : 
    1119            6 :         std::size_t count(
    1120              :             std::integral_constant<int, 2>) const noexcept
    1121              :         {
    1122            6 :             return 1;
    1123              :         }
    1124              : 
    1125            4 :         std::size_t count(
    1126              :             std::integral_constant<int, 4>) const noexcept
    1127              :         {
    1128            4 :             return 1 + h.count();
    1129              :         }
    1130              : 
    1131              :         route_result invoke(Req&, Res&,
    1132              :             std::integral_constant<int, 0>) const = delete;
    1133              : 
    1134              :         // (Req, Res)
    1135          198 :         route_result invoke(Req& req, Res& res,
    1136              :             std::integral_constant<int, 1>) const
    1137              :         {
    1138          198 :             auto const& ec = static_cast<
    1139              :                 basic_response const&>(res).ec_;
    1140          198 :             if(ec.failed())
    1141           12 :                 return beast2::route::next;
    1142              :             // avoid racing on res.resume_
    1143          186 :             res.resume_ = res.pos_;
    1144          186 :             auto rv = h(req, res);
    1145          186 :             if(rv == beast2::route::detach)
    1146           12 :                 return rv;
    1147          174 :             res.resume_ = 0; // revert
    1148          174 :             return rv;
    1149              :         }
    1150              : 
    1151              :         // (Req&, Res&, error_code)
    1152              :         route_result
    1153           46 :         invoke(Req& req, Res& res,
    1154              :             std::integral_constant<int, 2>) const
    1155              :         {
    1156           46 :             auto const& ec = static_cast<
    1157              :                 basic_response const&>(res).ec_;
    1158           46 :             if(! ec.failed())
    1159            8 :                 return beast2::route::next;
    1160              :             // avoid racing on res.resume_
    1161           38 :             res.resume_ = res.pos_;
    1162           38 :             auto rv = h(req, res, ec);
    1163           38 :             if(rv == beast2::route::detach)
    1164            0 :                 return rv;
    1165           38 :             res.resume_ = 0; // revert
    1166           38 :             return rv;
    1167              :         }
    1168              : 
    1169              :         // any_router
    1170           17 :         route_result invoke(Req& req, Res& res,
    1171              :             std::integral_constant<int, 4>) const
    1172              :         {
    1173           17 :             auto const& ec = static_cast<
    1174              :                 basic_response const&>(res).ec_;
    1175           17 :             if(! ec.failed())
    1176           16 :                 return h.dispatch_impl(req, res);
    1177            1 :             return beast2::route::next;
    1178              :         }
    1179              :     };
    1180              : 
    1181              :     template<std::size_t N>
    1182              :     struct handler_list_impl : handler_list
    1183              :     {
    1184              :         template<class... HN>
    1185          277 :         explicit handler_list_impl(HN&&... hn)
    1186            0 :         {
    1187          277 :             n = sizeof...(HN);
    1188          277 :             p = v;
    1189          277 :             assign<0>(std::forward<HN>(hn)...);
    1190          277 :         }
    1191              : 
    1192              :     private:
    1193              :         template<std::size_t I, class H1, class... HN>
    1194          330 :         void assign(H1&& h1, HN&&... hn)
    1195              :         {
    1196          330 :             v[I] = handler_ptr(new handler_impl<H1>(
    1197              :                 std::forward<H1>(h1)));
    1198          330 :             assign<I+1>(std::forward<HN>(hn)...);
    1199          330 :         }
    1200              : 
    1201              :         template<std::size_t>
    1202          277 :         void assign()
    1203              :         {
    1204          277 :         }
    1205              : 
    1206              :         handler_ptr v[N];
    1207              :     };
    1208              : 
    1209              :     template<class... HN>
    1210              :     static auto
    1211          277 :     make_handler_list(HN&&... hn) ->
    1212              :         handler_list_impl<sizeof...(HN)>
    1213              :     {
    1214              :         return handler_list_impl<sizeof...(HN)>(
    1215          277 :             std::forward<HN>(hn)...);
    1216              :     }
    1217              : 
    1218              :     void append(layer& e,
    1219              :         http_proto::method verb,
    1220              :         handler_list const& handlers)
    1221              :     {
    1222              :         add_impl(e, verb, handlers);
    1223              :     }
    1224              : };
    1225              : 
    1226              : //-----------------------------------------------
    1227              : 
    1228              : template<class Req, class Res>
    1229              : class basic_router<Req, Res>::
    1230              :     fluent_route
    1231              : {
    1232              : public:
    1233              :     fluent_route(fluent_route const&) = default;
    1234              : 
    1235              :     /** Add handlers that apply to all HTTP methods.
    1236              :         
    1237              :         This registers regular handlers that run for any request matching
    1238              :         the route's pattern, regardless of HTTP method. Handlers are
    1239              :         appended to the route's handler sequence and are invoked in
    1240              :         registration order whenever a preceding handler returns
    1241              :         @ref route::next. Error handlers and routers cannot be passed here.
    1242              : 
    1243              :         This function returns a @ref fluent_route, allowing additional
    1244              :         method registrations to be chained. For example:
    1245              :         @code
    1246              :         router.route("/resource")
    1247              :             .all(log_request)
    1248              :             .get(show_resource)
    1249              :             .post(update_resource);
    1250              :         @endcode
    1251              : 
    1252              :         @param h1 The first handler to add.
    1253              :         @param hn Additional handlers to add, invoked after @p h1 in
    1254              :             registration order.
    1255              :         @return The @ref fluent_route for further chained registrations.
    1256              :     */
    1257              :     template<class H1, class... HN>
    1258           14 :     auto all(
    1259              :         H1&& h1, HN&&... hn) ->
    1260              :             fluent_route
    1261              :     {
    1262              :         // If you get a compile error on this line it means that
    1263              :         // one or more of the provided types is not a valid handler.
    1264              :         // Error handlers and routers cannot be passed here.
    1265              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1266           14 :         owner_.add_impl(e_, core::string_view(), make_handler_list(
    1267              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1268           14 :         return *this;
    1269              :     }
    1270              : 
    1271              :     /** Add handlers for a specific HTTP method.
    1272              :         
    1273              :         This registers regular handlers for the given method on the
    1274              :         current route, participating in dispatch as described in the
    1275              :         @ref basic_router class documentation. Handlers are appended
    1276              :         to the route's handler sequence and invoked in registration
    1277              :         order whenever a preceding handler returns @ref route::next.
    1278              :         Error handlers and routers cannot be passed here.
    1279              : 
    1280              :         @param verb The HTTP method to match.
    1281              :         @param h1 The first handler to add.
    1282              :         @param hn Additional handlers to add, invoked after @p h1 in
    1283              :             registration order.
    1284              :         @return The @ref fluent_route for further chained registrations.
    1285              :     */
    1286              :     template<class H1, class... HN>
    1287           68 :     auto add(
    1288              :         http_proto::method verb,
    1289              :         H1&& h1, HN&&... hn) ->
    1290              :             fluent_route
    1291              :     {
    1292              :         // If you get a compile error on this line it means that
    1293              :         // one or more of the provided types is not a valid handler.
    1294              :         // Error handlers and routers cannot be passed here.
    1295              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1296           68 :         owner_.add_impl(e_, verb, make_handler_list(
    1297              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1298           67 :         return *this;
    1299              :     }
    1300              : 
    1301              :     /** Add handlers for a method name.
    1302              : 
    1303              :         This registers regular handlers for the given HTTP method string
    1304              :         on the current route, participating in dispatch as described in
    1305              :         the @ref basic_router class documentation. This overload is
    1306              :         intended for methods not represented by @ref http_proto::method.
    1307              :         Handlers are appended to the route's handler sequence and invoked
    1308              :         in registration order whenever a preceding handler returns
    1309              :         @ref route::next.
    1310              : 
    1311              :         @par Constraints
    1312              :         @li Each handler must be a regular handler; error handlers and
    1313              :             routers cannot be passed.
    1314              : 
    1315              :         @param verb The HTTP method string to match.
    1316              :         @param h1 The first handler to add.
    1317              :         @param hn Additional handlers to add, invoked after @p h1 in
    1318              :             registration order.
    1319              :         @return The @ref fluent_route for further chained registrations.
    1320              :     */
    1321              :     template<class H1, class... HN>
    1322            9 :     auto add(
    1323              :         core::string_view verb,
    1324              :         H1&& h1, HN&&... hn) ->
    1325              :             fluent_route
    1326              :     {
    1327              :         // If you get a compile error on this line it means that
    1328              :         // one or more of the provided types is not a valid handler.
    1329              :         // Error handlers and routers cannot be passed here.
    1330              :         BOOST_CORE_STATIC_ASSERT(handler_check<1, H1, HN...>::value);
    1331            9 :         owner_.add_impl(e_, verb, make_handler_list(
    1332              :             std::forward<H1>(h1), std::forward<HN>(hn)...));
    1333            9 :         return *this;
    1334              :     }
    1335              : 
    1336              : private:
    1337              :     friend class basic_router;
    1338           63 :     fluent_route(
    1339              :         basic_router& owner,
    1340              :         core::string_view pattern)
    1341           63 :         : e_(owner.new_layer(pattern))
    1342           62 :         , owner_(owner)
    1343              :     {
    1344           62 :     }
    1345              : 
    1346              :     layer& e_;
    1347              :     basic_router& owner_;
    1348              : };
    1349              : 
    1350              : } // beast2
    1351              : } // boost
    1352              : 
    1353              : #endif
        

Generated by: LCOV version 2.1