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