6 #include <unordered_map>
10 #include <type_traits>
12 #include "crow/common.h"
13 #include "crow/http_response.h"
14 #include "crow/http_request.h"
15 #include "crow/utility.h"
16 #include "crow/logging.h"
17 #include "crow/websocket.h"
19 #include "crow/middleware.h"
24 constexpr
const uint16_t INVALID_BP_ID{((uint16_t)-1)};
31 template<
typename App>
35 template<
typename App,
typename MW,
typename... Middlewares>
38 using MwContainer =
typename App::mw_container_t;
39 static_assert(black_magic::has_type<MW, MwContainer>::value,
"Middleware must be present in app");
40 static_assert(std::is_base_of<crow::ILocalMiddleware, MW>::value,
"Middleware must extend ILocalMiddleware");
41 int idx = black_magic::tuple_index<MW, MwContainer>::value;
42 indices_.push_back(idx);
43 push<
App, Middlewares...>();
48 indices_.insert(indices_.begin(), other.indices_.cbegin(), other.indices_.cend());
53 indices_.insert(indices_.end(), other.indices_.cbegin(), other.indices_.cend());
58 indices_.resize(indices_.size() - other.indices_.size());
63 return indices_.empty();
69 std::sort(indices_.begin(), indices_.end());
70 indices_.erase(std::unique(indices_.begin(), indices_.end()), indices_.end());
73 const std::vector<int>& indices()
79 std::vector<int> indices_;
92 rule_(std::move(rule))
98 virtual void validate() = 0;
108 std::unique_ptr<BaseRule> upgrade()
110 if (rule_to_upgrade_)
111 return std::move(rule_to_upgrade_);
121 #ifdef CROW_ENABLE_SSL
129 uint32_t get_methods()
135 void foreach_method(F f)
137 for (uint32_t method = 0, method_bit = 1; method < static_cast<uint32_t>(HTTPMethod::InternalMethodCount); method++, method_bit <<= 1)
139 if (methods_ & method_bit)
144 std::string custom_templates_base;
146 const std::string& rule() {
return rule_; }
149 uint32_t methods_{1 <<
static_cast<int>(HTTPMethod::Get)};
155 std::unique_ptr<BaseRule> rule_to_upgrade_;
168 namespace routing_handler_call_helper
170 template<
typename T,
int Pos>
174 static const int pos = Pos;
177 template<
typename H1>
181 const routing_params& params;
186 template<
typename F,
int NInt,
int NU
int,
int NDouble,
int NString,
typename S1,
typename S2>
190 template<
typename F,
int NInt,
int NUint,
int NDouble,
int NString,
typename... Args1,
typename... Args2>
191 struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
193 void operator()(F cparams)
195 using pushed =
typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
196 call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
200 template<
typename F,
int NInt,
int NUint,
int NDouble,
int NString,
typename... Args1,
typename... Args2>
201 struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
203 void operator()(F cparams)
205 using pushed =
typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
206 call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, pushed>()(cparams);
210 template<
typename F,
int NInt,
int NUint,
int NDouble,
int NString,
typename... Args1,
typename... Args2>
211 struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
213 void operator()(F cparams)
215 using pushed =
typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
216 call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, pushed>()(cparams);
220 template<
typename F,
int NInt,
int NUint,
int NDouble,
int NString,
typename... Args1,
typename... Args2>
221 struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
223 void operator()(F cparams)
225 using pushed =
typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
226 call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, pushed>()(cparams);
230 template<
typename F,
int NInt,
int NUint,
int NDouble,
int NString,
typename... Args1>
231 struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
233 void operator()(F cparams)
238 cparams.params.template get<typename Args1::type>(Args1::pos)...);
242 template<
typename Func,
typename... ArgsWrapped>
245 template<
typename... Args>
246 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)
249 #ifdef CROW_CAN_USE_CPP14
260 template<
typename Req,
typename... Args>
277 template<
typename... Args>
278 void set_(Func f,
typename std::enable_if<
279 std::is_same<
typename std::tuple_element<0, std::tuple<Args..., void>>::type,
const request&>::value &&
280 !std::is_same<
typename std::tuple_element<1, std::tuple<Args..., void, void>>::type,
response&>::value,
292 template<
typename... Args>
293 void set_(Func f,
typename std::enable_if<
294 std::is_same<
typename std::tuple_element<0, std::tuple<Args..., void>>::type,
const request&>::value &&
295 std::is_same<
typename std::tuple_element<1, std::tuple<Args..., void, void>>::type,
response&>::value,
298 handler_ = std::move(f);
301 template<
typename... Args>
305 using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
308 template<
typename... Args>
312 using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
315 template<
typename... Args>
319 using args_type = black_magic::S<typename black_magic::promote_t<Args>...>;
324 void operator()(
request& req,
response& res,
const routing_params& params)
333 decltype(handler_)>{handler_, params, req, res});
347 template<
typename Func>
348 typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<>>::value,
void>::type
351 static_assert(!std::is_same<
void, decltype(f())>::value,
352 "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
355 #ifdef CROW_CAN_USE_CPP14
366 template<
typename Func>
367 typename std::enable_if<
368 !black_magic::CallHelper<Func, black_magic::S<>>::value &&
369 black_magic::CallHelper<Func, black_magic::S<crow::request>>::value,
373 static_assert(!std::is_same<
void, decltype(f(std::declval<crow::request>()))>::value,
374 "Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
377 #ifdef CROW_CAN_USE_CPP14
388 template<
typename Func>
389 typename std::enable_if<
390 !black_magic::CallHelper<Func, black_magic::S<>>::value &&
391 !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
392 black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
396 static_assert(std::is_same<
void, decltype(f(std::declval<crow::response&>()))>::value,
397 "Handler function with response argument should have void return type");
399 #ifdef CROW_CAN_USE_CPP14
409 template<
typename Func>
410 typename std::enable_if<
411 !black_magic::CallHelper<Func, black_magic::S<>>::value &&
412 !black_magic::CallHelper<Func, black_magic::S<crow::request>>::value &&
413 !black_magic::CallHelper<Func, black_magic::S<crow::response&>>::value,
417 static_assert(std::is_same<
void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>()))>::value,
418 "Handler function with response argument should have void return type");
420 handler_ = std::move(f);
425 return (handler_ !=
nullptr);
440 template<
typename App>
449 max_payload_(UINT64_MAX)
452 void validate()
override
464 new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
466 #ifdef CROW_ENABLE_SSL
469 new crow::websocket::Connection<SSLAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
477 max_payload_override_ =
true;
481 self_t& subprotocols(
const std::vector<std::string>& subprotocols)
483 subprotocols_ = subprotocols;
487 template<
typename Func>
488 self_t& onopen(Func f)
494 template<
typename Func>
495 self_t& onmessage(Func f)
497 message_handler_ = f;
501 template<
typename Func>
502 self_t& onclose(Func f)
508 template<
typename Func>
509 self_t& onerror(Func f)
515 template<
typename Func>
516 self_t& onaccept(Func f)
528 std::function<bool(
const crow::request&,
void**)> accept_handler_;
529 uint64_t max_payload_;
530 bool max_payload_override_ =
false;
531 std::vector<std::string> subprotocols_;
543 template<
typename App>
547 static_cast<self_t*
>(
this)->rule_to_upgrade_.reset(p);
551 self_t& name(std::string name) noexcept
553 static_cast<self_t*
>(
this)->name_ = std::move(name);
554 return static_cast<self_t&
>(*this);
557 self_t& methods(HTTPMethod method)
559 static_cast<self_t*
>(
this)->methods_ = 1 <<
static_cast<int>(method);
560 return static_cast<self_t&
>(*this);
563 template<
typename... MethodArgs>
564 self_t& methods(HTTPMethod method, MethodArgs... args_method)
566 methods(args_method...);
567 static_cast<self_t*
>(
this)->methods_ |= 1 <<
static_cast<int>(method);
568 return static_cast<self_t&
>(*this);
572 template<
typename App,
typename... Middlewares>
575 static_cast<self_t*
>(
this)->mw_indices_.template push<App, Middlewares...>();
576 return static_cast<self_t&
>(*this);
588 void validate()
override
590 if (!erased_handler_)
592 throw std::runtime_error(name_ + (!name_.empty() ?
": " :
"") +
"no handler for url " + rule_);
596 void handle(
request& req,
response& res,
const routing_params& params)
override
598 if (!custom_templates_base.empty())
600 else if (mustache::detail::get_template_base_directory_ref() !=
"templates")
602 erased_handler_(req, res, params);
605 template<
typename Func>
606 void operator()(Func f)
608 #ifdef CROW_MSVC_WORKAROUND
609 using function_t = utility::function_traits<decltype(&Func::operator())>;
611 using function_t = utility::function_traits<Func>;
613 erased_handler_ = wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
619 #ifdef CROW_MSVC_WORKAROUND
620 template<
typename Func,
size_t... Indices>
622 template<
typename Func,
unsigned... Indices>
625 wrap(Func f, black_magic::seq<Indices...>)
627 #ifdef CROW_MSVC_WORKAROUND
628 using function_t = utility::function_traits<decltype(&Func::operator())>;
630 using function_t = utility::function_traits<Func>;
632 if (!black_magic::is_parameter_tag_compatible(
633 black_magic::get_parameter_tag_runtime(rule_.c_str()),
634 black_magic::compute_parameter_tag_from_args_list<
635 typename function_t::template arg<Indices>...>::value))
637 throw std::runtime_error(
"route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
641 typename function_t::template arg<Indices>...>(std::move(f));
645 template<
typename Func>
646 void operator()(std::string name, Func&& f)
648 name_ = std::move(name);
649 (*this).template operator()<Func>(std::forward(f));
653 std::function<void(
request&,
response&,
const routing_params&)> erased_handler_;
657 template<
typename... Args>
667 void validate()
override
669 if (rule_.at(0) !=
'/')
670 throw std::runtime_error(
"Internal error: Routes must start with a '/'");
674 throw std::runtime_error(name_ + (!name_.empty() ?
": " :
"") +
"no handler for url " + rule_);
678 template<
typename Func>
679 void operator()(Func&& f)
682 #ifdef CROW_CAN_USE_CPP14
688 detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
692 template<
typename Func>
693 void operator()(std::string name, Func&& f)
695 name_ = std::move(name);
696 (*this).template operator()<Func>(std::forward(f));
699 void handle(
request& req,
response& res,
const routing_params& params)
override
701 if (!custom_templates_base.empty())
703 else if (mustache::detail::get_template_base_directory_ref() != mustache::detail::get_global_template_base_directory_ref())
709 black_magic::S<Args...>,
718 const int RULE_SPECIAL_REDIRECT_SLASH = 1;
727 uint16_t rule_index{};
729 uint16_t blueprint_index{INVALID_BP_ID};
731 ParamType param = ParamType::MAX;
732 std::vector<Node> children;
734 bool IsSimpleNode()
const
736 return !rule_index &&
737 blueprint_index == INVALID_BP_ID &&
738 children.size() < 2 &&
739 param == ParamType::MAX &&
740 std::all_of(std::begin(children), std::end(children), [](
const Node& x) {
741 return x.param == ParamType::MAX;
745 Node& add_child_node()
747 children.emplace_back();
748 return children.back();
759 return head_.children.empty();
764 for (
auto& child : head_.children)
772 void optimizeNode(Node& node)
774 if (node.children.empty())
776 if (node.IsSimpleNode())
778 auto children_temp = std::move(node.children);
779 auto& child_temp = children_temp[0];
780 node.key += child_temp.key;
781 node.rule_index = child_temp.rule_index;
782 node.blueprint_index = child_temp.blueprint_index;
783 node.children = std::move(child_temp.children);
788 for (
auto& child : node.children)
795 void debug_node_print(
const Node& node,
int level)
797 if (node.param != ParamType::MAX)
802 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
805 case ParamType::UINT:
806 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
809 case ParamType::DOUBLE:
810 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
813 case ParamType::STRING:
814 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
817 case ParamType::PATH:
818 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
822 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
828 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ " << node.key;
830 for (
const auto& child : node.children)
832 debug_node_print(child, level + 1);
839 CROW_LOG_DEBUG <<
"└➙ ROOT";
840 for (
const auto& child : head_.children)
841 debug_node_print(child, 1);
846 if (!head_.IsSimpleNode())
847 throw std::runtime_error(
"Internal error: Trie header should be simple!");
852 routing_handle_result find(
const std::string& req_url,
const Node& node,
unsigned pos = 0, routing_params* params =
nullptr, std::vector<uint16_t>* blueprints =
nullptr)
const
855 routing_params empty;
856 if (params ==
nullptr)
859 std::vector<uint16_t> MT;
860 if (blueprints ==
nullptr)
864 std::vector<uint16_t> found_BP;
865 routing_params match_params;
867 auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
868 found_BP = std::move(ret.blueprint_indices);
869 if (ret.rule_index && (!found || found > ret.rule_index))
871 found = ret.rule_index;
872 match_params = std::move(ret.r_params);
877 if (pos == req_url.size())
879 found_BP = std::move(*blueprints);
880 return routing_handle_result{node.rule_index, *blueprints, *params};
883 bool found_fragment =
false;
885 for (
const auto& child : node.children)
887 if (child.param != ParamType::MAX)
889 if (child.param == ParamType::INT)
891 char c = req_url[pos];
892 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-')
896 long long int value = strtoll(req_url.data() + pos, &eptr, 10);
897 if (errno != ERANGE && eptr != req_url.data() + pos)
899 found_fragment =
true;
900 params->int_params.push_back(value);
901 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
902 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
904 params->int_params.pop_back();
905 if (!blueprints->empty()) blueprints->pop_back();
910 else if (child.param == ParamType::UINT)
912 char c = req_url[pos];
913 if ((c >=
'0' && c <=
'9') || c ==
'+')
917 unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
918 if (errno != ERANGE && eptr != req_url.data() + pos)
920 found_fragment =
true;
921 params->uint_params.push_back(value);
922 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
923 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
925 params->uint_params.pop_back();
926 if (!blueprints->empty()) blueprints->pop_back();
931 else if (child.param == ParamType::DOUBLE)
933 char c = req_url[pos];
934 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-' || c ==
'.')
938 double value = strtod(req_url.data() + pos, &eptr);
939 if (errno != ERANGE && eptr != req_url.data() + pos)
941 found_fragment =
true;
942 params->double_params.push_back(value);
943 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
944 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
946 params->double_params.pop_back();
947 if (!blueprints->empty()) blueprints->pop_back();
952 else if (child.param == ParamType::STRING)
955 for (; epos < req_url.size(); epos++)
957 if (req_url[epos] ==
'/')
963 found_fragment =
true;
964 params->string_params.push_back(req_url.substr(pos, epos - pos));
965 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
966 auto ret = find(req_url, child, epos, params, blueprints);
968 params->string_params.pop_back();
969 if (!blueprints->empty()) blueprints->pop_back();
973 else if (child.param == ParamType::PATH)
975 size_t epos = req_url.size();
979 found_fragment =
true;
980 params->string_params.push_back(req_url.substr(pos, epos - pos));
981 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
982 auto ret = find(req_url, child, epos, params, blueprints);
984 params->string_params.pop_back();
985 if (!blueprints->empty()) blueprints->pop_back();
992 const std::string& fragment = child.key;
993 if (req_url.compare(pos, fragment.size(), fragment) == 0)
995 found_fragment =
true;
996 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
997 auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
999 if (!blueprints->empty()) blueprints->pop_back();
1004 if (!found_fragment)
1005 found_BP = std::move(*blueprints);
1007 return routing_handle_result{found, found_BP, match_params};
1010 routing_handle_result find(
const std::string& req_url)
const
1012 return find(req_url, head_);
1016 void add(
const std::string& url, uint16_t rule_index,
unsigned bp_prefix_length = 0, uint16_t blueprint_index = INVALID_BP_ID)
1020 bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
1022 for (
unsigned i = 0; i < url.size(); i++)
1027 static struct ParamTraits
1033 {ParamType::INT,
"<int>"},
1034 {ParamType::UINT,
"<uint>"},
1035 {ParamType::DOUBLE,
"<float>"},
1036 {ParamType::DOUBLE,
"<double>"},
1037 {ParamType::STRING,
"<str>"},
1038 {ParamType::STRING,
"<string>"},
1039 {ParamType::PATH,
"<path>"},
1042 for (
const auto& x : paramTraits)
1044 if (url.compare(i, x.name.size(), x.name) == 0)
1047 for (
auto& child : idx->children)
1049 if (child.param == x.type)
1060 auto new_node_idx = &idx->add_child_node();
1061 new_node_idx->param = x.type;
1073 bool piece_found =
false;
1074 for (
auto& child : idx->children)
1076 if (child.key[0] == c)
1085 auto new_node_idx = &idx->add_child_node();
1086 new_node_idx->key = c;
1088 if (has_blueprint && i == bp_prefix_length)
1089 new_node_idx->blueprint_index = blueprint_index;
1096 if (idx->rule_index)
1097 throw std::runtime_error(
"handler already exists for " + url);
1098 idx->rule_index = rule_index;
1115 static_dir_(prefix),
1116 templates_dir_(prefix)
1119 Blueprint(
const std::string& prefix,
const std::string& static_dir):
1120 prefix_(prefix), static_dir_(static_dir){};
1122 Blueprint(
const std::string& prefix,
const std::string& static_dir,
const std::string& templates_dir):
1123 prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){};
1140 *
this = std::move(value);
1147 prefix_ = std::move(value.prefix_);
1148 static_dir_ = std::move(value.static_dir_);
1149 templates_dir_ = std::move(value.templates_dir_);
1150 all_rules_ = std::move(value.all_rules_);
1151 catchall_rule_ = std::move(value.catchall_rule_);
1152 blueprints_ = std::move(value.blueprints_);
1153 mw_indices_ = std::move(value.mw_indices_);
1159 return value.prefix() == prefix_;
1164 return value.prefix() != prefix_;
1167 std::string prefix()
const
1172 std::string static_dir()
const
1185 DynamicRule& new_rule_dynamic(
const std::string& rule)
1187 std::string new_rule =
'/' + prefix_ + rule;
1188 auto ruleObject =
new DynamicRule(std::move(new_rule));
1189 ruleObject->custom_templates_base = templates_dir_;
1190 all_rules_.emplace_back(ruleObject);
1195 template<u
int64_t N>
1196 typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(
const std::string& rule)
1198 std::string new_rule =
'/' + prefix_ + rule;
1199 using RuleT =
typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1201 auto ruleObject =
new RuleT(std::move(new_rule));
1202 ruleObject->custom_templates_base = templates_dir_;
1203 all_rules_.emplace_back(ruleObject);
1208 void register_blueprint(
Blueprint& blueprint)
1210 if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1212 apply_blueprint(blueprint);
1213 blueprints_.emplace_back(&blueprint);
1216 throw std::runtime_error(
"blueprint \"" + blueprint.prefix_ +
"\" already exists in blueprint \"" + prefix_ +
'\"');
1222 return catchall_rule_;
1225 template<
typename App,
typename... Middlewares>
1228 mw_indices_.push<
App, Middlewares...>();
1232 void apply_blueprint(
Blueprint& blueprint)
1235 blueprint.prefix_ = prefix_ +
'/' + blueprint.prefix_;
1236 blueprint.static_dir_ = static_dir_ +
'/' + blueprint.static_dir_;
1237 blueprint.templates_dir_ = templates_dir_ +
'/' + blueprint.templates_dir_;
1238 for (
auto& rule : blueprint.all_rules_)
1240 std::string new_rule =
'/' + prefix_ + rule->rule_;
1241 rule->rule_ = new_rule;
1243 for (
Blueprint* bp_child : blueprint.blueprints_)
1246 apply_blueprint(bp_ref);
1250 std::string prefix_;
1251 std::string static_dir_;
1252 std::string templates_dir_;
1253 std::vector<std::unique_ptr<BaseRule>> all_rules_;
1255 std::vector<Blueprint*> blueprints_;
1269 DynamicRule& new_rule_dynamic(
const std::string& rule)
1272 all_rules_.emplace_back(ruleObject);
1277 template<u
int64_t N>
1278 typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(
const std::string& rule)
1280 using RuleT =
typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1282 auto ruleObject =
new RuleT(rule);
1283 all_rules_.emplace_back(ruleObject);
1290 return catchall_rule_;
1293 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject)
1295 internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
1298 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject,
const uint16_t& BP_index, std::vector<Blueprint*>& blueprints)
1300 bool has_trailing_slash =
false;
1301 std::string rule_without_trailing_slash;
1302 if (rule.size() > 1 && rule.back() ==
'/')
1304 has_trailing_slash =
true;
1305 rule_without_trailing_slash = rule;
1306 rule_without_trailing_slash.pop_back();
1309 ruleObject->mw_indices_.pack();
1311 ruleObject->foreach_method([&](
int method) {
1312 per_methods_[method].rules.emplace_back(ruleObject);
1313 per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1, BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0, BP_index);
1317 if (has_trailing_slash)
1319 per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH, BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0, BP_index);
1323 ruleObject->set_added();
1326 void register_blueprint(
Blueprint& blueprint)
1328 if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1330 blueprints_.emplace_back(&blueprint);
1333 throw std::runtime_error(
"blueprint \"" + blueprint.prefix_ +
"\" already exists in router");
1336 void get_recursive_child_methods(
Blueprint* blueprint, std::vector<HTTPMethod>& methods)
1339 if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
1341 for (
Blueprint* bp : blueprint->blueprints_)
1343 get_recursive_child_methods(bp, methods);
1346 else if (!blueprint->static_dir_.empty())
1347 methods.emplace_back(HTTPMethod::Get);
1348 for (
auto& rule : blueprint->all_rules_)
1350 rule->foreach_method([&methods](
unsigned method) {
1351 HTTPMethod method_final =
static_cast<HTTPMethod
>(method);
1352 if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
1353 methods.emplace_back(method_final);
1358 void validate_bp() {
1361 validate_bp(blueprints_, blueprint_mw);
1366 for (
unsigned i = 0; i < blueprints.size(); i++)
1370 if (blueprint->is_added())
continue;
1372 if (blueprint->static_dir_ ==
"" && blueprint->all_rules_.empty())
1374 std::vector<HTTPMethod> methods;
1375 get_recursive_child_methods(blueprint, methods);
1376 for (HTTPMethod x : methods)
1378 int i =
static_cast<int>(x);
1379 per_methods_[i].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), i);
1383 current_mw.merge_back(blueprint->mw_indices_);
1384 for (
auto& rule : blueprint->all_rules_)
1386 if (rule && !rule->is_added())
1388 auto upgraded = rule->upgrade();
1390 rule = std::move(upgraded);
1392 rule->mw_indices_.merge_front(current_mw);
1393 internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
1396 validate_bp(blueprint->blueprints_, current_mw);
1397 current_mw.pop_back(blueprint->mw_indices_);
1398 blueprint->set_added();
1404 for (
auto& rule : all_rules_)
1406 if (rule && !rule->is_added())
1408 auto upgraded = rule->upgrade();
1410 rule = std::move(upgraded);
1412 internal_add_rule_object(rule->rule(), rule.get());
1415 for (
auto& per_method : per_methods_)
1417 per_method.trie.validate();
1422 template<
typename Adaptor>
1423 void handle_upgrade(
const request& req,
response& res, Adaptor&& adaptor)
1425 if (req.method >= HTTPMethod::InternalMethodCount)
1428 auto& per_method = per_methods_[
static_cast<int>(req.method)];
1429 auto& rules = per_method.rules;
1430 unsigned rule_index = per_method.trie.find(req.
url).rule_index;
1434 for (
auto& per_method : per_methods_)
1436 if (per_method.trie.find(req.
url).rule_index)
1438 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" " << method_name(req.method);
1445 CROW_LOG_INFO <<
"Cannot match rules " << req.
url;
1451 if (rule_index >= rules.size())
1452 throw std::runtime_error(
"Trie internal structure corrupted!");
1454 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1456 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.
url;
1460 if (req.get_header_value(
"Host").empty())
1466 res.
add_header(
"Location",
"http://" + req.get_header_value(
"Host") + req.
url +
"/");
1472 CROW_LOG_DEBUG <<
"Matched rule (upgrade) '" << rules[rule_index]->rule_ <<
"' " <<
static_cast<uint32_t
>(req.method) <<
" / " << rules[rule_index]->get_methods();
1476 rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
1480 exception_handler_(res);
1486 void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
1496 auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
1498 bp_i[index] < blueprints.size() &&
1499 blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() +
'/')) == 0;
1501 if (index < bp_i.size())
1504 if (verify_prefix())
1506 found_bps.push_back(blueprints[bp_i[index]]);
1507 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1511 if (found_bps.size() < 2)
1514 found_bps.push_back(blueprints_[bp_i[index]]);
1518 found_bps.pop_back();
1519 Blueprint* last_element = found_bps.back();
1520 found_bps.push_back(last_element->blueprints_[bp_i[index]]);
1522 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1531 std::vector<Blueprint*> bps_found;
1532 get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1533 for (
int i = bps_found.size() - 1; i > 0; i--)
1535 std::vector<uint16_t> bpi = found.blueprint_indices;
1536 if (bps_found[i]->catchall_rule().has_handler())
1540 bps_found[i]->catchall_rule().handler_(req, res);
1544 exception_handler_(res);
1546 #ifdef CROW_ENABLE_DEBUG
1547 return std::string(
"Redirected to Blueprint \"" + bps_found[i]->prefix() +
"\" Catchall rule");
1549 return std::string();
1553 if (catchall_rule_.has_handler())
1557 catchall_rule_.handler_(req, res);
1561 exception_handler_(res);
1563 #ifdef CROW_ENABLE_DEBUG
1564 return std::string(
"Redirected to global Catchall rule");
1566 return std::string();
1569 return std::string();
1572 std::unique_ptr<routing_handle_result> handle_initial(
request& req,
response& res)
1574 HTTPMethod method_actual = req.method;
1576 std::unique_ptr<routing_handle_result> found{
1579 std::vector<uint16_t>(),
1581 HTTPMethod::InternalMethodCount)};
1584 if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
1586 else if (req.method == HTTPMethod::Head)
1588 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1590 if (!found->rule_index)
1592 method_actual = HTTPMethod::Get;
1593 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1594 if (!found->rule_index)
1596 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1597 res = response(404);
1604 found->method = method_actual;
1607 else if (req.method == HTTPMethod::Options)
1609 std::string allow =
"OPTIONS, HEAD, ";
1611 if (req.
url ==
"/*")
1613 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1615 if (
static_cast<int>(HTTPMethod::Head) == i)
1618 if (!per_methods_[i].trie.is_empty())
1620 allow += method_name(
static_cast<HTTPMethod
>(i)) +
", ";
1623 allow = allow.substr(0, allow.size() - 2);
1624 res = response(204);
1627 found->method = method_actual;
1632 bool rules_matched =
false;
1633 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1635 if (per_methods_[i].trie.find(req.
url).rule_index)
1637 rules_matched =
true;
1639 if (
static_cast<int>(HTTPMethod::Head) == i)
1642 allow += method_name(
static_cast<HTTPMethod
>(i)) +
", ";
1647 allow = allow.substr(0, allow.size() - 2);
1648 res = response(204);
1651 found->method = method_actual;
1656 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1657 res = response(404);
1665 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1667 if (!found->rule_index)
1669 for (
auto& per_method : per_methods_)
1671 if (per_method.trie.find(req.
url).rule_index)
1673 const std::string error_message(
get_error(405, *found, req, res));
1674 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" " << method_name(method_actual) <<
". " << error_message;
1681 const std::string error_message(
get_error(404, *found, req, res));
1682 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url <<
". " << error_message;
1687 found->method = method_actual;
1692 template<
typename App>
1693 void handle(request& req, response& res, routing_handle_result found)
1695 HTTPMethod method_actual = found.method;
1696 auto& rules = per_methods_[
static_cast<int>(method_actual)].rules;
1697 unsigned rule_index = found.rule_index;
1699 if (rule_index >= rules.size())
1700 throw std::runtime_error(
"Trie internal structure corrupted!");
1702 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1704 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.url;
1705 res = response(301);
1708 if (req.get_header_value(
"Host").empty())
1710 res.add_header(
"Location", req.url +
"/");
1714 res.add_header(
"Location",
"http://" + req.get_header_value(
"Host") + req.url +
"/");
1720 CROW_LOG_DEBUG <<
"Matched rule '" << rules[rule_index]->rule_ <<
"' " <<
static_cast<uint32_t
>(req.method) <<
" / " << rules[rule_index]->get_methods();
1724 auto& rule = rules[rule_index];
1725 handle_rule<App>(rule, req, res, found.r_params);
1729 exception_handler_(res);
1735 template<
typename App>
1736 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0,
void>::type
1739 if (!rule->mw_indices_.empty())
1741 auto& ctx = *
reinterpret_cast<typename App::context_t*
>(req.middleware_context);
1742 auto& container = *
reinterpret_cast<typename App::mw_container_t*
>(req.middleware_container);
1743 detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
1745 auto glob_completion_handler = std::move(res.complete_request_handler_);
1746 res.complete_request_handler_ = [] {};
1748 detail::middleware_call_helper<decltype(crit_fwd),
1749 0,
typename App::context_t,
typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1753 glob_completion_handler();
1757 res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
1758 detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
1760 detail::after_handlers_call_helper<
1762 std::tuple_size<typename App::mw_container_t>::value - 1,
1763 typename App::context_t,
1764 typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1765 glob_completion_handler();
1768 rule->handle(req, res, rp);
1771 template<
typename App>
1772 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0,
void>::type
1775 rule->handle(req, res, rp);
1780 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1782 Trie& trie_ = per_methods_[i].trie;
1783 if (!trie_.is_empty())
1785 CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1786 trie_.debug_print();
1791 std::vector<Blueprint*>& blueprints()
1798 return exception_handler_;
1801 static void default_exception_handler(response& res)
1804 res = response(500);
1810 catch (
const std::exception& e)
1812 CROW_LOG_ERROR <<
"An uncaught exception occurred: " << e.what();
1816 CROW_LOG_ERROR <<
"An uncaught exception occurred. The type was unknown so no information was available.";
1821 CatchallRule catchall_rule_;
1825 std::vector<BaseRule*> rules;
1832 std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1833 std::vector<std::unique_ptr<BaseRule>> all_rules_;
1834 std::vector<Blueprint*> blueprints_;
1835 std::function<void(
crow::response&)> exception_handler_ = &default_exception_handler;
A base class for all rules.
Definition: routing.h:89
A blueprint can be considered a smaller section of a Crow app, specifically where the router is conce...
Definition: routing.h:1111
Definition: routing.h:342
The main server application class.
Definition: app.h:199
self_t & websocket_max_payload(uint64_t max_payload)
Set the default max payload size for websockets.
Definition: app.h:278
A rule that can change its parameters during runtime.
Definition: routing.h:582
Handles matching requests to existing rules and upgrade requests.
Definition: routing.h:1264
std::string get_error(unsigned short code, routing_handle_result &found, const request &req, response &res)
Is used to handle errors, you insert the error code, found route, request, and response....
Definition: routing.h:1528
Default rule created when CROW_ROUTE is called.
Definition: routing.h:659
A search tree.
Definition: routing.h:723
bool is_empty()
Check whether or not the trie is empty.
Definition: routing.h:757
A rule dealing with websockets.
Definition: routing.h:442
self_t & max_payload(uint64_t max_payload)
Override the global payload limit for this single WebSocket rule.
Definition: routing.h:474
A websocket connection.
Definition: websocket.h:106
This file includes the definition of the crow::mustache namespace and its members.
void set_base(const std::string &path)
Defines the templates directory path at route level. By default is templates/.
Definition: mustache.h:738
The main namespace of the library. In this namespace is defined the most important classes and functi...
Crow< Middlewares... > App
Alias of Crow<Middlewares...>. Useful if you want a instance of an Crow application that require Midd...
Definition: app.h:773
Allows the user to assign parameters using functions.
Definition: routing.h:540
self_t & middlewares()
Enable local middleware for this handler.
Definition: routing.h:573
Definition: socket_adaptors.h:107
A wrapper for the asio::ip::tcp::socket and asio::ssl::stream.
Definition: socket_adaptors.h:39
Definition: routing.h:726
Typesafe wrapper for storing lists of middleware as their indices in the App.
Definition: routing.h:30
Definition: routing.h:303
Definition: routing.h:262
Definition: routing.h:244
Definition: routing.h:172
Definition: routing.h:179
Definition: routing.h:188
An HTTP request.
Definition: http_request.h:36
std::string url
The endpoint without any parameters.
Definition: http_request.h:39
HTTP response.
Definition: http_response.h:34
void add_header(std::string key, std::string value)
Add a new header to the response.
Definition: http_response.h:58
bool skip_body
Whether this is a response to a HEAD request.
Definition: http_response.h:47
int code
The Status code for the response.
Definition: http_response.h:40
void set_header(std::string key, std::string value)
Set the value of an existing header in the response.
Definition: http_response.h:51
void end()
Set the response completion flag and call the handler (to send the response).
Definition: http_response.h:237
A base class for websocket connection.
Definition: websocket.h:62