From 3081e4e1a82a4efd8feff68850c4cc04af230cd7 Mon Sep 17 00:00:00 2001 From: ipknHama Date: Wed, 21 Sep 2016 23:11:06 +0900 Subject: Cleanup include folder into crow subfolder - only crow.h is exposed now --- include/routing.h | 1056 ----------------------------------------------------- 1 file changed, 1056 deletions(-) delete mode 100644 include/routing.h (limited to 'include/routing.h') diff --git a/include/routing.h b/include/routing.h deleted file mode 100644 index 337d258..0000000 --- a/include/routing.h +++ /dev/null @@ -1,1056 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "http_response.h" -#include "http_request.h" -#include "utility.h" -#include "logging.h" -#include "websocket.h" - -namespace crow -{ - class BaseRule - { - public: - BaseRule(std::string rule) - : rule_(std::move(rule)) - { - } - - virtual ~BaseRule() - { - } - - virtual void validate() = 0; - std::unique_ptr upgrade() - { - if (rule_to_upgrade_) - return std::move(rule_to_upgrade_); - return {}; - } - - virtual void handle(const request&, response&, const routing_params&) = 0; - virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&) - { - res = response(404); - res.end(); - } -#ifdef CROW_ENABLE_SSL - virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&) - { - res = response(404); - res.end(); - } -#endif - - uint32_t get_methods() - { - return methods_; - } - - protected: - uint32_t methods_{1<<(int)HTTPMethod::Get}; - - std::string rule_; - std::string name_; - - std::unique_ptr rule_to_upgrade_; - - friend class Router; - template - friend struct RuleParameterTraits; - }; - - - namespace detail - { - namespace routing_handler_call_helper - { - template - struct call_pair - { - using type = T; - static const int pos = Pos; - }; - - template - struct call_params - { - H1& handler; - 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) - { - cparams.handler( - cparams.req, - cparams.res, - cparams.params.template get(Args1::pos)... - ); - } - }; - - template - struct Wrapped - { - template - void set(Func f, typename std::enable_if< - !std::is_same>::type, const request&>::value - , int>::type = 0) - { - handler_ = ( -#ifdef CROW_CAN_USE_CPP14 - [f = std::move(f)] -#else - [f] -#endif - (const request&, response& res, Args... args){ - res = response(f(args...)); - res.end(); - }); - } - - template - struct req_handler_wrapper - { - req_handler_wrapper(Func f) - : f(std::move(f)) - { - } - - void operator()(const request& req, response& res, Args... args) - { - res = response(f(req, args...)); - res.end(); - } - - Func f; - }; - - template - void set(Func f, typename std::enable_if< - std::is_same>::type, const request&>::value && - !std::is_same>::type, response&>::value - , int>::type = 0) - { - handler_ = req_handler_wrapper(std::move(f)); - /*handler_ = ( - [f = std::move(f)] - (const request& req, response& res, Args... args){ - res = response(f(req, args...)); - res.end(); - });*/ - } - - template - void set(Func f, typename std::enable_if< - std::is_same>::type, const request&>::value && - std::is_same>::type, response&>::value - , int>::type = 0) - { - handler_ = std::move(f); - } - - template - struct handler_type_helper - { - using type = std::function; - using args_type = black_magic::S...>; - }; - - template - struct handler_type_helper - { - using type = std::function; - using args_type = black_magic::S...>; - }; - - template - struct handler_type_helper - { - using type = std::function; - using args_type = black_magic::S...>; - }; - - typename handler_type_helper::type handler_; - - void operator()(const request& req, response& res, const routing_params& params) - { - detail::routing_handler_call_helper::call< - detail::routing_handler_call_helper::call_params< - decltype(handler_)>, - 0, 0, 0, 0, - typename handler_type_helper::args_type, - black_magic::S<> - >()( - detail::routing_handler_call_helper::call_params< - decltype(handler_)> - {handler_, params, req, res} - ); - } - }; - - } - } - - class WebSocketRule : public BaseRule - { - using self_t = WebSocketRule; - public: - WebSocketRule(std::string rule) - : BaseRule(std::move(rule)) - { - } - - void validate() override - { - } - - void handle(const request&, response& res, const routing_params&) override - { - res = response(404); - res.end(); - } - - void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override - { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); - } -#ifdef CROW_ENABLE_SSL - void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override - { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); - } -#endif - - template - self_t& onopen(Func f) - { - open_handler_ = f; - return *this; - } - - template - self_t& onmessage(Func f) - { - message_handler_ = f; - return *this; - } - - template - self_t& onclose(Func f) - { - close_handler_ = f; - return *this; - } - - template - self_t& onerror(Func f) - { - error_handler_ = f; - return *this; - } - - protected: - std::function open_handler_; - std::function message_handler_; - std::function close_handler_; - std::function error_handler_; - }; - - template - struct RuleParameterTraits - { - using self_t = T; - WebSocketRule& websocket() - { - auto p =new WebSocketRule(((self_t*)this)->rule_); - ((self_t*)this)->rule_to_upgrade_.reset(p); - return *p; - } - - self_t& name(std::string name) noexcept - { - ((self_t*)this)->name_ = std::move(name); - return (self_t&)*this; - } - - self_t& methods(HTTPMethod method) - { - ((self_t*)this)->methods_ = 1 << (int)method; - return (self_t&)*this; - } - - template - self_t& methods(HTTPMethod method, MethodArgs ... args_method) - { - methods(args_method...); - ((self_t*)this)->methods_ |= 1 << (int)method; - return (self_t&)*this; - } - - }; - - class DynamicRule : public BaseRule, public RuleParameterTraits - { - public: - - DynamicRule(std::string rule) - : BaseRule(std::move(rule)) - { - } - - void validate() override - { - if (!erased_handler_) - { - throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_); - } - } - - void handle(const request& req, response& res, const routing_params& params) override - { - erased_handler_(req, res, params); - } - - template - void operator()(Func f) - { -#ifdef CROW_MSVC_WORKAROUND - using function_t = utility::function_traits; -#else - using function_t = utility::function_traits; -#endif - erased_handler_ = wrap(std::move(f), black_magic::gen_seq()); - } - - // enable_if Arg1 == request && Arg2 == response - // enable_if Arg1 == request && Arg2 != resposne - // enable_if Arg1 != request -#ifdef CROW_MSVC_WORKAROUND - template -#else - template -#endif - std::function - wrap(Func f, black_magic::seq) - { -#ifdef CROW_MSVC_WORKAROUND - using function_t = utility::function_traits; -#else - using function_t = utility::function_traits; -#endif - if (!black_magic::is_parameter_tag_compatible( - black_magic::get_parameter_tag_runtime(rule_.c_str()), - black_magic::compute_parameter_tag_from_args_list< - typename function_t::template arg...>::value)) - { - throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_); - } - auto ret = detail::routing_handler_call_helper::Wrapped...>(); - ret.template set< - typename function_t::template arg... - >(std::move(f)); - return ret; - } - - template - void operator()(std::string name, Func&& f) - { - name_ = std::move(name); - (*this).template operator()(std::forward(f)); - } - private: - std::function erased_handler_; - - }; - - template - class TaggedRule : public BaseRule, public RuleParameterTraits> - { - public: - using self_t = TaggedRule; - - TaggedRule(std::string rule) - : BaseRule(std::move(rule)) - { - } - - void validate() override - { - if (!handler_) - { - 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 parameters"); - 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)](const request&, response& res, Args ... args){ - res = response(f(args...)); - res.end(); - }; - } - - 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 parameters"); - 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_ = [f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){ - res = response(f(req, args...)); - res.end(); - }; - } - - 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 parameters"); - static_assert(std::is_same(), std::declval(), std::declval()...))>::value, - "Handler function with response argument should have void return type"); - - handler_ = std::move(f); - } - - 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 - { - detail::routing_handler_call_helper::call< - detail::routing_handler_call_helper::call_params< - decltype(handler_)>, - 0, 0, 0, 0, - black_magic::S, - black_magic::S<> - >()( - detail::routing_handler_call_helper::call_params< - decltype(handler_)> - {handler_, params, req, res} - ); - } - - private: - std::function handler_; - - }; - - const int RULE_SPECIAL_REDIRECT_SLASH = 1; - - class Trie - { - public: - struct Node - { - unsigned rule_index{}; - std::array param_childrens{}; - std::unordered_map children; - - bool IsSimpleNode() const - { - return - !rule_index && - std::all_of( - std::begin(param_childrens), - std::end(param_childrens), - [](unsigned x){ return !x; }); - } - }; - - Trie() : nodes_(1) - { - } - -private: - void optimizeNode(Node* node) - { - for(auto x : node->param_childrens) - { - if (!x) - continue; - Node* child = &nodes_[x]; - optimizeNode(child); - } - if (node->children.empty()) - return; - bool mergeWithChild = true; - for(auto& kv : node->children) - { - Node* child = &nodes_[kv.second]; - if (!child->IsSimpleNode()) - { - mergeWithChild = false; - break; - } - } - if (mergeWithChild) - { - decltype(node->children) merged; - for(auto& kv : node->children) - { - Node* child = &nodes_[kv.second]; - for(auto& child_kv : child->children) - { - merged[kv.first + child_kv.first] = child_kv.second; - } - } - node->children = std::move(merged); - optimizeNode(node); - } - else - { - for(auto& kv : node->children) - { - Node* child = &nodes_[kv.second]; - optimizeNode(child); - } - } - } - - void optimize() - { - optimizeNode(head()); - } - -public: - void validate() - { - if (!head()->IsSimpleNode()) - throw std::runtime_error("Internal error: Trie header should be simple!"); - optimize(); - } - - std::pair find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const - { - routing_params empty; - if (params == nullptr) - params = ∅ - - unsigned found{}; - routing_params match_params; - - if (node == nullptr) - node = head(); - if (pos == req_url.size()) - return {node->rule_index, *params}; - - auto update_found = [&found, &match_params](std::pair& ret) - { - if (ret.first && (!found || found > ret.first)) - { - found = ret.first; - match_params = std::move(ret.second); - } - }; - - if (node->param_childrens[(int)ParamType::INT]) - { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-') - { - char* eptr; - errno = 0; - long long int value = strtoll(req_url.data()+pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data()+pos) - { - params->int_params.push_back(value); - auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::INT]], eptr - req_url.data(), params); - update_found(ret); - params->int_params.pop_back(); - } - } - } - - if (node->param_childrens[(int)ParamType::UINT]) - { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+') - { - char* eptr; - errno = 0; - unsigned long long int value = strtoull(req_url.data()+pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data()+pos) - { - params->uint_params.push_back(value); - auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::UINT]], eptr - req_url.data(), params); - update_found(ret); - params->uint_params.pop_back(); - } - } - } - - if (node->param_childrens[(int)ParamType::DOUBLE]) - { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') - { - char* eptr; - errno = 0; - double value = strtod(req_url.data()+pos, &eptr); - if (errno != ERANGE && eptr != req_url.data()+pos) - { - params->double_params.push_back(value); - auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::DOUBLE]], eptr - req_url.data(), params); - update_found(ret); - params->double_params.pop_back(); - } - } - } - - if (node->param_childrens[(int)ParamType::STRING]) - { - size_t epos = pos; - for(; epos < req_url.size(); epos ++) - { - if (req_url[epos] == '/') - break; - } - - if (epos != pos) - { - params->string_params.push_back(req_url.substr(pos, epos-pos)); - auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::STRING]], epos, params); - update_found(ret); - params->string_params.pop_back(); - } - } - - if (node->param_childrens[(int)ParamType::PATH]) - { - size_t epos = req_url.size(); - - if (epos != pos) - { - params->string_params.push_back(req_url.substr(pos, epos-pos)); - auto ret = find(req_url, &nodes_[node->param_childrens[(int)ParamType::PATH]], epos, params); - update_found(ret); - params->string_params.pop_back(); - } - } - - for(auto& kv : node->children) - { - const std::string& fragment = kv.first; - const Node* child = &nodes_[kv.second]; - - if (req_url.compare(pos, fragment.size(), fragment) == 0) - { - auto ret = find(req_url, child, pos + fragment.size(), params); - update_found(ret); - } - } - - return {found, match_params}; - } - - void add(const std::string& url, unsigned rule_index) - { - unsigned idx{0}; - - for(unsigned i = 0; i < url.size(); i ++) - { - char c = url[i]; - if (c == '<') - { - static struct ParamTraits - { - ParamType type; - std::string name; - } paramTraits[] = - { - { ParamType::INT, "" }, - { ParamType::UINT, "" }, - { ParamType::DOUBLE, "" }, - { ParamType::DOUBLE, "" }, - { ParamType::STRING, "" }, - { ParamType::STRING, "" }, - { ParamType::PATH, "" }, - }; - - for(auto& x:paramTraits) - { - if (url.compare(i, x.name.size(), x.name) == 0) - { - if (!nodes_[idx].param_childrens[(int)x.type]) - { - auto new_node_idx = new_node(); - nodes_[idx].param_childrens[(int)x.type] = new_node_idx; - } - idx = nodes_[idx].param_childrens[(int)x.type]; - i += x.name.size(); - break; - } - } - - i --; - } - else - { - std::string piece(&c, 1); - if (!nodes_[idx].children.count(piece)) - { - auto new_node_idx = new_node(); - nodes_[idx].children.emplace(piece, new_node_idx); - } - idx = nodes_[idx].children[piece]; - } - } - if (nodes_[idx].rule_index) - throw std::runtime_error("handler already exists for " + url); - nodes_[idx].rule_index = rule_index; - } - private: - void debug_node_print(Node* n, int level) - { - for(int i = 0; i < (int)ParamType::MAX; i ++) - { - if (n->param_childrens[i]) - { - CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "("<param_childrens[i]<<") "*/; - switch((ParamType)i) - { - case ParamType::INT: - CROW_LOG_DEBUG << ""; - break; - case ParamType::UINT: - CROW_LOG_DEBUG << ""; - break; - case ParamType::DOUBLE: - CROW_LOG_DEBUG << ""; - break; - case ParamType::STRING: - CROW_LOG_DEBUG << ""; - break; - case ParamType::PATH: - CROW_LOG_DEBUG << ""; - break; - default: - CROW_LOG_DEBUG << ""; - break; - } - - debug_node_print(&nodes_[n->param_childrens[i]], level+1); - } - } - for(auto& kv : n->children) - { - CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "(" << kv.second << ") "*/ << kv.first; - debug_node_print(&nodes_[kv.second], level+1); - } - } - - public: - void debug_print() - { - debug_node_print(head(), 0); - } - - private: - const Node* head() const - { - return &nodes_.front(); - } - - Node* head() - { - return &nodes_.front(); - } - - unsigned new_node() - { - nodes_.resize(nodes_.size()+1); - return nodes_.size() - 1; - } - - std::vector nodes_; - }; - - class Router - { - public: - Router() : rules_(2) - { - } - - DynamicRule& new_rule_dynamic(const std::string& rule) - { - auto ruleObject = new DynamicRule(rule); - - internal_add_rule_object(rule, ruleObject); - - return *ruleObject; - } - - 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); - - internal_add_rule_object(rule, ruleObject); - - return *ruleObject; - } - - void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject) - { - rules_.emplace_back(ruleObject); - trie_.add(rule, rules_.size() - 1); - - // directory case: - // request to `/about' url matches `/about/' rule - if (rule.size() > 1 && rule.back() == '/') - { - std::string rule_without_trailing_slash = rule; - rule_without_trailing_slash.pop_back(); - trie_.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH); - } - } - - void validate() - { - trie_.validate(); - for(auto& rule:rules_) - { - if (rule) - { - auto upgraded = rule->upgrade(); - if (upgraded) - rule = std::move(upgraded); - rule->validate(); - } - } - } - - template - void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) - { - auto found = trie_.find(req.url); - unsigned rule_index = found.first; - if (!rule_index) - { - CROW_LOG_DEBUG << "Cannot match rules " << req.url; - res = response(404); - res.end(); - return; - } - - if (rule_index >= rules_.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) - { - CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url; - res = response(301); - - // TODO absolute url building - if (req.get_header_value("Host").empty()) - { - res.add_header("Location", req.url + "/"); - } - else - { - res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/"); - } - res.end(); - return; - } - - if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0) - { - CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods(); - res = response(404); - res.end(); - return; - } - - CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods(); - - // any uncaught exceptions become 500s - try - { - rules_[rule_index]->handle_upgrade(req, res, std::move(adaptor)); - } - catch(std::exception& e) - { - CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = response(500); - res.end(); - return; - } - catch(...) - { - CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available."; - res = response(500); - res.end(); - return; - } - } - - void handle(const request& req, response& res) - { - auto found = trie_.find(req.url); - - unsigned rule_index = found.first; - - if (!rule_index) - { - CROW_LOG_DEBUG << "Cannot match rules " << req.url; - res = response(404); - res.end(); - return; - } - - if (rule_index >= rules_.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) - { - CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url; - res = response(301); - - // TODO absolute url building - if (req.get_header_value("Host").empty()) - { - res.add_header("Location", req.url + "/"); - } - else - { - res.add_header("Location", "http://" + req.get_header_value("Host") + req.url + "/"); - } - res.end(); - return; - } - - if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0) - { - CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods(); - res = response(404); - res.end(); - return; - } - - CROW_LOG_DEBUG << "Matched rule '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods(); - - // any uncaught exceptions become 500s - try - { - rules_[rule_index]->handle(req, res, found.second); - } - catch(std::exception& e) - { - CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = response(500); - res.end(); - return; - } - catch(...) - { - CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available."; - res = response(500); - res.end(); - return; - } - } - - void debug_print() - { - trie_.debug_print(); - } - - private: - std::vector> rules_; - Trie trie_; - }; -} -- cgit v1.2.3-54-g00ecf