1*c1a75ebcSrohitpai #pragma once 2*c1a75ebcSrohitpai 3*c1a75ebcSrohitpai #include "async_resp.hpp" 4*c1a75ebcSrohitpai #include "http_request.hpp" 5*c1a75ebcSrohitpai #include "http_response.hpp" 6*c1a75ebcSrohitpai #include "logging.hpp" 7*c1a75ebcSrohitpai #include "redfishoemrule.hpp" 8*c1a75ebcSrohitpai #include "sub_route_trie.hpp" 9*c1a75ebcSrohitpai #include "utility.hpp" 10*c1a75ebcSrohitpai #include "utils/query_param.hpp" 11*c1a75ebcSrohitpai #include "verb.hpp" 12*c1a75ebcSrohitpai 13*c1a75ebcSrohitpai #include <array> 14*c1a75ebcSrohitpai #include <cstddef> 15*c1a75ebcSrohitpai #include <cstdint> 16*c1a75ebcSrohitpai #include <functional> 17*c1a75ebcSrohitpai #include <memory> 18*c1a75ebcSrohitpai #include <optional> 19*c1a75ebcSrohitpai #include <stdexcept> 20*c1a75ebcSrohitpai #include <string> 21*c1a75ebcSrohitpai #include <string_view> 22*c1a75ebcSrohitpai #include <utility> 23*c1a75ebcSrohitpai #include <vector> 24*c1a75ebcSrohitpai 25*c1a75ebcSrohitpai namespace redfish 26*c1a75ebcSrohitpai { 27*c1a75ebcSrohitpai 28*c1a75ebcSrohitpai // Helper struct to allow parsing string literals at compile time until 29*c1a75ebcSrohitpai // std::string is supported in constexpr context. 30*c1a75ebcSrohitpai // NOLINTBEGIN 31*c1a75ebcSrohitpai template <size_t N> 32*c1a75ebcSrohitpai struct StringLiteral 33*c1a75ebcSrohitpai { 34*c1a75ebcSrohitpai constexpr StringLiteral(const char (&str)[N]) 35*c1a75ebcSrohitpai { 36*c1a75ebcSrohitpai std::copy_n(str, N, value); 37*c1a75ebcSrohitpai } 38*c1a75ebcSrohitpai 39*c1a75ebcSrohitpai constexpr operator std::string_view() const 40*c1a75ebcSrohitpai { 41*c1a75ebcSrohitpai return std::string_view(std::data(value), N - 1); 42*c1a75ebcSrohitpai } 43*c1a75ebcSrohitpai 44*c1a75ebcSrohitpai char value[N]; 45*c1a75ebcSrohitpai }; 46*c1a75ebcSrohitpai // Explicit deduction guide to prevent Clang warnings 47*c1a75ebcSrohitpai template <size_t N> 48*c1a75ebcSrohitpai StringLiteral(const char (&)[N]) -> StringLiteral<N>; 49*c1a75ebcSrohitpai // NOLINTEND 50*c1a75ebcSrohitpai 51*c1a75ebcSrohitpai class OemRouter 52*c1a75ebcSrohitpai { 53*c1a75ebcSrohitpai using SubRouteTrie = crow::SubRouteTrie<crow::SubRouteNode>; 54*c1a75ebcSrohitpai 55*c1a75ebcSrohitpai public: 56*c1a75ebcSrohitpai OemRouter() = default; 57*c1a75ebcSrohitpai 58*c1a75ebcSrohitpai template <StringLiteral URI> 59*c1a75ebcSrohitpai constexpr auto& newRule(HttpVerb method) 60*c1a75ebcSrohitpai { 61*c1a75ebcSrohitpai auto& perMethod = perMethods[static_cast<size_t>(method)]; 62*c1a75ebcSrohitpai constexpr std::string_view rule = URI; 63*c1a75ebcSrohitpai constexpr uint64_t numArgs = crow::utility::getParameterTag(rule); 64*c1a75ebcSrohitpai 65*c1a75ebcSrohitpai if constexpr (numArgs == 0) 66*c1a75ebcSrohitpai { 67*c1a75ebcSrohitpai using RuleT = OemRule<>; 68*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 69*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 70*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 71*c1a75ebcSrohitpai return *ptr; 72*c1a75ebcSrohitpai } 73*c1a75ebcSrohitpai else if constexpr (numArgs == 1) 74*c1a75ebcSrohitpai { 75*c1a75ebcSrohitpai using RuleT = OemRule<std::string>; 76*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 77*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 78*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 79*c1a75ebcSrohitpai return *ptr; 80*c1a75ebcSrohitpai } 81*c1a75ebcSrohitpai else if constexpr (numArgs == 2) 82*c1a75ebcSrohitpai { 83*c1a75ebcSrohitpai using RuleT = OemRule<std::string, std::string>; 84*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 85*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 86*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 87*c1a75ebcSrohitpai return *ptr; 88*c1a75ebcSrohitpai } 89*c1a75ebcSrohitpai else if constexpr (numArgs == 3) 90*c1a75ebcSrohitpai { 91*c1a75ebcSrohitpai using RuleT = OemRule<std::string, std::string, std::string>; 92*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 93*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 94*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 95*c1a75ebcSrohitpai return *ptr; 96*c1a75ebcSrohitpai } 97*c1a75ebcSrohitpai else if constexpr (numArgs == 4) 98*c1a75ebcSrohitpai { 99*c1a75ebcSrohitpai using RuleT = 100*c1a75ebcSrohitpai OemRule<std::string, std::string, std::string, std::string>; 101*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 102*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 103*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 104*c1a75ebcSrohitpai return *ptr; 105*c1a75ebcSrohitpai } 106*c1a75ebcSrohitpai else 107*c1a75ebcSrohitpai { 108*c1a75ebcSrohitpai using RuleT = OemRule<std::string, std::string, std::string, 109*c1a75ebcSrohitpai std::string, std::string>; 110*c1a75ebcSrohitpai std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); 111*c1a75ebcSrohitpai RuleT* ptr = ruleObject.get(); 112*c1a75ebcSrohitpai perMethod.internalAdd(rule, std::move(ruleObject)); 113*c1a75ebcSrohitpai return *ptr; 114*c1a75ebcSrohitpai } 115*c1a75ebcSrohitpai } 116*c1a75ebcSrohitpai 117*c1a75ebcSrohitpai struct PerMethod 118*c1a75ebcSrohitpai { 119*c1a75ebcSrohitpai std::vector<std::unique_ptr<OemBaseRule>> rules; 120*c1a75ebcSrohitpai SubRouteTrie trie; 121*c1a75ebcSrohitpai // rule index 0 has special meaning; preallocate it to avoid 122*c1a75ebcSrohitpai // duplication. 123*c1a75ebcSrohitpai PerMethod() : rules(1) {} 124*c1a75ebcSrohitpai 125*c1a75ebcSrohitpai void internalAdd(std::string_view rule, 126*c1a75ebcSrohitpai std::unique_ptr<OemBaseRule>&& ruleObject) 127*c1a75ebcSrohitpai { 128*c1a75ebcSrohitpai rules.emplace_back(std::move(ruleObject)); 129*c1a75ebcSrohitpai trie.add(rule, static_cast<unsigned>(rules.size() - 1U)); 130*c1a75ebcSrohitpai // request to /resource/#/frag matches /resource#/frag 131*c1a75ebcSrohitpai size_t hashPos = rule.find("/#/"); 132*c1a75ebcSrohitpai if (hashPos != std::string_view::npos) 133*c1a75ebcSrohitpai { 134*c1a75ebcSrohitpai std::string url(rule.substr(0, hashPos)); 135*c1a75ebcSrohitpai url += '#'; 136*c1a75ebcSrohitpai url += rule.substr(hashPos + 2); // Skip "/#" (2 characters) 137*c1a75ebcSrohitpai std::string_view fragRule = url; 138*c1a75ebcSrohitpai trie.add(fragRule, static_cast<unsigned>(rules.size() - 1U)); 139*c1a75ebcSrohitpai } 140*c1a75ebcSrohitpai } 141*c1a75ebcSrohitpai }; 142*c1a75ebcSrohitpai 143*c1a75ebcSrohitpai struct FindRoute 144*c1a75ebcSrohitpai { 145*c1a75ebcSrohitpai std::vector<OemBaseRule*> fragmentRules; 146*c1a75ebcSrohitpai std::vector<std::string> params; 147*c1a75ebcSrohitpai }; 148*c1a75ebcSrohitpai 149*c1a75ebcSrohitpai struct FindRouteResponse 150*c1a75ebcSrohitpai { 151*c1a75ebcSrohitpai FindRoute route; 152*c1a75ebcSrohitpai }; 153*c1a75ebcSrohitpai 154*c1a75ebcSrohitpai static FindRoute findRouteByPerMethod(std::string_view url, 155*c1a75ebcSrohitpai const PerMethod& perMethod) 156*c1a75ebcSrohitpai { 157*c1a75ebcSrohitpai FindRoute route; 158*c1a75ebcSrohitpai 159*c1a75ebcSrohitpai SubRouteTrie::FindResult found = perMethod.trie.find(url); 160*c1a75ebcSrohitpai route.params = std::move(found.params); 161*c1a75ebcSrohitpai for (auto fragmentRuleIndex : found.fragmentRuleIndexes) 162*c1a75ebcSrohitpai { 163*c1a75ebcSrohitpai if (fragmentRuleIndex >= perMethod.rules.size()) 164*c1a75ebcSrohitpai { 165*c1a75ebcSrohitpai throw std::runtime_error("Trie internal structure corrupted!"); 166*c1a75ebcSrohitpai } 167*c1a75ebcSrohitpai route.fragmentRules.emplace_back( 168*c1a75ebcSrohitpai (perMethod.rules[fragmentRuleIndex]).get()); 169*c1a75ebcSrohitpai } 170*c1a75ebcSrohitpai 171*c1a75ebcSrohitpai return route; 172*c1a75ebcSrohitpai } 173*c1a75ebcSrohitpai 174*c1a75ebcSrohitpai FindRouteResponse findRoute(const crow::Request& req) const 175*c1a75ebcSrohitpai { 176*c1a75ebcSrohitpai FindRouteResponse findRoute; 177*c1a75ebcSrohitpai std::optional<HttpVerb> verb = httpVerbFromBoost(req.method()); 178*c1a75ebcSrohitpai if (!verb) 179*c1a75ebcSrohitpai { 180*c1a75ebcSrohitpai return findRoute; 181*c1a75ebcSrohitpai } 182*c1a75ebcSrohitpai size_t reqMethodIndex = static_cast<size_t>(*verb); 183*c1a75ebcSrohitpai if (reqMethodIndex >= perMethods.size()) 184*c1a75ebcSrohitpai { 185*c1a75ebcSrohitpai return findRoute; 186*c1a75ebcSrohitpai } 187*c1a75ebcSrohitpai 188*c1a75ebcSrohitpai FindRoute route = findRouteByPerMethod(req.url().encoded_path(), 189*c1a75ebcSrohitpai perMethods[reqMethodIndex]); 190*c1a75ebcSrohitpai if (!route.fragmentRules.empty()) 191*c1a75ebcSrohitpai { 192*c1a75ebcSrohitpai findRoute.route = route; 193*c1a75ebcSrohitpai } 194*c1a75ebcSrohitpai else 195*c1a75ebcSrohitpai { 196*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG( 197*c1a75ebcSrohitpai "No fragments for for url {}, method {}", 198*c1a75ebcSrohitpai req.url().encoded_path(), 199*c1a75ebcSrohitpai httpVerbToString(static_cast<HttpVerb>(reqMethodIndex))); 200*c1a75ebcSrohitpai } 201*c1a75ebcSrohitpai 202*c1a75ebcSrohitpai return findRoute; 203*c1a75ebcSrohitpai } 204*c1a75ebcSrohitpai 205*c1a75ebcSrohitpai void validate() 206*c1a75ebcSrohitpai { 207*c1a75ebcSrohitpai for (PerMethod& perMethod : perMethods) 208*c1a75ebcSrohitpai { 209*c1a75ebcSrohitpai perMethod.trie.validate(); 210*c1a75ebcSrohitpai } 211*c1a75ebcSrohitpai } 212*c1a75ebcSrohitpai 213*c1a75ebcSrohitpai void debugPrint() 214*c1a75ebcSrohitpai { 215*c1a75ebcSrohitpai for (size_t i = 0; i < perMethods.size(); i++) 216*c1a75ebcSrohitpai { 217*c1a75ebcSrohitpai BMCWEB_LOG_CRITICAL("{}", 218*c1a75ebcSrohitpai httpVerbToString(static_cast<HttpVerb>(i))); 219*c1a75ebcSrohitpai perMethods[i].trie.debugPrint(); 220*c1a75ebcSrohitpai } 221*c1a75ebcSrohitpai } 222*c1a75ebcSrohitpai 223*c1a75ebcSrohitpai void handle(const crow::Request& req, 224*c1a75ebcSrohitpai const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) const 225*c1a75ebcSrohitpai { 226*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("Checking OEM routes"); 227*c1a75ebcSrohitpai FindRouteResponse foundRoute = findRoute(req); 228*c1a75ebcSrohitpai std::vector<OemBaseRule*> fragments = 229*c1a75ebcSrohitpai std::move(foundRoute.route.fragmentRules); 230*c1a75ebcSrohitpai std::vector<std::string> params = std::move(foundRoute.route.params); 231*c1a75ebcSrohitpai if (!fragments.empty()) 232*c1a75ebcSrohitpai { 233*c1a75ebcSrohitpai std::function<void(crow::Response&)> handler = 234*c1a75ebcSrohitpai asyncResp->res.releaseCompleteRequestHandler(); 235*c1a75ebcSrohitpai auto multiResp = std::make_shared<bmcweb::AsyncResp>(); 236*c1a75ebcSrohitpai multiResp->res.setCompleteRequestHandler(std::move(handler)); 237*c1a75ebcSrohitpai 238*c1a75ebcSrohitpai // Copy so that they exists when completion handler is called. 239*c1a75ebcSrohitpai auto uriFragments = 240*c1a75ebcSrohitpai std::make_shared<std::vector<OemBaseRule*>>(fragments); 241*c1a75ebcSrohitpai auto uriParams = std::make_shared<std::vector<std::string>>(params); 242*c1a75ebcSrohitpai 243*c1a75ebcSrohitpai asyncResp->res.setCompleteRequestHandler(std::bind_front( 244*c1a75ebcSrohitpai query_param::MultiAsyncResp::startMultiFragmentHandle, 245*c1a75ebcSrohitpai std::make_shared<crow::Request>(req), multiResp, uriFragments, 246*c1a75ebcSrohitpai uriParams)); 247*c1a75ebcSrohitpai } 248*c1a75ebcSrohitpai else 249*c1a75ebcSrohitpai { 250*c1a75ebcSrohitpai BMCWEB_LOG_DEBUG("No OEM routes found"); 251*c1a75ebcSrohitpai } 252*c1a75ebcSrohitpai } 253*c1a75ebcSrohitpai 254*c1a75ebcSrohitpai private: 255*c1a75ebcSrohitpai std::array<PerMethod, static_cast<size_t>(HttpVerb::Max)> perMethods; 256*c1a75ebcSrohitpai }; 257*c1a75ebcSrohitpai } // namespace redfish 258