blob: 5205a5b229fa1c2e056ed16aae540bfe7f186db3 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "async_resp.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -07004#include "dbus_privileges.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08005#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06006#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07007#include "http_request.hpp"
8#include "http_response.hpp"
9#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -030010#include "privileges.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070011#include "routing/baserule.hpp"
12#include "routing/dynamicrule.hpp"
13#include "routing/sserule.hpp"
14#include "routing/taggedrule.hpp"
15#include "routing/websocketrule.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053016#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070017#include "utility.hpp"
Ed Tanous3d183202023-03-10 09:21:58 -080018#include "utils/dbus_utils.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070019#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070020#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070021
Tanousf00032d2018-11-05 01:18:10 -030022#include <boost/container/flat_map.hpp>
Ed Tanousd9e89df2024-03-27 14:08:59 -070023#include <boost/container/small_vector.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024
Ed Tanousd9e89df2024-03-27 14:08:59 -070025#include <algorithm>
Ed Tanouse0d918b2018-03-27 17:41:04 -070026#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070027#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070028#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070029#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070030#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070031#include <optional>
Ed Tanousa3b9eb92024-06-03 08:39:37 -070032#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070033#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070034#include <utility>
35#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070036
Ed Tanous1abe55e2018-09-05 08:30:59 -070037namespace crow
38{
Tanousf00032d2018-11-05 01:18:10 -030039
Ed Tanous1abe55e2018-09-05 08:30:59 -070040class Trie
41{
42 public:
43 struct Node
44 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070045 unsigned ruleIndex = 0U;
46
47 size_t stringParamChild = 0U;
48 size_t pathParamChild = 0U;
49
Ed Tanousa94ac612022-02-22 11:13:24 -080050 using ChildMap = boost::container::flat_map<
51 std::string, unsigned, std::less<>,
Ed Tanousd9e89df2024-03-27 14:08:59 -070052 boost::container::small_vector<std::pair<std::string, unsigned>,
53 1>>;
Ed Tanousa94ac612022-02-22 11:13:24 -080054 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -070055
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 bool isSimpleNode() const
57 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070058 return ruleIndex == 0 && stringParamChild == 0 &&
59 pathParamChild == 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -070060 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070061 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070062
Patrick Williams89492a12023-05-10 07:51:34 -050063 Trie() : nodes(1) {}
Ed Tanous1abe55e2018-09-05 08:30:59 -070064
65 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -070066 void optimizeNode(Node& node)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070068 if (node.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070070 optimizeNode(nodes[node.stringParamChild]);
Ed Tanous7045c8d2017-04-03 10:04:37 -070071 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070072 if (node.pathParamChild != 0U)
73 {
74 optimizeNode(nodes[node.pathParamChild]);
75 }
76
77 if (node.children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -070078 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -070080 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070081 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070083 bool didMerge = false;
84 Node::ChildMap merged;
85 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070086 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070087 Node& child = nodes[kv.second];
88 if (child.isSimpleNode())
89 {
90 for (const Node::ChildMap::value_type& childKv :
91 child.children)
92 {
93 merged[kv.first + childKv.first] = childKv.second;
94 didMerge = true;
95 }
96 }
97 else
98 {
99 merged[kv.first] = kv.second;
100 }
101 }
102 node.children = std::move(merged);
103 if (!didMerge)
104 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700108
109 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700111 optimizeNode(nodes[kv.second]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 }
113 }
114
115 void optimize()
116 {
117 optimizeNode(head());
118 }
119
120 public:
121 void validate()
122 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 optimize();
124 }
125
Ed Tanousd9e89df2024-03-27 14:08:59 -0700126 void findRouteIndexesHelper(std::string_view reqUrl,
127 std::vector<unsigned>& routeIndexes,
128 const Node& node) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700130 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 {
132 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700133 const Node& child = nodes[kv.second];
134 if (reqUrl.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700136 if (child.ruleIndex != 0 && fragment != "/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700138 routeIndexes.push_back(child.ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700140 findRouteIndexesHelper(reqUrl, routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 }
142 else
143 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700144 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700146 findRouteIndexesHelper(reqUrl.substr(fragment.size()),
147 routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700148 }
149 }
150 }
151 }
152
Ed Tanousd9e89df2024-03-27 14:08:59 -0700153 void findRouteIndexes(const std::string& reqUrl,
154 std::vector<unsigned>& routeIndexes) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700156 findRouteIndexesHelper(reqUrl, routeIndexes, head());
157 }
158
159 struct FindResult
160 {
161 unsigned ruleIndex;
162 std::vector<std::string> params;
163 };
164
165 private:
166 FindResult findHelper(const std::string_view reqUrl, const Node& node,
167 std::vector<std::string>& params) const
168 {
169 if (reqUrl.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700170 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700171 return {node.ruleIndex, params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700172 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173
Ed Tanousd9e89df2024-03-27 14:08:59 -0700174 if (node.stringParamChild != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700175 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700176 size_t epos = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000177 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000179 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700180 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700182 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 }
184
Ed Tanousd9e89df2024-03-27 14:08:59 -0700185 if (epos != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700186 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700187 params.emplace_back(reqUrl.substr(0, epos));
188 FindResult ret = findHelper(
189 reqUrl.substr(epos), nodes[node.stringParamChild], params);
190 if (ret.ruleIndex != 0U)
191 {
192 return {ret.ruleIndex, std::move(ret.params)};
193 }
194 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 }
196 }
197
Ed Tanousd9e89df2024-03-27 14:08:59 -0700198 if (node.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700200 params.emplace_back(reqUrl);
201 FindResult ret = findHelper("", nodes[node.pathParamChild], params);
202 if (ret.ruleIndex != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700203 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700204 return {ret.ruleIndex, std::move(ret.params)};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700206 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 }
208
Ed Tanousd9e89df2024-03-27 14:08:59 -0700209 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700210 {
211 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700212 const Node& child = nodes[kv.second];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213
Ed Tanousd9e89df2024-03-27 14:08:59 -0700214 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700216 FindResult ret = findHelper(reqUrl.substr(fragment.size()),
217 child, params);
218 if (ret.ruleIndex != 0U)
219 {
220 return {ret.ruleIndex, std::move(ret.params)};
221 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 }
223 }
224
Ed Tanousd9e89df2024-03-27 14:08:59 -0700225 return {0U, std::vector<std::string>()};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700226 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227
Ed Tanousd9e89df2024-03-27 14:08:59 -0700228 public:
229 FindResult find(const std::string_view reqUrl) const
230 {
231 std::vector<std::string> start;
232 return findHelper(reqUrl, head(), start);
233 }
234
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700235 void add(std::string_view urlIn, unsigned ruleIndex)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700236 {
Ed Tanous271584a2019-07-09 16:24:22 -0700237 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700239 std::string_view url = urlIn;
240
Ed Tanousd9e89df2024-03-27 14:08:59 -0700241 while (!url.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700243 char c = url[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 if (c == '<')
245 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700246 bool found = false;
247 for (const std::string_view str1 :
248 {"<str>", "<string>", "<path>"})
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700250 if (!url.starts_with(str1))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700252 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700254 found = true;
255 Node& node = nodes[idx];
256 size_t* param = &node.stringParamChild;
257 if (str1 == "<path>")
258 {
259 param = &node.pathParamChild;
260 }
261 if (*param == 0U)
262 {
263 *param = newNode();
264 }
265 idx = *param;
266
267 url.remove_prefix(str1.size());
268 break;
269 }
270 if (found)
271 {
272 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
274
Manojkiran Edaefff2b52024-06-18 18:01:46 +0530275 BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700276 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700278 std::string piece(&c, 1);
279 if (!nodes[idx].children.contains(piece))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700281 unsigned newNodeIdx = newNode();
282 nodes[idx].children.emplace(piece, newNodeIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700284 idx = nodes[idx].children[piece];
285 url.remove_prefix(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700286 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700287 Node& node = nodes[idx];
288 if (node.ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700289 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700290 BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700291 throw std::runtime_error(
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700292 std::format("handler already exists for \"{}\"", urlIn));
Ed Tanous3174e4d2020-10-07 11:41:22 -0700293 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700294 node.ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700295 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700298 void debugNodePrint(Node& n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700300 std::string spaces(level, ' ');
301 if (n.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700303 BMCWEB_LOG_DEBUG("{}<str>", spaces);
304 debugNodePrint(nodes[n.stringParamChild], level + 5);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700306 if (n.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700308 BMCWEB_LOG_DEBUG("{} <path>", spaces);
309 debugNodePrint(nodes[n.pathParamChild], level + 6);
310 }
311 for (const Node::ChildMap::value_type& kv : n.children)
312 {
313 BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
314 debugNodePrint(nodes[kv.second], level + kv.first.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 }
316 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700317
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 public:
319 void debugPrint()
320 {
Ed Tanous271584a2019-07-09 16:24:22 -0700321 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700322 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700323
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700325 const Node& head() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700327 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329
Ed Tanousd9e89df2024-03-27 14:08:59 -0700330 Node& head()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700332 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333 }
334
335 unsigned newNode()
336 {
337 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -0700338 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339 }
340
341 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700342};
343
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344class Router
345{
346 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -0700347 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700348
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 DynamicRule& newRuleDynamic(const std::string& rule)
350 {
351 std::unique_ptr<DynamicRule> ruleObject =
352 std::make_unique<DynamicRule>(rule);
353 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -0300354 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700355
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700357 }
358
Ed Tanousd9e89df2024-03-27 14:08:59 -0700359 template <uint64_t NumArgs>
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700360 auto& newRuleTagged(const std::string& rule)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700361 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700362 if constexpr (NumArgs == 0)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700363 {
364 using RuleT = TaggedRule<>;
365 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
366 RuleT* ptr = ruleObject.get();
367 allRules.emplace_back(std::move(ruleObject));
368 return *ptr;
369 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700370 else if constexpr (NumArgs == 1)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700371 {
372 using RuleT = TaggedRule<std::string>;
373 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
374 RuleT* ptr = ruleObject.get();
375 allRules.emplace_back(std::move(ruleObject));
376 return *ptr;
377 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700378 else if constexpr (NumArgs == 2)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700379 {
380 using RuleT = TaggedRule<std::string, std::string>;
381 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
382 RuleT* ptr = ruleObject.get();
383 allRules.emplace_back(std::move(ruleObject));
384 return *ptr;
385 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700386 else if constexpr (NumArgs == 3)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700387 {
388 using RuleT = TaggedRule<std::string, std::string, std::string>;
389 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
390 RuleT* ptr = ruleObject.get();
391 allRules.emplace_back(std::move(ruleObject));
392 return *ptr;
393 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700394 else if constexpr (NumArgs == 4)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700395 {
396 using RuleT =
397 TaggedRule<std::string, std::string, std::string, std::string>;
398 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
399 RuleT* ptr = ruleObject.get();
400 allRules.emplace_back(std::move(ruleObject));
401 return *ptr;
402 }
403 else
404 {
405 using RuleT = TaggedRule<std::string, std::string, std::string,
406 std::string, std::string>;
407 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
408 RuleT* ptr = ruleObject.get();
409 allRules.emplace_back(std::move(ruleObject));
410 return *ptr;
411 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700412 static_assert(NumArgs <= 5, "Max number of args supported is 5");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700413 }
414
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700415 struct PerMethod
416 {
417 std::vector<BaseRule*> rules;
418 Trie trie;
419 // rule index 0 has special meaning; preallocate it to avoid
420 // duplication.
421 PerMethod() : rules(1) {}
422
423 void internalAdd(std::string_view rule, BaseRule* ruleObject)
424 {
425 rules.emplace_back(ruleObject);
426 trie.add(rule, static_cast<unsigned>(rules.size() - 1U));
427 // directory case:
428 // request to `/about' url matches `/about/' rule
429 if (rule.size() > 2 && rule.back() == '/')
430 {
431 trie.add(rule.substr(0, rule.size() - 1),
432 static_cast<unsigned>(rules.size() - 1));
433 }
434 }
435 };
436
Tanousf00032d2018-11-05 01:18:10 -0300437 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
Tanousf00032d2018-11-05 01:18:10 -0300439 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 {
Tanousf00032d2018-11-05 01:18:10 -0300441 return;
442 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700443 for (size_t method = 0; method <= maxVerbIndex; method++)
Tanousf00032d2018-11-05 01:18:10 -0300444 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700445 size_t methodBit = 1 << method;
Ed Tanouse662eae2022-01-25 10:39:19 -0800446 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -0300447 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700448 perMethods[method].internalAdd(rule, ruleObject);
Tanousf00032d2018-11-05 01:18:10 -0300449 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700451
452 if (ruleObject->isNotFound)
453 {
454 notFoundRoutes.internalAdd(rule, ruleObject);
455 }
456
457 if (ruleObject->isMethodNotAllowed)
458 {
459 methodNotAllowedRoutes.internalAdd(rule, ruleObject);
460 }
461
462 if (ruleObject->isUpgrade)
463 {
464 upgradeRoutes.internalAdd(rule, ruleObject);
465 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700466 }
467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 void validate()
469 {
Tanousf00032d2018-11-05 01:18:10 -0300470 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 {
472 if (rule)
473 {
Tanousf00032d2018-11-05 01:18:10 -0300474 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700475 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700476 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700478 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700479 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -0300480 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 }
482 }
Tanousf00032d2018-11-05 01:18:10 -0300483 for (PerMethod& perMethod : perMethods)
484 {
485 perMethod.trie.validate();
486 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700487 }
488
Ed Tanous44e45182022-07-26 16:47:23 -0700489 struct FindRoute
490 {
491 BaseRule* rule = nullptr;
Ed Tanous15a42df2023-02-09 18:08:23 -0800492 std::vector<std::string> params;
Ed Tanous44e45182022-07-26 16:47:23 -0700493 };
494
495 struct FindRouteResponse
496 {
497 std::string allowHeader;
498 FindRoute route;
499 };
500
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700501 static FindRoute findRouteByPerMethod(std::string_view url,
502 const PerMethod& perMethod)
Ed Tanous759cf102022-07-31 16:36:52 -0700503 {
504 FindRoute route;
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700505
Ed Tanousd9e89df2024-03-27 14:08:59 -0700506 Trie::FindResult found = perMethod.trie.find(url);
507 if (found.ruleIndex >= perMethod.rules.size())
Ed Tanous759cf102022-07-31 16:36:52 -0700508 {
509 throw std::runtime_error("Trie internal structure corrupted!");
510 }
511 // Found a 404 route, switch that in
Ed Tanousd9e89df2024-03-27 14:08:59 -0700512 if (found.ruleIndex != 0U)
Ed Tanous759cf102022-07-31 16:36:52 -0700513 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700514 route.rule = perMethod.rules[found.ruleIndex];
515 route.params = std::move(found.params);
Ed Tanous759cf102022-07-31 16:36:52 -0700516 }
517 return route;
518 }
519
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700520 FindRouteResponse findRoute(const Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -0700521 {
522 FindRouteResponse findRoute;
523
Ed Tanous2c9efc32022-07-31 22:08:26 -0700524 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
525 if (!verb)
526 {
527 return findRoute;
528 }
529 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -0700530 // Check to see if this url exists at any verb
531 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
532 perMethodIndex++)
533 {
534 // Make sure it's safe to deference the array at that index
535 static_assert(maxVerbIndex <
536 std::tuple_size_v<decltype(perMethods)>);
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700537 FindRoute route = findRouteByPerMethod(req.url().encoded_path(),
538 perMethods[perMethodIndex]);
Ed Tanous759cf102022-07-31 16:36:52 -0700539 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -0700540 {
541 continue;
542 }
543 if (!findRoute.allowHeader.empty())
544 {
545 findRoute.allowHeader += ", ";
546 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700547 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
548 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -0700549 if (perMethodIndex == reqMethodIndex)
550 {
Ed Tanous759cf102022-07-31 16:36:52 -0700551 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -0700552 }
553 }
554 return findRoute;
555 }
556
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 template <typename Adaptor>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700558 void handleUpgrade(const std::shared_ptr<Request>& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530559 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
560 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700562 PerMethod& perMethod = upgradeRoutes;
Tanousf00032d2018-11-05 01:18:10 -0300563 Trie& trie = perMethod.trie;
564 std::vector<BaseRule*>& rules = perMethod.rules;
565
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700566 Trie::FindResult found = trie.find(req->url().encoded_path());
Ed Tanousd9e89df2024-03-27 14:08:59 -0700567 unsigned ruleIndex = found.ruleIndex;
Ed Tanouse662eae2022-01-25 10:39:19 -0800568 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700570 BMCWEB_LOG_DEBUG("Cannot match rules {}",
571 req->url().encoded_path());
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530572 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 return;
574 }
575
576 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700577 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700579 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530581 BaseRule& rule = *rules[ruleIndex];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700583 BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}'", rule.rule);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530585 // TODO(ed) This should be able to use std::bind_front, but it doesn't
586 // appear to work with the std::move on adaptor.
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700587 validatePrivilege(req, asyncResp, rule,
588 [req, &rule, asyncResp,
589 adaptor = std::forward<Adaptor>(adaptor)]() mutable {
590 rule.handleUpgrade(*req, asyncResp, std::move(adaptor));
Patrick Williams5a39f772023-10-20 11:20:21 -0500591 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700592 }
593
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700594 void handle(const std::shared_ptr<Request>& req,
zhanghch058d1b46d2021-04-01 11:18:24 +0800595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700597 std::optional<HttpVerb> verb = httpVerbFromBoost(req->method());
Ed Tanous2c9efc32022-07-31 22:08:26 -0700598 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -0700599 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800600 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -0300601 return;
Ed Tanous888880a2020-08-24 13:48:50 -0700602 }
Ed Tanous44e45182022-07-26 16:47:23 -0700603
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700604 FindRouteResponse foundRoute = findRoute(*req);
Ed Tanous44e45182022-07-26 16:47:23 -0700605
Ed Tanous759cf102022-07-31 16:36:52 -0700606 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -0700607 {
Ed Tanous759cf102022-07-31 16:36:52 -0700608 // Couldn't find a normal route with any verb, try looking for a 404
609 // route
610 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700611 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700612 foundRoute.route = findRouteByPerMethod(
613 req->url().encoded_path(), notFoundRoutes);
Ed Tanous759cf102022-07-31 16:36:52 -0700614 }
615 else
616 {
617 // See if we have a method not allowed (405) handler
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700618 foundRoute.route = findRouteByPerMethod(
619 req->url().encoded_path(), methodNotAllowedRoutes);
Ed Tanous44e45182022-07-26 16:47:23 -0700620 }
621 }
Ed Tanous759cf102022-07-31 16:36:52 -0700622
623 // Fill in the allow header if it's valid
624 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700625 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700626 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -0700627 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -0700628 }
Tanousf00032d2018-11-05 01:18:10 -0300629
Ed Tanous44e45182022-07-26 16:47:23 -0700630 // If we couldn't find a real route or a 404 route, return a generic
631 // response
632 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 {
Ed Tanous44e45182022-07-26 16:47:23 -0700634 if (foundRoute.allowHeader.empty())
635 {
636 asyncResp->res.result(boost::beast::http::status::not_found);
637 }
638 else
Ed Tanous2634dcd2019-03-26 09:28:06 -0700639 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700640 asyncResp->res.result(
641 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -0700642 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 return;
644 }
645
Ed Tanous44e45182022-07-26 16:47:23 -0700646 BaseRule& rule = *foundRoute.route.rule;
Ed Tanous15a42df2023-02-09 18:08:23 -0800647 std::vector<std::string> params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648
Ed Tanous62598e32023-07-17 17:06:25 -0700649 BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule,
650 static_cast<uint32_t>(*verb), rule.getMethods());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700651
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700652 if (req->session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +0000653 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700654 rule.handle(*req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +0000655 return;
656 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700657 validatePrivilege(
658 req, asyncResp, rule,
659 [req, asyncResp, &rule, params = std::move(params)]() {
660 rule.handle(*req, asyncResp, params);
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700661 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700662 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700663
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 void debugPrint()
665 {
Ed Tanous271584a2019-07-09 16:24:22 -0700666 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -0300667 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700668 BMCWEB_LOG_DEBUG("{}", httpVerbToString(static_cast<HttpVerb>(i)));
Tanousf00032d2018-11-05 01:18:10 -0300669 perMethods[i].trie.debugPrint();
670 }
Ed Tanous3dac7492017-08-02 13:46:20 -0700671 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700672
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 std::vector<const std::string*> getRoutes(const std::string& parent)
674 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -0300676
677 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 {
Tanousf00032d2018-11-05 01:18:10 -0300679 std::vector<unsigned> x;
680 pm.trie.findRouteIndexes(parent, x);
681 for (unsigned index : x)
682 {
683 ret.push_back(&pm.rules[index]->rule);
684 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 }
686 return ret;
687 }
688
689 private:
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700690 std::array<PerMethod, static_cast<size_t>(HttpVerb::Max)> perMethods;
Ed Tanous888880a2020-08-24 13:48:50 -0700691
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700692 PerMethod notFoundRoutes;
693 PerMethod upgradeRoutes;
694 PerMethod methodNotAllowedRoutes;
695
Tanousf00032d2018-11-05 01:18:10 -0300696 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700697};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698} // namespace crow