#pragma once #include "async_resp.hpp" #include "http_response.hpp" #include "logging.hpp" #include "redfishoemrule.hpp" #include "sub_request.hpp" #include "sub_route_trie.hpp" #include "utility.hpp" #include "utils/query_param.hpp" #include "verb.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace redfish { // Helper struct to allow parsing string literals at compile time until // std::string is supported in constexpr context. // NOLINTBEGIN template struct StringLiteral { constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } constexpr operator std::string_view() const { return std::string_view(std::data(value), N - 1); } char value[N]; }; // Explicit deduction guide to prevent Clang warnings template StringLiteral(const char (&)[N]) -> StringLiteral; // NOLINTEND class OemRouter { using SubRouteTrie = crow::SubRouteTrie; public: OemRouter() = default; template constexpr auto& newRule(HttpVerb method) { auto& perMethod = perMethods[static_cast(method)]; constexpr std::string_view rule = URI; constexpr uint64_t numArgs = crow::utility::getParameterTag(rule); if constexpr (numArgs == 0) { using RuleT = OemRule<>; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } else if constexpr (numArgs == 1) { using RuleT = OemRule; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } else if constexpr (numArgs == 2) { using RuleT = OemRule; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } else if constexpr (numArgs == 3) { using RuleT = OemRule; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } else if constexpr (numArgs == 4) { using RuleT = OemRule; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } else { using RuleT = OemRule; std::unique_ptr ruleObject = std::make_unique(rule); RuleT* ptr = ruleObject.get(); perMethod.internalAdd(rule, std::move(ruleObject)); return *ptr; } } struct PerMethod { std::vector> rules; SubRouteTrie trie; // rule index 0 has special meaning; preallocate it to avoid // duplication. PerMethod() : rules(1) {} void internalAdd(std::string_view rule, std::unique_ptr&& ruleObject) { rules.emplace_back(std::move(ruleObject)); trie.add(rule, static_cast(rules.size() - 1U)); // request to /resource/#/frag matches /resource#/frag size_t hashPos = rule.find("/#/"); if (hashPos != std::string_view::npos) { std::string url(rule.substr(0, hashPos)); url += '#'; url += rule.substr(hashPos + 2); // Skip "/#" (2 characters) std::string_view fragRule = url; trie.add(fragRule, static_cast(rules.size() - 1U)); } } }; struct FindRoute { std::vector fragmentRules; std::vector params; }; struct FindRouteResponse { FindRoute route; }; static FindRoute findRouteByPerMethod(std::string_view url, const PerMethod& perMethod) { FindRoute route; SubRouteTrie::FindResult found = perMethod.trie.find(url); route.params = std::move(found.params); for (auto fragmentRuleIndex : found.fragmentRuleIndexes) { if (fragmentRuleIndex >= perMethod.rules.size()) { throw std::runtime_error("Trie internal structure corrupted!"); } route.fragmentRules.emplace_back( (perMethod.rules[fragmentRuleIndex]).get()); } return route; } FindRouteResponse findRoute(const SubRequest& req) const { FindRouteResponse findRoute; std::optional verb = httpVerbFromBoost(req.method()); if (!verb) { return findRoute; } size_t reqMethodIndex = static_cast(*verb); if (reqMethodIndex >= perMethods.size()) { return findRoute; } FindRoute route = findRouteByPerMethod(req.url(), perMethods[reqMethodIndex]); if (!route.fragmentRules.empty()) { findRoute.route = route; } else { BMCWEB_LOG_DEBUG( "No fragments for url {}, method {}", req.url(), httpVerbToString(static_cast(reqMethodIndex))); } return findRoute; } void validate() { for (PerMethod& perMethod : perMethods) { perMethod.trie.validate(); } } void debugPrint() { for (size_t i = 0; i < perMethods.size(); i++) { BMCWEB_LOG_CRITICAL("{}", httpVerbToString(static_cast(i))); perMethods[i].trie.debugPrint(); } } void handle(const std::shared_ptr& req, const std::shared_ptr& asyncResp) const { BMCWEB_LOG_DEBUG("Checking OEM routes"); FindRouteResponse foundRoute = findRoute(*req); std::vector fragments = std::move(foundRoute.route.fragmentRules); std::vector params = std::move(foundRoute.route.params); if (!fragments.empty()) { std::function handler = asyncResp->res.releaseCompleteRequestHandler(); auto multiResp = std::make_shared(); multiResp->res.setCompleteRequestHandler(std::move(handler)); // Copy so that they exists when completion handler is called. auto uriFragments = std::make_shared>(fragments); auto uriParams = std::make_shared>(params); asyncResp->res.setCompleteRequestHandler(std::bind_front( query_param::MultiAsyncResp::startMultiFragmentHandle, req, multiResp, uriFragments, uriParams)); } else { BMCWEB_LOG_DEBUG("No OEM routes found"); } } private: std::array(HttpVerb::Max)> perMethods; }; } // namespace redfish