Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
middleware.h
1#pragma once
2
3#include "crow/http_request.h"
4#include "crow/http_response.h"
5#include "crow/utility.h"
6
7#include <tuple>
8#include <type_traits>
9#include <iostream>
10#include <utility>
11
12namespace crow // NOTE: Already documented in "crow/app.h"
13{
14
15 /// Local middleware should extend ILocalMiddleware
17 {
18 using call_global = std::false_type;
19 };
20
21 namespace detail
22 {
23 template<typename MW>
25 {
26 template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle>
27 struct get
28 {};
29 };
30
31 template<typename MW>
33 {
34 template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::before_handle>
35 struct get
36 {};
37 };
38
39 template<typename MW>
41 {
42 template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle>
43 struct get
44 {};
45 };
46
47 template<typename MW>
49 {
50 template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::after_handle>
51 struct get
52 {};
53 };
54
55 template<typename MW>
57 {
58 template<typename T, typename std::enable_if<T::call_global::value == false, bool>::type = true>
59 struct get
60 {};
61 };
62
63 template<typename T>
65 {
66 template<typename C>
67 static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
68
69 template<typename C>
70 static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
71
72 template<typename C>
73 static std::false_type f(...);
74
75 public:
76 static const bool value = decltype(f<T>(nullptr))::value;
77 };
78
79 template<typename T>
81 {
82 template<typename C>
83 static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
84
85 template<typename C>
86 static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
87
88 template<typename C>
89 static std::false_type f(...);
90
91 public:
92 static constexpr bool value = decltype(f<T>(nullptr))::value;
93 };
94
95 template<typename MW>
97 {
98 template<typename C>
99 static std::false_type f(typename check_global_call_false<MW>::template get<C>*);
100
101 template<typename C>
102 static std::true_type f(...);
103
104 static const bool value = decltype(f<MW>(nullptr))::value;
105 };
106
107 template<typename MW, typename Context, typename ParentContext>
108 typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
109 before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
110 {
111 mw.before_handle(req, res, ctx.template get<MW>(), ctx);
112 }
113
114 template<typename MW, typename Context, typename ParentContext>
115 typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
116 before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
117 {
118 mw.before_handle(req, res, ctx.template get<MW>());
119 }
120
121 template<typename MW, typename Context, typename ParentContext>
122 typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
123 after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
124 {
125 mw.after_handle(req, res, ctx.template get<MW>(), ctx);
126 }
127
128 template<typename MW, typename Context, typename ParentContext>
129 typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
130 after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
131 {
132 mw.after_handle(req, res, ctx.template get<MW>());
133 }
134
135
136 template<typename CallCriteria,
137 int N, typename Context, typename Container>
138 typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
139 middleware_call_helper(const CallCriteria& cc, Container& middlewares, request& req, response& res, Context& ctx)
140 {
141
142 using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
143
144 if (!cc.template enabled<CurrentMW>(N))
145 {
146 return middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx);
147 }
148
149 using parent_context_t = typename Context::template partial<N - 1>;
150 before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
151 if (res.is_completed())
152 {
153 after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
154 return true;
155 }
156
157 if (middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx))
158 {
159 after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
160 return true;
161 }
162
163 return false;
164 }
165
166 template<typename CallCriteria, int N, typename Context, typename Container>
167 typename std::enable_if<(N >= std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
168 middleware_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
169 {
170 return false;
171 }
172
173 template<typename CallCriteria, int N, typename Context, typename Container>
174 typename std::enable_if<(N < 0)>::type
175 after_handlers_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
176 {
177 }
178
179 template<typename CallCriteria, int N, typename Context, typename Container>
180 typename std::enable_if<(N == 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
181 {
182 using parent_context_t = typename Context::template partial<N - 1>;
183 using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
184 if (cc.template enabled<CurrentMW>(N))
185 {
186 after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
187 }
188 }
189
190 template<typename CallCriteria, int N, typename Context, typename Container>
191 typename std::enable_if<(N > 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
192 {
193 using parent_context_t = typename Context::template partial<N - 1>;
194 using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
195 if (cc.template enabled<CurrentMW>(N))
196 {
197 after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
198 }
199 after_handlers_call_helper<CallCriteria, N - 1, Context, Container>(cc, middlewares, ctx, req, res);
200 }
201
202 // A CallCriteria that accepts only global middleware
204 {
205 template<typename MW>
206 constexpr bool enabled(int) const
207 {
209 }
210 };
211
212 template<typename F, typename... Args>
213 typename std::enable_if<black_magic::CallHelper<F, black_magic::S<Args...>>::value, void>::type
214 wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
215 {
216 static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
217 "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
218
219 res = crow::response(f(std::forward<Args>(args)...));
220 res.end();
221 }
222
223 template<typename F, typename... Args>
224 typename std::enable_if<
225 !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
226 black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value,
227 void>::type
228 wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
229 {
230 static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
231 "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
232
233 res = crow::response(f(req, std::forward<Args>(args)...));
234 res.end();
235 }
236
237 template<typename F, typename... Args>
238 typename std::enable_if<
239 !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
240 !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
241 black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value,
242 void>::type
243 wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
244 {
245 static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
246 "Handler function with response argument should have void return type");
247
248 f(res, std::forward<Args>(args)...);
249 }
250
251 template<typename F, typename... Args>
252 typename std::enable_if<
253 !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
254 !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
255 !black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
256 black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
257 void>::type
258 wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
259 {
260 static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
261 "Handler function with response argument should have void return type");
262
263 f(req, res, std::forward<Args>(args)...);
264 }
265
266 // wrapped_handler_call transparently wraps a handler call behind (req, res, args...)
267 template<typename F, typename... Args>
268 typename std::enable_if<
269 !black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
270 !black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
271 !black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
272 !black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
273 void>::type
274 wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
275 {
276 static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
277 "Handler function with response argument should have void return type");
278
279 f(req, res, std::forward<Args>(args)...);
280 }
281
282 template<bool Reversed>
285
286 template<>
288 {
289 middleware_call_criteria_dynamic(const std::vector<int>& indices):
290 indices(indices), slider(0) {}
291
292 template<typename>
293 bool enabled(int mw_index) const
294 {
295 if (slider < int(indices.size()) && indices[slider] == mw_index)
296 {
297 slider++;
298 return true;
299 }
300 return false;
301 }
302
303 private:
304 const std::vector<int>& indices;
305 mutable int slider;
306 };
307
308 template<>
310 {
311 middleware_call_criteria_dynamic(const std::vector<int>& indices):
312 indices(indices), slider(int(indices.size()) - 1) {}
313
314 template<typename>
315 bool enabled(int mw_index) const
316 {
317 if (slider >= 0 && indices[slider] == mw_index)
318 {
319 slider--;
320 return true;
321 }
322 return false;
323 }
324
325 private:
326 const std::vector<int>& indices;
327 mutable int slider;
328 };
329
330 } // namespace detail
331} // namespace crow
The main namespace of the library. In this namespace is defined the most important classes and functi...
Local middleware should extend ILocalMiddleware.
Definition middleware.h:17
Definition middleware.h:57
Definition middleware.h:97
An HTTP request.
Definition http_request.h:36
HTTP response.
Definition http_response.h:34
void end()
Set the response completion flag and call the handler (to send the response).
Definition http_response.h:237