Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/http_proto
8 : //
9 :
10 : #ifndef BOOST_HTTP_PROTO_SERVER_ROUTER_TYPES_HPP
11 : #define BOOST_HTTP_PROTO_SERVER_ROUTER_TYPES_HPP
12 :
13 : #include <boost/http_proto/detail/config.hpp>
14 : #include <boost/http_proto/method.hpp>
15 : #include <boost/http_proto/detail/except.hpp>
16 : #include <boost/core/detail/string_view.hpp>
17 : #include <boost/system/error_code.hpp>
18 : #include <exception>
19 : #include <string>
20 : #include <type_traits>
21 :
22 : namespace boost {
23 : namespace http_proto {
24 :
25 : /** The result type returned by a route handler.
26 :
27 : Route handlers use this type to report errors that prevent
28 : normal processing. A handler must never return a non-failing
29 : (i.e. `ec.failed() == false`) value. Returning a default-constructed
30 : `system::error_code` is disallowed; handlers that complete
31 : successfully must instead return a valid @ref route result.
32 : */
33 : using route_result = system::error_code;
34 :
35 : /** Route handler return values
36 :
37 : These values determine how the caller proceeds after invoking
38 : a route handler. Each enumerator represents a distinct control
39 : action�whether the request was handled, should continue to the
40 : next route, transfers ownership of the session, or signals that
41 : the connection should be closed.
42 : */
43 : enum class route
44 : {
45 : /** The handler requests that the connection be closed.
46 :
47 : No further requests will be processed. The caller should
48 : close the connection once the current response, if any,
49 : has been sent.
50 : */
51 : close = 1,
52 :
53 : /** The handler completed the request.
54 :
55 : The response has been fully transmitted, and no further
56 : handlers or routes will be invoked. The caller should continue
57 : by either reading the next request on a persistent connection
58 : or closing the session if it is not keep-alive.
59 : */
60 : complete,
61 :
62 : /** The handler is suspending the route.
63 :
64 : When the handler returns this value, the router is placed into
65 : a suspended state which can later be reactivated by invoking
66 : @ref basic_router::resume. Depending on the implementation,
67 : this might detach the handler from the session until it is
68 : resumed.
69 : */
70 : suspend,
71 :
72 : /** The handler declined to process the request.
73 :
74 : The handler chose not to generate a response. The caller
75 : continues invoking the remaining handlers in the same route
76 : until one returns @ref send. If none do, the caller proceeds
77 : to evaluate the next matching route.
78 :
79 : This value is returned by @ref basic_router::dispatch if no
80 : handlers in any route handle the request.
81 : */
82 : next,
83 :
84 : /** The handler declined the current route.
85 :
86 : The handler wishes to skip any remaining handlers in the
87 : current route and move on to the next matching route. The
88 : caller stops invoking handlers in this route and resumes
89 : evaluation with the next candidate route.
90 : */
91 : next_route,
92 :
93 : /** The request was handled.
94 :
95 : The route handler processed the request and prepared
96 : the response serializer. The caller will send the response
97 : before reading the next request or closing the connection.
98 : */
99 : send
100 : };
101 :
102 : //------------------------------------------------
103 :
104 : } // http_proto
105 : namespace system {
106 : template<>
107 : struct is_error_code_enum<
108 : ::boost::http_proto::route>
109 : {
110 : static bool const value = true;
111 : };
112 : } // system
113 : namespace http_proto {
114 :
115 : namespace detail {
116 : struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE route_cat_type
117 : : system::error_category
118 : {
119 : BOOST_HTTP_PROTO_DECL const char* name() const noexcept override;
120 : BOOST_HTTP_PROTO_DECL std::string message(int) const override;
121 : BOOST_HTTP_PROTO_DECL char const* message(
122 : int, char*, std::size_t) const noexcept override;
123 43 : BOOST_SYSTEM_CONSTEXPR route_cat_type()
124 43 : : error_category(0x51c90d393754ecdf )
125 : {
126 43 : }
127 : };
128 : BOOST_HTTP_PROTO_DECL extern route_cat_type route_cat;
129 : } // detail
130 :
131 : inline
132 : BOOST_SYSTEM_CONSTEXPR
133 : system::error_code
134 2357 : make_error_code(route ev) noexcept
135 : {
136 : return system::error_code{static_cast<
137 : std::underlying_type<route>::type>(ev),
138 2357 : detail::route_cat};
139 : }
140 :
141 : /** Return true if `rv` is a route result.
142 :
143 : A @ref route_result can hold any error code,
144 : and this function returns `true` only if `rv`
145 : holds a value from the @ref route enumeration.
146 : */
147 223 : inline bool is_route_result(
148 : route_result rv) noexcept
149 : {
150 223 : return &rv.category() == &detail::route_cat;
151 : }
152 :
153 : //------------------------------------------------
154 :
155 : class resumer;
156 :
157 : /** Function to suspend a route handler from its session
158 :
159 : This holds an reference to an implementation
160 : which suspends the router which dispatched the handler.
161 : */
162 : class suspender
163 : {
164 : public:
165 : /** Base class of the implementation
166 : */
167 : struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE
168 : owner
169 : {
170 : BOOST_HTTP_PROTO_DECL
171 : virtual resumer do_suspend();
172 : virtual void do_resume(route_result const&) = 0;
173 : virtual void do_resume(std::exception_ptr) = 0;
174 : };
175 :
176 1 : suspender() = default;
177 : suspender(suspender const&) = default;
178 : suspender& operator=(suspender const&) = default;
179 :
180 : explicit
181 : suspender(
182 : owner& who) noexcept
183 : : p_(&who)
184 : {
185 : }
186 :
187 : /** Suspend and invoke the given function
188 :
189 : The function will be invoked with this equivalent signature:
190 : @code
191 : void( resumer );
192 : @endcode
193 :
194 : @return A @ref route_result equal to @ref route::suspend
195 : */
196 : template<class F>
197 : route_result
198 : operator()(F&& f);
199 :
200 : private:
201 : friend resumer;
202 : // Clang doesn't consider uninstantiated templates
203 : // when checking for unused private fields.
204 : owner* p_
205 : #if defined(__clang__)
206 : __attribute__((unused))
207 : #endif
208 : = nullptr;
209 : };
210 :
211 : //------------------------------------------------
212 :
213 : /** Function to resume a suspended route.
214 :
215 : This holds a reference to an implementation which resumes the handler's
216 : session. The resume function is typically obtained at the time the
217 : route is suspended.
218 : */
219 : class resumer
220 : {
221 : public:
222 : /** Constructor
223 :
224 : Default constructed resume functions will
225 : be empty. An exception is thrown when
226 : attempting to invoke an empty object.
227 : */
228 : resumer() = default;
229 :
230 : /** Constructor
231 :
232 : Copies of resume functions behave the same
233 : as the original
234 : */
235 : resumer(resumer const&) = default;
236 :
237 : /** Assignment
238 :
239 : Copies of resume functions behave the same
240 : as the original
241 : */
242 : resumer& operator=(resumer const&) = default;
243 :
244 : /** Constructor
245 : */
246 : explicit
247 : resumer(
248 : suspender::owner& who) noexcept
249 : : p_(&who)
250 : {
251 : }
252 :
253 : /** Resume the session
254 :
255 : When a session is resumed, routing continues as if the handler
256 : had returned the @ref route_result contained in @p rv.
257 :
258 : @param rv The route result to resume with.
259 :
260 : @throw std::invalid_argument If the object is empty.
261 : */
262 : void operator()(
263 : route_result const& rv) const
264 : {
265 : if(! p_)
266 : detail::throw_invalid_argument();
267 : p_->do_resume(rv);
268 : }
269 :
270 : /** Resume the session with an exception
271 :
272 : When a session is resumed with an exception, the exception
273 : is propagated through the router's error handling mechanism.
274 :
275 : @param ep The exception to propagate.
276 :
277 : @throw std::invalid_argument If the object is empty.
278 : */
279 : void operator()(
280 : std::exception_ptr ep) const
281 : {
282 : if(! p_)
283 : detail::throw_invalid_argument();
284 : p_->do_resume(ep);
285 : }
286 :
287 : private:
288 : suspender::owner* p_
289 : #if defined(__clang__)
290 : __attribute__((unused))
291 : #endif
292 : = nullptr;
293 : };
294 :
295 : template<class F>
296 : auto
297 : suspender::
298 : operator()(F&& f) ->
299 : route_result
300 : {
301 : if(! p_)
302 : detail::throw_logic_error();
303 : std::forward<F>(f)(p_->do_suspend());
304 : return route::suspend;
305 : }
306 :
307 : //------------------------------------------------
308 :
309 : namespace detail {
310 : class any_router;
311 : } // detail
312 : template<class>
313 : class basic_router;
314 :
315 : /** Base class for request objects
316 :
317 : This is a required public base for any `Request`
318 : type used with @ref basic_router.
319 : */
320 : class route_params_base
321 : {
322 : public:
323 : /** Return true if the request method matches `m`
324 : */
325 2 : bool is_method(
326 : http_proto::method m) const noexcept
327 : {
328 2 : return verb_ == m;
329 : }
330 :
331 : /** Return true if the request method matches `s`
332 : */
333 : BOOST_HTTP_PROTO_DECL
334 : bool is_method(
335 : core::string_view s) const noexcept;
336 :
337 : /** The mount path of the current router
338 :
339 : This is the portion of the request path
340 : which was matched to select the handler.
341 : The remaining portion is available in
342 : @ref path.
343 : */
344 : core::string_view base_path;
345 :
346 : /** The current pathname, relative to the base path
347 : */
348 : core::string_view path;
349 :
350 : private:
351 : friend class /*detail::*/any_router;
352 : template<class>
353 : friend class basic_router;
354 : struct match_result;
355 : route_params_base& operator=(
356 : route_params_base const&) = delete;
357 :
358 : std::string verb_str_;
359 : std::string decoded_path_;
360 : system::error_code ec_;
361 : std::exception_ptr ep_;
362 : std::size_t pos_ = 0;
363 : std::size_t resume_ = 0;
364 : http_proto::method verb_ =
365 : http_proto::method::unknown;
366 : bool addedSlash_ = false;
367 : bool case_sensitive = false;
368 : bool strict = false;
369 : };
370 :
371 : } // http_proto
372 : } // boost
373 :
374 : #endif
|