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