99.07% Lines (107/108) 100.00% Functions (36/36)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/http 7   // Official repository: https://github.com/cppalliance/http
8   // 8   //
9   9  
10   #ifndef BOOST_HTTP_SERVER_ROUTER_HPP 10   #ifndef BOOST_HTTP_SERVER_ROUTER_HPP
11   #define BOOST_HTTP_SERVER_ROUTER_HPP 11   #define BOOST_HTTP_SERVER_ROUTER_HPP
12   12  
13   #include <boost/http/detail/config.hpp> 13   #include <boost/http/detail/config.hpp>
14   #include <boost/http/server/route_handler.hpp> 14   #include <boost/http/server/route_handler.hpp>
15   #include <boost/http/server/detail/router_base.hpp> 15   #include <boost/http/server/detail/router_base.hpp>
16   #include <boost/http/method.hpp> 16   #include <boost/http/method.hpp>
17   #include <boost/url/url_view.hpp> 17   #include <boost/url/url_view.hpp>
18   #include <boost/mp11/algorithm.hpp> 18   #include <boost/mp11/algorithm.hpp>
19   #include <boost/assert.hpp> 19   #include <boost/assert.hpp>
20   #include <exception> 20   #include <exception>
21   #include <string_view> 21   #include <string_view>
22   #include <type_traits> 22   #include <type_traits>
23   23  
24   namespace boost { 24   namespace boost {
25   namespace http { 25   namespace http {
26   26  
27   template<class, class> class router; 27   template<class, class> class router;
28   28  
29   /** Configuration options for HTTP routers. 29   /** Configuration options for HTTP routers.
30   */ 30   */
31   struct router_options 31   struct router_options
32   { 32   {
33   /** Constructor. 33   /** Constructor.
34   34  
35   Routers constructed with default options inherit the values of 35   Routers constructed with default options inherit the values of
36   @ref case_sensitive and @ref strict from the parent router. 36   @ref case_sensitive and @ref strict from the parent router.
37   If there is no parent, both default to `false`. 37   If there is no parent, both default to `false`.
38   The value of @ref merge_params always defaults to `false` 38   The value of @ref merge_params always defaults to `false`
39   and is never inherited. 39   and is never inherited.
40   */ 40   */
HITCBC 41   168 router_options() = default; 41   168 router_options() = default;
42   42  
43   /** Set whether to merge parameters from parent routers. 43   /** Set whether to merge parameters from parent routers.
44   44  
45   This setting controls whether route parameters defined on parent 45   This setting controls whether route parameters defined on parent
46   routers are made available in nested routers. It is not inherited 46   routers are made available in nested routers. It is not inherited
47   and always defaults to `false`. 47   and always defaults to `false`.
48   48  
49   @par Example 49   @par Example
50   @code 50   @code
51   router r( router_options() 51   router r( router_options()
52   .merge_params( true ) 52   .merge_params( true )
53   .case_sensitive( true ) 53   .case_sensitive( true )
54   .strict( false ) ); 54   .strict( false ) );
55   @endcode 55   @endcode
56   56  
57   @param value `true` to merge parameters from parent routers. 57   @param value `true` to merge parameters from parent routers.
58   58  
59   @return A reference to `*this` for chaining. 59   @return A reference to `*this` for chaining.
60   */ 60   */
61   router_options& 61   router_options&
62   merge_params( 62   merge_params(
63   bool value) noexcept 63   bool value) noexcept
64   { 64   {
65   v_ = (v_ & ~1) | (value ? 1 : 0); 65   v_ = (v_ & ~1) | (value ? 1 : 0);
66   return *this; 66   return *this;
67   } 67   }
68   68  
69   /** Set whether pattern matching is case-sensitive. 69   /** Set whether pattern matching is case-sensitive.
70   70  
71   When this option is not set explicitly, the value is inherited 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. 72   from the parent router or defaults to `false` if there is no parent.
73   73  
74   @par Example 74   @par Example
75   @code 75   @code
76   router r( router_options() 76   router r( router_options()
77   .case_sensitive( true ) 77   .case_sensitive( true )
78   .strict( true ) ); 78   .strict( true ) );
79   @endcode 79   @endcode
80   80  
81   @param value `true` to perform case-sensitive path matching. 81   @param value `true` to perform case-sensitive path matching.
82   82  
83   @return A reference to `*this` for chaining. 83   @return A reference to `*this` for chaining.
84   */ 84   */
85   router_options& 85   router_options&
HITCBC 86   6 case_sensitive( 86   6 case_sensitive(
87   bool value) noexcept 87   bool value) noexcept
88   { 88   {
HITCBC 89   6 if(value) 89   6 if(value)
HITCBC 90   4 v_ = (v_ & ~6) | 2; 90   4 v_ = (v_ & ~6) | 2;
91   else 91   else
HITCBC 92   2 v_ = (v_ & ~6) | 4; 92   2 v_ = (v_ & ~6) | 4;
HITCBC 93   6 return *this; 93   6 return *this;
94   } 94   }
95   95  
96   /** Set whether pattern matching is strict. 96   /** Set whether pattern matching is strict.
97   97  
98   When this option is not set explicitly, the value is inherited 98   When this option is not set explicitly, the value is inherited
99   from the parent router or defaults to `false` if there is no parent. 99   from the parent router or defaults to `false` if there is no parent.
100   Strict matching treats a trailing slash as significant: 100   Strict matching treats a trailing slash as significant:
101   the pattern `"/api"` matches `"/api"` but not `"/api/"`. 101   the pattern `"/api"` matches `"/api"` but not `"/api/"`.
102   When strict matching is disabled, these paths are treated 102   When strict matching is disabled, these paths are treated
103   as equivalent. 103   as equivalent.
104   104  
105   @par Example 105   @par Example
106   @code 106   @code
107   router r( router_options() 107   router r( router_options()
108   .strict( true ) 108   .strict( true )
109   .case_sensitive( false ) ); 109   .case_sensitive( false ) );
110   @endcode 110   @endcode
111   111  
112   @param value `true` to enable strict path matching. 112   @param value `true` to enable strict path matching.
113   113  
114   @return A reference to `*this` for chaining. 114   @return A reference to `*this` for chaining.
115   */ 115   */
116   router_options& 116   router_options&
HITCBC 117   2 strict( 117   2 strict(
118   bool value) noexcept 118   bool value) noexcept
119   { 119   {
HITCBC 120   2 if(value) 120   2 if(value)
HITCBC 121   1 v_ = (v_ & ~24) | 8; 121   1 v_ = (v_ & ~24) | 8;
122   else 122   else
HITCBC 123   1 v_ = (v_ & ~24) | 16; 123   1 v_ = (v_ & ~24) | 16;
HITCBC 124   2 return *this; 124   2 return *this;
125   } 125   }
126   126  
127   private: 127   private:
128   template<class, class> friend class router; 128   template<class, class> friend class router;
129   unsigned int v_ = 0; 129   unsigned int v_ = 0;
130   }; 130   };
131   131  
132   //----------------------------------------------- 132   //-----------------------------------------------
133   133  
134   /** The default handler transform. 134   /** The default handler transform.
135   135  
136   Passes each handler through unchanged. This is the 136   Passes each handler through unchanged. This is the
137   default value of the `HT` template parameter on 137   default value of the `HT` template parameter on
138   @ref router. 138   @ref router.
139   */ 139   */
140   struct identity 140   struct identity
141   { 141   {
142   template< class T > 142   template< class T >
HITCBC 143   123 T operator()( T&& t ) const 143   123 T operator()( T&& t ) const
144   { 144   {
HITCBC 145   123 return std::forward<T>(t); 145   123 return std::forward<T>(t);
146   } 146   }
147   }; 147   };
148   148  
149   /** A container for HTTP route handlers. 149   /** A container for HTTP route handlers.
150   150  
151   `router` objects store and dispatch route handlers based on the 151   `router` objects store and dispatch route handlers based on the
152   HTTP method and path of an incoming request. Routes are added with a 152   HTTP method and path of an incoming request. Routes are added with a
153   path pattern, method, and an associated handler, and the router is then 153   path pattern, method, and an associated handler, and the router is then
154   used to dispatch the appropriate handler. 154   used to dispatch the appropriate handler.
155   155  
156   Routes are flattened into contiguous arrays as they are added, so 156   Routes are flattened into contiguous arrays as they are added, so
157   dispatch is always cache-friendly regardless of nesting depth. 157   dispatch is always cache-friendly regardless of nesting depth.
158   158  
159   Patterns used to create route definitions have percent-decoding applied 159   Patterns used to create route definitions have percent-decoding applied
160   when handlers are mounted. A literal "%2F" in the pattern string is 160   when handlers are mounted. A literal "%2F" in the pattern string is
161   indistinguishable from a literal '/'. For example, "/x%2Fz" is the 161   indistinguishable from a literal '/'. For example, "/x%2Fz" is the
162   same as "/x/z" when used as a pattern. 162   same as "/x/z" when used as a pattern.
163   163  
164   @par Example 164   @par Example
165   @code 165   @code
166   router<route_params> r; 166   router<route_params> r;
167   r.get( "/hello", 167   r.get( "/hello",
168   []( route_params& p ) 168   []( route_params& p )
169   { 169   {
170   p.res.status( status::ok ); 170   p.res.status( status::ok );
171   p.res.set_body( "Hello, world!" ); 171   p.res.set_body( "Hello, world!" );
172   return route_done; 172   return route_done;
173   } ); 173   } );
174   @endcode 174   @endcode
175   175  
176   Router objects use shared ownership via `shared_ptr`. Copies refer 176   Router objects use shared ownership via `shared_ptr`. Copies refer
177   to the same underlying data. Modifying a router after it has been 177   to the same underlying data. Modifying a router after it has been
178   copied is not permitted and results in undefined behavior. 178   copied is not permitted and results in undefined behavior.
179   179  
180   @par Path Pattern Syntax 180   @par Path Pattern Syntax
181   181  
182   Route patterns define which request paths match a route. Patterns 182   Route patterns define which request paths match a route. Patterns
183   support literal text, named parameters, wildcards, and optional 183   support literal text, named parameters, wildcards, and optional
184   groups. The syntax is inspired by Express.js path-to-regexp. 184   groups. The syntax is inspired by Express.js path-to-regexp.
185   185  
186   @code 186   @code
187   path = *token 187   path = *token
188   token = text / param / wildcard / group 188   token = text / param / wildcard / group
189   text = 1*( char / escaped ) ; literal characters 189   text = 1*( char / escaped ) ; literal characters
190   param = ":" name ; captures segment until '/' 190   param = ":" name ; captures segment until '/'
191   wildcard = "*" name ; captures everything to end 191   wildcard = "*" name ; captures everything to end
192   group = "{" *token "}" ; optional section 192   group = "{" *token "}" ; optional section
193   name = identifier / quoted ; plain or quoted name 193   name = identifier / quoted ; plain or quoted name
194   identifier = ( "$" / "_" / ALPHA ) *( "$" / "_" / ALNUM ) 194   identifier = ( "$" / "_" / ALPHA ) *( "$" / "_" / ALNUM )
195   quoted = DQUOTE 1*qchar DQUOTE ; allows spaces, punctuation 195   quoted = DQUOTE 1*qchar DQUOTE ; allows spaces, punctuation
196   escaped = "\" CHAR ; literal special character 196   escaped = "\" CHAR ; literal special character
197   @endcode 197   @endcode
198   198  
199   Named parameters capture path segments. A parameter matches any 199   Named parameters capture path segments. A parameter matches any
200   characters except `/` and must capture at least one character: 200   characters except `/` and must capture at least one character:
201   201  
202   - `/users/:id` matches `/users/42`, capturing `id = "42"` 202   - `/users/:id` matches `/users/42`, capturing `id = "42"`
203   - `/users/:userId/posts/:postId` matches `/users/5/posts/99` 203   - `/users/:userId/posts/:postId` matches `/users/5/posts/99`
204   - `/:from-:to` matches `/LAX-JFK`, capturing `from = "LAX"`, `to = "JFK"` 204   - `/:from-:to` matches `/LAX-JFK`, capturing `from = "LAX"`, `to = "JFK"`
205   205  
206   Wildcards capture everything from their position to the end of 206   Wildcards capture everything from their position to the end of
207   the path, including `/` characters. Optional groups match 207   the path, including `/` characters. Optional groups match
208   all-or-nothing: 208   all-or-nothing:
209   209  
210   - `/api{/v:version}` matches both `/api` and `/api/v2` 210   - `/api{/v:version}` matches both `/api` and `/api/v2`
211   - `/file{.:ext}` matches `/file` and `/file.json` 211   - `/file{.:ext}` matches `/file` and `/file.json`
212   212  
213   Reserved characters `( ) [ ] + ? !` are not allowed in patterns. 213   Reserved characters `( ) [ ] + ? !` are not allowed in patterns.
214   For wildcards, escaping, and quoted names, see the Route Patterns 214   For wildcards, escaping, and quoted names, see the Route Patterns
215   documentation. 215   documentation.
216   216  
217   @par Handlers 217   @par Handlers
218   218  
219   Regular handlers are invoked for matching routes and have this 219   Regular handlers are invoked for matching routes and have this
220   equivalent signature: 220   equivalent signature:
221   @code 221   @code
222   route_result handler( Params& p ) 222   route_result handler( Params& p )
223   @endcode 223   @endcode
224   224  
225   The return value is a @ref route_result used to indicate the desired 225   The return value is a @ref route_result used to indicate the desired
226   action through @ref route enum values, or to indicate that a failure 226   action through @ref route enum values, or to indicate that a failure
227   occurred. Failures are represented by error codes for which 227   occurred. Failures are represented by error codes for which
228   `system::error_code::failed()` returns `true`. 228   `system::error_code::failed()` returns `true`.
229   229  
230   When a failing error code is produced and remains unhandled, the 230   When a failing error code is produced and remains unhandled, the
231   router enters error-dispatching mode. In this mode, only error 231   router enters error-dispatching mode. In this mode, only error
232   handlers are invoked. Error handlers are registered globally or 232   handlers are invoked. Error handlers are registered globally or
233   for specific paths and execute in the order of registration whenever 233   for specific paths and execute in the order of registration whenever
234   a failing error code is present in the response. 234   a failing error code is present in the response.
235   235  
236   Error handlers have this equivalent signature: 236   Error handlers have this equivalent signature:
237   @code 237   @code
238   route_result error_handler( Params& p, system::error_code ec ) 238   route_result error_handler( Params& p, system::error_code ec )
239   @endcode 239   @endcode
240   240  
241   Each error handler may return any failing @ref system::error_code, 241   Each error handler may return any failing @ref system::error_code,
242   which is equivalent to calling: 242   which is equivalent to calling:
243   @code 243   @code
244   p.next( ec ); // with ec == true 244   p.next( ec ); // with ec == true
245   @endcode 245   @endcode
246   246  
247   Returning @ref route_next indicates that control should proceed to 247   Returning @ref route_next indicates that control should proceed to
248   the next matching error handler. Returning a different failing code 248   the next matching error handler. Returning a different failing code
249   replaces the current error and continues dispatch in error mode using 249   replaces the current error and continues dispatch in error mode using
250   that new code. Error handlers are invoked until one returns a result 250   that new code. Error handlers are invoked until one returns a result
251   other than @ref route_next. 251   other than @ref route_next.
252   252  
253   Exception handlers have this equivalent signature: 253   Exception handlers have this equivalent signature:
254   @code 254   @code
255   route_result exception_handler( Params& p, E ex ) 255   route_result exception_handler( Params& p, E ex )
256   @endcode 256   @endcode
257   257  
258   Where `E` is the type of exception caught. Handlers installed for an 258   Where `E` is the type of exception caught. Handlers installed for an
259   exception of type `E` will also be called when the exception type is 259   exception of type `E` will also be called when the exception type is
260   a derived class of `E`. Exception handlers are invoked in the order 260   a derived class of `E`. Exception handlers are invoked in the order
261   of registration whenever an exception is present in the request. 261   of registration whenever an exception is present in the request.
262   262  
263   The prefix match is not strict: middleware attached to `"/api"` 263   The prefix match is not strict: middleware attached to `"/api"`
264   will also match `"/api/users"` and `"/api/data"`. When registered 264   will also match `"/api/users"` and `"/api/data"`. When registered
265   before route handlers for the same prefix, middleware runs before 265   before route handlers for the same prefix, middleware runs before
266   those routes. This is analogous to `app.use( path, ... )` in 266   those routes. This is analogous to `app.use( path, ... )` in
267   Express.js. 267   Express.js.
268   268  
269   @par Handler Transforms 269   @par Handler Transforms
270   270  
271   The second template parameter `HT` is a <em>handler transform</em>. 271   The second template parameter `HT` is a <em>handler transform</em>.
272   A handler transform is a callable object that the router applies to 272   A handler transform is a callable object that the router applies to
273   each plain handler at registration time, producing a new callable 273   each plain handler at registration time, producing a new callable
274   with the canonical signature `route_task(Params&)`. 274   with the canonical signature `route_task(Params&)`.
275   275  
276   When a handler is registered that does not directly satisfy 276   When a handler is registered that does not directly satisfy
277   `route_task(Params&)`, the router applies `ht(handler)` to adapt 277   `route_task(Params&)`, the router applies `ht(handler)` to adapt
278   it. The transform is invoked <em>once</em> at registration time; 278   it. The transform is invoked <em>once</em> at registration time;
279   the returned callable is stored and invoked each time the route 279   the returned callable is stored and invoked each time the route
280   matches at dispatch time. 280   matches at dispatch time.
281   281  
282   Error handlers and exception handlers are never transformed. 282   Error handlers and exception handlers are never transformed.
283   Only plain route handlers pass through the transform. 283   Only plain route handlers pass through the transform.
284   284  
285   The default transform is @ref identity, which passes handlers 285   The default transform is @ref identity, which passes handlers
286   through unchanged. 286   through unchanged.
287   287  
288   A transform `HT` must satisfy the following contract: for any 288   A transform `HT` must satisfy the following contract: for any
289   handler `h` passed to the router, the expression `ht(h)` must 289   handler `h` passed to the router, the expression `ht(h)` must
290   return a callable `g` such that `g(Params&)` returns 290   return a callable `g` such that `g(Params&)` returns
291   @ref route_task. 291   @ref route_task.
292   292  
293   @par Example: Logging Transform 293   @par Example: Logging Transform
294   @code 294   @code
295   struct log_transform 295   struct log_transform
296   { 296   {
297   template<class Handler> 297   template<class Handler>
298   auto operator()(Handler h) const 298   auto operator()(Handler h) const
299   { 299   {
300   struct wrapper 300   struct wrapper
301   { 301   {
302   Handler h_; 302   Handler h_;
303   303  
304   route_task operator()(route_params& p) const 304   route_task operator()(route_params& p) const
305   { 305   {
306   auto t0 = steady_clock::now(); 306   auto t0 = steady_clock::now();
307   auto rv = co_await h_(p); 307   auto rv = co_await h_(p);
308   log_elapsed(steady_clock::now() - t0); 308   log_elapsed(steady_clock::now() - t0);
309   co_return rv; 309   co_return rv;
310   } 310   }
311   }; 311   };
312   return wrapper{ std::move(h) }; 312   return wrapper{ std::move(h) };
313   } 313   }
314   }; 314   };
315   315  
316   router<route_params> base; 316   router<route_params> base;
317   auto r = base.with_transform( log_transform{} ); 317   auto r = base.with_transform( log_transform{} );
318   318  
319   // The lambda is wrapped by log_transform at registration. 319   // The lambda is wrapped by log_transform at registration.
320   // At dispatch, log_transform::wrapper::operator() runs, 320   // At dispatch, log_transform::wrapper::operator() runs,
321   // which invokes the original lambda and logs elapsed time. 321   // which invokes the original lambda and logs elapsed time.
322   r.get( "/hello", []( route_params& p ) -> route_task { 322   r.get( "/hello", []( route_params& p ) -> route_task {
323   co_return route_done; 323   co_return route_done;
324   }); 324   });
325   @endcode 325   @endcode
326   326  
327   @par Example: Dependency Injection Transform 327   @par Example: Dependency Injection Transform
328   328  
329   A transform can adapt handlers whose parameters are not 329   A transform can adapt handlers whose parameters are not
330   `Params&` at all. The transform resolves each parameter 330   `Params&` at all. The transform resolves each parameter
331   from a service container at dispatch time: 331   from a service container at dispatch time:
332   332  
333   @code 333   @code
334   struct inject_transform 334   struct inject_transform
335   { 335   {
336   template<class Handler> 336   template<class Handler>
337   auto operator()(Handler h) const 337   auto operator()(Handler h) const
338   { 338   {
339   struct wrapper 339   struct wrapper
340   { 340   {
341   Handler h_; 341   Handler h_;
342   342  
343   route_task operator()(route_params& p) const 343   route_task operator()(route_params& p) const
344   { 344   {
345   // Look up each of h_'s parameter types 345   // Look up each of h_'s parameter types
346   // in p.route_data. Return route_next if 346   // in p.route_data. Return route_next if
347   // any are missing. 347   // any are missing.
348   co_return dynamic_invoke(p.route_data, h_); 348   co_return dynamic_invoke(p.route_data, h_);
349   } 349   }
350   }; 350   };
351   return wrapper{ std::move(h) }; 351   return wrapper{ std::move(h) };
352   } 352   }
353   }; 353   };
354   354  
355   router<route_params> base; 355   router<route_params> base;
356   auto r = base.with_transform( inject_transform{} ); 356   auto r = base.with_transform( inject_transform{} );
357   357  
358   // Parameters are resolved from p.route_data automatically. 358   // Parameters are resolved from p.route_data automatically.
359   // If UserService or Config are not in route_data, the 359   // If UserService or Config are not in route_data, the
360   // handler is skipped and route_next is returned. 360   // handler is skipped and route_next is returned.
361   r.get( "/users", []( 361   r.get( "/users", [](
362   UserService& svc, 362   UserService& svc,
363   Config const& cfg) -> route_result 363   Config const& cfg) -> route_result
364   { 364   {
365   // use svc and cfg... 365   // use svc and cfg...
366   return route_done; 366   return route_done;
367   }); 367   });
368   @endcode 368   @endcode
369   369  
370   @par Thread Safety 370   @par Thread Safety
371   371  
372   Member functions marked `const` such as @ref dispatch 372   Member functions marked `const` such as @ref dispatch
373   may be called concurrently on routers that refer to the same data. 373   may be called concurrently on routers that refer to the same data.
374   Modification of routers through calls to non-`const` member functions 374   Modification of routers through calls to non-`const` member functions
375   is not thread-safe and must not be performed concurrently with any 375   is not thread-safe and must not be performed concurrently with any
376   other member function. 376   other member function.
377   377  
378   @par Nesting Depth 378   @par Nesting Depth
379   379  
380   Routers may be nested to a maximum depth of `max_path_depth` (16 levels). 380   Routers may be nested to a maximum depth of `max_path_depth` (16 levels).
381   Exceeding this limit throws `std::length_error` when the nested router 381   Exceeding this limit throws `std::length_error` when the nested router
382   is added via @ref use. This limit ensures that dispatch never overflows 382   is added via @ref use. This limit ensures that dispatch never overflows
383   its fixed-size tracking arrays. 383   its fixed-size tracking arrays.
384   384  
385   @par Constraints 385   @par Constraints
386   386  
387   `Params` must be publicly derived from @ref route_params. 387   `Params` must be publicly derived from @ref route_params.
388   388  
389   @tparam Params The type of the parameters object passed to handlers. 389   @tparam Params The type of the parameters object passed to handlers.
390   */ 390   */
391   template<class P = route_params, class HT = identity> 391   template<class P = route_params, class HT = identity>
392   class router : public detail::router_base 392   class router : public detail::router_base
393   { 393   {
394   template<class, class> friend class router; 394   template<class, class> friend class router;
395   395  
396   HT ht_{}; 396   HT ht_{};
397   397  
398   static_assert(std::derived_from<P, route_params>); 398   static_assert(std::derived_from<P, route_params>);
399   399  
400   template<class T> 400   template<class T>
401   static inline constexpr char handler_kind = 401   static inline constexpr char handler_kind =
402   []() -> char 402   []() -> char
403   { 403   {
404   if constexpr (detail::returns_route_task< 404   if constexpr (detail::returns_route_task<
405   T, P&, system::error_code>) 405   T, P&, system::error_code>)
406   { 406   {
407   return is_error; 407   return is_error;
408   } 408   }
409   else if constexpr (detail::returns_route_task< 409   else if constexpr (detail::returns_route_task<
410   T, P&, std::exception_ptr>) 410   T, P&, std::exception_ptr>)
411   { 411   {
412   return is_exception; 412   return is_exception;
413   } 413   }
414   else if constexpr (detail::returns_route_task<T, P&>) 414   else if constexpr (detail::returns_route_task<T, P&>)
415   { 415   {
416   return is_plain; 416   return is_plain;
417   } 417   }
418   else if constexpr ( 418   else if constexpr (
419   std::is_invocable_v<HT const&, T> && 419   std::is_invocable_v<HT const&, T> &&
420   detail::returns_route_task< 420   detail::returns_route_task<
421   std::invoke_result_t<HT const&, T>, P&>) 421   std::invoke_result_t<HT const&, T>, P&>)
422   { 422   {
423   return is_plain; 423   return is_plain;
424   } 424   }
425   else 425   else
426   { 426   {
427   return is_invalid; 427   return is_invalid;
428   } 428   }
429   }(); 429   }();
430   430  
431   template<class T> 431   template<class T>
432   static inline constexpr bool is_sub_router = 432   static inline constexpr bool is_sub_router =
433   std::is_base_of_v<detail::router_base, std::decay_t<T>> && 433   std::is_base_of_v<detail::router_base, std::decay_t<T>> &&
434   std::is_convertible_v<std::decay_t<T> const volatile*, 434   std::is_convertible_v<std::decay_t<T> const volatile*,
435   detail::router_base const volatile*>; 435   detail::router_base const volatile*>;
436   436  
437   template<class... Ts> 437   template<class... Ts>
438   static inline constexpr bool handler_crvals = 438   static inline constexpr bool handler_crvals =
439   ((!std::is_lvalue_reference_v<Ts> || 439   ((!std::is_lvalue_reference_v<Ts> ||
440   std::is_const_v<std::remove_reference_t<Ts>> || 440   std::is_const_v<std::remove_reference_t<Ts>> ||
441   std::is_function_v<std::remove_reference_t<Ts>>) && ...); 441   std::is_function_v<std::remove_reference_t<Ts>>) && ...);
442   442  
443   template<char Mask, class... Ts> 443   template<char Mask, class... Ts>
444   static inline constexpr bool handler_check = 444   static inline constexpr bool handler_check =
445   (((handler_kind<Ts> & Mask) != 0) && ...); 445   (((handler_kind<Ts> & Mask) != 0) && ...);
446   446  
447   template<class H> 447   template<class H>
448   struct handler_impl : handler 448   struct handler_impl : handler
449   { 449   {
450   std::decay_t<H> h; 450   std::decay_t<H> h;
451   451  
452   template<class H_> 452   template<class H_>
HITCBC 453   142 explicit handler_impl(H_ h_) 453   142 explicit handler_impl(H_ h_)
454   : handler(handler_kind<H>) 454   : handler(handler_kind<H>)
HITCBC 455   142 , h(std::forward<H_>(h_)) 455   142 , h(std::forward<H_>(h_))
456   { 456   {
HITCBC 457   142 } 457   142 }
458   458  
HITCBC 459   120 auto invoke(route_params& rp) const -> 459   120 auto invoke(route_params& rp) const ->
460   route_task override 460   route_task override
461   { 461   {
462   if constexpr (detail::returns_route_task<H, P&>) 462   if constexpr (detail::returns_route_task<H, P&>)
463   { 463   {
HITCBC 464   108 return h(static_cast<P&>(rp)); 464   108 return h(static_cast<P&>(rp));
465   } 465   }
466   else if constexpr (detail::returns_route_task< 466   else if constexpr (detail::returns_route_task<
467   H, P&, system::error_code>) 467   H, P&, system::error_code>)
468   { 468   {
HITCBC 469   9 return h(static_cast<P&>(rp), rp.priv_.ec_); 469   9 return h(static_cast<P&>(rp), rp.priv_.ec_);
470   } 470   }
471   else if constexpr (detail::returns_route_task< 471   else if constexpr (detail::returns_route_task<
472   H, P&, std::exception_ptr>) 472   H, P&, std::exception_ptr>)
473   { 473   {
HITCBC 474   3 return h(static_cast<P&>(rp), rp.priv_.ep_); 474   3 return h(static_cast<P&>(rp), rp.priv_.ep_);
475   } 475   }
476   else 476   else
477   { 477   {
478   std::terminate(); 478   std::terminate();
479   } 479   }
480   } 480   }
481   }; 481   };
482   482  
483   template<class H> 483   template<class H>
HITCBC 484   142 static handler_ptr make_handler(H&& h) 484   142 static handler_ptr make_handler(H&& h)
485   { 485   {
HITCBC 486   142 return std::make_unique<handler_impl<H>>(std::forward<H>(h)); 486   142 return std::make_unique<handler_impl<H>>(std::forward<H>(h));
487   } 487   }
488   488  
489   template<class H> 489   template<class H>
490   struct options_handler_impl : options_handler 490   struct options_handler_impl : options_handler
491   { 491   {
492   std::decay_t<H> h; 492   std::decay_t<H> h;
493   493  
494   template<class H_> 494   template<class H_>
HITCBC 495   4 explicit options_handler_impl(H_&& h_) 495   4 explicit options_handler_impl(H_&& h_)
HITCBC 496   4 : h(std::forward<H_>(h_)) 496   4 : h(std::forward<H_>(h_))
497   { 497   {
HITCBC 498   4 } 498   4 }
499   499  
HITCBC 500   3 route_task invoke( 500   3 route_task invoke(
501   route_params& rp, 501   route_params& rp,
502   std::string_view allow) const override 502   std::string_view allow) const override
503   { 503   {
HITCBC 504   3 return h(static_cast<P&>(rp), allow); 504   3 return h(static_cast<P&>(rp), allow);
505   } 505   }
506   }; 506   };
507   507  
508   template<class T, std::size_t N> 508   template<class T, std::size_t N>
509   struct handlers_impl : handlers 509   struct handlers_impl : handlers
510   { 510   {
511   T const& ht; 511   T const& ht;
512   handler_ptr v[N]; 512   handler_ptr v[N];
513   513  
514   template<class... HN> 514   template<class... HN>
HITCBC 515   133 explicit handlers_impl(T const& ht_, HN&&... hn) 515   133 explicit handlers_impl(T const& ht_, HN&&... hn)
MISUBC 516   : ht(ht_) 516   : ht(ht_)
517   { 517   {
HITCBC 518   133 p = v; 518   133 p = v;
HITCBC 519   133 n = sizeof...(HN); 519   133 n = sizeof...(HN);
HITCBC 520   133 assign<0>(std::forward<HN>(hn)...); 520   133 assign<0>(std::forward<HN>(hn)...);
HITCBC 521   133 } 521   133 }
522   522  
523   private: 523   private:
524   template<std::size_t I, class H1, class... HN> 524   template<std::size_t I, class H1, class... HN>
HITCBC 525   142 void assign(H1&& h1, HN&&... hn) 525   142 void assign(H1&& h1, HN&&... hn)
526   { 526   {
527   if constexpr ( 527   if constexpr (
528   detail::returns_route_task< 528   detail::returns_route_task<
529   H1, P&, system::error_code> || 529   H1, P&, system::error_code> ||
530   detail::returns_route_task< 530   detail::returns_route_task<
531   H1, P&, std::exception_ptr>) 531   H1, P&, std::exception_ptr>)
532   { 532   {
HITCBC 533   12 v[I] = make_handler(std::forward<H1>(h1)); 533   12 v[I] = make_handler(std::forward<H1>(h1));
534   } 534   }
535   else if constexpr (detail::returns_route_task< 535   else if constexpr (detail::returns_route_task<
536   decltype(std::declval<T const&>()(std::declval<H1>())), P&>) 536   decltype(std::declval<T const&>()(std::declval<H1>())), P&>)
537   { 537   {
HITCBC 538   130 v[I] = make_handler(ht(std::forward<H1>(h1))); 538   130 v[I] = make_handler(ht(std::forward<H1>(h1)));
539   } 539   }
HITCBC 540   142 assign<I+1>(std::forward<HN>(hn)...); 540   142 assign<I+1>(std::forward<HN>(hn)...);
HITCBC 541   142 } 541   142 }
542   542  
543   template<std::size_t> 543   template<std::size_t>
HITCBC 544   133 void assign(int = 0) 544   133 void assign(int = 0)
545   { 545   {
HITCBC 546   133 } 546   133 }
547   }; 547   };
548   548  
549   template<class T, class... HN> 549   template<class T, class... HN>
HITCBC 550   133 static auto make_handlers(T const& ht, HN&&... hn) 550   133 static auto make_handlers(T const& ht, HN&&... hn)
551   { 551   {
552   return handlers_impl<T, sizeof...(HN)>(ht, 552   return handlers_impl<T, sizeof...(HN)>(ht,
HITCBC 553   133 std::forward<HN>(hn)...); 553   133 std::forward<HN>(hn)...);
554   } 554   }
555   555  
556   public: 556   public:
557   /** The type of params used in handlers. 557   /** The type of params used in handlers.
558   */ 558   */
559   using params_type = P; 559   using params_type = P;
560   560  
561   /** A fluent interface for defining handlers on a specific route. 561   /** A fluent interface for defining handlers on a specific route.
562   562  
563   This type represents a single route within the router and 563   This type represents a single route within the router and
564   provides a chainable API for registering handlers associated 564   provides a chainable API for registering handlers associated
565   with particular HTTP methods or for all methods collectively. 565   with particular HTTP methods or for all methods collectively.
566   566  
567   Typical usage registers one or more handlers for a route: 567   Typical usage registers one or more handlers for a route:
568   @code 568   @code
569   r.route( "/users/:id" ) 569   r.route( "/users/:id" )
570   .get( show_user ) 570   .get( show_user )
571   .put( update_user ) 571   .put( update_user )
572   .all( log_access ); 572   .all( log_access );
573   @endcode 573   @endcode
574   574  
575   Each call appends handlers in registration order. 575   Each call appends handlers in registration order.
576   */ 576   */
577   class fluent_route; 577   class fluent_route;
578   578  
HITCBC 579   5 router(router const&) = default; 579   5 router(router const&) = default;
HITCBC 580   2 router& operator=(router const&) = default; 580   2 router& operator=(router const&) = default;
HITCBC 581   3 router(router&&) = default; 581   3 router(router&&) = default;
582   router& operator=(router&&) = default; 582   router& operator=(router&&) = default;
583   583  
584   /** Constructor. 584   /** Constructor.
585   585  
586   Creates an empty router with the specified configuration. 586   Creates an empty router with the specified configuration.
587   Routers constructed with default options inherit the values 587   Routers constructed with default options inherit the values
588   of @ref router_options::case_sensitive and 588   of @ref router_options::case_sensitive and
589   @ref router_options::strict from the parent router, or default 589   @ref router_options::strict from the parent router, or default
590   to `false` if there is no parent. The value of 590   to `false` if there is no parent. The value of
591   @ref router_options::merge_params defaults to `false` and 591   @ref router_options::merge_params defaults to `false` and
592   is never inherited. 592   is never inherited.
593   593  
594   @param options The configuration options to use. 594   @param options The configuration options to use.
595   */ 595   */
596   explicit 596   explicit
HITCBC 597   168 router( 597   168 router(
598   router_options options = {}) 598   router_options options = {})
HITCBC 599   168 : detail::router_base(options.v_) 599   168 : detail::router_base(options.v_)
600   { 600   {
HITCBC 601   168 } 601   168 }
602   602  
603   /** Construct a router from another router with compatible types. 603   /** Construct a router from another router with compatible types.
604   604  
605   This constructs a router that shares the same underlying routing 605   This constructs a router that shares the same underlying routing
606   state as another router whose params and handler transform types 606   state as another router whose params and handler transform types
607   may differ. 607   may differ.
608   608  
609   The handler transform is initialized as follows: 609   The handler transform is initialized as follows:
610   - If `HT` is constructible from `OtherHT`, the transform is 610   - If `HT` is constructible from `OtherHT`, the transform is
611   move-constructed from the source router's transform. 611   move-constructed from the source router's transform.
612   - Otherwise, if `HT` is default-constructible, the transform 612   - Otherwise, if `HT` is default-constructible, the transform
613   is value-initialized. 613   is value-initialized.
614   614  
615   @par Constraints 615   @par Constraints
616   616  
617   `OtherParams` must be derived from `Params`, and `HT` must be 617   `OtherParams` must be derived from `Params`, and `HT` must be
618   either constructible from `OtherHT` or default-constructible. 618   either constructible from `OtherHT` or default-constructible.
619   619  
620   @param other The router to construct from. 620   @param other The router to construct from.
621   621  
622   @tparam OtherParams The params type of the source router. 622   @tparam OtherParams The params type of the source router.
623   623  
624   @tparam OtherHT The handler transform type of the source router. 624   @tparam OtherHT The handler transform type of the source router.
625   */ 625   */
626   template<class OtherP, class OtherHT> 626   template<class OtherP, class OtherHT>
627   requires std::derived_from<OtherP, P> && 627   requires std::derived_from<OtherP, P> &&
628   std::constructible_from<HT, OtherHT> 628   std::constructible_from<HT, OtherHT>
HITCBC 629   2 router( 629   2 router(
630   router<OtherP, OtherHT>&& other) noexcept 630   router<OtherP, OtherHT>&& other) noexcept
HITCBC 631   2 : detail::router_base(std::move(other)) 631   2 : detail::router_base(std::move(other))
HITCBC 632   2 , ht_(std::move(other.ht_)) 632   2 , ht_(std::move(other.ht_))
633   { 633   {
HITCBC 634   2 } 634   2 }
635   635  
636   /// @copydoc router(router<OtherP,OtherHT>&&) 636   /// @copydoc router(router<OtherP,OtherHT>&&)
637   template<class OtherP, class OtherHT> 637   template<class OtherP, class OtherHT>
638   requires std::derived_from<OtherP, P> && 638   requires std::derived_from<OtherP, P> &&
639   (!std::constructible_from<HT, OtherHT>) && 639   (!std::constructible_from<HT, OtherHT>) &&
640   std::default_initializable<HT> 640   std::default_initializable<HT>
HITCBC 641   2 router( 641   2 router(
642   router<OtherP, OtherHT>&& other) noexcept 642   router<OtherP, OtherHT>&& other) noexcept
HITCBC 643   2 : detail::router_base(std::move(other)) 643   2 : detail::router_base(std::move(other))
644   { 644   {
HITCBC 645   2 } 645   2 }
646   646  
647   /** Construct a router with a handler transform. 647   /** Construct a router with a handler transform.
648   648  
649   Creates a router that shares the routing state of @p other 649   Creates a router that shares the routing state of @p other
650   but applies @p ht to each plain handler before installation. 650   but applies @p ht to each plain handler before installation.
651   651  
652   @param other The router whose routing state to share. 652   @param other The router whose routing state to share.
653   653  
654   @param ht The handler transform to apply. 654   @param ht The handler transform to apply.
655   */ 655   */
656   template<class OtherHT> 656   template<class OtherHT>
HITCBC 657   3 router(router<P, OtherHT> const& other, HT ht) 657   3 router(router<P, OtherHT> const& other, HT ht)
658   : detail::router_base(other) 658   : detail::router_base(other)
HITCBC 659   3 , ht_(std::move(ht)) 659   3 , ht_(std::move(ht))
660   { 660   {
HITCBC 661   3 } 661   3 }
662   662  
663   /** Return a router that applies a transform to plain handlers. 663   /** Return a router that applies a transform to plain handlers.
664   664  
665   Creates a new router that shares the same underlying routing 665   Creates a new router that shares the same underlying routing
666   table but applies @p f to each plain handler before it is 666   table but applies @p f to each plain handler before it is
667   stored. Error and exception handlers are not affected by 667   stored. Error and exception handlers are not affected by
668   the transform. 668   the transform.
669   669  
670   The transform is invoked once at handler registration time. 670   The transform is invoked once at handler registration time.
671   For each plain handler `h` passed to the returned router, 671   For each plain handler `h` passed to the returned router,
672   `f(h)` must produce a callable `g` such that `g(Params&)` 672   `f(h)` must produce a callable `g` such that `g(Params&)`
673   returns @ref route_task. The callable `g` is what gets 673   returns @ref route_task. The callable `g` is what gets
674   stored and invoked at dispatch time. 674   stored and invoked at dispatch time.
675   675  
676   @par Shared State 676   @par Shared State
677   677  
678   The returned router shares the same routing table as 678   The returned router shares the same routing table as
679   `*this`. Routes added through either router are visible 679   `*this`. Routes added through either router are visible
680   during dispatch from both. The transform only controls 680   during dispatch from both. The transform only controls
681   how new handlers are wrapped when they are registered 681   how new handlers are wrapped when they are registered
682   through the returned router. 682   through the returned router.
683   683  
684   @par Example: Simple Transform 684   @par Example: Simple Transform
685   @code 685   @code
686   // A transform that logs before each handler runs 686   // A transform that logs before each handler runs
687   auto r = base.with_transform( 687   auto r = base.with_transform(
688   []( auto handler ) 688   []( auto handler )
689   { 689   {
690   struct wrapper 690   struct wrapper
691   { 691   {
692   decltype(handler) h_; 692   decltype(handler) h_;
693   route_task operator()(route_params& p) const 693   route_task operator()(route_params& p) const
694   { 694   {
695   std::cout << "dispatching\n"; 695   std::cout << "dispatching\n";
696   co_return co_await h_(p); 696   co_return co_await h_(p);
697   } 697   }
698   }; 698   };
699   return wrapper{ std::move(handler) }; 699   return wrapper{ std::move(handler) };
700   }); 700   });
701   @endcode 701   @endcode
702   702  
703   @par Example: Chaining Transforms 703   @par Example: Chaining Transforms
704   @code 704   @code
705   auto r1 = base.with_transform( first_transform{} ); 705   auto r1 = base.with_transform( first_transform{} );
706   auto r2 = base.with_transform( second_transform{} ); 706   auto r2 = base.with_transform( second_transform{} );
707   707  
708   // r1 applies first_transform to its handlers 708   // r1 applies first_transform to its handlers
709   // r2 applies second_transform to its handlers 709   // r2 applies second_transform to its handlers
710   // Both share the same routing table 710   // Both share the same routing table
711   @endcode 711   @endcode
712   712  
713   @par Constraints 713   @par Constraints
714   714  
715   `f(handler)` must return a callable `g` where 715   `f(handler)` must return a callable `g` where
716   `g(Params&)` returns @ref route_task. 716   `g(Params&)` returns @ref route_task.
717   717  
718   @param f The handler transform to apply. 718   @param f The handler transform to apply.
719   719  
720   @return A router with the transform applied. 720   @return A router with the transform applied.
721   */ 721   */
722   template<class F> 722   template<class F>
HITCBC 723   3 auto with_transform(F&& f) const -> 723   3 auto with_transform(F&& f) const ->
724   router<P, std::decay_t<F>> 724   router<P, std::decay_t<F>>
725   { 725   {
726   return router<P, std::decay_t<F>>( 726   return router<P, std::decay_t<F>>(
HITCBC 727   3 *this, std::forward<F>(f)); 727   3 *this, std::forward<F>(f));
728   } 728   }
729   729  
730   /** Dispatch a request using a known HTTP method. 730   /** Dispatch a request using a known HTTP method.
731   731  
732   @param verb The HTTP method to match. Must not be 732   @param verb The HTTP method to match. Must not be
733   @ref http::method::unknown. 733   @ref http::method::unknown.
734   734  
735   @param url The full request target used for route matching. 735   @param url The full request target used for route matching.
736   736  
737   @param p The typed params to pass to handlers. 737   @param p The typed params to pass to handlers.
738   738  
739   @return A task yielding the @ref route_result describing 739   @return A task yielding the @ref route_result describing
740   how routing completed. 740   how routing completed.
741   741  
742   @throws std::invalid_argument If @p verb is 742   @throws std::invalid_argument If @p verb is
743   @ref http::method::unknown. 743   @ref http::method::unknown.
744   */ 744   */
745   route_task 745   route_task
HITCBC 746   109 dispatch( 746   109 dispatch(
747   http::method verb, 747   http::method verb,
748   urls::url_view const& url, 748   urls::url_view const& url,
749   P& p) const 749   P& p) const
750   { 750   {
751   return detail::router_base::dispatch( 751   return detail::router_base::dispatch(
HITCBC 752   109 verb, url, static_cast<route_params&>(p)); 752   109 verb, url, static_cast<route_params&>(p));
753   } 753   }
754   754  
755   /** Dispatch a request using a method string. 755   /** Dispatch a request using a method string.
756   756  
757   @param verb The HTTP method string to match. Must not be empty. 757   @param verb The HTTP method string to match. Must not be empty.
758   758  
759   @param url The full request target used for route matching. 759   @param url The full request target used for route matching.
760   760  
761   @param p The typed params to pass to handlers. 761   @param p The typed params to pass to handlers.
762   762  
763   @return A task yielding the @ref route_result describing 763   @return A task yielding the @ref route_result describing
764   how routing completed. 764   how routing completed.
765   765  
766   @throws std::invalid_argument If @p verb is empty. 766   @throws std::invalid_argument If @p verb is empty.
767   */ 767   */
768   route_task 768   route_task
HITCBC 769   6 dispatch( 769   6 dispatch(
770   std::string_view verb, 770   std::string_view verb,
771   urls::url_view const& url, 771   urls::url_view const& url,
772   P& p) const 772   P& p) const
773   { 773   {
774   return detail::router_base::dispatch( 774   return detail::router_base::dispatch(
HITCBC 775   6 verb, url, static_cast<route_params&>(p)); 775   6 verb, url, static_cast<route_params&>(p));
776   } 776   }
777   777  
778   /** Add middleware handlers for a path prefix. 778   /** Add middleware handlers for a path prefix.
779   779  
780   Each handler registered with this function participates in the 780   Each handler registered with this function participates in the
781   routing and error-dispatch process for requests whose path begins 781   routing and error-dispatch process for requests whose path begins
782   with the specified prefix, as described in the @ref router 782   with the specified prefix, as described in the @ref router
783   class documentation. Handlers execute in the order they are added 783   class documentation. Handlers execute in the order they are added
784   and may return @ref route_next to transfer control to the 784   and may return @ref route_next to transfer control to the
785   subsequent handler in the chain. 785   subsequent handler in the chain.
786   786  
787   @par Example 787   @par Example
788   @code 788   @code
789   r.use( "/api", 789   r.use( "/api",
790   []( route_params& p ) 790   []( route_params& p )
791   { 791   {
792   if( ! authenticate( p ) ) 792   if( ! authenticate( p ) )
793   { 793   {
794   p.res.status( 401 ); 794   p.res.status( 401 );
795   p.res.set_body( "Unauthorized" ); 795   p.res.set_body( "Unauthorized" );
796   return route_done; 796   return route_done;
797   } 797   }
798   return route_next; 798   return route_next;
799   }, 799   },
800   []( route_params& p ) 800   []( route_params& p )
801   { 801   {
802   p.res.set_header( "X-Powered-By", "MyServer" ); 802   p.res.set_header( "X-Powered-By", "MyServer" );
803   return route_next; 803   return route_next;
804   } ); 804   } );
805   @endcode 805   @endcode
806   806  
807   @par Preconditions 807   @par Preconditions
808   808  
809   @p pattern must be a valid path prefix; it may be empty to 809   @p pattern must be a valid path prefix; it may be empty to
810   indicate the root scope. 810   indicate the root scope.
811   811  
812   @param pattern The pattern to match. 812   @param pattern The pattern to match.
813   813  
814   @param h1 The first handler to add. 814   @param h1 The first handler to add.
815   815  
816   @param hn Additional handlers to add, invoked after @p h1 in 816   @param hn Additional handlers to add, invoked after @p h1 in
817   registration order. 817   registration order.
818   */ 818   */
819   template<class H1, class... HN> 819   template<class H1, class... HN>
HITCBC 820   113 void use( 820   113 void use(
821   std::string_view pattern, 821   std::string_view pattern,
822   H1&& h1, HN&&... hn) 822   H1&& h1, HN&&... hn)
823   { 823   {
824   // Single sub-router case 824   // Single sub-router case
825   if constexpr(sizeof...(HN) == 0 && is_sub_router<H1>) 825   if constexpr(sizeof...(HN) == 0 && is_sub_router<H1>)
826   { 826   {
827   static_assert(!std::is_lvalue_reference_v<H1>, 827   static_assert(!std::is_lvalue_reference_v<H1>,
828   "pass sub-routers by value or std::move()"); 828   "pass sub-routers by value or std::move()");
HITCBC 829   50 this->inline_router(pattern, 829   50 this->inline_router(pattern,
HITCBC 830   50 std::forward<H1>(h1)); 830   50 std::forward<H1>(h1));
831   } 831   }
832   else 832   else
833   { 833   {
834   static_assert(handler_crvals<H1, HN...>, 834   static_assert(handler_crvals<H1, HN...>,
835   "pass handlers by value or std::move()"); 835   "pass handlers by value or std::move()");
836   static_assert(! handler_check<8, H1, HN...>, 836   static_assert(! handler_check<8, H1, HN...>,
837   "cannot use exception handlers here"); 837   "cannot use exception handlers here");
838   static_assert(handler_check<3, H1, HN...>, 838   static_assert(handler_check<3, H1, HN...>,
839   "invalid handler signature"); 839   "invalid handler signature");
HITCBC 840   63 this->add_middleware(pattern, make_handlers(ht_, 840   63 this->add_middleware(pattern, make_handlers(ht_,
841   std::forward<H1>(h1), std::forward<HN>(hn)...)); 841   std::forward<H1>(h1), std::forward<HN>(hn)...));
842   } 842   }
HITCBC 843   112 } 843   112 }
844   844  
845   /** Add global middleware handlers. 845   /** Add global middleware handlers.
846   846  
847   Each handler registered with this function participates in the 847   Each handler registered with this function participates in the
848   routing and error-dispatch process as described in the 848   routing and error-dispatch process as described in the
849   @ref router class documentation. Handlers execute in the 849   @ref router class documentation. Handlers execute in the
850   order they are added and may return @ref route_next to transfer 850   order they are added and may return @ref route_next to transfer
851   control to the next handler in the chain. 851   control to the next handler in the chain.
852   852  
853   This is equivalent to writing: 853   This is equivalent to writing:
854   @code 854   @code
855   use( "/", h1, hn... ); 855   use( "/", h1, hn... );
856   @endcode 856   @endcode
857   857  
858   @par Example 858   @par Example
859   @code 859   @code
860   r.use( 860   r.use(
861   []( Params& p ) 861   []( Params& p )
862   { 862   {
863   p.res.erase( "X-Powered-By" ); 863   p.res.erase( "X-Powered-By" );
864   return route_next; 864   return route_next;
865   } ); 865   } );
866   @endcode 866   @endcode
867   867  
868   @par Constraints 868   @par Constraints
869   869  
870   @p h1 must not be convertible to @ref std::string_view. 870   @p h1 must not be convertible to @ref std::string_view.
871   871  
872   @param h1 The first handler to add. 872   @param h1 The first handler to add.
873   873  
874   @param hn Additional handlers to add, invoked after @p h1 in 874   @param hn Additional handlers to add, invoked after @p h1 in
875   registration order. 875   registration order.
876   */ 876   */
877   template<class H1, class... HN> 877   template<class H1, class... HN>
HITCBC 878   32 void use(H1&& h1, HN&&... hn) 878   32 void use(H1&& h1, HN&&... hn)
879   requires (!std::convertible_to<H1, std::string_view>) 879   requires (!std::convertible_to<H1, std::string_view>)
880   { 880   {
HITCBC 881   32 use(std::string_view(), 881   32 use(std::string_view(),
882   std::forward<H1>(h1), std::forward<HN>(hn)...); 882   std::forward<H1>(h1), std::forward<HN>(hn)...);
HITCBC 883   32 } 883   32 }
884   884  
885   /** Add exception handlers for a route pattern. 885   /** Add exception handlers for a route pattern.
886   886  
887   Registers one or more exception handlers that will be invoked 887   Registers one or more exception handlers that will be invoked
888   when an exception is thrown during request processing for routes 888   when an exception is thrown during request processing for routes
889   matching the specified pattern. 889   matching the specified pattern.
890   890  
891   Handlers are invoked in the order provided until one handles 891   Handlers are invoked in the order provided until one handles
892   the exception. 892   the exception.
893   893  
894   @par Example 894   @par Example
895   @code 895   @code
896   app.except( "/api*", 896   app.except( "/api*",
897   []( route_params& p, std::exception const& ex ) 897   []( route_params& p, std::exception const& ex )
898   { 898   {
899   p.res.set_status( 500 ); 899   p.res.set_status( 500 );
900   return route_done; 900   return route_done;
901   } ); 901   } );
902   @endcode 902   @endcode
903   903  
904   @param pattern The route pattern to match, or empty to match 904   @param pattern The route pattern to match, or empty to match
905   all routes. 905   all routes.
906   906  
907   @param h1 The first exception handler. 907   @param h1 The first exception handler.
908   908  
909   @param hn Additional exception handlers. 909   @param hn Additional exception handlers.
910   */ 910   */
911   template<class H1, class... HN> 911   template<class H1, class... HN>
HITCBC 912   2 void except( 912   2 void except(
913   std::string_view pattern, 913   std::string_view pattern,
914   H1&& h1, HN&&... hn) 914   H1&& h1, HN&&... hn)
915   { 915   {
916   static_assert(handler_crvals<H1, HN...>, 916   static_assert(handler_crvals<H1, HN...>,
917   "pass handlers by value or std::move()"); 917   "pass handlers by value or std::move()");
918   static_assert(handler_check<8, H1, HN...>, 918   static_assert(handler_check<8, H1, HN...>,
919   "only exception handlers are allowed here"); 919   "only exception handlers are allowed here");
HITCBC 920   2 this->add_middleware(pattern, make_handlers(ht_, 920   2 this->add_middleware(pattern, make_handlers(ht_,
921   std::forward<H1>(h1), std::forward<HN>(hn)...)); 921   std::forward<H1>(h1), std::forward<HN>(hn)...));
HITCBC 922   2 } 922   2 }
923   923  
924   /** Add global exception handlers. 924   /** Add global exception handlers.
925   925  
926   Registers one or more exception handlers that will be invoked 926   Registers one or more exception handlers that will be invoked
927   when an exception is thrown during request processing for any 927   when an exception is thrown during request processing for any
928   route. 928   route.
929   929  
930   Equivalent to calling `except( "", h1, hn... )`. 930   Equivalent to calling `except( "", h1, hn... )`.
931   931  
932   @par Example 932   @par Example
933   @code 933   @code
934   app.except( 934   app.except(
935   []( route_params& p, std::exception const& ex ) 935   []( route_params& p, std::exception const& ex )
936   { 936   {
937   p.res.set_status( 500 ); 937   p.res.set_status( 500 );
938   return route_done; 938   return route_done;
939   } ); 939   } );
940   @endcode 940   @endcode
941   941  
942   @param h1 The first exception handler. 942   @param h1 The first exception handler.
943   943  
944   @param hn Additional exception handlers. 944   @param hn Additional exception handlers.
945   */ 945   */
946   template<class H1, class... HN> 946   template<class H1, class... HN>
HITCBC 947   2 void except(H1&& h1, HN&&... hn) 947   2 void except(H1&& h1, HN&&... hn)
948   requires (!std::convertible_to<H1, std::string_view>) 948   requires (!std::convertible_to<H1, std::string_view>)
949   { 949   {
950   static_assert(handler_crvals<H1, HN...>, 950   static_assert(handler_crvals<H1, HN...>,
951   "pass handlers by value or std::move()"); 951   "pass handlers by value or std::move()");
952   static_assert(handler_check<8, H1, HN...>, 952   static_assert(handler_check<8, H1, HN...>,
953   "only exception handlers are allowed here"); 953   "only exception handlers are allowed here");
HITCBC 954   2 except(std::string_view(), 954   2 except(std::string_view(),
955   std::forward<H1>(h1), std::forward<HN>(hn)...); 955   std::forward<H1>(h1), std::forward<HN>(hn)...);
HITCBC 956   2 } 956   2 }
957   957  
958   /** Add handlers for all HTTP methods matching a path pattern. 958   /** Add handlers for all HTTP methods matching a path pattern.
959   959  
960   This registers regular handlers for the specified path pattern, 960   This registers regular handlers for the specified path pattern,
961   participating in dispatch as described in the @ref router 961   participating in dispatch as described in the @ref router
962   class documentation. Handlers run when the route matches, 962   class documentation. Handlers run when the route matches,
963   regardless of HTTP method, and execute in registration order. 963   regardless of HTTP method, and execute in registration order.
964   Error handlers and routers cannot be passed here. A new route 964   Error handlers and routers cannot be passed here. A new route
965   object is created even if the pattern already exists. 965   object is created even if the pattern already exists.
966   966  
967   @par Example 967   @par Example
968   @code 968   @code
969   r.route( "/status" ) 969   r.route( "/status" )
970   .add( method::head, check_headers ) 970   .add( method::head, check_headers )
971   .add( method::get, send_status ) 971   .add( method::get, send_status )
972   .all( log_access ); 972   .all( log_access );
973   @endcode 973   @endcode
974   974  
975   @par Preconditions 975   @par Preconditions
976   976  
977   @p pattern must be a valid path pattern; it must not be empty. 977   @p pattern must be a valid path pattern; it must not be empty.
978   978  
979   @param pattern The path pattern to match. 979   @param pattern The path pattern to match.
980   980  
981   @param h1 The first handler to add. 981   @param h1 The first handler to add.
982   982  
983   @param hn Additional handlers to add, invoked after @p h1 in 983   @param hn Additional handlers to add, invoked after @p h1 in
984   registration order. 984   registration order.
985   */ 985   */
986   template<class H1, class... HN> 986   template<class H1, class... HN>
HITCBC 987   7 void all( 987   7 void all(
988   std::string_view pattern, 988   std::string_view pattern,
989   H1&& h1, HN&&... hn) 989   H1&& h1, HN&&... hn)
990   { 990   {
991   static_assert(handler_crvals<H1, HN...>, 991   static_assert(handler_crvals<H1, HN...>,
992   "pass handlers by value or std::move()"); 992   "pass handlers by value or std::move()");
993   static_assert(handler_check<1, H1, HN...>, 993   static_assert(handler_check<1, H1, HN...>,
994   "only normal route handlers are allowed here"); 994   "only normal route handlers are allowed here");
HITCBC 995   7 this->route(pattern).all( 995   7 this->route(pattern).all(
996   std::forward<H1>(h1), std::forward<HN>(hn)...); 996   std::forward<H1>(h1), std::forward<HN>(hn)...);
HITCBC 997   7 } 997   7 }
998   998  
999   /** Add route handlers for a method and pattern. 999   /** Add route handlers for a method and pattern.
1000   1000  
1001   This registers regular handlers for the specified HTTP verb and 1001   This registers regular handlers for the specified HTTP verb and
1002   path pattern, participating in dispatch as described in the 1002   path pattern, participating in dispatch as described in the
1003   @ref router class documentation. Error handlers and 1003   @ref router class documentation. Error handlers and
1004   routers cannot be passed here. 1004   routers cannot be passed here.
1005   1005  
1006   @param verb The known HTTP method to match. 1006   @param verb The known HTTP method to match.
1007   1007  
1008   @param pattern The path pattern to match. 1008   @param pattern The path pattern to match.
1009   1009  
1010   @param h1 The first handler to add. 1010   @param h1 The first handler to add.
1011   1011  
1012   @param hn Additional handlers to add, invoked after @p h1 in 1012   @param hn Additional handlers to add, invoked after @p h1 in
1013   registration order. 1013   registration order.
1014   */ 1014   */
1015   template<class H1, class... HN> 1015   template<class H1, class... HN>
HITCBC 1016   61 void add( 1016   61 void add(
1017   http::method verb, 1017   http::method verb,
1018   std::string_view pattern, 1018   std::string_view pattern,
1019   H1&& h1, HN&&... hn) 1019   H1&& h1, HN&&... hn)
1020   { 1020   {
1021   static_assert(handler_crvals<H1, HN...>, 1021   static_assert(handler_crvals<H1, HN...>,
1022   "pass handlers by value or std::move()"); 1022   "pass handlers by value or std::move()");
1023   static_assert(handler_check<1, H1, HN...>, 1023   static_assert(handler_check<1, H1, HN...>,
1024   "only normal route handlers are allowed here"); 1024   "only normal route handlers are allowed here");
HITCBC 1025   61 this->route(pattern).add(verb, 1025   61 this->route(pattern).add(verb,
1026   std::forward<H1>(h1), std::forward<HN>(hn)...); 1026   std::forward<H1>(h1), std::forward<HN>(hn)...);
HITCBC 1027   49 } 1027   49 }
1028   1028  
1029   /** Add route handlers for a method string and pattern. 1029   /** Add route handlers for a method string and pattern.
1030   1030  
1031   This registers regular handlers for the specified HTTP verb and 1031   This registers regular handlers for the specified HTTP verb and
1032   path pattern, participating in dispatch as described in the 1032   path pattern, participating in dispatch as described in the
1033   @ref router class documentation. Error handlers and 1033   @ref router class documentation. Error handlers and
1034   routers cannot be passed here. 1034   routers cannot be passed here.
1035   1035  
1036   @param verb The HTTP method string to match. 1036   @param verb The HTTP method string to match.
1037   1037  
1038   @param pattern The path pattern to match. 1038   @param pattern The path pattern to match.
1039   1039  
1040   @param h1 The first handler to add. 1040   @param h1 The first handler to add.
1041   1041  
1042   @param hn Additional handlers to add, invoked after @p h1 in 1042   @param hn Additional handlers to add, invoked after @p h1 in
1043   registration order. 1043   registration order.
1044   */ 1044   */
1045   template<class H1, class... HN> 1045   template<class H1, class... HN>
HITCBC 1046   2 void add( 1046   2 void add(
1047   std::string_view verb, 1047   std::string_view verb,
1048   std::string_view pattern, 1048   std::string_view pattern,
1049   H1&& h1, HN&&... hn) 1049   H1&& h1, HN&&... hn)
1050   { 1050   {
1051   static_assert(handler_crvals<H1, HN...>, 1051   static_assert(handler_crvals<H1, HN...>,
1052   "pass handlers by value or std::move()"); 1052   "pass handlers by value or std::move()");
1053   static_assert(handler_check<1, H1, HN...>, 1053   static_assert(handler_check<1, H1, HN...>,
1054   "only normal route handlers are allowed here"); 1054   "only normal route handlers are allowed here");
HITCBC 1055   2 this->route(pattern).add(verb, 1055   2 this->route(pattern).add(verb,
1056   std::forward<H1>(h1), std::forward<HN>(hn)...); 1056   std::forward<H1>(h1), std::forward<HN>(hn)...);
HITCBC 1057   2 } 1057   2 }
1058   1058  
1059   /** Return a fluent route for the specified path pattern. 1059   /** Return a fluent route for the specified path pattern.
1060   1060  
1061   Adds a new route to the router for the given pattern. 1061   Adds a new route to the router for the given pattern.
1062   A new route object is always created, even if another 1062   A new route object is always created, even if another
1063   route with the same pattern already exists. The returned 1063   route with the same pattern already exists. The returned
1064   @ref fluent_route reference allows method-specific handler 1064   @ref fluent_route reference allows method-specific handler
1065   registration (such as GET or POST) or catch-all handlers 1065   registration (such as GET or POST) or catch-all handlers
1066   with @ref fluent_route::all. 1066   with @ref fluent_route::all.
1067   1067  
1068   @param pattern The path expression to match against request 1068   @param pattern The path expression to match against request
1069   targets. This may include parameters or wildcards following 1069   targets. This may include parameters or wildcards following
1070   the router's pattern syntax. May not be empty. 1070   the router's pattern syntax. May not be empty.
1071   1071  
1072   @return A fluent route interface for chaining handler 1072   @return A fluent route interface for chaining handler
1073   registrations. 1073   registrations.
1074   */ 1074   */
1075   auto 1075   auto
HITCBC 1076   77 route( 1076   77 route(
1077   std::string_view pattern) -> fluent_route 1077   std::string_view pattern) -> fluent_route
1078   { 1078   {
HITCBC 1079   77 return fluent_route(*this, pattern); 1079   77 return fluent_route(*this, pattern);
1080   } 1080   }
1081   1081  
1082   /** Set the handler for automatic OPTIONS responses. 1082   /** Set the handler for automatic OPTIONS responses.
1083   1083  
1084   When an OPTIONS request matches a route but no explicit OPTIONS 1084   When an OPTIONS request matches a route but no explicit OPTIONS
1085   handler is registered, this handler is invoked with the pre-built 1085   handler is registered, this handler is invoked with the pre-built
1086   Allow header value. This follows Express.js semantics where 1086   Allow header value. This follows Express.js semantics where
1087   explicit OPTIONS handlers take priority. 1087   explicit OPTIONS handlers take priority.
1088   1088  
1089   @param h A callable with signature `route_task(P&, std::string_view)` 1089   @param h A callable with signature `route_task(P&, std::string_view)`
1090   where the string_view contains the pre-built Allow header value. 1090   where the string_view contains the pre-built Allow header value.
1091   */ 1091   */
1092   template<class H> 1092   template<class H>
HITCBC 1093   4 void set_options_handler(H&& h) 1093   4 void set_options_handler(H&& h)
1094   { 1094   {
1095   static_assert( 1095   static_assert(
1096   std::is_invocable_r_v<route_task, const std::decay_t<H>&, P&, std::string_view>, 1096   std::is_invocable_r_v<route_task, const std::decay_t<H>&, P&, std::string_view>,
1097   "Handler must have signature: route_task(P&, std::string_view)"); 1097   "Handler must have signature: route_task(P&, std::string_view)");
HITCBC 1098   4 this->set_options_handler_impl( 1098   4 this->set_options_handler_impl(
1099   std::make_unique<options_handler_impl<H>>( 1099   std::make_unique<options_handler_impl<H>>(
1100   std::forward<H>(h))); 1100   std::forward<H>(h)));
HITCBC 1101   4 } 1101   4 }
1102   }; 1102   };
1103   1103  
1104   template<class P, class HT> 1104   template<class P, class HT>
1105   class router<P, HT>:: 1105   class router<P, HT>::
1106   fluent_route 1106   fluent_route
1107   { 1107   {
1108   public: 1108   public:
1109   fluent_route(fluent_route const&) = default; 1109   fluent_route(fluent_route const&) = default;
1110   1110  
1111   /** Add handlers that apply to all HTTP methods. 1111   /** Add handlers that apply to all HTTP methods.
1112   1112  
1113   This registers regular handlers that run for any request matching 1113   This registers regular handlers that run for any request matching
1114   the route's pattern, regardless of HTTP method. Handlers are 1114   the route's pattern, regardless of HTTP method. Handlers are
1115   appended to the route's handler sequence and are invoked in 1115   appended to the route's handler sequence and are invoked in
1116   registration order whenever a preceding handler returns 1116   registration order whenever a preceding handler returns
1117   @ref route_next. Error handlers and routers cannot be passed here. 1117   @ref route_next. Error handlers and routers cannot be passed here.
1118   1118  
1119   This function returns a @ref fluent_route, allowing additional 1119   This function returns a @ref fluent_route, allowing additional
1120   method registrations to be chained. For example: 1120   method registrations to be chained. For example:
1121   @code 1121   @code
1122   r.route( "/resource" ) 1122   r.route( "/resource" )
1123   .all( log_request ) 1123   .all( log_request )
1124   .add( method::get, show_resource ) 1124   .add( method::get, show_resource )
1125   .add( method::post, update_resource ); 1125   .add( method::post, update_resource );
1126   @endcode 1126   @endcode
1127   1127  
1128   @param h1 The first handler to add. 1128   @param h1 The first handler to add.
1129   1129  
1130   @param hn Additional handlers to add, invoked after @p h1 in 1130   @param hn Additional handlers to add, invoked after @p h1 in
1131   registration order. 1131   registration order.
1132   1132  
1133   @return A reference to `*this` for chained registrations. 1133   @return A reference to `*this` for chained registrations.
1134   */ 1134   */
1135   template<class H1, class... HN> 1135   template<class H1, class... HN>
HITCBC 1136   8 auto all( 1136   8 auto all(
1137   H1&& h1, HN&&... hn) -> 1137   H1&& h1, HN&&... hn) ->
1138   fluent_route 1138   fluent_route
1139   { 1139   {
1140   static_assert(handler_check<1, H1, HN...>); 1140   static_assert(handler_check<1, H1, HN...>);
HITCBC 1141   16 owner_.add_to_route(route_idx_, std::string_view{}, 1141   16 owner_.add_to_route(route_idx_, std::string_view{},
HITCBC 1142   8 owner_.make_handlers(owner_.ht_, 1142   8 owner_.make_handlers(owner_.ht_,
1143   std::forward<H1>(h1), std::forward<HN>(hn)...)); 1143   std::forward<H1>(h1), std::forward<HN>(hn)...));
HITCBC 1144   8 return *this; 1144   8 return *this;
1145   } 1145   }
1146   1146  
1147   /** Add handlers for a specific HTTP method. 1147   /** Add handlers for a specific HTTP method.
1148   1148  
1149   This registers regular handlers for the given method on the 1149   This registers regular handlers for the given method on the
1150   current route, participating in dispatch as described in the 1150   current route, participating in dispatch as described in the
1151   @ref router class documentation. Handlers are appended 1151   @ref router class documentation. Handlers are appended
1152   to the route's handler sequence and invoked in registration 1152   to the route's handler sequence and invoked in registration
1153   order whenever a preceding handler returns @ref route_next. 1153   order whenever a preceding handler returns @ref route_next.
1154   Error handlers and routers cannot be passed here. 1154   Error handlers and routers cannot be passed here.
1155   1155  
1156   @param verb The HTTP method to match. 1156   @param verb The HTTP method to match.
1157   1157  
1158   @param h1 The first handler to add. 1158   @param h1 The first handler to add.
1159   1159  
1160   @param hn Additional handlers to add, invoked after @p h1 in 1160   @param hn Additional handlers to add, invoked after @p h1 in
1161   registration order. 1161   registration order.
1162   1162  
1163   @return A reference to `*this` for chained registrations. 1163   @return A reference to `*this` for chained registrations.
1164   */ 1164   */
1165   template<class H1, class... HN> 1165   template<class H1, class... HN>
HITCBC 1166   58 auto add( 1166   58 auto add(
1167   http::method verb, 1167   http::method verb,
1168   H1&& h1, HN&&... hn) -> 1168   H1&& h1, HN&&... hn) ->
1169   fluent_route 1169   fluent_route
1170   { 1170   {
1171   static_assert(handler_check<1, H1, HN...>); 1171   static_assert(handler_check<1, H1, HN...>);
HITCBC 1172   116 owner_.add_to_route(route_idx_, verb, 1172   116 owner_.add_to_route(route_idx_, verb,
HITCBC 1173   58 owner_.make_handlers(owner_.ht_, 1173   58 owner_.make_handlers(owner_.ht_,
1174   std::forward<H1>(h1), std::forward<HN>(hn)...)); 1174   std::forward<H1>(h1), std::forward<HN>(hn)...));
HITCBC 1175   58 return *this; 1175   58 return *this;
1176   } 1176   }
1177   1177  
1178   /** Add handlers for a method string. 1178   /** Add handlers for a method string.
1179   1179  
1180   This registers regular handlers for the given HTTP method string 1180   This registers regular handlers for the given HTTP method string
1181   on the current route, participating in dispatch as described in 1181   on the current route, participating in dispatch as described in
1182   the @ref router class documentation. This overload is 1182   the @ref router class documentation. This overload is
1183   intended for methods not represented by @ref http::method. 1183   intended for methods not represented by @ref http::method.
1184   Handlers are appended to the route's handler sequence and invoked 1184   Handlers are appended to the route's handler sequence and invoked
1185   in registration order whenever a preceding handler returns 1185   in registration order whenever a preceding handler returns
1186   @ref route_next. Error handlers and routers cannot be passed here. 1186   @ref route_next. Error handlers and routers cannot be passed here.
1187   1187  
1188   @param verb The HTTP method string to match. 1188   @param verb The HTTP method string to match.
1189   1189  
1190   @param h1 The first handler to add. 1190   @param h1 The first handler to add.
1191   1191  
1192   @param hn Additional handlers to add, invoked after @p h1 in 1192   @param hn Additional handlers to add, invoked after @p h1 in
1193   registration order. 1193   registration order.
1194   1194  
1195   @return A reference to `*this` for chained registrations. 1195   @return A reference to `*this` for chained registrations.
1196   */ 1196   */
1197   template<class H1, class... HN> 1197   template<class H1, class... HN>
HITCBC 1198   2 auto add( 1198   2 auto add(
1199   std::string_view verb, 1199   std::string_view verb,
1200   H1&& h1, HN&&... hn) -> 1200   H1&& h1, HN&&... hn) ->
1201   fluent_route 1201   fluent_route
1202   { 1202   {
1203   static_assert(handler_check<1, H1, HN...>); 1203   static_assert(handler_check<1, H1, HN...>);
HITCBC 1204   4 owner_.add_to_route(route_idx_, verb, 1204   4 owner_.add_to_route(route_idx_, verb,
HITCBC 1205   2 owner_.make_handlers(owner_.ht_, 1205   2 owner_.make_handlers(owner_.ht_,
1206   std::forward<H1>(h1), std::forward<HN>(hn)...)); 1206   std::forward<H1>(h1), std::forward<HN>(hn)...));
HITCBC 1207   2 return *this; 1207   2 return *this;
1208   } 1208   }
1209   1209  
1210   private: 1210   private:
1211   friend class router; 1211   friend class router;
HITCBC 1212   77 fluent_route( 1212   77 fluent_route(
1213   router& owner, 1213   router& owner,
1214   std::string_view pattern) 1214   std::string_view pattern)
HITCBC 1215   77 : route_idx_(owner.new_route(pattern)) 1215   77 : route_idx_(owner.new_route(pattern))
HITCBC 1216   65 , owner_(owner) 1216   65 , owner_(owner)
1217   { 1217   {
HITCBC 1218   65 } 1218   65 }
1219   1219  
1220   std::size_t route_idx_; 1220   std::size_t route_idx_;
1221   router& owner_; 1221   router& owner_;
1222   }; 1222   };
1223   1223  
1224   } // http 1224   } // http
1225   } // boost 1225   } // boost
1226   1226  
1227   #endif 1227   #endif