GCC Code Coverage Report


Directory: ./
File: libs/http_proto/include/boost/http_proto/server/basic_router.hpp
Date: 2025-12-01 15:47:27
Exec Total Coverage
Lines: 112 115 97.4%
Functions: 203 217 93.5%
Branches: 53 57 93.0%

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