xref: /openbmc/bmcweb/redfish-core/include/sub_route_trie.hpp (revision c1a75ebc267a78853fb26a3da8c6b3388e6ee07c)
1*c1a75ebcSrohitpai // SPDX-License-Identifier: Apache-2.0
2*c1a75ebcSrohitpai // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*c1a75ebcSrohitpai #pragma once
4*c1a75ebcSrohitpai 
5*c1a75ebcSrohitpai #include "logging.hpp"
6*c1a75ebcSrohitpai #include "routing/trie.hpp"
7*c1a75ebcSrohitpai 
8*c1a75ebcSrohitpai #include <cerrno>
9*c1a75ebcSrohitpai #include <cstdlib>
10*c1a75ebcSrohitpai #include <format>
11*c1a75ebcSrohitpai #include <stdexcept>
12*c1a75ebcSrohitpai #include <string>
13*c1a75ebcSrohitpai #include <string_view>
14*c1a75ebcSrohitpai #include <vector>
15*c1a75ebcSrohitpai 
16*c1a75ebcSrohitpai namespace crow
17*c1a75ebcSrohitpai {
18*c1a75ebcSrohitpai 
19*c1a75ebcSrohitpai struct SubRouteNode : public crow::Node
20*c1a75ebcSrohitpai {
21*c1a75ebcSrohitpai     using ChildMap = crow::Node::ChildMap;
22*c1a75ebcSrohitpai     ChildMap fragmentChildren;
23*c1a75ebcSrohitpai 
isSimpleNodecrow::SubRouteNode24*c1a75ebcSrohitpai     bool isSimpleNode() const
25*c1a75ebcSrohitpai     {
26*c1a75ebcSrohitpai         return crow::Node::isSimpleNode() && fragmentChildren.empty();
27*c1a75ebcSrohitpai     }
28*c1a75ebcSrohitpai };
29*c1a75ebcSrohitpai 
30*c1a75ebcSrohitpai template <typename ContainedType>
31*c1a75ebcSrohitpai class SubRouteTrie : public crow::Trie<ContainedType>
32*c1a75ebcSrohitpai {
33*c1a75ebcSrohitpai   public:
34*c1a75ebcSrohitpai     struct FindResult
35*c1a75ebcSrohitpai     {
36*c1a75ebcSrohitpai         std::vector<std::string> params;
37*c1a75ebcSrohitpai         std::vector<unsigned> fragmentRuleIndexes;
38*c1a75ebcSrohitpai     };
39*c1a75ebcSrohitpai 
40*c1a75ebcSrohitpai   private:
findHelper(const std::string_view reqUrl,const ContainedType & node,std::vector<std::string> & params) const41*c1a75ebcSrohitpai     FindResult findHelper(const std::string_view reqUrl,
42*c1a75ebcSrohitpai                           const ContainedType& node,
43*c1a75ebcSrohitpai                           std::vector<std::string>& params) const
44*c1a75ebcSrohitpai     {
45*c1a75ebcSrohitpai         if (reqUrl.empty())
46*c1a75ebcSrohitpai         {
47*c1a75ebcSrohitpai             FindResult result = {params, {}};
48*c1a75ebcSrohitpai             for (const auto& [fragment, fragmentRuleIndex] :
49*c1a75ebcSrohitpai                  node.fragmentChildren)
50*c1a75ebcSrohitpai             {
51*c1a75ebcSrohitpai                 result.fragmentRuleIndexes.push_back(fragmentRuleIndex);
52*c1a75ebcSrohitpai             }
53*c1a75ebcSrohitpai             return result;
54*c1a75ebcSrohitpai         }
55*c1a75ebcSrohitpai 
56*c1a75ebcSrohitpai         if (node.stringParamChild != 0U)
57*c1a75ebcSrohitpai         {
58*c1a75ebcSrohitpai             size_t epos = reqUrl.find('/');
59*c1a75ebcSrohitpai             if (epos == std::string_view::npos)
60*c1a75ebcSrohitpai             {
61*c1a75ebcSrohitpai                 params.emplace_back(reqUrl);
62*c1a75ebcSrohitpai                 FindResult ret =
63*c1a75ebcSrohitpai                     findHelper("", this->nodes[node.stringParamChild], params);
64*c1a75ebcSrohitpai                 if (!ret.fragmentRuleIndexes.empty())
65*c1a75ebcSrohitpai                 {
66*c1a75ebcSrohitpai                     return ret;
67*c1a75ebcSrohitpai                 }
68*c1a75ebcSrohitpai                 params.pop_back();
69*c1a75ebcSrohitpai             }
70*c1a75ebcSrohitpai             else
71*c1a75ebcSrohitpai             {
72*c1a75ebcSrohitpai                 params.emplace_back(reqUrl.substr(0, epos));
73*c1a75ebcSrohitpai                 FindResult ret =
74*c1a75ebcSrohitpai                     findHelper(reqUrl.substr(epos),
75*c1a75ebcSrohitpai                                this->nodes[node.stringParamChild], params);
76*c1a75ebcSrohitpai                 if (!ret.fragmentRuleIndexes.empty())
77*c1a75ebcSrohitpai                 {
78*c1a75ebcSrohitpai                     return ret;
79*c1a75ebcSrohitpai                 }
80*c1a75ebcSrohitpai                 params.pop_back();
81*c1a75ebcSrohitpai             }
82*c1a75ebcSrohitpai         }
83*c1a75ebcSrohitpai 
84*c1a75ebcSrohitpai         if (node.pathParamChild != 0U)
85*c1a75ebcSrohitpai         {
86*c1a75ebcSrohitpai             params.emplace_back(reqUrl);
87*c1a75ebcSrohitpai             FindResult ret =
88*c1a75ebcSrohitpai                 findHelper("", this->nodes[node.pathParamChild], params);
89*c1a75ebcSrohitpai             if (!ret.fragmentRuleIndexes.empty())
90*c1a75ebcSrohitpai             {
91*c1a75ebcSrohitpai                 return ret;
92*c1a75ebcSrohitpai             }
93*c1a75ebcSrohitpai             params.pop_back();
94*c1a75ebcSrohitpai         }
95*c1a75ebcSrohitpai 
96*c1a75ebcSrohitpai         for (const typename ContainedType::ChildMap::value_type& kv :
97*c1a75ebcSrohitpai              node.children)
98*c1a75ebcSrohitpai         {
99*c1a75ebcSrohitpai             const std::string& fragment = kv.first;
100*c1a75ebcSrohitpai             const ContainedType& child = this->nodes[kv.second];
101*c1a75ebcSrohitpai 
102*c1a75ebcSrohitpai             if (reqUrl.starts_with(fragment))
103*c1a75ebcSrohitpai             {
104*c1a75ebcSrohitpai                 FindResult ret =
105*c1a75ebcSrohitpai                     findHelper(reqUrl.substr(fragment.size()), child, params);
106*c1a75ebcSrohitpai                 if (!ret.fragmentRuleIndexes.empty())
107*c1a75ebcSrohitpai                 {
108*c1a75ebcSrohitpai                     return ret;
109*c1a75ebcSrohitpai                 }
110*c1a75ebcSrohitpai             }
111*c1a75ebcSrohitpai         }
112*c1a75ebcSrohitpai 
113*c1a75ebcSrohitpai         return {std::vector<std::string>(), {}};
114*c1a75ebcSrohitpai     }
115*c1a75ebcSrohitpai 
116*c1a75ebcSrohitpai   public:
find(const std::string_view reqUrl) const117*c1a75ebcSrohitpai     FindResult find(const std::string_view reqUrl) const
118*c1a75ebcSrohitpai     {
119*c1a75ebcSrohitpai         std::vector<std::string> start;
120*c1a75ebcSrohitpai         return findHelper(reqUrl, this->head(), start);
121*c1a75ebcSrohitpai     }
122*c1a75ebcSrohitpai 
add(std::string_view urlIn,unsigned ruleIndex)123*c1a75ebcSrohitpai     void add(std::string_view urlIn, unsigned ruleIndex)
124*c1a75ebcSrohitpai     {
125*c1a75ebcSrohitpai         size_t idx = 0;
126*c1a75ebcSrohitpai 
127*c1a75ebcSrohitpai         std::string_view url = urlIn;
128*c1a75ebcSrohitpai 
129*c1a75ebcSrohitpai         std::string_view fragment;
130*c1a75ebcSrohitpai         // Check if the URL contains a fragment (#)
131*c1a75ebcSrohitpai         size_t fragmentPos = urlIn.find('#');
132*c1a75ebcSrohitpai         size_t queryPos = urlIn.find('?');
133*c1a75ebcSrohitpai         if (fragmentPos != std::string::npos && queryPos == std::string::npos &&
134*c1a75ebcSrohitpai             fragmentPos != urlIn.length() - 1)
135*c1a75ebcSrohitpai         {
136*c1a75ebcSrohitpai             fragment = urlIn.substr(fragmentPos + 1);
137*c1a75ebcSrohitpai             url = urlIn.substr(0, fragmentPos);
138*c1a75ebcSrohitpai         }
139*c1a75ebcSrohitpai 
140*c1a75ebcSrohitpai         if (fragment.empty())
141*c1a75ebcSrohitpai         {
142*c1a75ebcSrohitpai             BMCWEB_LOG_CRITICAL("empty fragment on rule \"{}\"", urlIn);
143*c1a75ebcSrohitpai             throw std::runtime_error(
144*c1a75ebcSrohitpai                 std::format("empty fragment on rule \"{}\"", urlIn));
145*c1a75ebcSrohitpai         }
146*c1a75ebcSrohitpai 
147*c1a75ebcSrohitpai         while (!url.empty())
148*c1a75ebcSrohitpai         {
149*c1a75ebcSrohitpai             char c = url[0];
150*c1a75ebcSrohitpai             if (c == '<')
151*c1a75ebcSrohitpai             {
152*c1a75ebcSrohitpai                 bool found = false;
153*c1a75ebcSrohitpai                 for (const std::string_view str1 :
154*c1a75ebcSrohitpai                      {"<str>", "<string>", "<path>"})
155*c1a75ebcSrohitpai                 {
156*c1a75ebcSrohitpai                     if (!url.starts_with(str1))
157*c1a75ebcSrohitpai                     {
158*c1a75ebcSrohitpai                         continue;
159*c1a75ebcSrohitpai                     }
160*c1a75ebcSrohitpai                     found = true;
161*c1a75ebcSrohitpai                     ContainedType& node = this->nodes[idx];
162*c1a75ebcSrohitpai                     size_t* param = &node.stringParamChild;
163*c1a75ebcSrohitpai                     if (str1 == "<path>")
164*c1a75ebcSrohitpai                     {
165*c1a75ebcSrohitpai                         param = &node.pathParamChild;
166*c1a75ebcSrohitpai                     }
167*c1a75ebcSrohitpai                     if (*param == 0U)
168*c1a75ebcSrohitpai                     {
169*c1a75ebcSrohitpai                         *param = this->newNode();
170*c1a75ebcSrohitpai                     }
171*c1a75ebcSrohitpai                     idx = *param;
172*c1a75ebcSrohitpai 
173*c1a75ebcSrohitpai                     url.remove_prefix(str1.size());
174*c1a75ebcSrohitpai                     break;
175*c1a75ebcSrohitpai                 }
176*c1a75ebcSrohitpai                 if (found)
177*c1a75ebcSrohitpai                 {
178*c1a75ebcSrohitpai                     continue;
179*c1a75ebcSrohitpai                 }
180*c1a75ebcSrohitpai 
181*c1a75ebcSrohitpai                 BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn);
182*c1a75ebcSrohitpai                 return;
183*c1a75ebcSrohitpai             }
184*c1a75ebcSrohitpai             std::string piece(&c, 1);
185*c1a75ebcSrohitpai             if (!this->nodes[idx].children.contains(piece))
186*c1a75ebcSrohitpai             {
187*c1a75ebcSrohitpai                 unsigned newNodeIdx = this->newNode();
188*c1a75ebcSrohitpai                 this->nodes[idx].children.emplace(piece, newNodeIdx);
189*c1a75ebcSrohitpai             }
190*c1a75ebcSrohitpai             idx = this->nodes[idx].children[piece];
191*c1a75ebcSrohitpai             url.remove_prefix(1);
192*c1a75ebcSrohitpai         }
193*c1a75ebcSrohitpai         ContainedType& node = this->nodes[idx];
194*c1a75ebcSrohitpai         if (node.fragmentChildren.find(fragment) != node.fragmentChildren.end())
195*c1a75ebcSrohitpai         {
196*c1a75ebcSrohitpai             BMCWEB_LOG_CRITICAL(
197*c1a75ebcSrohitpai                 R"(fragment handler already exists for "{}" fragment "{}")",
198*c1a75ebcSrohitpai                 urlIn, fragment);
199*c1a75ebcSrohitpai             throw std::runtime_error(std::format(
200*c1a75ebcSrohitpai                 R"(handler already exists for url "{}" fragment "{}")", urlIn,
201*c1a75ebcSrohitpai                 fragment));
202*c1a75ebcSrohitpai         }
203*c1a75ebcSrohitpai 
204*c1a75ebcSrohitpai         node.fragmentChildren.emplace(fragment, ruleIndex);
205*c1a75ebcSrohitpai     }
206*c1a75ebcSrohitpai 
207*c1a75ebcSrohitpai   private:
debugNodePrint(ContainedType & n,size_t level)208*c1a75ebcSrohitpai     void debugNodePrint(ContainedType& n, size_t level)
209*c1a75ebcSrohitpai     {
210*c1a75ebcSrohitpai         std::string spaces(level, ' ');
211*c1a75ebcSrohitpai         if (n.stringParamChild != 0U)
212*c1a75ebcSrohitpai         {
213*c1a75ebcSrohitpai             BMCWEB_LOG_DEBUG("{}<str>", spaces);
214*c1a75ebcSrohitpai             debugNodePrint(this->nodes[n.stringParamChild], level + 5);
215*c1a75ebcSrohitpai         }
216*c1a75ebcSrohitpai         if (n.pathParamChild != 0U)
217*c1a75ebcSrohitpai         {
218*c1a75ebcSrohitpai             BMCWEB_LOG_DEBUG("{} <path>", spaces);
219*c1a75ebcSrohitpai             debugNodePrint(this->nodes[n.pathParamChild], level + 6);
220*c1a75ebcSrohitpai         }
221*c1a75ebcSrohitpai         for (const typename ContainedType::ChildMap::value_type& kv :
222*c1a75ebcSrohitpai              n.fragmentChildren)
223*c1a75ebcSrohitpai         {
224*c1a75ebcSrohitpai             BMCWEB_LOG_DEBUG("{}#{}", spaces, kv.first);
225*c1a75ebcSrohitpai         }
226*c1a75ebcSrohitpai         for (const typename ContainedType::ChildMap::value_type& kv :
227*c1a75ebcSrohitpai              n.children)
228*c1a75ebcSrohitpai         {
229*c1a75ebcSrohitpai             BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
230*c1a75ebcSrohitpai             debugNodePrint(this->nodes[kv.second], level + kv.first.size());
231*c1a75ebcSrohitpai         }
232*c1a75ebcSrohitpai     }
233*c1a75ebcSrohitpai 
234*c1a75ebcSrohitpai   public:
debugPrint()235*c1a75ebcSrohitpai     void debugPrint()
236*c1a75ebcSrohitpai     {
237*c1a75ebcSrohitpai         debugNodePrint(this->head(), 0U);
238*c1a75ebcSrohitpai     }
239*c1a75ebcSrohitpai };
240*c1a75ebcSrohitpai 
241*c1a75ebcSrohitpai } // namespace crow
242