#pragma once #include #include #include #include #include #include #include "common.h" #include "http_response.h" #include "http_request.h" #include "utility.h" #include "logging.h" namespace crow { class BaseRule { public: virtual ~BaseRule() { } virtual void validate() = 0; virtual void handle(const request&, response&, const routing_params&) = 0; protected: }; template class TaggedRule : public BaseRule { private: template struct call_params { H1& handler; H2& handler_with_req; H3& handler_with_req_res; const routing_params& params; const request& req; response& res; }; template struct call { }; template struct call, black_magic::S> { void operator()(F cparams) { using pushed = typename black_magic::S::template push_back>; call, pushed>()(cparams); } }; template struct call, black_magic::S> { void operator()(F cparams) { using pushed = typename black_magic::S::template push_back>; call, pushed>()(cparams); } }; template struct call, black_magic::S> { void operator()(F cparams) { using pushed = typename black_magic::S::template push_back>; call, pushed>()(cparams); } }; template struct call, black_magic::S> { void operator()(F cparams) { using pushed = typename black_magic::S::template push_back>; call, pushed>()(cparams); } }; template struct call, black_magic::S> { void operator()(F cparams) { if (cparams.handler) { cparams.res = cparams.handler( cparams.params.template get(Args1::pos)... ); cparams.res.end(); return; } if (cparams.handler_with_req) { cparams.res = cparams.handler_with_req( cparams.req, cparams.params.template get(Args1::pos)... ); cparams.res.end(); return; } if (cparams.handler_with_req_res) { cparams.handler_with_req_res( cparams.req, cparams.res, cparams.params.template get(Args1::pos)... ); return; } #ifdef CROW_ENABLE_LOGGING std::cerr << "ERROR cannot find handler" << std::endl; #endif // we already found matched url; this is server error cparams.res = response(500); } }; public: using self_t = TaggedRule; TaggedRule(std::string rule) : rule_(std::move(rule)) { } self_t& name(std::string name) noexcept { name_ = std::move(name); return *this; } self_t& methods(HTTPMethod method) { methods_ = 1<<(int)method; } template self_t& methods(HTTPMethod method, MethodArgs ... args_method) { methods(args_method...); methods_ |= 1<<(int)method; } void validate() { if (!handler_ && !handler_with_req_ && !handler_with_req_res_) { throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_); } } template typename std::enable_if>::value, void>::type operator()(Func&& f) { static_assert(black_magic::CallHelper>::value || black_magic::CallHelper>::value , "Handler type is mismatched with URL paramters"); static_assert(!std::is_same()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); handler_ = [f = std::move(f)](Args ... args){ return response(f(args...)); }; handler_with_req_ = nullptr; handler_with_req_res_ = nullptr; } template typename std::enable_if< !black_magic::CallHelper>::value && black_magic::CallHelper>::value, void>::type operator()(Func&& f) { static_assert(black_magic::CallHelper>::value || black_magic::CallHelper>::value, "Handler type is mismatched with URL paramters"); static_assert(!std::is_same(), std::declval()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); handler_with_req_ = [f = std::move(f)](const crow::request& req, Args ... args){ return response(f(req, args...)); }; handler_ = nullptr; handler_with_req_res_ = nullptr; } template typename std::enable_if< !black_magic::CallHelper>::value && !black_magic::CallHelper>::value, void>::type operator()(Func&& f) { static_assert(black_magic::CallHelper>::value || black_magic::CallHelper>::value || black_magic::CallHelper>::value , "Handler type is mismatched with URL paramters"); static_assert(std::is_same(), std::declval(), std::declval()...))>::value, "Handler function with response argument should have void return type"); handler_with_req_res_ = std::move(f); //[f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){ // f(req, response, args...); //}; handler_ = nullptr; handler_with_req_ = nullptr; } template void operator()(std::string name, Func&& f) { name_ = std::move(name); (*this).template operator()(std::forward(f)); } void handle(const request& req, response& res, const routing_params& params) override { call< call_params< decltype(handler_), decltype(handler_with_req_), decltype(handler_with_req_res_)>, 0, 0, 0, 0, black_magic::S, black_magic::S<> >()( call_params< decltype(handler_), decltype(handler_with_req_), decltype(handler_with_req_res_)> {handler_, handler_with_req_, handler_with_req_res_, params, req, res} ); } private: std::function handler_; std::function handler_with_req_; std::function handler_with_req_res_; std::string rule_; std::string name_; uint32_t methods_{1<<(int)HTTPMethod::GET}; template struct call_pair { using type = T; static const int pos = Pos; }; friend class Router; }; class Trie { public: struct Node { unsigned rule_index{}; std::array param_childrens{}; std::unordered_map children; bool IsSimpleNode() const; }; Trie(); void validate(); void add(const std::string& url, unsigned rule_index); std::pair find( const request& req, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const; void debug_print(); private: void debug_node_print(Node* n, int level); void optimizeNode(Node* node); void optimize(); const Node* head() const; Node* head(); unsigned new_node(); private: std::vector nodes_; }; class Router { public: Router(); template typename black_magic::arguments::type::template rebind& new_rule_tagged(const std::string& rule) { using RuleT = typename black_magic::arguments::type::template rebind; auto ruleObject = new RuleT(rule); rules_.emplace_back(ruleObject); trie_.add(rule, rules_.size() - 1); return *ruleObject; } void validate(); void handle(const request& req, response& res); void debug_print(); private: std::vector> rules_; Trie trie_; }; }