#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 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 class CallCriteria, // Checks if QueryMW should be called in this context int N, typename Context, typename Container> typename std::enable_if<(N < std::tuple_size::type>::value), bool>::type middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx) { using CurrentMW = typename std::tuple_element::type>::type; if (!CallCriteria::value) { return middleware_call_helper(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(middlewares, req, res, ctx)) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); return true; } return false; } template class CallCriteria, int N, typename Context, typename Container> typename std::enable_if<(N >= std::tuple_size::type>::value), bool>::type middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) { return false; } template class CallCriteria, int N, typename Context, typename Container> typename std::enable_if<(N < 0)>::type after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) { } template class CallCriteria, int N, typename Context, typename Container> typename std::enable_if<(N == 0)>::type after_handlers_call_helper(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 (CallCriteria::value) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); } } template class CallCriteria, int N, typename Context, typename Container> typename std::enable_if<(N > 0)>::type after_handlers_call_helper(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 (CallCriteria::value) { after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); } after_handlers_call_helper(middlewares, ctx, req, res); } // A CallCriteria that accepts only global middleware template struct middleware_call_criteria_only_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; }; // wrapped_handler_call transparently wraps a handler call behind (req, res, args...) template typename std::enable_if::value>::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 typename std::enable_if::value && !black_magic::is_callable::value>::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 typename std::enable_if::value>::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::value>::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::value>::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 struct handler_middleware_wrapper { // CallCriteria bound to the current Middlewares pack template struct middleware_call_criteria { static constexpr bool value = black_magic::has_type>::value; }; template void operator()(crow::request& req, crow::response& res, Args&&... args) const { auto& ctx = *reinterpret_cast(req.middleware_context); auto& container = *reinterpret_cast(req.middleware_container); bool completed = middleware_call_helper(container, req, res, ctx); if (completed) return; auto glob_completion_handler = std::move(res.complete_request_handler_); auto completion_handler = [&ctx, &container, &req, &res, &glob_completion_handler] { after_handlers_call_helper< middleware_call_criteria, std::tuple_size::value - 1, typename App::context_t, typename App::mw_container_t>(container, ctx, req, res); glob_completion_handler(); }; res.complete_request_handler_ = std::move(completion_handler); wrapped_handler_call(req, res, f, std::forward(args)...); } F f; }; template struct handler_call_bridge { template using check_app_contains = typename black_magic::has_type; static_assert(black_magic::all_true<(std::is_base_of::value)...>::value, "Local middleware has to inherit crow::ILocalMiddleware"); static_assert(black_magic::all_true<(check_app_contains::value)...>::value, "Local middleware has to be listed in app middleware"); template void operator()(F&& f) const { auto wrapped = handler_middleware_wrapper{std::forward(f)}; tptr->operator()(std::move(wrapped)); } Route* tptr; }; } // namespace detail } // namespace crow