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 42 : BOOST_SYSTEM_CONSTEXPR route_cat_type()
124 42 : : error_category(0x51c90d393754ecdf )
125 : {
126 42 : }
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 virtual resumer do_suspend();
171 : virtual void do_resume(route_result const&) = 0;
172 : };
173 :
174 1 : suspender() = default;
175 : suspender(suspender const&) = default;
176 : suspender& operator=(suspender const&) = default;
177 :
178 : explicit
179 : suspender(
180 : owner& who) noexcept
181 : : p_(&who)
182 : {
183 : }
184 :
185 : /** Suspend and invoke the given function
186 :
187 : The function will be invoked with this equivalent signature:
188 : @code
189 : void( resumer );
190 : @endcode
191 :
192 : @return A @ref route_result equal to @ref route::suspend
193 : */
194 : template<class F>
195 : route_result
196 : operator()(F&& f);
197 :
198 : private:
199 : friend resumer;
200 : // Clang doesn't consider uninstantiated templates
201 : // when checking for unused private fields.
202 : owner* p_
203 : #if defined(__clang__)
204 : __attribute__((unused))
205 : #endif
206 : = nullptr;
207 : };
208 :
209 : //------------------------------------------------
210 :
211 : /** Function to resume a suspended route.
212 :
213 : This holds a reference to an implementation which resumes the handler's
214 : session. The resume function is typically obtained at the time the
215 : route is suspended.
216 : */
217 : class resumer
218 : {
219 : public:
220 : /** Constructor
221 :
222 : Default constructed resume functions will
223 : be empty. An exception is thrown when
224 : attempting to invoke an empty object.
225 : */
226 : resumer() = default;
227 :
228 : /** Constructor
229 :
230 : Copies of resume functions behave the same
231 : as the original
232 : */
233 : resumer(resumer const&) = default;
234 :
235 : /** Assignment
236 :
237 : Copies of resume functions behave the same
238 : as the original
239 : */
240 : resumer& operator=(resumer const&) = default;
241 :
242 : /** Constructor
243 : */
244 : explicit
245 : resumer(
246 : suspender::owner& who) noexcept
247 : : p_(&who)
248 : {
249 : }
250 :
251 : /** Resume the session
252 :
253 : When a session is resumed, routing continues as if the handler
254 : had returned the @ref route_result contained in @p rv.
255 :
256 : @param ec The error code to resume with.
257 :
258 : @throw std::invalid_argument If the object is empty.
259 : */
260 : void operator()(
261 : route_result const& rv) const
262 : {
263 : if(! p_)
264 : detail::throw_invalid_argument();
265 : p_->do_resume(rv);
266 : }
267 :
268 : private:
269 : suspender::owner* p_
270 : #if defined(__clang__)
271 : __attribute__((unused))
272 : #endif
273 : = nullptr;
274 : };
275 :
276 : template<class F>
277 : auto
278 : suspender::
279 : operator()(F&& f) ->
280 : route_result
281 : {
282 : if(! p_)
283 : detail::throw_logic_error();
284 : std::forward<F>(f)(p_->do_suspend());
285 : return route::suspend;
286 : }
287 :
288 : //------------------------------------------------
289 :
290 : namespace detail {
291 : class any_router;
292 : } // detail
293 : template<class>
294 : class basic_router;
295 :
296 : /** Base class for request objects
297 :
298 : This is a required public base for any `Request`
299 : type used with @ref basic_router.
300 : */
301 : class route_params_base
302 : {
303 : public:
304 : /** Return true if the request method matches `m`
305 : */
306 2 : bool is_method(
307 : http_proto::method m) const noexcept
308 : {
309 2 : return verb_ == m;
310 : }
311 :
312 : /** Return true if the request method matches `s`
313 : */
314 : BOOST_HTTP_PROTO_DECL
315 : bool is_method(
316 : core::string_view s) const noexcept;
317 :
318 : /** The mount path of the current router
319 :
320 : This is the portion of the request path
321 : which was matched to select the handler.
322 : The remaining portion is available in
323 : @ref path.
324 : */
325 : core::string_view base_path;
326 :
327 : /** The current pathname, relative to the base path
328 : */
329 : core::string_view path;
330 :
331 : private:
332 : friend class /*detail::*/any_router;
333 : template<class>
334 : friend class basic_router;
335 : struct match_result;
336 : route_params_base& operator=(
337 : route_params_base const&) = delete;
338 :
339 : std::string verb_str_;
340 : std::string decoded_path_;
341 : system::error_code ec_;
342 : std::exception_ptr ep_;
343 : std::size_t pos_ = 0;
344 : std::size_t resume_ = 0;
345 : http_proto::method verb_ =
346 : http_proto::method::unknown;
347 : bool addedSlash_ = false;
348 : bool case_sensitive = false;
349 : bool strict = false;
350 : };
351 :
352 : } // http_proto
353 : } // boost
354 :
355 : #endif
|