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