#pragma once #include "crow/http_request.h" #include "crow/http_response.h" #include "crow/utility.h" #include #include #include #include namespace crow { /// Local middleware should extend ILocalMiddleware struct ILocalMiddleware { using call_global = std::false_type; }; namespace detail { template struct check_before_handle_arity_3_const { template struct get {}; }; template struct check_before_handle_arity_3 { template struct get {}; }; template struct check_after_handle_arity_3_const { template struct get {}; }; template struct check_after_handle_arity_3 { template struct get {}; }; template struct check_global_call_false { template::type = true> struct get {}; }; template struct is_before_handle_arity_3_impl { template static std::true_type f(typename check_before_handle_arity_3_const::template get*); template static std::true_type f(typename check_before_handle_arity_3::template get*); template static std::false_type f(...); public: static const bool value = decltype(f(nullptr))::value; }; template struct is_after_handle_arity_3_impl { template static std::true_type f(typename check_after_handle_arity_3_const::template get*); template static std::true_type f(typename check_after_handle_arity_3::template get*); template static std::false_type f(...); public: static constexpr bool value = decltype(f(nullptr))::value; }; template struct is_middleware_global { template static std::false_type f(typename check_global_call_false::template get*); template static std::true_type f(...); static const bool value = decltype(f(nullptr))::value; }; template typename std::enable_if::value>::type before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { mw.before_handle(req, res, ctx.template get(), ctx); } template typename std::enable_if::value>::type before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { mw.before_handle(req, res, ctx.template get()); } template typename std::enable_if::value>::type after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { mw.after_handle(req, res, ctx.template get(), ctx); } template typename std::enable_if::value>::type after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { mw.after_handle(req, res, ctx.template get()); } template typename std::enable_if<(N < std::tuple_size::type>::value), bool>::type middleware_call_helper(const CallCriteria& cc, Container& middlewares, request& req, response& res, Context& ctx) { using CurrentMW = typename std::tuple_element::type>::type; if (!cc.template enabled(N)) { return middleware_call_helper(cc, middlewares, req, res, ctx); } using parent_context_t = typename Context::template partial; before_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); if (res.is_completed()) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); return true; } if (middleware_call_helper(cc, middlewares, req, res, ctx)) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); return true; } return false; } template typename std::enable_if<(N >= std::tuple_size::type>::value), bool>::type middleware_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) { return false; } template typename std::enable_if<(N < 0)>::type after_handlers_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) { } template typename std::enable_if<(N == 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res) { using parent_context_t = typename Context::template partial; using CurrentMW = typename std::tuple_element::type>::type; if (cc.template enabled(N)) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); } } template typename std::enable_if<(N > 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res) { using parent_context_t = typename Context::template partial; using CurrentMW = typename std::tuple_element::type>::type; if (cc.template enabled(N)) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); } after_handlers_call_helper(cc, middlewares, ctx, req, res); } // A CallCriteria that accepts only global middleware struct middleware_call_criteria_only_global { template constexpr bool enabled(int) const { return is_middleware_global::value; } }; template typename std::enable_if>::value, void>::type wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args) { static_assert(!std::is_same()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable"); res = crow::response(f(std::forward(args)...)); res.end(); } template typename std::enable_if< !black_magic::CallHelper>::value && black_magic::CallHelper>::value, void>::type wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args) { static_assert(!std::is_same(), std::declval()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable"); res = crow::response(f(req, std::forward(args)...)); res.end(); } template typename std::enable_if< !black_magic::CallHelper>::value && !black_magic::CallHelper>::value && black_magic::CallHelper>::value, void>::type wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args) { static_assert(std::is_same(), std::declval()...))>::value, "Handler function with response argument should have void return type"); f(res, std::forward(args)...); } template typename std::enable_if< !black_magic::CallHelper>::value && !black_magic::CallHelper>::value && !black_magic::CallHelper>::value && black_magic::CallHelper>::value, void>::type wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args) { static_assert(std::is_same(), std::declval(), std::declval()...))>::value, "Handler function with response argument should have void return type"); f(req, res, std::forward(args)...); } // wrapped_handler_call transparently wraps a handler call behind (req, res, args...) template typename std::enable_if< !black_magic::CallHelper>::value && !black_magic::CallHelper>::value && !black_magic::CallHelper>::value && !black_magic::CallHelper>::value, void>::type wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args) { static_assert(std::is_same(), std::declval(), std::declval()...))>::value, "Handler function with response argument should have void return type"); f(req, res, std::forward(args)...); } template struct middleware_call_criteria_dynamic {}; template<> struct middleware_call_criteria_dynamic { middleware_call_criteria_dynamic(const std::vector& indices): indices(indices), slider(0) {} template bool enabled(int mw_index) const { if (slider < int(indices.size()) && indices[slider] == mw_index) { slider++; return true; } return false; } private: const std::vector& indices; mutable int slider; }; template<> struct middleware_call_criteria_dynamic { middleware_call_criteria_dynamic(const std::vector& indices): indices(indices), slider(int(indices.size()) - 1) {} template bool enabled(int mw_index) const { if (slider >= 0 && indices[slider] == mw_index) { slider--; return true; } return false; } private: const std::vector& indices; mutable int slider; }; } // namespace detail } // namespace crow