1*c1a75ebcSrohitpai // SPDX-License-Identifier: Apache-2.0 2*c1a75ebcSrohitpai // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*c1a75ebcSrohitpai #pragma once 4*c1a75ebcSrohitpai 5*c1a75ebcSrohitpai #include "logging.hpp" 6*c1a75ebcSrohitpai #include "routing/trie.hpp" 7*c1a75ebcSrohitpai 8*c1a75ebcSrohitpai #include <cerrno> 9*c1a75ebcSrohitpai #include <cstdlib> 10*c1a75ebcSrohitpai #include <format> 11*c1a75ebcSrohitpai #include <stdexcept> 12*c1a75ebcSrohitpai #include <string> 13*c1a75ebcSrohitpai #include <string_view> 14*c1a75ebcSrohitpai #include <vector> 15*c1a75ebcSrohitpai 16*c1a75ebcSrohitpai namespace crow 17*c1a75ebcSrohitpai { 18*c1a75ebcSrohitpai 19*c1a75ebcSrohitpai struct SubRouteNode : public crow::Node 20*c1a75ebcSrohitpai { 21*c1a75ebcSrohitpai using ChildMap = crow::Node::ChildMap; 22*c1a75ebcSrohitpai ChildMap fragmentChildren; 23*c1a75ebcSrohitpai isSimpleNodecrow::SubRouteNode24*c1a75ebcSrohitpai bool isSimpleNode() const 25*c1a75ebcSrohitpai { 26*c1a75ebcSrohitpai return crow::Node::isSimpleNode() && fragmentChildren.empty(); 27*c1a75ebcSrohitpai } 28*c1a75ebcSrohitpai }; 29*c1a75ebcSrohitpai 30*c1a75ebcSrohitpai template <typename ContainedType> 31*c1a75ebcSrohitpai class SubRouteTrie : public crow::Trie<ContainedType> 32*c1a75ebcSrohitpai { 33*c1a75ebcSrohitpai public: 34*c1a75ebcSrohitpai struct FindResult 35*c1a75ebcSrohitpai { 36*c1a75ebcSrohitpai std::vector<std::string> params; 37*c1a75ebcSrohitpai std::vector<unsigned> fragmentRuleIndexes; 38*c1a75ebcSrohitpai }; 39*c1a75ebcSrohitpai 40*c1a75ebcSrohitpai private: findHelper(const std::string_view reqUrl,const ContainedType & node,std::vector<std::string> & params) const41*c1a75ebcSrohitpai FindResult findHelper(const std::string_view reqUrl, 42*c1a75ebcSrohitpai const ContainedType& node, 43*c1a75ebcSrohitpai std::vector<std::string>& params) const 44*c1a75ebcSrohitpai { 45*c1a75ebcSrohitpai if (reqUrl.empty()) 46*c1a75ebcSrohitpai { 47*c1a75ebcSrohitpai FindResult result = {params, {}}; 48*c1a75ebcSrohitpai for (const auto& [fragment, fragmentRuleIndex] : 49*c1a75ebcSrohitpai node.fragmentChildren) 50*c1a75ebcSrohitpai { 51*c1a75ebcSrohitpai result.fragmentRuleIndexes.push_back(fragmentRuleIndex); 52*c1a75ebcSrohitpai } 53*c1a75ebcSrohitpai return result; 54*c1a75ebcSrohitpai } 55*c1a75ebcSrohitpai 56*c1a75ebcSrohitpai if (node.stringParamChild != 0U) 57*c1a75ebcSrohitpai { 58*c1a75ebcSrohitpai size_t epos = reqUrl.find('/'); 59*c1a75ebcSrohitpai if (epos == std::string_view::npos) 60*c1a75ebcSrohitpai { 61*c1a75ebcSrohitpai params.emplace_back(reqUrl); 62*c1a75ebcSrohitpai FindResult ret = 63*c1a75ebcSrohitpai findHelper("", this->nodes[node.stringParamChild], params); 64*c1a75ebcSrohitpai if (!ret.fragmentRuleIndexes.empty()) 65*c1a75ebcSrohitpai { 66*c1a75ebcSrohitpai return ret; 67*c1a75ebcSrohitpai } 68*c1a75ebcSrohitpai params.pop_back(); 69*c1a75ebcSrohitpai } 70*c1a75ebcSrohitpai else 71*c1a75ebcSrohitpai { 72*c1a75ebcSrohitpai params.emplace_back(reqUrl.substr(0, epos)); 73*c1a75ebcSrohitpai FindResult ret = 74*c1a75ebcSrohitpai findHelper(reqUrl.substr(epos), 75*c1a75ebcSrohitpai this->nodes[node.stringParamChild], params); 76*c1a75ebcSrohitpai if (!ret.fragmentRuleIndexes.empty()) 77*c1a75ebcSrohitpai { 78*c1a75ebcSrohitpai return ret; 79*c1a75ebcSrohitpai } 80*c1a75ebcSrohitpai params.pop_back(); 81*c1a75ebcSrohitpai } 82*c1a75ebcSrohitpai } 83*c1a75ebcSrohitpai 84*c1a75ebcSrohitpai if (node.pathParamChild != 0U) 85*c1a75ebcSrohitpai { 86*c1a75ebcSrohitpai params.emplace_back(reqUrl); 87*c1a75ebcSrohitpai FindResult ret = 88*c1a75ebcSrohitpai findHelper("", this->nodes[node.pathParamChild], params); 89*c1a75ebcSrohitpai if (!ret.fragmentRuleIndexes.empty()) 90*c1a75ebcSrohitpai { 91*c1a75ebcSrohitpai return ret; 92*c1a75ebcSrohitpai } 93*c1a75ebcSrohitpai params.pop_back(); 94*c1a75ebcSrohitpai } 95*c1a75ebcSrohitpai 96*c1a75ebcSrohitpai for (const typename ContainedType::ChildMap::value_type& kv : 97*c1a75ebcSrohitpai node.children) 98*c1a75ebcSrohitpai { 99*c1a75ebcSrohitpai const std::string& fragment = kv.first; 100*c1a75ebcSrohitpai const ContainedType& child = this->nodes[kv.second]; 101*c1a75ebcSrohitpai 102*c1a75ebcSrohitpai if (reqUrl.starts_with(fragment)) 103*c1a75ebcSrohitpai { 104*c1a75ebcSrohitpai FindResult ret = 105*c1a75ebcSrohitpai findHelper(reqUrl.substr(fragment.size()), child, params); 106*c1a75ebcSrohitpai if (!ret.fragmentRuleIndexes.empty()) 107*c1a75ebcSrohitpai { 108*c1a75ebcSrohitpai return ret; 109*c1a75ebcSrohitpai } 110*c1a75ebcSrohitpai } 111*c1a75ebcSrohitpai } 112*c1a75ebcSrohitpai 113*c1a75ebcSrohitpai return {std::vector<std::string>(), {}}; 114*c1a75ebcSrohitpai } 115*c1a75ebcSrohitpai 116*c1a75ebcSrohitpai public: find(const std::string_view reqUrl) const117*c1a75ebcSrohitpai FindResult find(const std::string_view reqUrl) const 118*c1a75ebcSrohitpai { 119*c1a75ebcSrohitpai std::vector<std::string> start; 120*c1a75ebcSrohitpai return findHelper(reqUrl, this->head(), start); 121*c1a75ebcSrohitpai } 122*c1a75ebcSrohitpai add(std::string_view urlIn,unsigned ruleIndex)123*c1a75ebcSrohitpai void add(std::string_view urlIn, unsigned ruleIndex) 124*c1a75ebcSrohitpai { 125*c1a75ebcSrohitpai size_t idx = 0; 126*c1a75ebcSrohitpai 127*c1a75ebcSrohitpai std::string_view url = urlIn; 128*c1a75ebcSrohitpai 129*c1a75ebcSrohitpai std::string_view fragment; 130*c1a75ebcSrohitpai // Check if the URL contains a fragment (#) 131*c1a75ebcSrohitpai size_t fragmentPos = urlIn.find('#'); 132*c1a75ebcSrohitpai size_t queryPos = urlIn.find('?'); 133*c1a75ebcSrohitpai if (fragmentPos != std::string::npos && queryPos == std::string::npos && 134*c1a75ebcSrohitpai fragmentPos != urlIn.length() - 1) 135*c1a75ebcSrohitpai { 136*c1a75ebcSrohitpai fragment = urlIn.substr(fragmentPos + 1); 137*c1a75ebcSrohitpai url = urlIn.substr(0, fragmentPos); 138*c1a75ebcSrohitpai } 139*c1a75ebcSrohitpai 140*c1a75ebcSrohitpai if (fragment.empty()) 141*c1a75ebcSrohitpai { 142*c1a75ebcSrohitpai BMCWEB_LOG_CRITICAL("empty fragment on rule \"{}\"", urlIn); 143*c1a75ebcSrohitpai throw std::runtime_error( 144*c1a75ebcSrohitpai std::format("empty fragment on rule \"{}\"", urlIn)); 145*c1a75ebcSrohitpai } 146*c1a75ebcSrohitpai 147*c1a75ebcSrohitpai while (!url.empty()) 148*c1a75ebcSrohitpai { 149*c1a75ebcSrohitpai char c = url[0]; 150*c1a75ebcSrohitpai if (c == '<') 151*c1a75ebcSrohitpai { 152*c1a75ebcSrohitpai bool found = false; 153*c1a75ebcSrohitpai for (const std::string_view str1 : 154*c1a75ebcSrohitpai {"<str>", "<string>", "<path>"}) 155*c1a75ebcSrohitpai { 156*c1a75ebcSrohitpai if (!url.starts_with(str1)) 157*c1a75ebcSrohitpai { 158*c1a75ebcSrohitpai continue; 159*c1a75ebcSrohitpai } 160*c1a75ebcSrohitpai found = true; 161*c1a75ebcSrohitpai ContainedType& node = this->nodes[idx]; 162*c1a75ebcSrohitpai size_t* param = &node.stringParamChild; 163*c1a75ebcSrohitpai if (str1 == "<path>") 164*c1a75ebcSrohitpai { 165*c1a75ebcSrohitpai param = &node.pathParamChild; 166*c1a75ebcSrohitpai } 167*c1a75ebcSrohitpai if (*param == 0U) 168*c1a75ebcSrohitpai { 169*c1a75ebcSrohitpai *param = this->newNode(); 170*c1a75ebcSrohitpai } 171*c1a75ebcSrohitpai idx = *param; 172*c1a75ebcSrohitpai 173*c1a75ebcSrohitpai url.remove_prefix(str1.size()); 174*c1a75ebcSrohitpai break; 175*c1a75ebcSrohitpai } 176*c1a75ebcSrohitpai if (found) 177*c1a75ebcSrohitpai { 178*c1a75ebcSrohitpai continue; 179*c1a75ebcSrohitpai } 180*c1a75ebcSrohitpai 181*c1a75ebcSrohitpai BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn); 182*c1a75ebcSrohitpai return; 183*c1a75ebcSrohitpai } 184*c1a75ebcSrohitpai std::string piece(&c, 1); 185*c1a75ebcSrohitpai if (!this->nodes[idx].children.contains(piece)) 186*c1a75ebcSrohitpai { 187*c1a75ebcSrohitpai unsigned newNodeIdx = this->newNode(); 188*c1a75ebcSrohitpai this->nodes[idx].children.emplace(piece, newNodeIdx); 189*c1a75ebcSrohitpai } 190*c1a75ebcSrohitpai idx = this->nodes[idx].children[piece]; 191*c1a75ebcSrohitpai url.remove_prefix(1); 192*c1a75ebcSrohitpai } 193*c1a75ebcSrohitpai ContainedType& node = this->nodes[idx]; 194*c1a75ebcSrohitpai if (node.fragmentChildren.find(fragment) != node.fragmentChildren.end()) 195*c1a75ebcSrohitpai { 196*c1a75ebcSrohitpai BMCWEB_LOG_CRITICAL( 197*c1a75ebcSrohitpai R"(fragment handler already exists for "{}" fragment "{}")", 198*c1a75ebcSrohitpai urlIn, fragment); 199*c1a75ebcSrohitpai throw std::runtime_error(std::format( 200*c1a75ebcSrohitpai R"(handler already exists for url "{}" fragment "{}")", urlIn, 201*c1a75ebcSrohitpai fragment)); 202*c1a75ebcSrohitpai } 203*c1a75ebcSrohitpai 204*c1a75ebcSrohitpai node.fragmentChildren.emplace(fragment, ruleIndex); 205*c1a75ebcSrohitpai } 206*c1a75ebcSrohitpai 207*c1a75ebcSrohitpai private: debugNodePrint(ContainedType & n,size_t level)208*c1a75ebcSrohitpai void debugNodePrint(ContainedType& n, size_t level) 209*c1a75ebcSrohitpai { 210*c1a75ebcSrohitpai std::string spaces(level, ' '); 211*c1a75ebcSrohitpai if (n.stringParamChild != 0U) 212*c1a75ebcSrohitpai { 213*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("{}<str>", spaces); 214*c1a75ebcSrohitpai debugNodePrint(this->nodes[n.stringParamChild], level + 5); 215*c1a75ebcSrohitpai } 216*c1a75ebcSrohitpai if (n.pathParamChild != 0U) 217*c1a75ebcSrohitpai { 218*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("{} <path>", spaces); 219*c1a75ebcSrohitpai debugNodePrint(this->nodes[n.pathParamChild], level + 6); 220*c1a75ebcSrohitpai } 221*c1a75ebcSrohitpai for (const typename ContainedType::ChildMap::value_type& kv : 222*c1a75ebcSrohitpai n.fragmentChildren) 223*c1a75ebcSrohitpai { 224*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("{}#{}", spaces, kv.first); 225*c1a75ebcSrohitpai } 226*c1a75ebcSrohitpai for (const typename ContainedType::ChildMap::value_type& kv : 227*c1a75ebcSrohitpai n.children) 228*c1a75ebcSrohitpai { 229*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first); 230*c1a75ebcSrohitpai debugNodePrint(this->nodes[kv.second], level + kv.first.size()); 231*c1a75ebcSrohitpai } 232*c1a75ebcSrohitpai } 233*c1a75ebcSrohitpai 234*c1a75ebcSrohitpai public: debugPrint()235*c1a75ebcSrohitpai void debugPrint() 236*c1a75ebcSrohitpai { 237*c1a75ebcSrohitpai debugNodePrint(this->head(), 0U); 238*c1a75ebcSrohitpai } 239*c1a75ebcSrohitpai }; 240*c1a75ebcSrohitpai 241*c1a75ebcSrohitpai } // namespace crow 242