773 size_t blueprint_index{INVALID_BP_ID};
775 ParamType param = ParamType::MAX;
776 std::vector<Node> children;
778 bool IsSimpleNode()
const
780 return !rule_index &&
781 blueprint_index == INVALID_BP_ID &&
782 children.size() < 2 &&
783 param == ParamType::MAX &&
784 std::all_of(std::begin(children), std::end(children), [](
const Node& x) {
785 return x.param == ParamType::MAX;
789 Node& add_child_node()
791 children.emplace_back();
792 return children.back();
803 return head_.children.empty();
808 for (
auto& child : head_.children)
816 void optimizeNode(Node& node)
818 if (node.children.empty())
820 if (node.IsSimpleNode())
822 auto children_temp = std::move(node.children);
823 auto& child_temp = children_temp[0];
824 node.key += child_temp.key;
825 node.rule_index = child_temp.rule_index;
826 node.blueprint_index = child_temp.blueprint_index;
827 node.children = std::move(child_temp.children);
832 for (
auto& child : node.children)
839 void debug_node_print(
const Node& node,
size_t level)
841 if (node.param != ParamType::MAX)
846 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
849 case ParamType::UINT:
850 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
853 case ParamType::DOUBLE:
854 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
857 case ParamType::STRING:
858 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
861 case ParamType::PATH:
862 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
866 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
872 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ " << node.key;
874 for (
const auto& child : node.children)
876 debug_node_print(child, level + 1);
883 CROW_LOG_DEBUG <<
"└➙ ROOT";
884 for (
const auto& child : head_.children)
885 debug_node_print(child, 1);
890 if (!head_.IsSimpleNode())
891 throw std::runtime_error(
"Internal error: Trie header should be simple!");
896 routing_handle_result find(
const std::string& req_url,
const Node& node,
size_t pos = 0, routing_params* params =
nullptr, std::vector<size_t>* blueprints =
nullptr)
const
899 routing_params empty;
900 if (params ==
nullptr)
903 std::vector<size_t> MT;
904 if (blueprints ==
nullptr)
908 std::vector<size_t> found_BP;
909 routing_params match_params;
911 auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
912 found_BP = std::move(ret.blueprint_indices);
913 if (ret.rule_index && (!found || found > ret.rule_index))
915 found = ret.rule_index;
916 match_params = std::move(ret.r_params);
921 if (pos == req_url.size())
923 found_BP = std::move(*blueprints);
924 return routing_handle_result{node.rule_index, *blueprints, *params};
927 bool found_fragment =
false;
929 for (
const auto& child : node.children)
931 if (child.param != ParamType::MAX)
933 if (child.param == ParamType::INT)
935 char c = req_url[pos];
936 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-')
940 long long int value = strtoll(req_url.data() + pos, &eptr, 10);
941 if (errno != ERANGE && eptr != req_url.data() + pos)
943 found_fragment =
true;
944 params->int_params.push_back(value);
945 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
946 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
948 params->int_params.pop_back();
949 if (!blueprints->empty()) blueprints->pop_back();
954 else if (child.param == ParamType::UINT)
956 char c = req_url[pos];
957 if ((c >=
'0' && c <=
'9') || c ==
'+')
961 unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
962 if (errno != ERANGE && eptr != req_url.data() + pos)
964 found_fragment =
true;
965 params->uint_params.push_back(value);
966 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
967 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
969 params->uint_params.pop_back();
970 if (!blueprints->empty()) blueprints->pop_back();
975 else if (child.param == ParamType::DOUBLE)
977 char c = req_url[pos];
978 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-' || c ==
'.')
982 double value = strtod(req_url.data() + pos, &eptr);
983 if (errno != ERANGE && eptr != req_url.data() + pos)
985 found_fragment =
true;
986 params->double_params.push_back(value);
987 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
988 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
990 params->double_params.pop_back();
991 if (!blueprints->empty()) blueprints->pop_back();
996 else if (child.param == ParamType::STRING)
999 for (; epos < req_url.size(); epos++)
1001 if (req_url[epos] ==
'/')
1007 found_fragment =
true;
1008 params->string_params.push_back(req_url.substr(pos, epos - pos));
1009 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
1010 auto ret = find(req_url, child, epos, params, blueprints);
1012 params->string_params.pop_back();
1013 if (!blueprints->empty()) blueprints->pop_back();
1017 else if (child.param == ParamType::PATH)
1019 size_t epos = req_url.size();
1023 found_fragment =
true;
1024 params->string_params.push_back(req_url.substr(pos, epos - pos));
1025 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
1026 auto ret = find(req_url, child, epos, params, blueprints);
1028 params->string_params.pop_back();
1029 if (!blueprints->empty()) blueprints->pop_back();
1036 const std::string& fragment = child.key;
1037 if (req_url.compare(pos, fragment.size(), fragment) == 0)
1039 found_fragment =
true;
1040 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
1041 auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
1043 if (!blueprints->empty()) blueprints->pop_back();
1048 if (!found_fragment)
1049 found_BP = std::move(*blueprints);
1051 return routing_handle_result{found, found_BP, match_params};
1054 routing_handle_result find(
const std::string& req_url)
const
1056 return find(req_url, head_);
1060 void add(
const std::string& url,
size_t rule_index,
unsigned bp_prefix_length = 0,
size_t blueprint_index = INVALID_BP_ID)
1064 bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
1066 for (
size_t i = 0; i < url.size(); i++)
1071 static struct ParamTraits
1077 {ParamType::INT,
"<int>"},
1078 {ParamType::UINT,
"<uint>"},
1079 {ParamType::DOUBLE,
"<float>"},
1080 {ParamType::DOUBLE,
"<double>"},
1081 {ParamType::STRING,
"<str>"},
1082 {ParamType::STRING,
"<string>"},
1083 {ParamType::PATH,
"<path>"},
1086 for (
const auto& x : paramTraits)
1088 if (url.compare(i, x.name.size(), x.name) == 0)
1091 for (
auto& child : idx->children)
1093 if (child.param == x.type)
1104 auto new_node_idx = &idx->add_child_node();
1105 new_node_idx->param = x.type;
1117 bool piece_found =
false;
1118 for (
auto& child : idx->children)
1120 if (child.key[0] == c)
1129 auto new_node_idx = &idx->add_child_node();
1130 new_node_idx->key = c;
1132 if (has_blueprint && i == bp_prefix_length)
1133 new_node_idx->blueprint_index = blueprint_index;
1140 if (idx->rule_index)
1141 throw std::runtime_error(
"handler already exists for " + url);
1142 idx->rule_index = rule_index;
1313 Router() : using_ssl(
false)
1316 DynamicRule& new_rule_dynamic(
const std::string& rule)
1319 all_rules_.emplace_back(ruleObject);
1324 template<u
int64_t N>
1325 typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(
const std::string& rule)
1327 using RuleT =
typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1329 return new_rule<RuleT>(rule);
1332 template<
typename RuleT=StaticRule>
1333 auto& new_rule(
const std::string& rule)
1335 auto ruleObject =
new RuleT(rule);
1336 all_rules_.emplace_back(ruleObject);
1343 return catchall_rule_;
1346 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject)
1348 internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
1351 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject,
const size_t& BP_index, std::vector<Blueprint*>& blueprints)
1353 bool has_trailing_slash =
false;
1354 std::string rule_without_trailing_slash;
1355 if (rule.size() > 1 && rule.back() ==
'/')
1357 has_trailing_slash =
true;
1358 rule_without_trailing_slash = rule;
1359 rule_without_trailing_slash.pop_back();
1362 ruleObject->mw_indices_.pack();
1364 ruleObject->foreach_method([&](
int method) {
1365 per_methods_[method].rules.emplace_back(ruleObject);
1366 per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1,
1367 BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0,
1372 if (has_trailing_slash)
1374 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);
1378 ruleObject->set_added();
1381 void register_blueprint(
Blueprint& blueprint)
1383 if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1385 blueprints_.emplace_back(&blueprint);
1388 throw std::runtime_error(
"blueprint \"" + blueprint.prefix_ +
"\" already exists in router");
1391 void get_recursive_child_methods(
Blueprint* blueprint, std::vector<HTTPMethod>& methods)
1394 if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
1396 for (
Blueprint* bp : blueprint->blueprints_)
1398 get_recursive_child_methods(bp, methods);
1401 else if (!blueprint->static_dir_.empty())
1402 methods.emplace_back(HTTPMethod::Get);
1403 for (
auto& rule : blueprint->all_rules_)
1405 rule->foreach_method([&methods](
unsigned method) {
1406 HTTPMethod method_final =
static_cast<HTTPMethod
>(method);
1407 if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
1408 methods.emplace_back(method_final);
1417 validate_bp(blueprints_, blueprint_mw);
1422 for (
unsigned i = 0; i < blueprints.size(); i++)
1426 if (blueprint->is_added())
continue;
1428 if (blueprint->static_dir_ ==
"" && blueprint->all_rules_.empty())
1430 std::vector<HTTPMethod> methods;
1431 get_recursive_child_methods(blueprint, methods);
1432 for (HTTPMethod x : methods)
1434 int method_index =
static_cast<int>(x);
1435 per_methods_[method_index].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), method_index);
1439 current_mw.merge_back(blueprint->mw_indices_);
1440 for (
auto& rule : blueprint->all_rules_)
1442 if (rule && !rule->is_added())
1444 auto upgraded = rule->upgrade();
1446 rule = std::move(upgraded);
1448 rule->mw_indices_.merge_front(current_mw);
1449 internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
1452 validate_bp(blueprint->blueprints_, current_mw);
1453 current_mw.pop_back(blueprint->mw_indices_);
1454 blueprint->set_added();
1460 for (
auto& rule : all_rules_)
1462 if (rule && !rule->is_added())
1464 auto upgraded = rule->upgrade();
1466 rule = std::move(upgraded);
1468 internal_add_rule_object(rule->rule(), rule.get());
1471 for (
auto& per_method : per_methods_)
1473 per_method.trie.validate();
1478 template<
typename App,
typename Adaptor>
1481 if (req.method >= HTTPMethod::InternalMethodCount)
1484 auto& per_method = per_methods_[
static_cast<int>(req.method)];
1485 auto& rules = per_method.rules;
1486 size_t rule_index = per_method.trie.find(req.
url).rule_index;
1490 for (
auto& method : per_methods_)
1492 if (method.trie.find(req.
url).rule_index)
1494 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" " << method_name(req.method);
1501 CROW_LOG_INFO <<
"Cannot match rules " << req.
url;
1507 if (rule_index >= rules.size())
1508 throw std::runtime_error(
"Trie internal structure corrupted!");
1510 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1512 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.
url;
1514 auto pos_of_first_non_slash = req.
url.find_first_not_of(
'/');
1515 if (pos_of_first_non_slash==1)
1520 res.
add_header(
"Location",req.
url.substr(pos_of_first_non_slash-1)+
"/");
1526 CROW_LOG_DEBUG <<
"Matched rule (upgrade) '" << rules[rule_index]->rule_ <<
"' "
1527 <<
static_cast<uint64_t
>(req.method) <<
" / "
1528 << rules[rule_index]->get_methods();
1532 handle_upgrade_rule<App>(*rules[rule_index], req, res, std::move(adaptor));
1536 exception_handler_(res);
1542 void get_found_bp(
const std::vector<size_t>& bp_i,
const std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps,
size_t index = 0)
1552 auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
1554 bp_i[index] < blueprints.size() &&
1555 blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() +
'/')) == 0;
1557 if (index < bp_i.size())
1560 if (verify_prefix())
1562 found_bps.push_back(blueprints[bp_i[index]]);
1563 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1567 if (found_bps.size() < 2)
1570 found_bps.push_back(blueprints_[bp_i[index]]);
1574 found_bps.pop_back();
1575 Blueprint* last_element = found_bps.back();
1576 found_bps.push_back(last_element->blueprints_[bp_i[index]]);
1578 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1584 std::vector<Blueprint*> bps_found;
1585 get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1586 if (!bps_found.empty()) {
1587 for (
size_t i = bps_found.size() - 1; i > 0; i--)
1589 if (bps_found[i]->catchall_rule().has_handler()) {
1590 return bps_found[i]->catchall_rule();
1594 return catchall_rule_;
1599 const std::string EMPTY;
1601 std::vector<Blueprint*> bps_found;
1602 get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1603 if (!bps_found.empty()) {
1604 for (
size_t i = bps_found.size() - 1; i > 0; i--) {
1605 if (bps_found[i]->catchall_rule().has_handler()) {
1606#ifdef CROW_ENABLE_DEBUG
1607 return std::string(
"Redirected to Blueprint \"" + bps_found[i]->prefix() +
"\" Catchall rule");
1613 }
else if (catchall_rule_.has_handler()) {
1614#ifdef CROW_ENABLE_DEBUG
1615 return std::string(
"Redirected to global Catchall rule");
1623 std::unique_ptr<routing_handle_result> handle_initial(
request& req,
response& res)
1625 HTTPMethod method_actual = req.method;
1627 std::unique_ptr<routing_handle_result> found{
1630 std::vector<size_t>(),
1632 HTTPMethod::InternalMethodCount)};
1635 if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
1637 else if (req.method == HTTPMethod::Head)
1639 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1641 if (!found->rule_index)
1643 method_actual = HTTPMethod::Get;
1644 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1645 if (!found->rule_index)
1647 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1655 found->method = method_actual;
1658 else if (req.method == HTTPMethod::Options)
1660 std::string allow =
"OPTIONS, HEAD";
1662 if (req.
url ==
"/*")
1664 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1666 if (
static_cast<int>(HTTPMethod::Head) == i)
1669 if (!per_methods_[i].trie.is_empty())
1672 allow.append(method_name(
static_cast<HTTPMethod
>(i)));
1675#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1678 res =
response(crow::status::NO_CONTENT);
1683 found->method = method_actual;
1688 bool rules_matched =
false;
1689 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1691 if (per_methods_[i].trie.find(req.
url).rule_index)
1693 rules_matched =
true;
1695 if (
static_cast<int>(HTTPMethod::Head) == i)
1699 allow.append(method_name(
static_cast<HTTPMethod
>(i)));
1704#ifdef CROW_RETURNS_OK_ON_HTTP_OPTIONS_REQUEST
1707 res =
response(crow::status::NO_CONTENT);
1711 found->method = method_actual;
1716 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1725 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1727 if (!found->rule_index)
1729 for (
auto& per_method : per_methods_)
1731 if (per_method.trie.find(req.
url).rule_index)
1734 found->catch_all =
true;
1735 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" "
1736 << method_name(method_actual) <<
". " << get_error(*found);
1743 found->catch_all =
true;
1744 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url <<
". " << get_error(*found);
1748 found->method = method_actual;
1753 template<
typename App>
1756 if (found.catch_all) {
1757 auto catch_all = get_catch_all(found);
1758 if (catch_all.has_handler()) {
1761 catch_all.handler_(req, res);
1765 exception_handler_(res);
1770 HTTPMethod method_actual = found.method;
1771 const auto& rules = per_methods_[
static_cast<int>(method_actual)].rules;
1772 const size_t rule_index = found.rule_index;
1774 if (rule_index >= rules.size())
1775 throw std::runtime_error(
"Trie internal structure corrupted!");
1776 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) {
1777 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.
url;
1779 auto pos_of_first_non_slash = req.
url.find_first_not_of(
'/');
1780 if (pos_of_first_non_slash==1)
1785 res.
add_header(
"Location",req.
url.substr(pos_of_first_non_slash-1)+
"/");
1789 CROW_LOG_DEBUG <<
"Matched rule '" << rules[rule_index]->rule_ <<
"' " <<
static_cast<uint64_t
>(req.
1790 method) <<
" / " << rules[rule_index]->get_methods();
1793 BaseRule &rule = *rules[rule_index];
1794 handle_rule<App>(rule, req, res, found.r_params);
1796 exception_handler_(res);
1803 template<
typename App>
1804 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0,
void>::type
1807 if (!rule.mw_indices_.empty())
1809 auto& ctx = *
reinterpret_cast<typename
App::context_t*
>(req.middleware_context);
1810 auto& container = *
reinterpret_cast<typename App::mw_container_t*
>(req.middleware_container);
1813 auto glob_completion_handler = std::move(res.complete_request_handler_);
1814 res.complete_request_handler_ = [] {};
1816 detail::middleware_call_helper<
decltype(crit_fwd),
1817 0,
typename App::context_t,
typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1821 glob_completion_handler();
1825 res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
1828 detail::after_handlers_call_helper<
1830 std::tuple_size<typename App::mw_container_t>::value - 1,
1832 typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1833 glob_completion_handler();
1836 rule.handle(req, res, rp);
1839 template<
typename App>
1840 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0,
void>::type
1843 rule.handle(req, res, rp);
1846 template<
typename App,
typename Adaptor>
1847 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0,
void>::type
1850 if (!rule.mw_indices_.empty())
1852 auto& ctx = *
reinterpret_cast<typename
App::context_t*
>(req.middleware_context);
1853 auto& container = *
reinterpret_cast<typename App::mw_container_t*
>(req.middleware_container);
1856 detail::middleware_call_helper<
decltype(crit_fwd),
1857 0,
typename App::context_t,
typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1863 rule.handle_upgrade(req, res, std::move(adaptor));
1866 template<
typename App,
typename Adaptor>
1867 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0,
void>::type
1870 rule.handle_upgrade(req, res, std::move(adaptor));
1875 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1877 Trie& trie_ = per_methods_[i].trie;
1880 CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1881 trie_.debug_print();
1886 std::vector<Blueprint*>& blueprints()
1893 return exception_handler_;
1896 static void default_exception_handler(
response& res)
1908 res.
body = e.what();
1910 catch (
const std::exception& e)
1912 CROW_LOG_ERROR <<
"An uncaught exception occurred: " << e.what();
1916 CROW_LOG_ERROR <<
"An uncaught exception occurred. The type was unknown so no information was available.";
1925 std::vector<BaseRule*> rules;
1932 std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1933 std::vector<std::unique_ptr<BaseRule>> all_rules_;
1934 std::vector<Blueprint*> blueprints_;
1935 std::function<void(
crow::response&)> exception_handler_ = &default_exception_handler;