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/beast2
8 : //
9 :
10 : #ifndef BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
11 : #define BOOST_BEAST2_SERVER_BASIC_ROUTER_HPP
12 :
13 : #include <boost/beast2/detail/config.hpp>
14 : #include <boost/beast2/server/detail/any_router.hpp>
15 : #include <boost/beast2/detail/call_traits.hpp>
16 : #include <boost/beast2/detail/type_traits.hpp>
17 : #include <boost/http_proto/method.hpp>
18 : #include <boost/url/url_view.hpp>
19 : #include <boost/core/detail/string_view.hpp>
20 : #include <type_traits>
21 :
22 : namespace boost {
23 : namespace beast2 {
24 :
25 : struct route_state
26 : {
27 : private:
28 : friend class detail::any_router;
29 : template<class, class>
30 : friend class basic_router;
31 :
32 : std::size_t pos = 0;
33 : std::size_t resume = 0;
34 : route_result ec;
35 : http_proto::method verb;
36 : core::string_view verb_str;
37 : std::string decoded_path;
38 : };
39 :
40 : //------------------------------------------------
41 :
42 : /** A container for HTTP route handlers
43 :
44 : The basic_router class template is used to
45 : store and invoke route handlers based on
46 : the request method and path.
47 : Handlers are added to the router using
48 : the member functions such as @ref get,
49 : @ref post, and @ref all.
50 : Handlers are invoked by calling the
51 : function call operator with a request
52 : and response object.
53 :
54 : Express treats all route definitions as decoded path patterns, not raw URL-encoded ones.
55 : So a literal %2F in the pattern string is indistinguishable from a literal / once Express builds the layer.
56 : Therefore "/x%2Fz" is the same as "/x/z"
57 :
58 : @par Example
59 : @code
60 : using router_type = basic_router<Req, Res>;
61 : router_type router;
62 : router.get("/hello",
63 : [](Req& req, Res& res)
64 : {
65 : res.status(http_proto::status::ok);
66 : res.set_body("Hello, world!");
67 : return system::error_code{};
68 : });
69 : @endcode
70 :
71 : @tparam Req The type of request object.
72 : @tparam Res The type of response object.
73 : */
74 : template<class Req, class Res>
75 : class basic_router : public detail::any_router
76 : {
77 : static constexpr http_proto::method
78 : middleware = http_proto::method::unknown;
79 :
80 : public:
81 : /** The type of request object used in handlers
82 :
83 : Route handlers must have this invocable signature
84 : @code
85 : system::error_code(Req&, Res&)
86 : @endcode
87 : */
88 : using request_type = Req;
89 :
90 : /** The type of response object used in handlers
91 :
92 : Route handlers must have this invocable signature
93 : @code
94 : system::error_code(Req&, Res&)
95 : @endcode
96 : */
97 : using response_type = Res;
98 :
99 : /** Constructor
100 : */
101 7 : basic_router()
102 : : any_router(
103 21 : [](void* preq) -> req_info
104 : {
105 14 : auto& req = *reinterpret_cast<Req*>(preq);
106 14 : return req_info{ req.base_path, req.path };
107 7 : })
108 : {
109 7 : }
110 :
111 : /** Constructor
112 : */
113 : template<
114 : class OtherReq, class OtherRes,
115 : class = typename std::enable_if<
116 : detail::derived_from<Req, OtherReq>::value &&
117 : detail::derived_from<Res, OtherRes>::value>::type
118 : >
119 : basic_router(
120 : basic_router<OtherReq, OtherRes> const& other)
121 : : any_router(other)
122 : {
123 : }
124 :
125 : /** Add one or more global middleware handlers.
126 :
127 : Each handler registered with this function runs for every incoming
128 : request, regardless of its HTTP method or path. Handlers execute in
129 : the order they were added, and may call `next()` to transfer control
130 : to the subsequent handler in the chain.
131 :
132 : This is equivalent to writing
133 : @code
134 : use( "/", h1, hn... );
135 : @endcode
136 : */
137 : template<class H1, class... HN
138 : , class = typename std::enable_if<! std::is_convertible<
139 : H1, core::string_view>::value>::type
140 : >
141 1 : void use(H1&& h1, HN&&... hn)
142 : {
143 1 : use("/", std::forward<H1>(h1), std::forward<HN>(hn)...);
144 1 : }
145 :
146 : /** Add one or more middleware handlers for a path prefix.
147 :
148 : Each handler registered with this function runs for every request
149 : whose path begins with the specified prefix, regardless of the
150 : request method. The prefix match is not strict: middleware attached
151 : to `"/api"` will also match `"/api/users"` and `"/api/data"`.
152 : Handlers execute in the order they were added, and may call `next()`
153 : to transfer control to the subsequent handler.
154 :
155 : This function behaves analogously to `app.use(path, ...)` in
156 : Express.js. The registered middleware executes for requests matching
157 : the prefix, and when registered before route handlers for the same
158 : prefix, runs prior to those routes.
159 :
160 : @par Example
161 : @code
162 : router.use("/api",
163 : [](Request& req, Response& res)
164 : {
165 : if(!authenticate(req))
166 : return res.setStatus(401), res.end("Unauthorized");
167 : return res.next();
168 : },
169 : [](Request&, Response& res)
170 : {
171 : res.setHeader("X-Powered-By", "MyServer");
172 : });
173 : @endcode
174 :
175 : @par Constraints
176 : - `pattern` must be a valid path prefix; it may be empty to indicate
177 : the root scope.
178 : - Each handler must be callable with the signature
179 : `void(Request&, Response&, NextHandler)`.
180 : - Each handler must be copy- or move-constructible, depending on how
181 : it is passed.
182 :
183 : @throws Any exception thrown by a handler during execution.
184 :
185 : @param pattern The path prefix to match. Middleware runs for any
186 : request whose path begins with this prefix.
187 : @param h0 The first middleware handler to install.
188 : @param hn Additional middleware handlers to install, executed in
189 : declaration order.
190 :
191 : @return (none)
192 : */
193 : template<class H0, class... HN>
194 9 : void use(
195 : core::string_view pattern,
196 : H0&& h0, HN... hn)
197 : {
198 9 : if(pattern.empty())
199 0 : pattern = "/";
200 9 : append(false, middleware, pattern,
201 : std::forward<H0>(h0),
202 : std::forward<HN>(hn)...);
203 9 : }
204 :
205 : template<class H0, class... HN>
206 : void add(
207 : http_proto::method method,
208 : core::string_view pattern,
209 : H0&& h0, HN&&... hn)
210 : {
211 : append(false, method, pattern,
212 : std::forward<H0>(h0),
213 : std::forward<HN>(hn)...);
214 : }
215 :
216 : /** Add an error handler
217 : */
218 : template<class H0, class... HN>
219 : void err(
220 : H0&& h0, HN&&... hn)
221 : {
222 : append_err(
223 : std::forward<H0>(h0),
224 : std::forward<HN>(hn)...);
225 : }
226 :
227 : /** Add a route handler matching all methods and the given pattern
228 : The handler will run for every request matching the entire pattern.
229 : */
230 : template<class H0, class... HN>
231 : void all(
232 : core::string_view pattern,
233 : H0&& h0, HN&&... hn)
234 : {
235 : return add(middleware, pattern,
236 : std::forward<H0>(h0), std::forward<HN>(hn)...);
237 : }
238 :
239 : /** Add a GET handler
240 : */
241 : template<class H0, class... HN>
242 : void get(
243 : core::string_view pattern,
244 : H0&& h0, HN&&... hn)
245 : {
246 : add(http_proto::method::get, pattern,
247 : std::forward<H0>(h0), std::forward<HN>(hn)...);
248 : }
249 :
250 : template<class H0, class... HN>
251 : void post(
252 : core::string_view pattern,
253 : H0&& h0, HN&&... hn)
254 : {
255 : add(http_proto::method::post, pattern,
256 : std::forward<H0>(h0), std::forward<HN>(hn)...);
257 : }
258 :
259 7 : auto dispatch(
260 : http_proto::method method,
261 : urls::url_view const& url,
262 : Req& req, Res& res, route_state& st) ->
263 : route_result
264 : {
265 7 : return dispatch_impl(
266 7 : method, url, &req, &res, st);
267 : }
268 :
269 : auto
270 0 : resume(
271 : Req& req,
272 : Res& res,
273 : route_result const& ec,
274 : route_state& st) const ->
275 : route_result
276 : {
277 0 : st.pos = 0;
278 0 : st.ec = ec;
279 0 : return dispatch_impl(&req, &res, st);
280 : }
281 :
282 : private:
283 : template<class H>
284 9 : handler_ptr make_handler(H&& h)
285 : {
286 18 : return handler_ptr(new handler_impl<
287 18 : Req, Res, H>(std::forward<H>(h)));
288 : }
289 :
290 9 : void append(bool, http_proto::method,
291 : core::string_view) const noexcept
292 : {
293 9 : }
294 :
295 : template<class H0, class... HN>
296 9 : void append(bool prefix, http_proto::method method,
297 : core::string_view pat, H0&& h, HN&&... hn)
298 : {
299 9 : any_router::append(prefix, method, pat,
300 : make_handler<H0>(std::forward<H0>(h)));
301 9 : append(prefix, method, pat, std::forward<HN>(hn)...);
302 9 : }
303 :
304 : template<class H0, class... HN>
305 : void append_err(H0&& h, HN&&... hn)
306 : {
307 : append(true, middleware, {},
308 : std::forward<H0>(h),
309 : std::forward<HN>(hn)...);
310 : }
311 : };
312 :
313 : } // beast2
314 : } // boost
315 :
316 : #endif
|