#pragma once #include #include #include #include #include #include #include "common.h" #include "http_response.h" #include "http_request.h" #include "utility.h" //TEST #include namespace flask { class BaseRule { public: BaseRule(std::string rule) : rule_(std::move(rule)) { } virtual ~BaseRule() { } BaseRule& name(std::string name) { name_ = std::move(name); return *this; } virtual void validate() = 0; virtual response handle(const request&, const routing_params&) = 0; protected: std::string rule_; std::string name_; }; class Rule : public BaseRule { public: Rule(std::string rule) : BaseRule(std::move(rule)) { } Rule& name(std::string name) { name_ = std::move(name); return *this; } template void operator()(Func&& f) { static_assert(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, flask::resposne"); handler_ = [f = std::move(f)]{ return response(f()); }; } template void operator()(std::string name, Func&& f) { static_assert(black_magic::CallHelper>::value, "Handler type is mismatched with URL paramters"); name_ = std::move(name); handler_ = [f = std::move(f)]{ return response(f()); }; } void validate() { if (!handler_) { throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_); } } response handle(const request&, const routing_params&) { return handler_(); } protected: std::function handler_; }; template class TaggedRule : public BaseRule { private: template struct call { }; template struct call, black_magic::S> { response operator()(F& handler, const routing_params& params) { using pushed = typename black_magic::S::template push_back>; return call, pushed>()(handler, params); } }; template struct call, black_magic::S> { response operator()(F& handler, const routing_params& params) { using pushed = typename black_magic::S::template push_back>; return call, pushed>()(handler, params); } }; template struct call, black_magic::S> { response operator()(F& handler, const routing_params& params) { using pushed = typename black_magic::S::template push_back>; return call, pushed>()(handler, params); } }; template struct call, black_magic::S> { response operator()(F& handler, const routing_params& params) { using pushed = typename black_magic::S::template push_back>; return call, pushed>()(handler, params); } }; template struct call, black_magic::S> { response operator()(F& handler, const routing_params& params) { return handler( params.get(Args1::pos)... ); //return response(500); } }; public: TaggedRule(std::string rule) : BaseRule(std::move(rule)) { } TaggedRule& name(std::string name) { name_ = std::move(name); return *this; } void validate() { } template void operator()(Func&& f) { static_assert(black_magic::CallHelper>::value, "Handler type is mismatched with URL paramters"); handler_ = [f = std::move(f)](Args ... args){ return response(f(args...)); }; } template void operator()(std::string name, Func&& f) { static_assert(black_magic::CallHelper>::value, "Handler type is mismatched with URL paramters"); name_ = std::move(name); handler_ = [f = std::move(f)](Args ... args){ return response(f(args...)); }; } response handle(const request&, const routing_params& params) { //return handler_(); return call, black_magic::S<>>()(handler_, params); //return response(500); } private: std::function handler_; template struct call_pair { using type = T; static const int pos = Pos; }; }; class Trie { public: struct Node { std::unordered_map children; unsigned rule_index{}; std::array param_childrens{}; 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& 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 request& req, 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 = 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, &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, &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, &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, &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, &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, child, pos + fragment.size(), params); update_found(ret); } } return {found, std::move(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 == '<') { bool found = false; 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 it = std::begin(paramTraits); it != std::end(paramTraits); ++it) { if (url.compare(i, it->name.size(), it->name) == 0) { idx = nodes_[idx].param_childrens[(int)it->type] = new_node(); i += it->name.size(); found = true; break; } } if (!found) { throw std::runtime_error("Invalid parameter type: " + url + " (" + boost::lexical_cast(i) + ")"); } i --; } else { std::string piece(&c, 1); if (!nodes_[idx].children.count(piece)) { nodes_[idx].children.emplace(piece, new_node()); } 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: 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_(1) {} 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; } Rule& new_rule(const std::string& rule) { Rule* r(new Rule(rule)); rules_.emplace_back(r); trie_.add(rule, rules_.size() - 1); return *r; } void validate() { trie_.validate(); for(auto& rule:rules_) { if (rule) rule->validate(); } } response handle(const request& req) { auto found = trie_.find(req); unsigned rule_index = found.first; if (!rule_index) return response(404); if (rule_index >= rules_.size()) throw std::runtime_error("Trie internal structure corrupted!"); return rules_[rule_index]->handle(req, found.second); } private: std::vector> rules_; Trie trie_; }; }