diff options
author | ipknHama <ipknhama@gmail.com> | 2016-09-21 23:11:06 +0900 |
---|---|---|
committer | ipknHama <ipknhama@gmail.com> | 2016-09-21 23:11:06 +0900 |
commit | 3081e4e1a82a4efd8feff68850c4cc04af230cd7 (patch) | |
tree | 3ad16ef2e940abd2d47c4ac3ca224365387b7f37 /include/crow/routing.h | |
parent | 8b04940d2f28290451db439ad29155a0b8771ba3 (diff) | |
download | crow-3081e4e1a82a4efd8feff68850c4cc04af230cd7.tar.gz crow-3081e4e1a82a4efd8feff68850c4cc04af230cd7.zip |
Cleanup include folder into crow subfolder
- only crow.h is exposed now
Diffstat (limited to 'include/crow/routing.h')
-rw-r--r-- | include/crow/routing.h | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/include/crow/routing.h b/include/crow/routing.h new file mode 100644 index 0000000..a8aa233 --- /dev/null +++ b/include/crow/routing.h @@ -0,0 +1,1056 @@ +#pragma once + +#include <cstdint> +#include <utility> +#include <tuple> +#include <unordered_map> +#include <memory> +#include <boost/lexical_cast.hpp> +#include <vector> + +#include "crow/common.h" +#include "crow/http_response.h" +#include "crow/http_request.h" +#include "crow/utility.h" +#include "crow/logging.h" +#include "crow/websocket.h" + +namespace crow +{ + class BaseRule + { + public: + BaseRule(std::string rule) + : rule_(std::move(rule)) + { + } + + virtual ~BaseRule() + { + } + + virtual void validate() = 0; + std::unique_ptr<BaseRule> 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<BaseRule> rule_to_upgrade_; + + friend class Router; + template <typename T> + friend struct RuleParameterTraits; + }; + + + namespace detail + { + namespace routing_handler_call_helper + { + template <typename T, int Pos> + struct call_pair + { + using type = T; + static const int pos = Pos; + }; + + template <typename H1> + struct call_params + { + H1& handler; + const routing_params& params; + const request& req; + response& res; + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> + struct call + { + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2> + struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>> + { + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>; + call<F, NInt+1, NUint, NDouble, NString, + black_magic::S<Args1...>, pushed>()(cparams); + } + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2> + struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> + { + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>; + call<F, NInt, NUint+1, NDouble, NString, + black_magic::S<Args1...>, pushed>()(cparams); + } + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2> + struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>> + { + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>; + call<F, NInt, NUint, NDouble+1, NString, + black_magic::S<Args1...>, pushed>()(cparams); + } + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2> + struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> + { + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>; + call<F, NInt, NUint, NDouble, NString+1, + black_magic::S<Args1...>, pushed>()(cparams); + } + }; + + template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1> + struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>> + { + void operator()(F cparams) + { + cparams.handler( + cparams.req, + cparams.res, + cparams.params.template get<typename Args1::type>(Args1::pos)... + ); + } + }; + + template <typename Func, typename ... ArgsWrapped> + struct Wrapped + { + template <typename ... Args> + void set(Func f, typename std::enable_if< + !std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::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 <typename Req, typename ... Args> + 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 <typename ... Args> + void set(Func f, typename std::enable_if< + std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && + !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value + , int>::type = 0) + { + handler_ = req_handler_wrapper<Args...>(std::move(f)); + /*handler_ = ( + [f = std::move(f)] + (const request& req, response& res, Args... args){ + res = response(f(req, args...)); + res.end(); + });*/ + } + + template <typename ... Args> + void set(Func f, typename std::enable_if< + std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && + std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value + , int>::type = 0) + { + handler_ = std::move(f); + } + + template <typename ... Args> + struct handler_type_helper + { + using type = std::function<void(const crow::request&, crow::response&, Args...)>; + using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename ... Args> + struct handler_type_helper<const request&, Args...> + { + using type = std::function<void(const crow::request&, crow::response&, Args...)>; + using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename ... Args> + struct handler_type_helper<const request&, response&, Args...> + { + using type = std::function<void(const crow::request&, crow::response&, Args...)>; + using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + typename handler_type_helper<ArgsWrapped...>::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<ArgsWrapped...>::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<SocketAdaptor>(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<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); + } +#endif + + template <typename Func> + self_t& onopen(Func f) + { + open_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onmessage(Func f) + { + message_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onclose(Func f) + { + close_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onerror(Func f) + { + error_handler_ = f; + return *this; + } + + protected: + std::function<void(crow::websocket::connection&)> open_handler_; + std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; + std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; + std::function<void(crow::websocket::connection&)> error_handler_; + }; + + template <typename T> + 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 <typename ... MethodArgs> + 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<DynamicRule> + { + 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 <typename Func> + void operator()(Func f) + { +#ifdef CROW_MSVC_WORKAROUND + using function_t = utility::function_traits<decltype(&Func::operator())>; +#else + using function_t = utility::function_traits<Func>; +#endif + erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>()); + } + + // enable_if Arg1 == request && Arg2 == response + // enable_if Arg1 == request && Arg2 != resposne + // enable_if Arg1 != request +#ifdef CROW_MSVC_WORKAROUND + template <typename Func, size_t ... Indices> +#else + template <typename Func, unsigned ... Indices> +#endif + std::function<void(const request&, response&, const routing_params&)> + wrap(Func f, black_magic::seq<Indices...>) + { +#ifdef CROW_MSVC_WORKAROUND + using function_t = utility::function_traits<decltype(&Func::operator())>; +#else + using function_t = utility::function_traits<Func>; +#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<Indices>...>::value)) + { + throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_); + } + auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>(); + ret.template set< + typename function_t::template arg<Indices>... + >(std::move(f)); + return ret; + } + + template <typename Func> + void operator()(std::string name, Func&& f) + { + name_ = std::move(name); + (*this).template operator()<Func>(std::forward(f)); + } + private: + std::function<void(const request&, response&, const routing_params&)> erased_handler_; + + }; + + template <typename ... Args> + class TaggedRule : public BaseRule, public RuleParameterTraits<TaggedRule<Args...>> + { + public: + using self_t = TaggedRule<Args...>; + + 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 Func> + typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type + operator()(Func&& f) + { + static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value , + "Handler type is mismatched with URL parameters"); + static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::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 Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && + black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value, + void>::type + operator()(Func&& f) + { + static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::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 Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && + !black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value, + void>::type + operator()(Func&& f) + { + static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value || + black_magic::CallHelper<Func, black_magic::S<crow::request, crow::response&, Args...>>::value + , + "Handler type is mismatched with URL parameters"); + static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value, + "Handler function with response argument should have void return type"); + + handler_ = std::move(f); + } + + template <typename Func> + void operator()(std::string name, Func&& f) + { + name_ = std::move(name); + (*this).template operator()<Func>(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<Args...>, + black_magic::S<> + >()( + detail::routing_handler_call_helper::call_params< + decltype(handler_)> + {handler_, params, req, res} + ); + } + + private: + std::function<void(const crow::request&, crow::response&, Args...)> handler_; + + }; + + const int RULE_SPECIAL_REDIRECT_SLASH = 1; + + class Trie + { + public: + struct Node + { + unsigned rule_index{}; + std::array<unsigned, (int)ParamType::MAX> param_childrens{}; + std::unordered_map<std::string, unsigned> 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<unsigned, routing_params> 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<unsigned, routing_params>& 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, "<int>" }, + { ParamType::UINT, "<uint>" }, + { ParamType::DOUBLE, "<float>" }, + { ParamType::DOUBLE, "<double>" }, + { ParamType::STRING, "<str>" }, + { ParamType::STRING, "<string>" }, + { ParamType::PATH, "<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, ' ') /*<< "("<<n->param_childrens[i]<<") "*/; + switch((ParamType)i) + { + case ParamType::INT: + CROW_LOG_DEBUG << "<int>"; + break; + case ParamType::UINT: + CROW_LOG_DEBUG << "<uint>"; + break; + case ParamType::DOUBLE: + CROW_LOG_DEBUG << "<float>"; + break; + case ParamType::STRING: + CROW_LOG_DEBUG << "<str>"; + break; + case ParamType::PATH: + CROW_LOG_DEBUG << "<path>"; + break; + default: + CROW_LOG_DEBUG << "<ERROR>"; + 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<Node> 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 <uint64_t N> + typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule) + { + using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>; + 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 <typename Adaptor> + 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<std::unique_ptr<BaseRule>> rules_; + Trie trie_; + }; +} |