Crow  1.1
A C++ microframework for the web
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 
12 namespace crow
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>
284  {};
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
Local middleware should extend ILocalMiddleware.
Definition: middleware.h:17
Definition: middleware.h:49
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