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_ROUTE_HANDLER_HPP
10  
#ifndef BOOST_HTTP_SERVER_ROUTE_HANDLER_HPP
11  
#define BOOST_HTTP_SERVER_ROUTE_HANDLER_HPP
11  
#define BOOST_HTTP_SERVER_ROUTE_HANDLER_HPP
12  

12  

13  
#include <boost/http/detail/config.hpp>
13  
#include <boost/http/detail/config.hpp>
14  
#include <boost/http/method.hpp>
14  
#include <boost/http/method.hpp>
15  
#include <boost/http/detail/except.hpp>
15  
#include <boost/http/detail/except.hpp>
16  
#include <boost/http/datastore.hpp>
16  
#include <boost/http/datastore.hpp>
17  
#include <boost/http/request.hpp>
17  
#include <boost/http/request.hpp>
18  
#include <boost/http/response.hpp>
18  
#include <boost/http/response.hpp>
19  
#include <boost/core/detail/string_view.hpp>
19  
#include <boost/core/detail/string_view.hpp>
20  
#include <boost/capy/buffers.hpp>
20  
#include <boost/capy/buffers.hpp>
21  
#include <boost/capy/buffers/make_buffer.hpp>
21  
#include <boost/capy/buffers/make_buffer.hpp>
22  
#include <boost/capy/io_result.hpp>
22  
#include <boost/capy/io_result.hpp>
23  
#include <boost/capy/io_task.hpp>
23  
#include <boost/capy/io_task.hpp>
24  
#include <boost/capy/task.hpp>
24  
#include <boost/capy/task.hpp>
25  
#include <boost/capy/write.hpp>
25  
#include <boost/capy/write.hpp>
26  
#include <boost/capy/io/any_buffer_source.hpp>
26  
#include <boost/capy/io/any_buffer_source.hpp>
27  
#include <boost/capy/io/any_buffer_sink.hpp>
27  
#include <boost/capy/io/any_buffer_sink.hpp>
28  
#include <boost/url/url_view.hpp>
28  
#include <boost/url/url_view.hpp>
29  
#include <boost/system/error_category.hpp>
29  
#include <boost/system/error_category.hpp>
30  
#include <boost/system/error_code.hpp>
30  
#include <boost/system/error_code.hpp>
31  
#include <concepts>
31  
#include <concepts>
32  
#include <exception>
32  
#include <exception>
33  
#include <memory>
33  
#include <memory>
34  
#include <span>
34  
#include <span>
35  
#include <string>
35  
#include <string>
36  
#include <type_traits>
36  
#include <type_traits>
37  
#include <utility>
37  
#include <utility>
38  
#include <vector>
38  
#include <vector>
39  

39  

40  
namespace boost {
40  
namespace boost {
41  
namespace http {
41  
namespace http {
42  

42  

43  
/** Directive values for route handler results.
43  
/** Directive values for route handler results.
44  

44  

45  
    These values indicate how the router should proceed
45  
    These values indicate how the router should proceed
46  
    after a handler completes. Handlers return one of
46  
    after a handler completes. Handlers return one of
47  
    the predefined constants (@ref route_done, @ref route_next,
47  
    the predefined constants (@ref route_done, @ref route_next,
48  
    @ref route_next_route, @ref route_close) or an error code.
48  
    @ref route_next_route, @ref route_close) or an error code.
49  

49  

50  
    @see route_result, route_task
50  
    @see route_result, route_task
51  
*/
51  
*/
52  
enum class route_what
52  
enum class route_what
53  
{
53  
{
54  
    /// Handler completed successfully, response was sent
54  
    /// Handler completed successfully, response was sent
55  
    done,
55  
    done,
56  

56  

57  
    /// Handler declined, try next handler in the route
57  
    /// Handler declined, try next handler in the route
58  
    next,
58  
    next,
59  

59  

60  
    /// Handler declined, skip to next matching route
60  
    /// Handler declined, skip to next matching route
61  
    next_route,
61  
    next_route,
62  

62  

63  
    /// Handler requests connection closure
63  
    /// Handler requests connection closure
64  
    close,
64  
    close,
65  

65  

66  
    /// Handler encountered an error
66  
    /// Handler encountered an error
67  
    error
67  
    error
68  
};
68  
};
69  

69  

70  
//------------------------------------------------
70  
//------------------------------------------------
71  

71  

72  
/** The result type returned by route handlers.
72  
/** The result type returned by route handlers.
73  

73  

74  
    This class represents the outcome of a route handler.
74  
    This class represents the outcome of a route handler.
75  
    Handlers return this type to indicate how the router
75  
    Handlers return this type to indicate how the router
76  
    should proceed. Construct from a directive constant
76  
    should proceed. Construct from a directive constant
77  
    or an error code:
77  
    or an error code:
78  

78  

79  
    @code
79  
    @code
80  
    route_task my_handler(route_params& p)
80  
    route_task my_handler(route_params& p)
81  
    {
81  
    {
82  
        if(! authorized(p))
82  
        if(! authorized(p))
83  
            co_return route_next;        // try next handler
83  
            co_return route_next;        // try next handler
84  

84  

85  
        if(auto ec = process(p); ec)
85  
        if(auto ec = process(p); ec)
86  
            co_return ec;                // return error
86  
            co_return ec;                // return error
87  

87  

88  
        co_return route_done;            // success
88  
        co_return route_done;            // success
89  
    }
89  
    }
90  
    @endcode
90  
    @endcode
91  

91  

92  
    @par Checking Results
92  
    @par Checking Results
93  

93  

94  
    Use @ref what() to determine the directive, and
94  
    Use @ref what() to determine the directive, and
95  
    @ref error() to retrieve any error code:
95  
    @ref error() to retrieve any error code:
96  

96  

97  
    @code
97  
    @code
98  
    route_result rv = co_await handler(p);
98  
    route_result rv = co_await handler(p);
99  
    if(rv.what() == route_what::error)
99  
    if(rv.what() == route_what::error)
100  
        handle_error(rv.error());
100  
        handle_error(rv.error());
101  
    @endcode
101  
    @endcode
102  

102  

103  
    @see route_task, route_what, route_done, route_next
103  
    @see route_task, route_what, route_done, route_next
104  
*/
104  
*/
105  
class BOOST_HTTP_DECL
105  
class BOOST_HTTP_DECL
106  
    route_result
106  
    route_result
107  
{
107  
{
108  
    system::error_code ec_;
108  
    system::error_code ec_;
109  

109  

110  
    template<route_what T>
110  
    template<route_what T>
111  
    struct what_t {};
111  
    struct what_t {};
112  

112  

113  
    route_result(system::error_code ec);
113  
    route_result(system::error_code ec);
114  
    void set(route_what w);
114  
    void set(route_what w);
115  

115  

116  
public:
116  
public:
117  
    route_result() = default;
117  
    route_result() = default;
118  

118  

119  
    /** Construct from a directive constant.
119  
    /** Construct from a directive constant.
120  

120  

121  
        This constructor allows implicit conversion from
121  
        This constructor allows implicit conversion from
122  
        the predefined constants (@ref route_done, @ref route_next,
122  
        the predefined constants (@ref route_done, @ref route_next,
123  
        @ref route_next_route, @ref route_close).
123  
        @ref route_next_route, @ref route_close).
124  

124  

125  
        @code
125  
        @code
126  
        route_task handler(route_params& p)
126  
        route_task handler(route_params& p)
127  
        {
127  
        {
128  
            co_return route_done;  // implicitly converts
128  
            co_return route_done;  // implicitly converts
129  
        }
129  
        }
130  
        @endcode
130  
        @endcode
131  
    */
131  
    */
132  
    template<route_what W>
132  
    template<route_what W>
133  
    route_result(what_t<W>)
133  
    route_result(what_t<W>)
134  
    {
134  
    {
135  
        static_assert(W != route_what::error);
135  
        static_assert(W != route_what::error);
136  
        set(W);
136  
        set(W);
137  
    }
137  
    }
138  

138  

139  
    /** Return the directive for this result.
139  
    /** Return the directive for this result.
140  

140  

141  
        Call this to determine how the router should proceed:
141  
        Call this to determine how the router should proceed:
142  

142  

143  
        @code
143  
        @code
144  
        route_result rv = co_await handler(p);
144  
        route_result rv = co_await handler(p);
145  
        switch(rv.what())
145  
        switch(rv.what())
146  
        {
146  
        {
147  
        case route_what::done:
147  
        case route_what::done:
148  
            // response sent, done with request
148  
            // response sent, done with request
149  
            break;
149  
            break;
150  
        case route_what::next:
150  
        case route_what::next:
151  
            // try next handler
151  
            // try next handler
152  
            break;
152  
            break;
153  
        case route_what::error:
153  
        case route_what::error:
154  
            log_error(rv.error());
154  
            log_error(rv.error());
155  
            break;
155  
            break;
156  
        }
156  
        }
157  
        @endcode
157  
        @endcode
158  

158  

159  
        @return The directive value.
159  
        @return The directive value.
160  
    */
160  
    */
161  
    auto
161  
    auto
162  
    what() const noexcept ->
162  
    what() const noexcept ->
163  
        route_what;
163  
        route_what;
164  

164  

165  
    /** Return the error code, if any.
165  
    /** Return the error code, if any.
166  

166  

167  
        If @ref what() returns `route_what::error`, this
167  
        If @ref what() returns `route_what::error`, this
168  
        returns the underlying error code. Otherwise returns
168  
        returns the underlying error code. Otherwise returns
169  
        a default-constructed (non-failing) error code.
169  
        a default-constructed (non-failing) error code.
170  

170  

171  
        @return The error code, or a non-failing code.
171  
        @return The error code, or a non-failing code.
172  
    */
172  
    */
173  
    auto
173  
    auto
174  
    error() const noexcept ->
174  
    error() const noexcept ->
175  
        system::error_code;
175  
        system::error_code;
176  

176  

177  
    /** Return true if the result indicates an error.
177  
    /** Return true if the result indicates an error.
178  

178  

179  
        @return `true` if @ref what() equals `route_what::error`.
179  
        @return `true` if @ref what() equals `route_what::error`.
180  
    */
180  
    */
181  
    bool failed() const noexcept
181  
    bool failed() const noexcept
182  
    {
182  
    {
183  
        return what() == route_what::error;
183  
        return what() == route_what::error;
184  
    }
184  
    }
185  

185  

186  
    static constexpr route_result::what_t<route_what::done> route_done{};
186  
    static constexpr route_result::what_t<route_what::done> route_done{};
187  
    static constexpr route_result::what_t<route_what::next> route_next{};
187  
    static constexpr route_result::what_t<route_what::next> route_next{};
188  
    static constexpr route_result::what_t<route_what::next_route> route_next_route{};
188  
    static constexpr route_result::what_t<route_what::next_route> route_next_route{};
189  
    static constexpr route_result::what_t<route_what::close> route_close{};
189  
    static constexpr route_result::what_t<route_what::close> route_close{};
190  
    friend route_result route_error(system::error_code ec) noexcept;
190  
    friend route_result route_error(system::error_code ec) noexcept;
191  

191  

192  
    template<class E>
192  
    template<class E>
193  
    friend auto route_error(E e) noexcept ->
193  
    friend auto route_error(E e) noexcept ->
194  
        std::enable_if_t<
194  
        std::enable_if_t<
195  
            system::is_error_code_enum<E>::value,
195  
            system::is_error_code_enum<E>::value,
196  
            route_result>;
196  
            route_result>;
197  
};
197  
};
198  

198  

199  
//------------------------------------------------
199  
//------------------------------------------------
200  

200  

201  
/** Handler completed successfully.
201  
/** Handler completed successfully.
202  

202  

203  
    Return this from a handler to indicate the response
203  
    Return this from a handler to indicate the response
204  
    was sent and the request is complete:
204  
    was sent and the request is complete:
205  

205  

206  
    @code
206  
    @code
207  
    route_task handler(route_params& p)
207  
    route_task handler(route_params& p)
208  
    {
208  
    {
209  
        p.res.set(field::content_type, "text/plain");
209  
        p.res.set(field::content_type, "text/plain");
210  
        co_await p.send("Hello, World!");
210  
        co_await p.send("Hello, World!");
211  
        co_return route_done;
211  
        co_return route_done;
212  
    }
212  
    }
213  
    @endcode
213  
    @endcode
214  
*/
214  
*/
215  
inline constexpr decltype(auto) route_done = route_result::route_done;
215  
inline constexpr decltype(auto) route_done = route_result::route_done;
216  

216  

217  
/** Handler declined, try next handler.
217  
/** Handler declined, try next handler.
218  

218  

219  
    Return this from a handler to decline processing
219  
    Return this from a handler to decline processing
220  
    and allow the next handler in the route to try:
220  
    and allow the next handler in the route to try:
221  

221  

222  
    @code
222  
    @code
223  
    route_task auth_handler(route_params& p)
223  
    route_task auth_handler(route_params& p)
224  
    {
224  
    {
225  
        if(! p.req.exists(field::authorization))
225  
        if(! p.req.exists(field::authorization))
226  
            co_return route_next;  // let another handler try
226  
            co_return route_next;  // let another handler try
227  

227  

228  
        // process authenticated request...
228  
        // process authenticated request...
229  
        co_return route_done;
229  
        co_return route_done;
230  
    }
230  
    }
231  
    @endcode
231  
    @endcode
232  
*/
232  
*/
233  
inline constexpr decltype(auto) route_next = route_result::route_next;
233  
inline constexpr decltype(auto) route_next = route_result::route_next;
234  

234  

235  
/** Handler declined, skip to next route.
235  
/** Handler declined, skip to next route.
236  

236  

237  
    Return this from a handler to skip all remaining
237  
    Return this from a handler to skip all remaining
238  
    handlers in the current route and proceed to the
238  
    handlers in the current route and proceed to the
239  
    next matching route:
239  
    next matching route:
240  

240  

241  
    @code
241  
    @code
242  
    route_task version_check(route_params& p)
242  
    route_task version_check(route_params& p)
243  
    {
243  
    {
244  
        if(p.req.version() < 11)
244  
        if(p.req.version() < 11)
245  
            co_return route_next_route;  // skip this route
245  
            co_return route_next_route;  // skip this route
246  

246  

247  
        co_return route_next;  // continue with this route
247  
        co_return route_next;  // continue with this route
248  
    }
248  
    }
249  
    @endcode
249  
    @endcode
250  
*/
250  
*/
251  
inline constexpr decltype(auto) route_next_route = route_result::route_next_route;
251  
inline constexpr decltype(auto) route_next_route = route_result::route_next_route;
252  

252  

253  
/** Handler requests connection closure.
253  
/** Handler requests connection closure.
254  

254  

255  
    Return this from a handler to immediately close
255  
    Return this from a handler to immediately close
256  
    the connection without sending a response:
256  
    the connection without sending a response:
257  

257  

258  
    @code
258  
    @code
259  
    route_task ban_check(route_params& p)
259  
    route_task ban_check(route_params& p)
260  
    {
260  
    {
261  
        if(is_banned(p.req.remote_address()))
261  
        if(is_banned(p.req.remote_address()))
262  
            co_return route_close;  // drop connection
262  
            co_return route_close;  // drop connection
263  

263  

264  
        co_return route_next;
264  
        co_return route_next;
265  
    }
265  
    }
266  
    @endcode
266  
    @endcode
267  
*/
267  
*/
268  
inline constexpr decltype(auto) route_close = route_result::route_close;
268  
inline constexpr decltype(auto) route_close = route_result::route_close;
269  

269  

270  
/** Construct from an error code.
270  
/** Construct from an error code.
271  

271  

272  
    Use this constructor to return an error from a handler.
272  
    Use this constructor to return an error from a handler.
273  
    The error code must represent a failure condition.
273  
    The error code must represent a failure condition.
274  

274  

275  
    @param ec The error code to return.
275  
    @param ec The error code to return.
276  

276  

277  
    @throw std::invalid_argument if `!ec` (non-failing code).
277  
    @throw std::invalid_argument if `!ec` (non-failing code).
278  
*/
278  
*/
279  
inline route_result route_error(system::error_code ec) noexcept
279  
inline route_result route_error(system::error_code ec) noexcept
280  
{
280  
{
281  
    return route_result(ec);
281  
    return route_result(ec);
282  
}
282  
}
283  

283  

284  
/** Construct from an error enum.
284  
/** Construct from an error enum.
285  

285  

286  
    Use this overload to return an error from a handler
286  
    Use this overload to return an error from a handler
287  
    using any type satisfying `is_error_code_enum`.
287  
    using any type satisfying `is_error_code_enum`.
288  

288  

289  
    @param e The error enum value to return.
289  
    @param e The error enum value to return.
290  
*/
290  
*/
291  
template<class E>
291  
template<class E>
292  
auto route_error(E e) noexcept ->
292  
auto route_error(E e) noexcept ->
293  
    std::enable_if_t<
293  
    std::enable_if_t<
294  
        system::is_error_code_enum<E>::value,
294  
        system::is_error_code_enum<E>::value,
295  
        route_result>
295  
        route_result>
296  
{
296  
{
297  
    return route_result(make_error_code(e));
297  
    return route_result(make_error_code(e));
298  
}
298  
}
299  

299  

300  
//------------------------------------------------
300  
//------------------------------------------------
301  

301  

302  
/** Convenience alias for route handler return type.
302  
/** Convenience alias for route handler return type.
303  

303  

304  
    Route handlers are coroutines that return a @ref route_result
304  
    Route handlers are coroutines that return a @ref route_result
305  
    indicating how the router should proceed. This alias simplifies
305  
    indicating how the router should proceed. This alias simplifies
306  
    handler declarations:
306  
    handler declarations:
307  

307  

308  
    @code
308  
    @code
309  
    route_task my_handler(route_params& p)
309  
    route_task my_handler(route_params& p)
310  
    {
310  
    {
311  
        // process request...
311  
        // process request...
312  
        co_return route_done;
312  
        co_return route_done;
313  
    }
313  
    }
314  

314  

315  
    route_task auth_middleware(route_params& p)
315  
    route_task auth_middleware(route_params& p)
316  
    {
316  
    {
317  
        if(! check_token(p))
317  
        if(! check_token(p))
318  
        {
318  
        {
319  
            p.res.set_status(status::unauthorized);
319  
            p.res.set_status(status::unauthorized);
320  
            co_await p.send();
320  
            co_await p.send();
321  
            co_return route_done;
321  
            co_return route_done;
322  
        }
322  
        }
323  
        co_return route_next;  // continue to next handler
323  
        co_return route_next;  // continue to next handler
324  
    }
324  
    }
325  
    @endcode
325  
    @endcode
326  

326  

327  
    @see route_result, route_params
327  
    @see route_result, route_params
328  
*/
328  
*/
329  
using route_task = capy::task<route_result>;
329  
using route_task = capy::task<route_result>;
330  

330  

331  
//------------------------------------------------
331  
//------------------------------------------------
332  

332  

333  
template<class, class> class router;
333  
template<class, class> class router;
334  

334  

335  
namespace detail {
335  
namespace detail {
336  

336  

337  
struct route_params_access;
337  
struct route_params_access;
338  
class router_base;
338  
class router_base;
339  

339  

340  
struct route_params_base_privates
340  
struct route_params_base_privates
341  
{
341  
{
342  
    std::string verb_str_;
342  
    std::string verb_str_;
343  
    std::string decoded_path_;
343  
    std::string decoded_path_;
344  
    system::error_code ec_;
344  
    system::error_code ec_;
345  
    std::exception_ptr ep_;
345  
    std::exception_ptr ep_;
346  
    std::size_t pos_ = 0;
346  
    std::size_t pos_ = 0;
347  
    std::size_t resume_ = 0;
347  
    std::size_t resume_ = 0;
348  
    http::method verb_ =
348  
    http::method verb_ =
349  
        http::method::unknown;
349  
        http::method::unknown;
350  
    bool addedSlash_ = false;
350  
    bool addedSlash_ = false;
351  
    bool case_sensitive = false;
351  
    bool case_sensitive = false;
352  
    bool strict = false;
352  
    bool strict = false;
353  
    char kind_ = 0;
353  
    char kind_ = 0;
354  
};
354  
};
355  

355  

356  
} // detail
356  
} // detail
357  

357  

358  
//------------------------------------------------
358  
//------------------------------------------------
359  

359  

360  
/** Parameters object for HTTP route handlers.
360  
/** Parameters object for HTTP route handlers.
361  

361  

362  
    This structure holds all the context needed for a route
362  
    This structure holds all the context needed for a route
363  
    handler to process an HTTP request and generate a response.
363  
    handler to process an HTTP request and generate a response.
364  

364  

365  
    @par Example
365  
    @par Example
366  
    @code
366  
    @code
367  
    route_task my_handler(route_params& p)
367  
    route_task my_handler(route_params& p)
368  
    {
368  
    {
369  
        p.res.set(field::content_type, "text/plain");
369  
        p.res.set(field::content_type, "text/plain");
370  
        co_await p.send("Hello, World!");
370  
        co_await p.send("Hello, World!");
371  
        co_return route_done;
371  
        co_return route_done;
372  
    }
372  
    }
373  
    @endcode
373  
    @endcode
374  

374  

375  
    @see route_task, route_result
375  
    @see route_task, route_result
376  
*/
376  
*/
377  
class BOOST_HTTP_SYMBOL_VISIBLE
377  
class BOOST_HTTP_SYMBOL_VISIBLE
378  
    route_params
378  
    route_params
379  
{
379  
{
380  
    detail::route_params_base_privates priv_;
380  
    detail::route_params_base_privates priv_;
381  

381  

382  
public:
382  
public:
383  
    struct match_result;
383  
    struct match_result;
384  

384  

385  
    /** Return true if the request method matches `m`
385  
    /** Return true if the request method matches `m`
386  
    */
386  
    */
387  
    bool is_method(
387  
    bool is_method(
388  
        http::method m) const noexcept
388  
        http::method m) const noexcept
389  
    {
389  
    {
390  
        return priv_.verb_ == m;
390  
        return priv_.verb_ == m;
391  
    }
391  
    }
392  

392  

393  
    /** Return true if the request method matches `s`
393  
    /** Return true if the request method matches `s`
394  
    */
394  
    */
395  
    BOOST_HTTP_DECL
395  
    BOOST_HTTP_DECL
396  
    bool is_method(
396  
    bool is_method(
397  
        core::string_view s) const noexcept;
397  
        core::string_view s) const noexcept;
398  

398  

399  
    /** The mount path of the current router
399  
    /** The mount path of the current router
400  

400  

401  
        This is the portion of the request path
401  
        This is the portion of the request path
402  
        which was matched to select the handler.
402  
        which was matched to select the handler.
403  
        The remaining portion is available in
403  
        The remaining portion is available in
404  
        @ref path.
404  
        @ref path.
405  
    */
405  
    */
406  
    core::string_view base_path;
406  
    core::string_view base_path;
407  

407  

408  
    /** The current pathname, relative to the base path
408  
    /** The current pathname, relative to the base path
409  
    */
409  
    */
410  
    core::string_view path;
410  
    core::string_view path;
411  

411  

412  
    /** Captured route parameters
412  
    /** Captured route parameters
413  

413  

414  
        Contains name-value pairs extracted from the path
414  
        Contains name-value pairs extracted from the path
415  
        by matching :param and *wildcard tokens.
415  
        by matching :param and *wildcard tokens.
416  
    */
416  
    */
417  
    std::vector<std::pair<std::string, std::string>> params;
417  
    std::vector<std::pair<std::string, std::string>> params;
418  

418  

419  
    /// The complete request target
419  
    /// The complete request target
420  
    urls::url_view url;
420  
    urls::url_view url;
421  

421  

422  
    /// The HTTP request
422  
    /// The HTTP request
423  
    http::request req;
423  
    http::request req;
424  

424  

425  
    /// The HTTP response
425  
    /// The HTTP response
426  
    http::response res;
426  
    http::response res;
427  

427  

428  
    /// Provides access to the request body
428  
    /// Provides access to the request body
429  
    capy::any_buffer_source req_body;
429  
    capy::any_buffer_source req_body;
430  

430  

431  
    /// Provides access to the response body
431  
    /// Provides access to the response body
432  
    capy::any_buffer_sink res_body;
432  
    capy::any_buffer_sink res_body;
433  

433  

434  
    /// Arbitrary per-route data
434  
    /// Arbitrary per-route data
435  
    http::datastore route_data;
435  
    http::datastore route_data;
436  

436  

437  
    /// Arbitrary per-session data
437  
    /// Arbitrary per-session data
438  
    http::datastore session_data;
438  
    http::datastore session_data;
439  

439  

440  
    BOOST_HTTP_DECL ~route_params();
440  
    BOOST_HTTP_DECL ~route_params();
441  
    BOOST_HTTP_DECL void reset();
441  
    BOOST_HTTP_DECL void reset();
442  
    BOOST_HTTP_DECL route_params& status(http::status code);
442  
    BOOST_HTTP_DECL route_params& status(http::status code);
443  

443  

444  
    /** Send the response with an optional body.
444  
    /** Send the response with an optional body.
445  
    */
445  
    */
446  
    BOOST_HTTP_DECL capy::io_task<> send(std::string_view body = {});
446  
    BOOST_HTTP_DECL capy::io_task<> send(std::string_view body = {});
447  

447  

448  
private:
448  
private:
449  
    template<class, class>
449  
    template<class, class>
450  
    friend class router;
450  
    friend class router;
451  
    friend class detail::router_base;
451  
    friend class detail::router_base;
452  
    friend struct detail::route_params_access;
452  
    friend struct detail::route_params_access;
453  

453  

454  
    route_params& operator=(
454  
    route_params& operator=(
455  
        route_params const&) = delete;
455  
        route_params const&) = delete;
456  
};
456  
};
457  

457  

458  
struct route_params::
458  
struct route_params::
459  
    match_result
459  
    match_result
460  
{
460  
{
461  
    std::vector<std::pair<std::string, std::string>> params_;
461  
    std::vector<std::pair<std::string, std::string>> params_;
462  

462  

463  
    void adjust_path(
463  
    void adjust_path(
464  
        route_params& p,
464  
        route_params& p,
465  
        std::size_t n)
465  
        std::size_t n)
466  
    {
466  
    {
467  
        n_ = n;
467  
        n_ = n;
468  
        if(n_ == 0)
468  
        if(n_ == 0)
469  
            return;
469  
            return;
470  
        p.base_path = {
470  
        p.base_path = {
471  
            p.base_path.data(),
471  
            p.base_path.data(),
472  
            p.base_path.size() + n_ };
472  
            p.base_path.size() + n_ };
473  
        if(n_ < p.path.size())
473  
        if(n_ < p.path.size())
474  
        {
474  
        {
475  
            p.path.remove_prefix(n_);
475  
            p.path.remove_prefix(n_);
476  
        }
476  
        }
477  
        else
477  
        else
478  
        {
478  
        {
479  
            // append a soft slash
479  
            // append a soft slash
480  
            p.path = { p.priv_.decoded_path_.data() +
480  
            p.path = { p.priv_.decoded_path_.data() +
481  
                p.priv_.decoded_path_.size() - 1, 1};
481  
                p.priv_.decoded_path_.size() - 1, 1};
482  
            BOOST_ASSERT(p.path == "/");
482  
            BOOST_ASSERT(p.path == "/");
483  
        }
483  
        }
484  
    }
484  
    }
485  

485  

486  
    void restore_path(
486  
    void restore_path(
487  
        route_params& p)
487  
        route_params& p)
488  
    {
488  
    {
489  
        if( n_ > 0 &&
489  
        if( n_ > 0 &&
490  
            p.priv_.addedSlash_ &&
490  
            p.priv_.addedSlash_ &&
491  
            p.path.data() ==
491  
            p.path.data() ==
492  
                p.priv_.decoded_path_.data() +
492  
                p.priv_.decoded_path_.data() +
493  
                p.priv_.decoded_path_.size() - 1)
493  
                p.priv_.decoded_path_.size() - 1)
494  
        {
494  
        {
495  
            // remove soft slash
495  
            // remove soft slash
496  
            p.path = {
496  
            p.path = {
497  
                p.base_path.data() +
497  
                p.base_path.data() +
498  
                p.base_path.size(), 0 };
498  
                p.base_path.size(), 0 };
499  
        }
499  
        }
500  
        p.base_path.remove_suffix(n_);
500  
        p.base_path.remove_suffix(n_);
501  
        p.path = {
501  
        p.path = {
502  
            p.path.data() - n_,
502  
            p.path.data() - n_,
503  
            p.path.size() + n_ };
503  
            p.path.size() + n_ };
504  
    }
504  
    }
505  

505  

506  
private:
506  
private:
507  
    std::size_t n_ = 0; // chars moved from path to base_path
507  
    std::size_t n_ = 0; // chars moved from path to base_path
508  
};
508  
};
509  

509  

510  
//------------------------------------------------
510  
//------------------------------------------------
511  

511  

512  
namespace detail {
512  
namespace detail {
513  

513  

514  
template<class H, class... Args>
514  
template<class H, class... Args>
515  
concept returns_route_task = std::same_as<
515  
concept returns_route_task = std::same_as<
516  
    std::invoke_result_t<H, Args...>, route_task>;
516  
    std::invoke_result_t<H, Args...>, route_task>;
517  

517  

518  
struct route_params_access
518  
struct route_params_access
519  
{
519  
{
520  
    route_params& rp;
520  
    route_params& rp;
521  

521  

522  
    route_params_base_privates& operator*() const noexcept
522  
    route_params_base_privates& operator*() const noexcept
523  
    {
523  
    {
524  
        return rp.priv_;
524  
        return rp.priv_;
525  
    }
525  
    }
526  

526  

527  
    route_params_base_privates* operator->() const noexcept
527  
    route_params_base_privates* operator->() const noexcept
528  
    {
528  
    {
529  
        return &rp.priv_;
529  
        return &rp.priv_;
530  
    }
530  
    }
531  
};
531  
};
532  

532  

533  
} // detail
533  
} // detail
534  

534  

535  
} // http
535  
} // http
536  
} // boost
536  
} // boost
537  

537  

538  
#endif
538  
#endif