xref: /openbmc/bmcweb/redfish-core/include/redfish_oem_routing.hpp (revision fdf51f5c824273aafaa9262932735ca443db23eb)
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