From 031615ac866cc3c8f1900dd4b4aae2106ad31230 Mon Sep 17 00:00:00 2001 From: ipknHama Date: Thu, 7 Aug 2014 01:18:33 +0900 Subject: source resturcturing + CMake --- include/routing.h | 656 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 include/routing.h (limited to 'include/routing.h') diff --git a/include/routing.h b/include/routing.h new file mode 100644 index 0000000..28700c1 --- /dev/null +++ b/include/routing.h @@ -0,0 +1,656 @@ +#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 + { + 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 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 = 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, &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, 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_(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; + } + + void validate() + { + trie_.validate(); + for(auto& rule:rules_) + { + if (rule) + rule->validate(); + } + } + + void handle(const request& req, response& res) + { + auto found = trie_.find(req); + + 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!"); + + CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'"; + + rules_[rule_index]->handle(req, res, found.second); + } + + void debug_print() + { + trie_.debug_print(); + } + + private: + std::vector> rules_; + Trie trie_; + }; +} -- cgit v1.2.3-54-g00ecf From 1b83b78c8344b40355d9238d2fbeaf9a9b348ef9 Mon Sep 17 00:00:00 2001 From: ipknHama Date: Thu, 7 Aug 2014 05:25:18 +0900 Subject: spliting header implementation into cpp files, routing.cpp created --- include/common.h | 2 +- include/json.h | 29 ++--- include/routing.h | 362 ++++------------------------------------------------- src/CMakeLists.txt | 1 + src/routing.cpp | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 399 insertions(+), 357 deletions(-) create mode 100644 src/routing.cpp (limited to 'include/routing.h') diff --git a/include/common.h b/include/common.h index 048a7fc..5720b46 100644 --- a/include/common.h +++ b/include/common.h @@ -18,7 +18,7 @@ namespace crow TRACE, }; - std::string method_name(HTTPMethod method) + inline std::string method_name(HTTPMethod method) { switch(method) { diff --git a/include/json.h b/include/json.h index b58a75e..bcfa24e 100644 --- a/include/json.h +++ b/include/json.h @@ -29,7 +29,7 @@ namespace crow namespace json { - void escape(const std::string& str, std::string& ret) + inline void escape(const std::string& str, std::string& ret) { ret.reserve(ret.size() + str.size()+str.size()/4); for(char c:str) @@ -63,7 +63,7 @@ namespace crow } } } - std::string escape(const std::string& str) + inline std::string escape(const std::string& str) { std::string ret; escape(str, ret); @@ -596,50 +596,47 @@ namespace crow namespace detail { } - bool operator == (const rvalue& l, const std::string& r) + inline bool operator == (const rvalue& l, const std::string& r) { return l.s() == r; } - bool operator == (const std::string& l, const rvalue& r) + inline bool operator == (const std::string& l, const rvalue& r) { return l == r.s(); } - bool operator != (const rvalue& l, const std::string& r) + inline bool operator != (const rvalue& l, const std::string& r) { return l.s() != r; } - bool operator != (const std::string& l, const rvalue& r) + inline bool operator != (const std::string& l, const rvalue& r) { return l != r.s(); } - bool operator == (const rvalue& l, double r) + inline bool operator == (const rvalue& l, double r) { return l.d() == r; } - bool operator == (double l, const rvalue& r) + inline bool operator == (double l, const rvalue& r) { return l == r.d(); } - bool operator != (const rvalue& l, double r) + inline bool operator != (const rvalue& l, double r) { return l.d() != r; } - bool operator != (double l, const rvalue& r) + inline bool operator != (double l, const rvalue& r) { return l != r.d(); } - //inline rvalue decode(const std::string& s) - //{ - //} inline rvalue load_nocopy_internal(char* data, size_t size) { //static const char* escaped = "\"\\/\b\f\n\r\t"; @@ -1298,13 +1295,13 @@ namespace crow friend std::string dump(const wvalue& v); }; - void dump_string(const std::string& str, std::string& out) + inline void dump_string(const std::string& str, std::string& out) { out.push_back('"'); escape(str, out); out.push_back('"'); } - void dump_internal(const wvalue& v, std::string& out) + inline void dump_internal(const wvalue& v, std::string& out) { switch(v.t_) { @@ -1362,7 +1359,7 @@ namespace crow } } - std::string dump(const wvalue& v) + inline std::string dump(const wvalue& v) { std::string ret; ret.reserve(v.estimate_length()); diff --git a/include/routing.h b/include/routing.h index 28700c1..2483bb9 100644 --- a/include/routing.h +++ b/include/routing.h @@ -279,329 +279,42 @@ namespace crow 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; }); - } + bool IsSimpleNode() const; }; - 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 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 = 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, &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(); + Trie(); - 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(); - } - } + void validate(); - for(auto& kv : node->children) - { - const std::string& fragment = kv.first; - const Node* child = &nodes_[kv.second]; + void add(const std::string& url, unsigned rule_index); - if (req.url.compare(pos, fragment.size(), fragment) == 0) - { - auto ret = find(req, child, pos + fragment.size(), params); - update_found(ret); - } - } + std::pair find( + const request& req, + const Node* node = nullptr, + unsigned pos = 0, + routing_params* params = nullptr) const; - return {found, match_params}; - } + void debug_print(); - 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); - } + void debug_node_print(Node* n, int level); - private: - const Node* head() const - { - return &nodes_.front(); - } + void optimizeNode(Node* node); + void optimize(); - Node* head() - { - return &nodes_.front(); - } + const Node* head() const; + Node* head(); - unsigned new_node() - { - nodes_.resize(nodes_.size()+1); - return nodes_.size() - 1; - } + unsigned new_node(); + private: std::vector nodes_; }; class Router { public: - Router() : rules_(1) {} + Router(); template typename black_magic::arguments::type::template rebind& new_rule_tagged(const std::string& rule) { @@ -612,42 +325,11 @@ public: return *ruleObject; } - void validate() - { - trie_.validate(); - for(auto& rule:rules_) - { - if (rule) - rule->validate(); - } - } - - void handle(const request& req, response& res) - { - auto found = trie_.find(req); - - unsigned rule_index = found.first; - - if (!rule_index) - { - CROW_LOG_DEBUG << "Cannot match rules " << req.url; - res = response(404); - res.end(); - return; - } + void validate(); - if (rule_index >= rules_.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'"; - - rules_[rule_index]->handle(req, res, found.second); - } - - void debug_print() - { - trie_.debug_print(); - } + void handle(const request& req, response& res); + + void debug_print(); private: std::vector> rules_; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ac6f63..5cb30b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ set(CROW_SRCS #${PROJECT_SOURCE_DIR}/some.cpp #${PROJECT_SOURCE_DIR}/someother.cpp ${PROJECT_SOURCE_DIR}/../http-parser/http_parser.c +routing.cpp ) set_source_files_properties(${PROJECT_SOURCE_DIR}/../http-parser/http_parser.c PROPERTIES LANGUAGE C ) diff --git a/src/routing.cpp b/src/routing.cpp new file mode 100644 index 0000000..04a5e1c --- /dev/null +++ b/src/routing.cpp @@ -0,0 +1,362 @@ +#include "routing.h" + +namespace crow +{ + bool Trie::Node::IsSimpleNode() const + { + return + !rule_index && + std::all_of( + std::begin(param_childrens), + std::end(param_childrens), + [](unsigned x){ return !x; }); + } + + Trie::Trie() + : nodes_(1) + { + } + + void Trie::optimizeNode(Trie::Node* node) + { + for(auto x : node->param_childrens) + { + if (!x) + continue; + Trie::Node* child = &nodes_[x]; + optimizeNode(child); + } + if (node->children.empty()) + return; + bool mergeWithChild = true; + for(auto& kv : node->children) + { + Trie::Node* child = &nodes_[kv.second]; + if (!child->IsSimpleNode()) + { + mergeWithChild = false; + break; + } + } + if (mergeWithChild) + { + decltype(node->children) merged; + for(auto& kv : node->children) + { + Trie::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) + { + Trie::Node* child = &nodes_[kv.second]; + optimizeNode(child); + } + } + } + + void Trie::optimize() + { + optimizeNode(head()); + } + + void Trie::validate() + { + if (!head()->IsSimpleNode()) + throw std::runtime_error("Internal error: Trie header should be simple!"); + optimize(); + } + + std::pair Trie::find( + const request& req, + const Trie::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, &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 Trie::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, match_params}; + } + + void Trie::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; + } + + void Trie::debug_node_print(Trie::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); + } + } + + void Trie::debug_print() + { + debug_node_print(head(), 0); + } + + const Trie::Node* Trie::head() const + { + return &nodes_.front(); + } + + Trie::Node* Trie::head() + { + return &nodes_.front(); + } + + unsigned Trie::new_node() + { + nodes_.resize(nodes_.size()+1); + return nodes_.size() - 1; + } + + Router::Router() + : rules_(1) + { + } + + void Router::validate() + { + trie_.validate(); + for(auto& rule:rules_) + { + if (rule) + rule->validate(); + } + } + + void Router::handle(const request& req, response& res) + { + auto found = trie_.find(req); + + 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!"); + + CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'"; + + rules_[rule_index]->handle(req, res, found.second); + } + + void Router::debug_print() + { + trie_.debug_print(); + } +} -- cgit v1.2.3-54-g00ecf From 81fcf4af0127d93e817877d0ce1223b606d30fd6 Mon Sep 17 00:00:00 2001 From: ipknHama Date: Thu, 7 Aug 2014 06:18:21 +0900 Subject: decide to be header only --- include/routing.h | 362 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/CMakeLists.txt | 1 - src/routing.cpp | 362 ----------------------------------------------------- 3 files changed, 340 insertions(+), 385 deletions(-) delete mode 100644 src/routing.cpp (limited to 'include/routing.h') diff --git a/include/routing.h b/include/routing.h index 2483bb9..28700c1 100644 --- a/include/routing.h +++ b/include/routing.h @@ -279,42 +279,329 @@ namespace crow std::array param_childrens{}; std::unordered_map children; - bool IsSimpleNode() const; + bool IsSimpleNode() const + { + return + !rule_index && + std::all_of( + std::begin(param_childrens), + std::end(param_childrens), + [](unsigned x){ return !x; }); + } }; - Trie(); + Trie() : nodes_(1) + { + } - void validate(); +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()); + } - void add(const std::string& url, unsigned rule_index); +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; + 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 = ∅ - void debug_print(); + unsigned found{}; + routing_params match_params; - private: - void debug_node_print(Node* n, int level); + 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, &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(); + } + } - void optimizeNode(Node* node); - void optimize(); + for(auto& kv : node->children) + { + const std::string& fragment = kv.first; + const Node* child = &nodes_[kv.second]; - const Node* head() const; - Node* head(); + if (req.url.compare(pos, fragment.size(), fragment) == 0) + { + auto ret = find(req, child, pos + fragment.size(), params); + update_found(ret); + } + } - unsigned new_node(); + 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(); + Router() : rules_(1) {} template typename black_magic::arguments::type::template rebind& new_rule_tagged(const std::string& rule) { @@ -325,11 +612,42 @@ namespace crow return *ruleObject; } - void validate(); + void validate() + { + trie_.validate(); + for(auto& rule:rules_) + { + if (rule) + rule->validate(); + } + } - void handle(const request& req, response& res); - - void debug_print(); + void handle(const request& req, response& res) + { + auto found = trie_.find(req); + + 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!"); + + CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'"; + + rules_[rule_index]->handle(req, res, found.second); + } + + void debug_print() + { + trie_.debug_print(); + } private: std::vector> rules_; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5cb30b8..4ac6f63 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,6 @@ set(CROW_SRCS #${PROJECT_SOURCE_DIR}/some.cpp #${PROJECT_SOURCE_DIR}/someother.cpp ${PROJECT_SOURCE_DIR}/../http-parser/http_parser.c -routing.cpp ) set_source_files_properties(${PROJECT_SOURCE_DIR}/../http-parser/http_parser.c PROPERTIES LANGUAGE C ) diff --git a/src/routing.cpp b/src/routing.cpp deleted file mode 100644 index 04a5e1c..0000000 --- a/src/routing.cpp +++ /dev/null @@ -1,362 +0,0 @@ -#include "routing.h" - -namespace crow -{ - bool Trie::Node::IsSimpleNode() const - { - return - !rule_index && - std::all_of( - std::begin(param_childrens), - std::end(param_childrens), - [](unsigned x){ return !x; }); - } - - Trie::Trie() - : nodes_(1) - { - } - - void Trie::optimizeNode(Trie::Node* node) - { - for(auto x : node->param_childrens) - { - if (!x) - continue; - Trie::Node* child = &nodes_[x]; - optimizeNode(child); - } - if (node->children.empty()) - return; - bool mergeWithChild = true; - for(auto& kv : node->children) - { - Trie::Node* child = &nodes_[kv.second]; - if (!child->IsSimpleNode()) - { - mergeWithChild = false; - break; - } - } - if (mergeWithChild) - { - decltype(node->children) merged; - for(auto& kv : node->children) - { - Trie::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) - { - Trie::Node* child = &nodes_[kv.second]; - optimizeNode(child); - } - } - } - - void Trie::optimize() - { - optimizeNode(head()); - } - - void Trie::validate() - { - if (!head()->IsSimpleNode()) - throw std::runtime_error("Internal error: Trie header should be simple!"); - optimize(); - } - - std::pair Trie::find( - const request& req, - const Trie::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, &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 Trie::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, match_params}; - } - - void Trie::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; - } - - void Trie::debug_node_print(Trie::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); - } - } - - void Trie::debug_print() - { - debug_node_print(head(), 0); - } - - const Trie::Node* Trie::head() const - { - return &nodes_.front(); - } - - Trie::Node* Trie::head() - { - return &nodes_.front(); - } - - unsigned Trie::new_node() - { - nodes_.resize(nodes_.size()+1); - return nodes_.size() - 1; - } - - Router::Router() - : rules_(1) - { - } - - void Router::validate() - { - trie_.validate(); - for(auto& rule:rules_) - { - if (rule) - rule->validate(); - } - } - - void Router::handle(const request& req, response& res) - { - auto found = trie_.find(req); - - 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!"); - - CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'"; - - rules_[rule_index]->handle(req, res, found.second); - } - - void Router::debug_print() - { - trie_.debug_print(); - } -} -- cgit v1.2.3-54-g00ecf