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