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