140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 304e438cbSEd Tanous #pragma once 404e438cbSEd Tanous 53ccb3adbSEd Tanous #include "async_resp.hpp" 608bbe119SEd Tanous #include "dbus_privileges.hpp" 704e438cbSEd Tanous #include "http_request.hpp" 804e438cbSEd Tanous #include "http_response.hpp" 904e438cbSEd Tanous #include "logging.hpp" 1008bbe119SEd Tanous #include "routing/baserule.hpp" 1108bbe119SEd Tanous #include "routing/dynamicrule.hpp" 1208bbe119SEd Tanous #include "routing/taggedrule.hpp" 132c9efc3cSEd Tanous #include "verb.hpp" 1404e438cbSEd Tanous 15*d7857201SEd Tanous #include <boost/beast/http/field.hpp> 16*d7857201SEd Tanous #include <boost/beast/http/status.hpp> 1704e438cbSEd Tanous #include <boost/container/flat_map.hpp> 18d9e89dfdSEd Tanous #include <boost/container/small_vector.hpp> 1904e438cbSEd Tanous 20d9e89dfdSEd Tanous #include <algorithm> 21*d7857201SEd Tanous #include <array> 2204e438cbSEd Tanous #include <cerrno> 2304e438cbSEd Tanous #include <cstdint> 2404e438cbSEd Tanous #include <cstdlib> 25*d7857201SEd Tanous #include <format> 26*d7857201SEd Tanous #include <functional> 2704e438cbSEd Tanous #include <memory> 282c9efc3cSEd Tanous #include <optional> 29*d7857201SEd Tanous #include <stdexcept> 30*d7857201SEd Tanous #include <string> 31a3b9eb98SEd Tanous #include <string_view> 3204e438cbSEd Tanous #include <tuple> 3304e438cbSEd Tanous #include <utility> 3404e438cbSEd Tanous #include <vector> 3504e438cbSEd Tanous 3604e438cbSEd Tanous namespace crow 3704e438cbSEd Tanous { 3804e438cbSEd Tanous 3904e438cbSEd Tanous class Trie 4004e438cbSEd Tanous { 4104e438cbSEd Tanous public: 4204e438cbSEd Tanous struct Node 4304e438cbSEd Tanous { 44d9e89dfdSEd Tanous unsigned ruleIndex = 0U; 45d9e89dfdSEd Tanous 46d9e89dfdSEd Tanous size_t stringParamChild = 0U; 47d9e89dfdSEd Tanous size_t pathParamChild = 0U; 48d9e89dfdSEd Tanous 49a94ac61fSEd Tanous using ChildMap = boost::container::flat_map< 50a94ac61fSEd Tanous std::string, unsigned, std::less<>, 51d9e89dfdSEd Tanous boost::container::small_vector<std::pair<std::string, unsigned>, 52d9e89dfdSEd Tanous 1>>; 53a94ac61fSEd Tanous ChildMap children; 5404e438cbSEd Tanous isSimpleNodecrow::Trie::Node5504e438cbSEd Tanous bool isSimpleNode() const 5604e438cbSEd Tanous { 57d9e89dfdSEd Tanous return ruleIndex == 0 && stringParamChild == 0 && 58d9e89dfdSEd Tanous pathParamChild == 0; 5904e438cbSEd Tanous } 6004e438cbSEd Tanous }; 6104e438cbSEd Tanous Trie()6289492a15SPatrick Williams Trie() : nodes(1) {} 6304e438cbSEd Tanous 6404e438cbSEd Tanous private: optimizeNode(Node & node)65d9e89dfdSEd Tanous void optimizeNode(Node& node) 6604e438cbSEd Tanous { 67d9e89dfdSEd Tanous if (node.stringParamChild != 0U) 6804e438cbSEd Tanous { 69d9e89dfdSEd Tanous optimizeNode(nodes[node.stringParamChild]); 7004e438cbSEd Tanous } 71d9e89dfdSEd Tanous if (node.pathParamChild != 0U) 72d9e89dfdSEd Tanous { 73d9e89dfdSEd Tanous optimizeNode(nodes[node.pathParamChild]); 7404e438cbSEd Tanous } 75d9e89dfdSEd Tanous 76d9e89dfdSEd Tanous if (node.children.empty()) 7704e438cbSEd Tanous { 7804e438cbSEd Tanous return; 7904e438cbSEd Tanous } 80d9e89dfdSEd Tanous while (true) 8104e438cbSEd Tanous { 82d9e89dfdSEd Tanous bool didMerge = false; 83a94ac61fSEd Tanous Node::ChildMap merged; 84d9e89dfdSEd Tanous for (const Node::ChildMap::value_type& kv : node.children) 8504e438cbSEd Tanous { 86d9e89dfdSEd Tanous Node& child = nodes[kv.second]; 87d9e89dfdSEd Tanous if (child.isSimpleNode()) 88d9e89dfdSEd Tanous { 89a94ac61fSEd Tanous for (const Node::ChildMap::value_type& childKv : 90d9e89dfdSEd Tanous child.children) 9104e438cbSEd Tanous { 9204e438cbSEd Tanous merged[kv.first + childKv.first] = childKv.second; 93d9e89dfdSEd Tanous didMerge = true; 9404e438cbSEd Tanous } 9504e438cbSEd Tanous } 9604e438cbSEd Tanous else 9704e438cbSEd Tanous { 98d9e89dfdSEd Tanous merged[kv.first] = kv.second; 9904e438cbSEd Tanous } 10004e438cbSEd Tanous } 101d9e89dfdSEd Tanous node.children = std::move(merged); 102d9e89dfdSEd Tanous if (!didMerge) 103d9e89dfdSEd Tanous { 104d9e89dfdSEd Tanous break; 105d9e89dfdSEd Tanous } 106d9e89dfdSEd Tanous } 107d9e89dfdSEd Tanous 108d9e89dfdSEd Tanous for (const Node::ChildMap::value_type& kv : node.children) 109d9e89dfdSEd Tanous { 110d9e89dfdSEd Tanous optimizeNode(nodes[kv.second]); 111d9e89dfdSEd Tanous } 11204e438cbSEd Tanous } 11304e438cbSEd Tanous optimize()11404e438cbSEd Tanous void optimize() 11504e438cbSEd Tanous { 11604e438cbSEd Tanous optimizeNode(head()); 11704e438cbSEd Tanous } 11804e438cbSEd Tanous 11904e438cbSEd Tanous public: validate()12004e438cbSEd Tanous void validate() 12104e438cbSEd Tanous { 12204e438cbSEd Tanous optimize(); 12304e438cbSEd Tanous } 12404e438cbSEd Tanous findRouteIndexesHelper(std::string_view reqUrl,std::vector<unsigned> & routeIndexes,const Node & node) const125d9e89dfdSEd Tanous void findRouteIndexesHelper(std::string_view reqUrl, 12681ce609eSEd Tanous std::vector<unsigned>& routeIndexes, 127d9e89dfdSEd Tanous const Node& node) const 12804e438cbSEd Tanous { 129d9e89dfdSEd Tanous for (const Node::ChildMap::value_type& kv : node.children) 13004e438cbSEd Tanous { 13104e438cbSEd Tanous const std::string& fragment = kv.first; 132d9e89dfdSEd Tanous const Node& child = nodes[kv.second]; 133d9e89dfdSEd Tanous if (reqUrl.empty()) 13404e438cbSEd Tanous { 135d9e89dfdSEd Tanous if (child.ruleIndex != 0 && fragment != "/") 13604e438cbSEd Tanous { 137d9e89dfdSEd Tanous routeIndexes.push_back(child.ruleIndex); 13804e438cbSEd Tanous } 139d9e89dfdSEd Tanous findRouteIndexesHelper(reqUrl, routeIndexes, child); 14004e438cbSEd Tanous } 14104e438cbSEd Tanous else 14204e438cbSEd Tanous { 143d9e89dfdSEd Tanous if (reqUrl.starts_with(fragment)) 14404e438cbSEd Tanous { 145d9e89dfdSEd Tanous findRouteIndexesHelper(reqUrl.substr(fragment.size()), 146d9e89dfdSEd Tanous routeIndexes, child); 14704e438cbSEd Tanous } 14804e438cbSEd Tanous } 14904e438cbSEd Tanous } 15004e438cbSEd Tanous } 15104e438cbSEd Tanous findRouteIndexes(const std::string & reqUrl,std::vector<unsigned> & routeIndexes) const152d9e89dfdSEd Tanous void findRouteIndexes(const std::string& reqUrl, 153d9e89dfdSEd Tanous std::vector<unsigned>& routeIndexes) const 15404e438cbSEd Tanous { 155d9e89dfdSEd Tanous findRouteIndexesHelper(reqUrl, routeIndexes, head()); 15604e438cbSEd Tanous } 15704e438cbSEd Tanous 158d9e89dfdSEd Tanous struct FindResult 15904e438cbSEd Tanous { 160d9e89dfdSEd Tanous unsigned ruleIndex; 161d9e89dfdSEd Tanous std::vector<std::string> params; 16204e438cbSEd Tanous }; 16304e438cbSEd Tanous 164d9e89dfdSEd Tanous private: findHelper(const std::string_view reqUrl,const Node & node,std::vector<std::string> & params) const165d9e89dfdSEd Tanous FindResult findHelper(const std::string_view reqUrl, const Node& node, 166d9e89dfdSEd Tanous std::vector<std::string>& params) const 16704e438cbSEd Tanous { 168d9e89dfdSEd Tanous if (reqUrl.empty()) 169d9e89dfdSEd Tanous { 170d9e89dfdSEd Tanous return {node.ruleIndex, params}; 171d9e89dfdSEd Tanous } 172d9e89dfdSEd Tanous 173d9e89dfdSEd Tanous if (node.stringParamChild != 0U) 174d9e89dfdSEd Tanous { 175d9e89dfdSEd Tanous size_t epos = 0; 17681ce609eSEd Tanous for (; epos < reqUrl.size(); epos++) 17704e438cbSEd Tanous { 17881ce609eSEd Tanous if (reqUrl[epos] == '/') 17904e438cbSEd Tanous { 18004e438cbSEd Tanous break; 18104e438cbSEd Tanous } 18204e438cbSEd Tanous } 18304e438cbSEd Tanous 184d9e89dfdSEd Tanous if (epos != 0) 18504e438cbSEd Tanous { 186d9e89dfdSEd Tanous params.emplace_back(reqUrl.substr(0, epos)); 187d9e89dfdSEd Tanous FindResult ret = findHelper( 188d9e89dfdSEd Tanous reqUrl.substr(epos), nodes[node.stringParamChild], params); 189d9e89dfdSEd Tanous if (ret.ruleIndex != 0U) 190d9e89dfdSEd Tanous { 191d9e89dfdSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 192d9e89dfdSEd Tanous } 193d9e89dfdSEd Tanous params.pop_back(); 19404e438cbSEd Tanous } 19504e438cbSEd Tanous } 19604e438cbSEd Tanous 197d9e89dfdSEd Tanous if (node.pathParamChild != 0U) 19804e438cbSEd Tanous { 199d9e89dfdSEd Tanous params.emplace_back(reqUrl); 200d9e89dfdSEd Tanous FindResult ret = findHelper("", nodes[node.pathParamChild], params); 201d9e89dfdSEd Tanous if (ret.ruleIndex != 0U) 20204e438cbSEd Tanous { 203d9e89dfdSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 20404e438cbSEd Tanous } 205d9e89dfdSEd Tanous params.pop_back(); 20604e438cbSEd Tanous } 20704e438cbSEd Tanous 208d9e89dfdSEd Tanous for (const Node::ChildMap::value_type& kv : node.children) 20904e438cbSEd Tanous { 21004e438cbSEd Tanous const std::string& fragment = kv.first; 211d9e89dfdSEd Tanous const Node& child = nodes[kv.second]; 21204e438cbSEd Tanous 213d9e89dfdSEd Tanous if (reqUrl.starts_with(fragment)) 21404e438cbSEd Tanous { 215bd79bce8SPatrick Williams FindResult ret = 216bd79bce8SPatrick Williams findHelper(reqUrl.substr(fragment.size()), child, params); 217d9e89dfdSEd Tanous if (ret.ruleIndex != 0U) 218d9e89dfdSEd Tanous { 219d9e89dfdSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 220d9e89dfdSEd Tanous } 22104e438cbSEd Tanous } 22204e438cbSEd Tanous } 22304e438cbSEd Tanous 224d9e89dfdSEd Tanous return {0U, std::vector<std::string>()}; 22504e438cbSEd Tanous } 22604e438cbSEd Tanous 227d9e89dfdSEd Tanous public: find(const std::string_view reqUrl) const228d9e89dfdSEd Tanous FindResult find(const std::string_view reqUrl) const 229d9e89dfdSEd Tanous { 230d9e89dfdSEd Tanous std::vector<std::string> start; 231d9e89dfdSEd Tanous return findHelper(reqUrl, head(), start); 232d9e89dfdSEd Tanous } 233d9e89dfdSEd Tanous add(std::string_view urlIn,unsigned ruleIndex)234a3b9eb98SEd Tanous void add(std::string_view urlIn, unsigned ruleIndex) 23504e438cbSEd Tanous { 23604e438cbSEd Tanous size_t idx = 0; 23704e438cbSEd Tanous 238a3b9eb98SEd Tanous std::string_view url = urlIn; 239a3b9eb98SEd Tanous 240d9e89dfdSEd Tanous while (!url.empty()) 24104e438cbSEd Tanous { 242d9e89dfdSEd Tanous char c = url[0]; 24304e438cbSEd Tanous if (c == '<') 24404e438cbSEd Tanous { 245d9e89dfdSEd Tanous bool found = false; 246d9e89dfdSEd Tanous for (const std::string_view str1 : 247d9e89dfdSEd Tanous {"<str>", "<string>", "<path>"}) 24804e438cbSEd Tanous { 249d9e89dfdSEd Tanous if (!url.starts_with(str1)) 25004e438cbSEd Tanous { 251d9e89dfdSEd Tanous continue; 25204e438cbSEd Tanous } 253d9e89dfdSEd Tanous found = true; 254d9e89dfdSEd Tanous Node& node = nodes[idx]; 255d9e89dfdSEd Tanous size_t* param = &node.stringParamChild; 256d9e89dfdSEd Tanous if (str1 == "<path>") 257d9e89dfdSEd Tanous { 258d9e89dfdSEd Tanous param = &node.pathParamChild; 259d9e89dfdSEd Tanous } 260d9e89dfdSEd Tanous if (*param == 0U) 261d9e89dfdSEd Tanous { 262d9e89dfdSEd Tanous *param = newNode(); 263d9e89dfdSEd Tanous } 264d9e89dfdSEd Tanous idx = *param; 265d9e89dfdSEd Tanous 266d9e89dfdSEd Tanous url.remove_prefix(str1.size()); 26704e438cbSEd Tanous break; 26804e438cbSEd Tanous } 269d9e89dfdSEd Tanous if (found) 270d9e89dfdSEd Tanous { 271d9e89dfdSEd Tanous continue; 27204e438cbSEd Tanous } 27304e438cbSEd Tanous 274efff2b5dSManojkiran Eda BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn); 275d9e89dfdSEd Tanous return; 27604e438cbSEd Tanous } 27704e438cbSEd Tanous std::string piece(&c, 1); 278d9e89dfdSEd Tanous if (!nodes[idx].children.contains(piece)) 27904e438cbSEd Tanous { 28004e438cbSEd Tanous unsigned newNodeIdx = newNode(); 28104e438cbSEd Tanous nodes[idx].children.emplace(piece, newNodeIdx); 28204e438cbSEd Tanous } 28304e438cbSEd Tanous idx = nodes[idx].children[piece]; 284d9e89dfdSEd Tanous url.remove_prefix(1); 28504e438cbSEd Tanous } 286a3b9eb98SEd Tanous Node& node = nodes[idx]; 287a3b9eb98SEd Tanous if (node.ruleIndex != 0U) 28804e438cbSEd Tanous { 289a3b9eb98SEd Tanous BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn); 290d9e89dfdSEd Tanous throw std::runtime_error( 291a3b9eb98SEd Tanous std::format("handler already exists for \"{}\"", urlIn)); 29204e438cbSEd Tanous } 293a3b9eb98SEd Tanous node.ruleIndex = ruleIndex; 29404e438cbSEd Tanous } 29504e438cbSEd Tanous 29604e438cbSEd Tanous private: debugNodePrint(Node & n,size_t level)297d9e89dfdSEd Tanous void debugNodePrint(Node& n, size_t level) 29804e438cbSEd Tanous { 299d9e89dfdSEd Tanous std::string spaces(level, ' '); 300d9e89dfdSEd Tanous if (n.stringParamChild != 0U) 30104e438cbSEd Tanous { 302d9e89dfdSEd Tanous BMCWEB_LOG_DEBUG("{}<str>", spaces); 303d9e89dfdSEd Tanous debugNodePrint(nodes[n.stringParamChild], level + 5); 30404e438cbSEd Tanous } 305d9e89dfdSEd Tanous if (n.pathParamChild != 0U) 30604e438cbSEd Tanous { 307d9e89dfdSEd Tanous BMCWEB_LOG_DEBUG("{} <path>", spaces); 308d9e89dfdSEd Tanous debugNodePrint(nodes[n.pathParamChild], level + 6); 309d9e89dfdSEd Tanous } 310d9e89dfdSEd Tanous for (const Node::ChildMap::value_type& kv : n.children) 311d9e89dfdSEd Tanous { 312d9e89dfdSEd Tanous BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first); 313d9e89dfdSEd Tanous debugNodePrint(nodes[kv.second], level + kv.first.size()); 31404e438cbSEd Tanous } 31504e438cbSEd Tanous } 31604e438cbSEd Tanous 31704e438cbSEd Tanous public: debugPrint()31804e438cbSEd Tanous void debugPrint() 31904e438cbSEd Tanous { 32004e438cbSEd Tanous debugNodePrint(head(), 0U); 32104e438cbSEd Tanous } 32204e438cbSEd Tanous 32304e438cbSEd Tanous private: head() const324d9e89dfdSEd Tanous const Node& head() const 32504e438cbSEd Tanous { 326d9e89dfdSEd Tanous return nodes.front(); 32704e438cbSEd Tanous } 32804e438cbSEd Tanous head()329d9e89dfdSEd Tanous Node& head() 33004e438cbSEd Tanous { 331d9e89dfdSEd Tanous return nodes.front(); 33204e438cbSEd Tanous } 33304e438cbSEd Tanous newNode()33404e438cbSEd Tanous unsigned newNode() 33504e438cbSEd Tanous { 33604e438cbSEd Tanous nodes.resize(nodes.size() + 1); 33704e438cbSEd Tanous return static_cast<unsigned>(nodes.size() - 1); 33804e438cbSEd Tanous } 33904e438cbSEd Tanous 34004e438cbSEd Tanous std::vector<Node> nodes; 34104e438cbSEd Tanous }; 34204e438cbSEd Tanous 34304e438cbSEd Tanous class Router 34404e438cbSEd Tanous { 34504e438cbSEd Tanous public: 34604e438cbSEd Tanous Router() = default; 34704e438cbSEd Tanous newRuleDynamic(const std::string & rule)34804e438cbSEd Tanous DynamicRule& newRuleDynamic(const std::string& rule) 34904e438cbSEd Tanous { 35004e438cbSEd Tanous std::unique_ptr<DynamicRule> ruleObject = 35104e438cbSEd Tanous std::make_unique<DynamicRule>(rule); 35204e438cbSEd Tanous DynamicRule* ptr = ruleObject.get(); 35304e438cbSEd Tanous allRules.emplace_back(std::move(ruleObject)); 35404e438cbSEd Tanous 35504e438cbSEd Tanous return *ptr; 35604e438cbSEd Tanous } 35704e438cbSEd Tanous 358d9e89dfdSEd Tanous template <uint64_t NumArgs> newRuleTagged(const std::string & rule)359cfe3bc0aSEd Tanous auto& newRuleTagged(const std::string& rule) 36004e438cbSEd Tanous { 361d9e89dfdSEd Tanous if constexpr (NumArgs == 0) 362cfe3bc0aSEd Tanous { 363cfe3bc0aSEd Tanous using RuleT = TaggedRule<>; 36404e438cbSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 36504e438cbSEd Tanous RuleT* ptr = ruleObject.get(); 36604e438cbSEd Tanous allRules.emplace_back(std::move(ruleObject)); 36704e438cbSEd Tanous return *ptr; 36804e438cbSEd Tanous } 369d9e89dfdSEd Tanous else if constexpr (NumArgs == 1) 370cfe3bc0aSEd Tanous { 371cfe3bc0aSEd Tanous using RuleT = TaggedRule<std::string>; 372cfe3bc0aSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 373cfe3bc0aSEd Tanous RuleT* ptr = ruleObject.get(); 374cfe3bc0aSEd Tanous allRules.emplace_back(std::move(ruleObject)); 375cfe3bc0aSEd Tanous return *ptr; 376cfe3bc0aSEd Tanous } 377d9e89dfdSEd Tanous else if constexpr (NumArgs == 2) 378cfe3bc0aSEd Tanous { 379cfe3bc0aSEd Tanous using RuleT = TaggedRule<std::string, std::string>; 380cfe3bc0aSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 381cfe3bc0aSEd Tanous RuleT* ptr = ruleObject.get(); 382cfe3bc0aSEd Tanous allRules.emplace_back(std::move(ruleObject)); 383cfe3bc0aSEd Tanous return *ptr; 384cfe3bc0aSEd Tanous } 385d9e89dfdSEd Tanous else if constexpr (NumArgs == 3) 386cfe3bc0aSEd Tanous { 387cfe3bc0aSEd Tanous using RuleT = TaggedRule<std::string, std::string, std::string>; 388cfe3bc0aSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 389cfe3bc0aSEd Tanous RuleT* ptr = ruleObject.get(); 390cfe3bc0aSEd Tanous allRules.emplace_back(std::move(ruleObject)); 391cfe3bc0aSEd Tanous return *ptr; 392cfe3bc0aSEd Tanous } 393d9e89dfdSEd Tanous else if constexpr (NumArgs == 4) 394cfe3bc0aSEd Tanous { 395cfe3bc0aSEd Tanous using RuleT = 396cfe3bc0aSEd Tanous TaggedRule<std::string, std::string, std::string, std::string>; 397cfe3bc0aSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 398cfe3bc0aSEd Tanous RuleT* ptr = ruleObject.get(); 399cfe3bc0aSEd Tanous allRules.emplace_back(std::move(ruleObject)); 400cfe3bc0aSEd Tanous return *ptr; 401cfe3bc0aSEd Tanous } 402cfe3bc0aSEd Tanous else 403cfe3bc0aSEd Tanous { 404cfe3bc0aSEd Tanous using RuleT = TaggedRule<std::string, std::string, std::string, 405cfe3bc0aSEd Tanous std::string, std::string>; 406cfe3bc0aSEd Tanous std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 407cfe3bc0aSEd Tanous RuleT* ptr = ruleObject.get(); 408cfe3bc0aSEd Tanous allRules.emplace_back(std::move(ruleObject)); 409cfe3bc0aSEd Tanous return *ptr; 410cfe3bc0aSEd Tanous } 411d9e89dfdSEd Tanous static_assert(NumArgs <= 5, "Max number of args supported is 5"); 412cfe3bc0aSEd Tanous } 41304e438cbSEd Tanous 414a3b9eb98SEd Tanous struct PerMethod 415a3b9eb98SEd Tanous { 416a3b9eb98SEd Tanous std::vector<BaseRule*> rules; 417a3b9eb98SEd Tanous Trie trie; 418a3b9eb98SEd Tanous // rule index 0 has special meaning; preallocate it to avoid 419a3b9eb98SEd Tanous // duplication. PerMethodcrow::Router::PerMethod420a3b9eb98SEd Tanous PerMethod() : rules(1) {} 421a3b9eb98SEd Tanous internalAddcrow::Router::PerMethod422a3b9eb98SEd Tanous void internalAdd(std::string_view rule, BaseRule* ruleObject) 423a3b9eb98SEd Tanous { 424a3b9eb98SEd Tanous rules.emplace_back(ruleObject); 425a3b9eb98SEd Tanous trie.add(rule, static_cast<unsigned>(rules.size() - 1U)); 426a3b9eb98SEd Tanous // directory case: 427a3b9eb98SEd Tanous // request to `/about' url matches `/about/' rule 428a3b9eb98SEd Tanous if (rule.size() > 2 && rule.back() == '/') 429a3b9eb98SEd Tanous { 430a3b9eb98SEd Tanous trie.add(rule.substr(0, rule.size() - 1), 431a3b9eb98SEd Tanous static_cast<unsigned>(rules.size() - 1)); 432a3b9eb98SEd Tanous } 433a3b9eb98SEd Tanous } 434a3b9eb98SEd Tanous }; 435a3b9eb98SEd Tanous internalAddRuleObject(const std::string & rule,BaseRule * ruleObject)43604e438cbSEd Tanous void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject) 43704e438cbSEd Tanous { 43804e438cbSEd Tanous if (ruleObject == nullptr) 43904e438cbSEd Tanous { 44004e438cbSEd Tanous return; 44104e438cbSEd Tanous } 442a3b9eb98SEd Tanous for (size_t method = 0; method <= maxVerbIndex; method++) 44304e438cbSEd Tanous { 444a3b9eb98SEd Tanous size_t methodBit = 1 << method; 445e662eae8SEd Tanous if ((ruleObject->methodsBitfield & methodBit) > 0U) 44604e438cbSEd Tanous { 447a3b9eb98SEd Tanous perMethods[method].internalAdd(rule, ruleObject); 448a3b9eb98SEd Tanous } 449a3b9eb98SEd Tanous } 450a3b9eb98SEd Tanous 451a3b9eb98SEd Tanous if (ruleObject->isNotFound) 45204e438cbSEd Tanous { 453a3b9eb98SEd Tanous notFoundRoutes.internalAdd(rule, ruleObject); 45404e438cbSEd Tanous } 455a3b9eb98SEd Tanous 456a3b9eb98SEd Tanous if (ruleObject->isMethodNotAllowed) 457a3b9eb98SEd Tanous { 458a3b9eb98SEd Tanous methodNotAllowedRoutes.internalAdd(rule, ruleObject); 45904e438cbSEd Tanous } 460a3b9eb98SEd Tanous 461a3b9eb98SEd Tanous if (ruleObject->isUpgrade) 462a3b9eb98SEd Tanous { 463a3b9eb98SEd Tanous upgradeRoutes.internalAdd(rule, ruleObject); 46404e438cbSEd Tanous } 46504e438cbSEd Tanous } 46604e438cbSEd Tanous validate()46704e438cbSEd Tanous void validate() 46804e438cbSEd Tanous { 46904e438cbSEd Tanous for (std::unique_ptr<BaseRule>& rule : allRules) 47004e438cbSEd Tanous { 47104e438cbSEd Tanous if (rule) 47204e438cbSEd Tanous { 47304e438cbSEd Tanous std::unique_ptr<BaseRule> upgraded = rule->upgrade(); 47404e438cbSEd Tanous if (upgraded) 47504e438cbSEd Tanous { 47604e438cbSEd Tanous rule = std::move(upgraded); 47704e438cbSEd Tanous } 47804e438cbSEd Tanous rule->validate(); 47904e438cbSEd Tanous internalAddRuleObject(rule->rule, rule.get()); 48004e438cbSEd Tanous } 48104e438cbSEd Tanous } 48204e438cbSEd Tanous for (PerMethod& perMethod : perMethods) 48304e438cbSEd Tanous { 48404e438cbSEd Tanous perMethod.trie.validate(); 48504e438cbSEd Tanous } 48604e438cbSEd Tanous } 48704e438cbSEd Tanous 48844e4518bSEd Tanous struct FindRoute 48944e4518bSEd Tanous { 49044e4518bSEd Tanous BaseRule* rule = nullptr; 49115a42df0SEd Tanous std::vector<std::string> params; 49244e4518bSEd Tanous }; 49344e4518bSEd Tanous 49444e4518bSEd Tanous struct FindRouteResponse 49544e4518bSEd Tanous { 49644e4518bSEd Tanous std::string allowHeader; 49744e4518bSEd Tanous FindRoute route; 49844e4518bSEd Tanous }; 49944e4518bSEd Tanous findRouteByPerMethod(std::string_view url,const PerMethod & perMethod)500a3b9eb98SEd Tanous static FindRoute findRouteByPerMethod(std::string_view url, 501a3b9eb98SEd Tanous const PerMethod& perMethod) 502759cf105SEd Tanous { 503759cf105SEd Tanous FindRoute route; 504a3b9eb98SEd Tanous 505d9e89dfdSEd Tanous Trie::FindResult found = perMethod.trie.find(url); 506d9e89dfdSEd Tanous if (found.ruleIndex >= perMethod.rules.size()) 507759cf105SEd Tanous { 508759cf105SEd Tanous throw std::runtime_error("Trie internal structure corrupted!"); 509759cf105SEd Tanous } 510759cf105SEd Tanous // Found a 404 route, switch that in 511d9e89dfdSEd Tanous if (found.ruleIndex != 0U) 512759cf105SEd Tanous { 513d9e89dfdSEd Tanous route.rule = perMethod.rules[found.ruleIndex]; 514d9e89dfdSEd Tanous route.params = std::move(found.params); 515759cf105SEd Tanous } 516759cf105SEd Tanous return route; 517759cf105SEd Tanous } 518759cf105SEd Tanous findRoute(const Request & req) const519102a4cdaSJonathan Doman FindRouteResponse findRoute(const Request& req) const 52044e4518bSEd Tanous { 52144e4518bSEd Tanous FindRouteResponse findRoute; 52244e4518bSEd Tanous 52344e4518bSEd Tanous // Check to see if this url exists at any verb 52444e4518bSEd Tanous for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex; 52544e4518bSEd Tanous perMethodIndex++) 52644e4518bSEd Tanous { 52744e4518bSEd Tanous // Make sure it's safe to deference the array at that index 528bd79bce8SPatrick Williams static_assert( 529bd79bce8SPatrick Williams maxVerbIndex < std::tuple_size_v<decltype(perMethods)>); 530a3b9eb98SEd Tanous FindRoute route = findRouteByPerMethod(req.url().encoded_path(), 531a3b9eb98SEd Tanous perMethods[perMethodIndex]); 532759cf105SEd Tanous if (route.rule == nullptr) 53344e4518bSEd Tanous { 53444e4518bSEd Tanous continue; 53544e4518bSEd Tanous } 53644e4518bSEd Tanous if (!findRoute.allowHeader.empty()) 53744e4518bSEd Tanous { 53844e4518bSEd Tanous findRoute.allowHeader += ", "; 53944e4518bSEd Tanous } 5402c9efc3cSEd Tanous HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex); 5412c9efc3cSEd Tanous findRoute.allowHeader += httpVerbToString(thisVerb); 54250bfc917SEd Tanous } 54350bfc917SEd Tanous 54450bfc917SEd Tanous std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 54550bfc917SEd Tanous if (!verb) 54650bfc917SEd Tanous { 54750bfc917SEd Tanous return findRoute; 54850bfc917SEd Tanous } 54950bfc917SEd Tanous size_t reqMethodIndex = static_cast<size_t>(*verb); 55050bfc917SEd Tanous if (reqMethodIndex >= perMethods.size()) 55150bfc917SEd Tanous { 55250bfc917SEd Tanous return findRoute; 55350bfc917SEd Tanous } 55450bfc917SEd Tanous 55550bfc917SEd Tanous FindRoute route = findRouteByPerMethod(req.url().encoded_path(), 55650bfc917SEd Tanous perMethods[reqMethodIndex]); 55750bfc917SEd Tanous if (route.rule != nullptr) 55844e4518bSEd Tanous { 559759cf105SEd Tanous findRoute.route = route; 56044e4518bSEd Tanous } 56150bfc917SEd Tanous 56244e4518bSEd Tanous return findRoute; 56344e4518bSEd Tanous } 56444e4518bSEd Tanous 56504e438cbSEd Tanous template <typename Adaptor> handleUpgrade(const std::shared_ptr<Request> & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,Adaptor && adaptor)566102a4cdaSJonathan Doman void handleUpgrade(const std::shared_ptr<Request>& req, 567a9f076e5SP Dheeraj Srujan Kumar const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 568a9f076e5SP Dheeraj Srujan Kumar Adaptor&& adaptor) 56904e438cbSEd Tanous { 570a3b9eb98SEd Tanous PerMethod& perMethod = upgradeRoutes; 57104e438cbSEd Tanous Trie& trie = perMethod.trie; 57204e438cbSEd Tanous std::vector<BaseRule*>& rules = perMethod.rules; 57304e438cbSEd Tanous 574102a4cdaSJonathan Doman Trie::FindResult found = trie.find(req->url().encoded_path()); 575d9e89dfdSEd Tanous unsigned ruleIndex = found.ruleIndex; 576e662eae8SEd Tanous if (ruleIndex == 0U) 57704e438cbSEd Tanous { 578102a4cdaSJonathan Doman BMCWEB_LOG_DEBUG("Cannot match rules {}", 579102a4cdaSJonathan Doman req->url().encoded_path()); 580a9f076e5SP Dheeraj Srujan Kumar asyncResp->res.result(boost::beast::http::status::not_found); 58104e438cbSEd Tanous return; 58204e438cbSEd Tanous } 58304e438cbSEd Tanous 58404e438cbSEd Tanous if (ruleIndex >= rules.size()) 58504e438cbSEd Tanous { 58604e438cbSEd Tanous throw std::runtime_error("Trie internal structure corrupted!"); 58704e438cbSEd Tanous } 58804e438cbSEd Tanous 5897e9093e6SP Dheeraj Srujan Kumar BaseRule& rule = *rules[ruleIndex]; 59004e438cbSEd Tanous 591a3b9eb98SEd Tanous BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}'", rule.rule); 59204e438cbSEd Tanous 5937e9093e6SP Dheeraj Srujan Kumar // TODO(ed) This should be able to use std::bind_front, but it doesn't 5947e9093e6SP Dheeraj Srujan Kumar // appear to work with the std::move on adaptor. 595bd79bce8SPatrick Williams validatePrivilege( 596bd79bce8SPatrick Williams req, asyncResp, rule, 597102a4cdaSJonathan Doman [req, &rule, asyncResp, 598102a4cdaSJonathan Doman adaptor = std::forward<Adaptor>(adaptor)]() mutable { 599102a4cdaSJonathan Doman rule.handleUpgrade(*req, asyncResp, std::move(adaptor)); 6007e9093e6SP Dheeraj Srujan Kumar }); 60104e438cbSEd Tanous } 60204e438cbSEd Tanous handle(const std::shared_ptr<Request> & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)603102a4cdaSJonathan Doman void handle(const std::shared_ptr<Request>& req, 6048d1b46d7Szhanghch05 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 60504e438cbSEd Tanous { 606102a4cdaSJonathan Doman FindRouteResponse foundRoute = findRoute(*req); 60744e4518bSEd Tanous 608759cf105SEd Tanous if (foundRoute.route.rule == nullptr) 609759cf105SEd Tanous { 61044e4518bSEd Tanous // Couldn't find a normal route with any verb, try looking for a 404 61144e4518bSEd Tanous // route 61244e4518bSEd Tanous if (foundRoute.allowHeader.empty()) 61388a03c55SEd Tanous { 614a3b9eb98SEd Tanous foundRoute.route = findRouteByPerMethod( 615a3b9eb98SEd Tanous req->url().encoded_path(), notFoundRoutes); 61644e4518bSEd Tanous } 61744e4518bSEd Tanous else 61844e4518bSEd Tanous { 619759cf105SEd Tanous // See if we have a method not allowed (405) handler 620a3b9eb98SEd Tanous foundRoute.route = findRouteByPerMethod( 621a3b9eb98SEd Tanous req->url().encoded_path(), methodNotAllowedRoutes); 622759cf105SEd Tanous } 623759cf105SEd Tanous } 624759cf105SEd Tanous 625759cf105SEd Tanous // Fill in the allow header if it's valid 626759cf105SEd Tanous if (!foundRoute.allowHeader.empty()) 627759cf105SEd Tanous { 62888a03c55SEd Tanous asyncResp->res.addHeader(boost::beast::http::field::allow, 62944e4518bSEd Tanous foundRoute.allowHeader); 63088a03c55SEd Tanous } 63104e438cbSEd Tanous 63244e4518bSEd Tanous // If we couldn't find a real route or a 404 route, return a generic 63344e4518bSEd Tanous // response 63444e4518bSEd Tanous if (foundRoute.route.rule == nullptr) 63504e438cbSEd Tanous { 63644e4518bSEd Tanous if (foundRoute.allowHeader.empty()) 63704e438cbSEd Tanous { 6388d1b46d7Szhanghch05 asyncResp->res.result(boost::beast::http::status::not_found); 63904e438cbSEd Tanous } 64044e4518bSEd Tanous else 64104e438cbSEd Tanous { 6428d1b46d7Szhanghch05 asyncResp->res.result( 6438d1b46d7Szhanghch05 boost::beast::http::status::method_not_allowed); 64444e4518bSEd Tanous } 64504e438cbSEd Tanous return; 64604e438cbSEd Tanous } 64704e438cbSEd Tanous 64844e4518bSEd Tanous BaseRule& rule = *foundRoute.route.rule; 64915a42df0SEd Tanous std::vector<std::string> params = std::move(foundRoute.route.params); 65044e4518bSEd Tanous 65162598e31SEd Tanous BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule, 65250bfc917SEd Tanous req->methodString(), rule.getMethods()); 65304e438cbSEd Tanous 654102a4cdaSJonathan Doman if (req->session == nullptr) 65504e438cbSEd Tanous { 656102a4cdaSJonathan Doman rule.handle(*req, asyncResp, params); 65704e438cbSEd Tanous return; 65804e438cbSEd Tanous } 659102a4cdaSJonathan Doman validatePrivilege( 660102a4cdaSJonathan Doman req, asyncResp, rule, 661102a4cdaSJonathan Doman [req, asyncResp, &rule, params = std::move(params)]() { 662102a4cdaSJonathan Doman rule.handle(*req, asyncResp, params); 663915d2d4eSEd Tanous }); 66404e438cbSEd Tanous } 66504e438cbSEd Tanous debugPrint()66604e438cbSEd Tanous void debugPrint() 66704e438cbSEd Tanous { 66804e438cbSEd Tanous for (size_t i = 0; i < perMethods.size(); i++) 66904e438cbSEd Tanous { 670d9e89dfdSEd Tanous BMCWEB_LOG_DEBUG("{}", httpVerbToString(static_cast<HttpVerb>(i))); 67104e438cbSEd Tanous perMethods[i].trie.debugPrint(); 67204e438cbSEd Tanous } 67304e438cbSEd Tanous } 67404e438cbSEd Tanous getRoutes(const std::string & parent)67504e438cbSEd Tanous std::vector<const std::string*> getRoutes(const std::string& parent) 67604e438cbSEd Tanous { 67704e438cbSEd Tanous std::vector<const std::string*> ret; 67804e438cbSEd Tanous 67904e438cbSEd Tanous for (const PerMethod& pm : perMethods) 68004e438cbSEd Tanous { 68104e438cbSEd Tanous std::vector<unsigned> x; 68204e438cbSEd Tanous pm.trie.findRouteIndexes(parent, x); 68304e438cbSEd Tanous for (unsigned index : x) 68404e438cbSEd Tanous { 68504e438cbSEd Tanous ret.push_back(&pm.rules[index]->rule); 68604e438cbSEd Tanous } 68704e438cbSEd Tanous } 68804e438cbSEd Tanous return ret; 68904e438cbSEd Tanous } 69004e438cbSEd Tanous 69104e438cbSEd Tanous private: 692a3b9eb98SEd Tanous std::array<PerMethod, static_cast<size_t>(HttpVerb::Max)> perMethods; 69304e438cbSEd Tanous 694a3b9eb98SEd Tanous PerMethod notFoundRoutes; 695a3b9eb98SEd Tanous PerMethod upgradeRoutes; 696a3b9eb98SEd Tanous PerMethod methodNotAllowedRoutes; 697a3b9eb98SEd Tanous 69804e438cbSEd Tanous std::vector<std::unique_ptr<BaseRule>> allRules; 69904e438cbSEd Tanous }; 70004e438cbSEd Tanous } // namespace crow 701