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
8 : //
9 :
10 : #ifndef BOOST_HTTP_SERVER_ROUTER_TYPES_HPP
11 : #define BOOST_HTTP_SERVER_ROUTER_TYPES_HPP
12 :
13 : #include <boost/http/detail/config.hpp>
14 : #include <boost/http/method.hpp>
15 : #include <boost/http/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 {
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 declined to process the request.
46 :
47 : The handler chose not to generate a response. The caller
48 : continues invoking the remaining handlers in the same route
49 : until one returns @ref send. If none do, the caller proceeds
50 : to evaluate the next matching route.
51 :
52 : This value is returned by @ref router::dispatch if no
53 : handlers in any route handle the request.
54 : */
55 : next,
56 :
57 : /** The handler declined the current route.
58 :
59 : The handler wishes to skip any remaining handlers in the
60 : current route and move on to the next matching route. The
61 : caller stops invoking handlers in this route and resumes
62 : evaluation with the next candidate route.
63 : */
64 : next_route
65 : };
66 :
67 : //------------------------------------------------
68 :
69 : } // http
70 : namespace system {
71 : template<>
72 : struct is_error_code_enum<
73 : ::boost::http::route>
74 : {
75 : static bool const value = true;
76 : };
77 : } // system
78 : namespace http {
79 :
80 : namespace detail {
81 : struct BOOST_HTTP_SYMBOL_VISIBLE route_cat_type
82 : : system::error_category
83 : {
84 : BOOST_HTTP_DECL const char* name() const noexcept override;
85 : BOOST_HTTP_DECL std::string message(int) const override;
86 : BOOST_HTTP_DECL char const* message(
87 : int, char*, std::size_t) const noexcept override;
88 : BOOST_SYSTEM_CONSTEXPR route_cat_type()
89 : : error_category(0x51c90d393754ecdf )
90 : {
91 : }
92 : };
93 : BOOST_HTTP_DECL extern route_cat_type route_cat;
94 : } // detail
95 :
96 : inline
97 : BOOST_SYSTEM_CONSTEXPR
98 : system::error_code
99 155 : make_error_code(route ev) noexcept
100 : {
101 : return system::error_code{static_cast<
102 : std::underlying_type<route>::type>(ev),
103 155 : detail::route_cat};
104 : }
105 :
106 : /** Return true if `rv` is a route result.
107 :
108 : A @ref route_result can hold any error code,
109 : and this function returns `true` only if `rv`
110 : holds a value from the @ref route enumeration.
111 : */
112 : inline bool is_route_result(
113 : route_result rv) noexcept
114 : {
115 : return &rv.category() == &detail::route_cat;
116 : }
117 :
118 : //------------------------------------------------
119 :
120 : namespace detail {
121 : class router_base;
122 : } // detail
123 : template<class> class router;
124 :
125 : struct route_params_base_privates
126 : {
127 : struct match_result;
128 :
129 : std::string verb_str_;
130 : std::string decoded_path_;
131 : system::error_code ec_;
132 : std::exception_ptr ep_;
133 : std::size_t pos_ = 0;
134 : std::size_t resume_ = 0;
135 : http::method verb_ =
136 : http::method::unknown;
137 : bool addedSlash_ = false;
138 : bool case_sensitive = false;
139 : bool strict = false;
140 : char kind_ = 0; // dispatch mode, initialized by flat_router::dispatch()
141 : };
142 :
143 : /** Base class for request objects
144 :
145 : This is a required public base for any `Request`
146 : type used with @ref router.
147 : */
148 : class route_params_base : public route_params_base_privates
149 : {
150 : public:
151 : /** Return true if the request method matches `m`
152 : */
153 : bool is_method(
154 : http::method m) const noexcept
155 : {
156 : return verb_ == m;
157 : }
158 :
159 : /** Return true if the request method matches `s`
160 : */
161 : BOOST_HTTP_DECL
162 : bool is_method(
163 : core::string_view s) const noexcept;
164 :
165 : /** The mount path of the current router
166 :
167 : This is the portion of the request path
168 : which was matched to select the handler.
169 : The remaining portion is available in
170 : @ref path.
171 : */
172 : core::string_view base_path;
173 :
174 : /** The current pathname, relative to the base path
175 : */
176 : core::string_view path;
177 :
178 : struct match_result;
179 :
180 : private:
181 : template<class>
182 : friend class router;
183 : friend struct route_params_access;
184 :
185 : route_params_base& operator=(
186 : route_params_base const&) = delete;
187 : };
188 :
189 : struct route_params_base::
190 : match_result
191 : {
192 73 : void adjust_path(
193 : route_params_base& p,
194 : std::size_t n)
195 : {
196 73 : n_ = n;
197 73 : if(n_ == 0)
198 39 : return;
199 34 : p.base_path = {
200 : p.base_path.data(),
201 34 : p.base_path.size() + n_ };
202 34 : if(n_ < p.path.size())
203 : {
204 13 : p.path.remove_prefix(n_);
205 : }
206 : else
207 : {
208 : // append a soft slash
209 21 : p.path = { p.decoded_path_.data() +
210 21 : p.decoded_path_.size() - 1, 1};
211 21 : BOOST_ASSERT(p.path == "/");
212 : }
213 : }
214 :
215 : void restore_path(
216 : route_params_base& p)
217 : {
218 : if( n_ > 0 &&
219 : p.addedSlash_ &&
220 : p.path.data() ==
221 : p.decoded_path_.data() +
222 : p.decoded_path_.size() - 1)
223 : {
224 : // remove soft slash
225 : p.path = {
226 : p.base_path.data() +
227 : p.base_path.size(), 0 };
228 : }
229 : p.base_path.remove_suffix(n_);
230 : p.path = {
231 : p.path.data() - n_,
232 : p.path.size() + n_ };
233 : }
234 :
235 : private:
236 : std::size_t n_ = 0; // chars moved from path to base_path
237 : };
238 :
239 :
240 : namespace detail {
241 :
242 : struct route_params_access
243 : {
244 : route_params_base& rp;
245 :
246 24 : route_params_base_privates* operator->() const noexcept
247 : {
248 24 : return &rp;
249 : }
250 : };
251 :
252 : } // detail
253 :
254 : } // http
255 : } // boost
256 :
257 : #endif
|