707 uint16_t rule_index{};
709 uint16_t blueprint_index{INVALID_BP_ID};
711 ParamType param = ParamType::MAX;
712 std::vector<Node> children;
714 bool IsSimpleNode()
const
716 return !rule_index &&
717 blueprint_index == INVALID_BP_ID &&
718 children.size() < 2 &&
719 param == ParamType::MAX &&
720 std::all_of(std::begin(children), std::end(children), [](
const Node& x) {
721 return x.param == ParamType::MAX;
725 Node& add_child_node()
727 children.emplace_back();
728 return children.back();
739 return head_.children.empty();
744 for (
auto& child : head_.children)
752 void optimizeNode(Node& node)
754 if (node.children.empty())
756 if (node.IsSimpleNode())
758 auto children_temp = std::move(node.children);
759 auto& child_temp = children_temp[0];
760 node.key += child_temp.key;
761 node.rule_index = child_temp.rule_index;
762 node.blueprint_index = child_temp.blueprint_index;
763 node.children = std::move(child_temp.children);
768 for (
auto& child : node.children)
775 void debug_node_print(
const Node& node,
int level)
777 if (node.param != ParamType::MAX)
782 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
785 case ParamType::UINT:
786 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
789 case ParamType::DOUBLE:
790 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
793 case ParamType::STRING:
794 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
797 case ParamType::PATH:
798 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
802 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ "
808 CROW_LOG_DEBUG << std::string(3 * level,
' ') <<
"└➝ " << node.key;
810 for (
const auto& child : node.children)
812 debug_node_print(child, level + 1);
819 CROW_LOG_DEBUG <<
"└➙ ROOT";
820 for (
const auto& child : head_.children)
821 debug_node_print(child, 1);
826 if (!head_.IsSimpleNode())
827 throw std::runtime_error(
"Internal error: Trie header should be simple!");
832 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
835 routing_params empty;
836 if (params ==
nullptr)
839 std::vector<uint16_t> MT;
840 if (blueprints ==
nullptr)
844 std::vector<uint16_t> found_BP;
845 routing_params match_params;
847 auto update_found = [&found, &found_BP, &match_params](routing_handle_result& ret) {
848 found_BP = std::move(ret.blueprint_indices);
849 if (ret.rule_index && (!found || found > ret.rule_index))
851 found = ret.rule_index;
852 match_params = std::move(ret.r_params);
857 if (pos == req_url.size())
859 found_BP = std::move(*blueprints);
860 return routing_handle_result{node.rule_index, *blueprints, *params};
863 bool found_fragment =
false;
865 for (
const auto& child : node.children)
867 if (child.param != ParamType::MAX)
869 if (child.param == ParamType::INT)
871 char c = req_url[pos];
872 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-')
876 long long int value = strtoll(req_url.data() + pos, &eptr, 10);
877 if (errno != ERANGE && eptr != req_url.data() + pos)
879 found_fragment =
true;
880 params->int_params.push_back(value);
881 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
882 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
884 params->int_params.pop_back();
885 if (!blueprints->empty()) blueprints->pop_back();
890 else if (child.param == ParamType::UINT)
892 char c = req_url[pos];
893 if ((c >=
'0' && c <=
'9') || c ==
'+')
897 unsigned long long int value = strtoull(req_url.data() + pos, &eptr, 10);
898 if (errno != ERANGE && eptr != req_url.data() + pos)
900 found_fragment =
true;
901 params->uint_params.push_back(value);
902 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
903 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
905 params->uint_params.pop_back();
906 if (!blueprints->empty()) blueprints->pop_back();
911 else if (child.param == ParamType::DOUBLE)
913 char c = req_url[pos];
914 if ((c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-' || c ==
'.')
918 double value = strtod(req_url.data() + pos, &eptr);
919 if (errno != ERANGE && eptr != req_url.data() + pos)
921 found_fragment =
true;
922 params->double_params.push_back(value);
923 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
924 auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
926 params->double_params.pop_back();
927 if (!blueprints->empty()) blueprints->pop_back();
932 else if (child.param == ParamType::STRING)
935 for (; epos < req_url.size(); epos++)
937 if (req_url[epos] ==
'/')
943 found_fragment =
true;
944 params->string_params.push_back(req_url.substr(pos, epos - pos));
945 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
946 auto ret = find(req_url, child, epos, params, blueprints);
948 params->string_params.pop_back();
949 if (!blueprints->empty()) blueprints->pop_back();
953 else if (child.param == ParamType::PATH)
955 size_t epos = req_url.size();
959 found_fragment =
true;
960 params->string_params.push_back(req_url.substr(pos, epos - pos));
961 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
962 auto ret = find(req_url, child, epos, params, blueprints);
964 params->string_params.pop_back();
965 if (!blueprints->empty()) blueprints->pop_back();
972 const std::string& fragment = child.key;
973 if (req_url.compare(pos, fragment.size(), fragment) == 0)
975 found_fragment =
true;
976 if (child.blueprint_index != INVALID_BP_ID) blueprints->push_back(child.blueprint_index);
977 auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
979 if (!blueprints->empty()) blueprints->pop_back();
985 found_BP = std::move(*blueprints);
987 return routing_handle_result{found, found_BP, match_params};
990 routing_handle_result find(
const std::string& req_url)
const
992 return find(req_url, head_);
996 void add(
const std::string& url, uint16_t rule_index,
unsigned bp_prefix_length = 0, uint16_t blueprint_index = INVALID_BP_ID)
1000 bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
1002 for (
unsigned i = 0; i < url.size(); i++)
1007 static struct ParamTraits
1013 {ParamType::INT,
"<int>"},
1014 {ParamType::UINT,
"<uint>"},
1015 {ParamType::DOUBLE,
"<float>"},
1016 {ParamType::DOUBLE,
"<double>"},
1017 {ParamType::STRING,
"<str>"},
1018 {ParamType::STRING,
"<string>"},
1019 {ParamType::PATH,
"<path>"},
1022 for (
const auto& x : paramTraits)
1024 if (url.compare(i, x.name.size(), x.name) == 0)
1027 for (
auto& child : idx->children)
1029 if (child.param == x.type)
1040 auto new_node_idx = &idx->add_child_node();
1041 new_node_idx->param = x.type;
1053 bool piece_found =
false;
1054 for (
auto& child : idx->children)
1056 if (child.key[0] == c)
1065 auto new_node_idx = &idx->add_child_node();
1066 new_node_idx->key = c;
1068 if (has_blueprint && i == bp_prefix_length)
1069 new_node_idx->blueprint_index = blueprint_index;
1076 if (idx->rule_index)
1077 throw std::runtime_error(
"handler already exists for " + url);
1078 idx->rule_index = rule_index;
1095 static_dir_(prefix),
1096 templates_dir_(prefix){};
1098 Blueprint(
const std::string& prefix,
const std::string& static_dir):
1099 prefix_(prefix), static_dir_(static_dir){};
1101 Blueprint(
const std::string& prefix,
const std::string& static_dir,
const std::string& templates_dir):
1102 prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){};
1119 *
this = std::move(value);
1126 prefix_ = std::move(value.prefix_);
1127 static_dir_ = std::move(value.static_dir_);
1128 templates_dir_ = std::move(value.templates_dir_);
1129 all_rules_ = std::move(value.all_rules_);
1130 catchall_rule_ = std::move(value.catchall_rule_);
1131 blueprints_ = std::move(value.blueprints_);
1132 mw_indices_ = std::move(value.mw_indices_);
1138 return value.prefix() == prefix_;
1143 return value.prefix() != prefix_;
1146 std::string prefix()
const
1151 std::string static_dir()
const
1166 DynamicRule& new_rule_dynamic(
const std::string& rule)
1168 std::string new_rule =
'/' + prefix_ + rule;
1169 auto ruleObject =
new DynamicRule(std::move(new_rule));
1170 ruleObject->custom_templates_base = templates_dir_;
1171 all_rules_.emplace_back(ruleObject);
1176 template<u
int64_t N>
1177 typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(
const std::string& rule)
1179 std::string new_rule =
'/' + prefix_ + rule;
1180 using RuleT =
typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1182 auto ruleObject =
new RuleT(std::move(new_rule));
1183 ruleObject->custom_templates_base = templates_dir_;
1184 all_rules_.emplace_back(ruleObject);
1189 void register_blueprint(
Blueprint& blueprint)
1191 if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1193 apply_blueprint(blueprint);
1194 blueprints_.emplace_back(&blueprint);
1197 throw std::runtime_error(
"blueprint \"" + blueprint.prefix_ +
"\" already exists in blueprint \"" + prefix_ +
'\"');
1203 return catchall_rule_;
1206 template<
typename App,
typename... Middlewares>
1209 mw_indices_.push<
App, Middlewares...>();
1213 void apply_blueprint(
Blueprint& blueprint)
1216 blueprint.prefix_ = prefix_ +
'/' + blueprint.prefix_;
1217 blueprint.static_dir_ = static_dir_ +
'/' + blueprint.static_dir_;
1218 blueprint.templates_dir_ = templates_dir_ +
'/' + blueprint.templates_dir_;
1219 for (
auto& rule : blueprint.all_rules_)
1221 std::string new_rule =
'/' + prefix_ + rule->rule_;
1222 rule->rule_ = new_rule;
1224 for (
Blueprint* bp_child : blueprint.blueprints_)
1227 apply_blueprint(bp_ref);
1231 std::string prefix_;
1232 std::string static_dir_;
1233 std::string templates_dir_;
1234 std::vector<std::unique_ptr<BaseRule>> all_rules_;
1236 std::vector<Blueprint*> blueprints_;
1249 Router() : using_ssl(
false)
1252 DynamicRule& new_rule_dynamic(
const std::string& rule)
1255 all_rules_.emplace_back(ruleObject);
1260 template<u
int64_t N>
1261 typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(
const std::string& rule)
1263 using RuleT =
typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
1265 auto ruleObject =
new RuleT(rule);
1266 all_rules_.emplace_back(ruleObject);
1273 return catchall_rule_;
1276 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject)
1278 internal_add_rule_object(rule, ruleObject, INVALID_BP_ID, blueprints_);
1281 void internal_add_rule_object(
const std::string& rule,
BaseRule* ruleObject,
const uint16_t& BP_index, std::vector<Blueprint*>& blueprints)
1283 bool has_trailing_slash =
false;
1284 std::string rule_without_trailing_slash;
1285 if (rule.size() > 1 && rule.back() ==
'/')
1287 has_trailing_slash =
true;
1288 rule_without_trailing_slash = rule;
1289 rule_without_trailing_slash.pop_back();
1292 ruleObject->mw_indices_.pack();
1294 ruleObject->foreach_method([&](
int method) {
1295 per_methods_[method].rules.emplace_back(ruleObject);
1296 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);
1300 if (has_trailing_slash)
1302 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);
1306 ruleObject->set_added();
1309 void register_blueprint(
Blueprint& blueprint)
1311 if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
1313 blueprints_.emplace_back(&blueprint);
1316 throw std::runtime_error(
"blueprint \"" + blueprint.prefix_ +
"\" already exists in router");
1319 void get_recursive_child_methods(
Blueprint* blueprint, std::vector<HTTPMethod>& methods)
1322 if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
1324 for (
Blueprint* bp : blueprint->blueprints_)
1326 get_recursive_child_methods(bp, methods);
1329 else if (!blueprint->static_dir_.empty())
1330 methods.emplace_back(HTTPMethod::Get);
1331 for (
auto& rule : blueprint->all_rules_)
1333 rule->foreach_method([&methods](
unsigned method) {
1334 HTTPMethod method_final =
static_cast<HTTPMethod
>(method);
1335 if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
1336 methods.emplace_back(method_final);
1345 validate_bp(blueprints_, blueprint_mw);
1350 for (
unsigned i = 0; i < blueprints.size(); i++)
1354 if (blueprint->is_added())
continue;
1356 if (blueprint->static_dir_ ==
"" && blueprint->all_rules_.empty())
1358 std::vector<HTTPMethod> methods;
1359 get_recursive_child_methods(blueprint, methods);
1360 for (HTTPMethod x : methods)
1362 int method_index =
static_cast<int>(x);
1363 per_methods_[method_index].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), method_index);
1367 current_mw.merge_back(blueprint->mw_indices_);
1368 for (
auto& rule : blueprint->all_rules_)
1370 if (rule && !rule->is_added())
1372 auto upgraded = rule->upgrade();
1374 rule = std::move(upgraded);
1376 rule->mw_indices_.merge_front(current_mw);
1377 internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
1380 validate_bp(blueprint->blueprints_, current_mw);
1381 current_mw.pop_back(blueprint->mw_indices_);
1382 blueprint->set_added();
1388 for (
auto& rule : all_rules_)
1390 if (rule && !rule->is_added())
1392 auto upgraded = rule->upgrade();
1394 rule = std::move(upgraded);
1396 internal_add_rule_object(rule->rule(), rule.get());
1399 for (
auto& per_method : per_methods_)
1401 per_method.trie.validate();
1406 template<
typename Adaptor>
1407 void handle_upgrade(
const request& req,
response& res, Adaptor&& adaptor)
1409 if (req.method >= HTTPMethod::InternalMethodCount)
1412 auto& per_method = per_methods_[
static_cast<int>(req.method)];
1413 auto& rules = per_method.rules;
1414 unsigned rule_index = per_method.trie.find(req.
url).rule_index;
1418 for (
auto& method : per_methods_)
1420 if (method.trie.find(req.
url).rule_index)
1422 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" " << method_name(req.method);
1429 CROW_LOG_INFO <<
"Cannot match rules " << req.
url;
1435 if (rule_index >= rules.size())
1436 throw std::runtime_error(
"Trie internal structure corrupted!");
1438 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1440 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.
url;
1447 CROW_LOG_DEBUG <<
"Matched rule (upgrade) '" << rules[rule_index]->rule_ <<
"' " <<
static_cast<uint32_t
>(req.method) <<
" / " << rules[rule_index]->get_methods();
1451 rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
1455 exception_handler_(res);
1461 void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
1471 auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
1473 bp_i[index] < blueprints.size() &&
1474 blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() +
'/')) == 0;
1476 if (index < bp_i.size())
1479 if (verify_prefix())
1481 found_bps.push_back(blueprints[bp_i[index]]);
1482 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1486 if (found_bps.size() < 2)
1489 found_bps.push_back(blueprints_[bp_i[index]]);
1493 found_bps.pop_back();
1494 Blueprint* last_element = found_bps.back();
1495 found_bps.push_back(last_element->blueprints_[bp_i[index]]);
1497 get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
1506 std::vector<Blueprint*> bps_found;
1507 get_found_bp(found.blueprint_indices, blueprints_, bps_found);
1508 for (
int i = bps_found.size() - 1; i > 0; i--)
1510 std::vector<uint16_t> bpi = found.blueprint_indices;
1511 if (bps_found[i]->catchall_rule().has_handler())
1515 bps_found[i]->catchall_rule().handler_(req, res);
1519 exception_handler_(res);
1521#ifdef CROW_ENABLE_DEBUG
1522 return std::string(
"Redirected to Blueprint \"" + bps_found[i]->prefix() +
"\" Catchall rule");
1524 return std::string();
1528 if (catchall_rule_.has_handler())
1532 catchall_rule_.handler_(req, res);
1536 exception_handler_(res);
1538#ifdef CROW_ENABLE_DEBUG
1539 return std::string(
"Redirected to global Catchall rule");
1541 return std::string();
1544 return std::string();
1547 std::unique_ptr<routing_handle_result> handle_initial(
request& req,
response& res)
1549 HTTPMethod method_actual = req.method;
1551 std::unique_ptr<routing_handle_result> found{
1554 std::vector<uint16_t>(),
1556 HTTPMethod::InternalMethodCount)};
1559 if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
1561 else if (req.method == HTTPMethod::Head)
1563 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1565 if (!found->rule_index)
1567 method_actual = HTTPMethod::Get;
1568 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1569 if (!found->rule_index)
1571 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1572 res = response(404);
1579 found->method = method_actual;
1582 else if (req.method == HTTPMethod::Options)
1584 std::string allow =
"OPTIONS, HEAD, ";
1586 if (req.
url ==
"/*")
1588 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1590 if (
static_cast<int>(HTTPMethod::Head) == i)
1593 if (!per_methods_[i].trie.is_empty())
1595 allow += method_name(
static_cast<HTTPMethod
>(i)) +
", ";
1598 allow = allow.substr(0, allow.size() - 2);
1599 res = response(204);
1602 found->method = method_actual;
1607 bool rules_matched =
false;
1608 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1610 if (per_methods_[i].trie.find(req.
url).rule_index)
1612 rules_matched =
true;
1614 if (
static_cast<int>(HTTPMethod::Head) == i)
1617 allow += method_name(
static_cast<HTTPMethod
>(i)) +
", ";
1622 allow = allow.substr(0, allow.size() - 2);
1623 res = response(204);
1626 found->method = method_actual;
1631 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url;
1632 res = response(404);
1640 *found = per_methods_[
static_cast<int>(method_actual)].trie.find(req.
url);
1642 if (!found->rule_index)
1644 for (
auto& per_method : per_methods_)
1646 if (per_method.trie.find(req.
url).rule_index)
1648 const std::string error_message(
get_error(405, *found, req, res));
1649 CROW_LOG_DEBUG <<
"Cannot match method " << req.
url <<
" " << method_name(method_actual) <<
". " << error_message;
1656 const std::string error_message(
get_error(404, *found, req, res));
1657 CROW_LOG_DEBUG <<
"Cannot match rules " << req.
url <<
". " << error_message;
1662 found->method = method_actual;
1667 template<
typename App>
1668 void handle(request& req, response& res, routing_handle_result found)
1670 HTTPMethod method_actual = found.method;
1671 auto& rules = per_methods_[
static_cast<int>(method_actual)].rules;
1672 unsigned rule_index = found.rule_index;
1674 if (rule_index >= rules.size())
1675 throw std::runtime_error(
"Trie internal structure corrupted!");
1677 if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
1679 CROW_LOG_INFO <<
"Redirecting to a url with trailing slash: " << req.url;
1680 res = response(301);
1681 res.add_header(
"Location", req.url +
"/");
1686 CROW_LOG_DEBUG <<
"Matched rule '" << rules[rule_index]->rule_ <<
"' " <<
static_cast<uint32_t
>(req.method) <<
" / " << rules[rule_index]->get_methods();
1690 BaseRule& rule = *rules[rule_index];
1691 handle_rule<App>(rule, req, res, found.r_params);
1695 exception_handler_(res);
1701 template<
typename App>
1702 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0,
void>::type
1705 if (!rule.mw_indices_.empty())
1707 auto& ctx = *
reinterpret_cast<typename App::context_t*
>(req.middleware_context);
1708 auto& container = *
reinterpret_cast<typename App::mw_container_t*
>(req.middleware_container);
1709 detail::middleware_call_criteria_dynamic<false> crit_fwd(rule.mw_indices_.indices());
1711 auto glob_completion_handler = std::move(res.complete_request_handler_);
1712 res.complete_request_handler_ = [] {};
1714 detail::middleware_call_helper<
decltype(crit_fwd),
1715 0,
typename App::context_t,
typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
1719 glob_completion_handler();
1723 res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
1724 detail::middleware_call_criteria_dynamic<true> crit_bwd(rule.mw_indices_.indices());
1726 detail::after_handlers_call_helper<
1728 std::tuple_size<typename App::mw_container_t>::value - 1,
1729 typename App::context_t,
1730 typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
1731 glob_completion_handler();
1734 rule.handle(req, res, rp);
1737 template<
typename App>
1738 typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0,
void>::type
1741 rule.handle(req, res, rp);
1746 for (
int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
1748 Trie& trie_ = per_methods_[i].trie;
1749 if (!trie_.is_empty())
1751 CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
1752 trie_.debug_print();
1757 std::vector<Blueprint*>& blueprints()
1764 return exception_handler_;
1767 static void default_exception_handler(response& res)
1770 res = response(500);
1776 catch (
const bad_request& e)
1778 res = response (400);
1779 res.body = e.what();
1781 catch (
const std::exception& e)
1783 CROW_LOG_ERROR <<
"An uncaught exception occurred: " << e.what();
1787 CROW_LOG_ERROR <<
"An uncaught exception occurred. The type was unknown so no information was available.";
1792 CatchallRule catchall_rule_;
1796 std::vector<BaseRule*> rules;
1803 std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
1804 std::vector<std::unique_ptr<BaseRule>> all_rules_;
1805 std::vector<Blueprint*> blueprints_;
1806 std::function<void(
crow::response&)> exception_handler_ = &default_exception_handler;