middleware.h 14 KB

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