/src/crow/include/crow/middleware.h
Line | Count | Source (jump to first uncovered line) |
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 | | |
12 | | namespace crow // NOTE: Already documented in "crow/app.h" |
13 | | { |
14 | | |
15 | | /// Local middleware should extend ILocalMiddleware |
16 | | struct ILocalMiddleware |
17 | | { |
18 | | using call_global = std::false_type; |
19 | | }; |
20 | | |
21 | | namespace detail |
22 | | { |
23 | | template<typename MW> |
24 | | struct check_before_handle_arity_3_const |
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> |
32 | | struct check_before_handle_arity_3 |
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> |
40 | | struct check_after_handle_arity_3_const |
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> |
48 | | struct check_after_handle_arity_3 |
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> |
56 | | struct check_global_call_false |
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> |
64 | | struct is_before_handle_arity_3_impl |
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> |
80 | | struct is_after_handle_arity_3_impl |
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> |
96 | | struct is_middleware_global |
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 | 0 | { |
170 | 0 | return false; |
171 | 0 | } |
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 | 0 | { |
177 | 0 | } |
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 |
203 | | struct middleware_call_criteria_only_global |
204 | | { |
205 | | template<typename MW> |
206 | | constexpr bool enabled(int) const |
207 | | { |
208 | | return is_middleware_global<MW>::value; |
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 | 0 | { |
230 | 0 | static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value, |
231 | 0 | "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable"); |
232 | |
|
233 | 0 | res = crow::response(f(req, std::forward<Args>(args)...)); |
234 | 0 | res.end(); |
235 | 0 | } |
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 | 0 | { |
245 | 0 | static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value, |
246 | 0 | "Handler function with response argument should have void return type"); |
247 | |
|
248 | 0 | f(res, std::forward<Args>(args)...); |
249 | 0 | } Unexecuted instantiation: _ZN4crow6detail20wrapped_handler_callIZNS_4CrowIJEE13add_blueprintEvEUlRNS_8responseENSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEEE_JSC_EEENS6_9enable_ifIXaaaantsr11black_magic10CallHelperIT_NS_11black_magic1SIJDpT0_EEEEE5valuentsr11black_magic10CallHelperISF_NSH_IJRNS_7requestESJ_EEEEE5valuesr11black_magic10CallHelperISF_NSH_IJS5_SJ_EEEEE5valueEvE4typeESM_S5_RKSF_DpOSI_ Unexecuted instantiation: _ZN4crow6detail20wrapped_handler_callIZNS_4CrowIJEE14add_static_dirEvEUlRNS_8responseENSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEEE_JSC_EEENS6_9enable_ifIXaaaantsr11black_magic10CallHelperIT_NS_11black_magic1SIJDpT0_EEEEE5valuentsr11black_magic10CallHelperISF_NSH_IJRNS_7requestESJ_EEEEE5valuesr11black_magic10CallHelperISF_NSH_IJS5_SJ_EEEEE5valueEvE4typeESM_S5_RKSF_DpOSI_ |
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> |
283 | | struct middleware_call_criteria_dynamic |
284 | | {}; |
285 | | |
286 | | template<> |
287 | | struct middleware_call_criteria_dynamic<false> |
288 | | { |
289 | | middleware_call_criteria_dynamic(const std::vector<int>& indices): |
290 | 0 | 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<> |
309 | | struct middleware_call_criteria_dynamic<true> |
310 | | { |
311 | | middleware_call_criteria_dynamic(const std::vector<int>& indices): |
312 | 0 | 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 |