blob: 7e481a0dd22eec8292d9101b7bedb7e438f08757 [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 Tanous7045c8d2017-04-03 10:04:37 -070032#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070033#include <utility>
34#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070035
Ed Tanous1abe55e2018-09-05 08:30:59 -070036namespace crow
37{
Tanousf00032d2018-11-05 01:18:10 -030038
Ed Tanous1abe55e2018-09-05 08:30:59 -070039class Trie
40{
41 public:
42 struct Node
43 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070044 unsigned ruleIndex = 0U;
45
46 size_t stringParamChild = 0U;
47 size_t pathParamChild = 0U;
48
Ed Tanousa94ac612022-02-22 11:13:24 -080049 using ChildMap = boost::container::flat_map<
50 std::string, unsigned, std::less<>,
Ed Tanousd9e89df2024-03-27 14:08:59 -070051 boost::container::small_vector<std::pair<std::string, unsigned>,
52 1>>;
Ed Tanousa94ac612022-02-22 11:13:24 -080053 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -070054
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 bool isSimpleNode() const
56 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070057 return ruleIndex == 0 && stringParamChild == 0 &&
58 pathParamChild == 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -070059 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070060 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070061
Patrick Williams89492a12023-05-10 07:51:34 -050062 Trie() : nodes(1) {}
Ed Tanous1abe55e2018-09-05 08:30:59 -070063
64 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -070065 void optimizeNode(Node& node)
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070067 if (node.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070069 optimizeNode(nodes[node.stringParamChild]);
Ed Tanous7045c8d2017-04-03 10:04:37 -070070 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070071 if (node.pathParamChild != 0U)
72 {
73 optimizeNode(nodes[node.pathParamChild]);
74 }
75
76 if (node.children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -070077 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -070079 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070080 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070082 bool didMerge = false;
83 Node::ChildMap merged;
84 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070085 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070086 Node& child = nodes[kv.second];
87 if (child.isSimpleNode())
88 {
89 for (const Node::ChildMap::value_type& childKv :
90 child.children)
91 {
92 merged[kv.first + childKv.first] = childKv.second;
93 didMerge = true;
94 }
95 }
96 else
97 {
98 merged[kv.first] = kv.second;
99 }
100 }
101 node.children = std::move(merged);
102 if (!didMerge)
103 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700107
108 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700110 optimizeNode(nodes[kv.second]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 }
112 }
113
114 void optimize()
115 {
116 optimizeNode(head());
117 }
118
119 public:
120 void validate()
121 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 optimize();
123 }
124
Ed Tanousd9e89df2024-03-27 14:08:59 -0700125 void findRouteIndexesHelper(std::string_view reqUrl,
126 std::vector<unsigned>& routeIndexes,
127 const Node& node) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700129 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
131 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700132 const Node& child = nodes[kv.second];
133 if (reqUrl.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700135 if (child.ruleIndex != 0 && fragment != "/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700137 routeIndexes.push_back(child.ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700138 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700139 findRouteIndexesHelper(reqUrl, routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140 }
141 else
142 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700143 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700145 findRouteIndexesHelper(reqUrl.substr(fragment.size()),
146 routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 }
148 }
149 }
150 }
151
Ed Tanousd9e89df2024-03-27 14:08:59 -0700152 void findRouteIndexes(const std::string& reqUrl,
153 std::vector<unsigned>& routeIndexes) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700154 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700155 findRouteIndexesHelper(reqUrl, routeIndexes, head());
156 }
157
158 struct FindResult
159 {
160 unsigned ruleIndex;
161 std::vector<std::string> params;
162 };
163
164 private:
165 FindResult findHelper(const std::string_view reqUrl, const Node& node,
166 std::vector<std::string>& params) const
167 {
168 if (reqUrl.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700169 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700170 return {node.ruleIndex, params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700171 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172
Ed Tanousd9e89df2024-03-27 14:08:59 -0700173 if (node.stringParamChild != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700174 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700175 size_t epos = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000176 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000178 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700179 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700181 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 }
183
Ed Tanousd9e89df2024-03-27 14:08:59 -0700184 if (epos != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700186 params.emplace_back(reqUrl.substr(0, epos));
187 FindResult ret = findHelper(
188 reqUrl.substr(epos), nodes[node.stringParamChild], params);
189 if (ret.ruleIndex != 0U)
190 {
191 return {ret.ruleIndex, std::move(ret.params)};
192 }
193 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 }
195 }
196
Ed Tanousd9e89df2024-03-27 14:08:59 -0700197 if (node.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700199 params.emplace_back(reqUrl);
200 FindResult ret = findHelper("", nodes[node.pathParamChild], params);
201 if (ret.ruleIndex != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700202 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700203 return {ret.ruleIndex, std::move(ret.params)};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700205 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700206 }
207
Ed Tanousd9e89df2024-03-27 14:08:59 -0700208 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209 {
210 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700211 const Node& child = nodes[kv.second];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212
Ed Tanousd9e89df2024-03-27 14:08:59 -0700213 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700215 FindResult ret = findHelper(reqUrl.substr(fragment.size()),
216 child, params);
217 if (ret.ruleIndex != 0U)
218 {
219 return {ret.ruleIndex, std::move(ret.params)};
220 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 }
222 }
223
Ed Tanousd9e89df2024-03-27 14:08:59 -0700224 return {0U, std::vector<std::string>()};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700225 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226
Ed Tanousd9e89df2024-03-27 14:08:59 -0700227 public:
228 FindResult find(const std::string_view reqUrl) const
229 {
230 std::vector<std::string> start;
231 return findHelper(reqUrl, head(), start);
232 }
233
234 void add(std::string_view url, unsigned ruleIndex)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 {
Ed Tanous271584a2019-07-09 16:24:22 -0700236 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700237
Ed Tanousd9e89df2024-03-27 14:08:59 -0700238 while (!url.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700239 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700240 char c = url[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 if (c == '<')
242 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700243 bool found = false;
244 for (const std::string_view str1 :
245 {"<str>", "<string>", "<path>"})
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700247 if (!url.starts_with(str1))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700249 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700251 found = true;
252 Node& node = nodes[idx];
253 size_t* param = &node.stringParamChild;
254 if (str1 == "<path>")
255 {
256 param = &node.pathParamChild;
257 }
258 if (*param == 0U)
259 {
260 *param = newNode();
261 }
262 idx = *param;
263
264 url.remove_prefix(str1.size());
265 break;
266 }
267 if (found)
268 {
269 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 }
271
Ed Tanousd9e89df2024-03-27 14:08:59 -0700272 BMCWEB_LOG_CRITICAL("Cant find tag for {}", url);
273 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700275 std::string piece(&c, 1);
276 if (!nodes[idx].children.contains(piece))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700278 unsigned newNodeIdx = newNode();
279 nodes[idx].children.emplace(piece, newNodeIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700281 idx = nodes[idx].children[piece];
282 url.remove_prefix(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 }
Ed Tanouse662eae2022-01-25 10:39:19 -0800284 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700285 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700286 throw std::runtime_error(
287 std::format("handler already exists for {}", url));
Ed Tanous3174e4d2020-10-07 11:41:22 -0700288 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700290 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700291
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700293 void debugNodePrint(Node& n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700295 std::string spaces(level, ' ');
296 if (n.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700298 BMCWEB_LOG_DEBUG("{}<str>", spaces);
299 debugNodePrint(nodes[n.stringParamChild], level + 5);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700301 if (n.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700303 BMCWEB_LOG_DEBUG("{} <path>", spaces);
304 debugNodePrint(nodes[n.pathParamChild], level + 6);
305 }
306 for (const Node::ChildMap::value_type& kv : n.children)
307 {
308 BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
309 debugNodePrint(nodes[kv.second], level + kv.first.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 }
311 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 public:
314 void debugPrint()
315 {
Ed Tanous271584a2019-07-09 16:24:22 -0700316 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700318
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700320 const Node& head() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700322 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700324
Ed Tanousd9e89df2024-03-27 14:08:59 -0700325 Node& head()
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 }
329
330 unsigned newNode()
331 {
332 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -0700333 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334 }
335
336 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700337};
338
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339class Router
340{
341 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -0700342 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700343
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344 DynamicRule& newRuleDynamic(const std::string& rule)
345 {
346 std::unique_ptr<DynamicRule> ruleObject =
347 std::make_unique<DynamicRule>(rule);
348 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -0300349 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700352 }
353
Ed Tanousd9e89df2024-03-27 14:08:59 -0700354 template <uint64_t NumArgs>
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700355 auto& newRuleTagged(const std::string& rule)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700357 if constexpr (NumArgs == 0)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700358 {
359 using RuleT = TaggedRule<>;
360 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
361 RuleT* ptr = ruleObject.get();
362 allRules.emplace_back(std::move(ruleObject));
363 return *ptr;
364 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700365 else if constexpr (NumArgs == 1)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700366 {
367 using RuleT = TaggedRule<std::string>;
368 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
369 RuleT* ptr = ruleObject.get();
370 allRules.emplace_back(std::move(ruleObject));
371 return *ptr;
372 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700373 else if constexpr (NumArgs == 2)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700374 {
375 using RuleT = TaggedRule<std::string, std::string>;
376 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
377 RuleT* ptr = ruleObject.get();
378 allRules.emplace_back(std::move(ruleObject));
379 return *ptr;
380 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700381 else if constexpr (NumArgs == 3)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700382 {
383 using RuleT = TaggedRule<std::string, std::string, std::string>;
384 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
385 RuleT* ptr = ruleObject.get();
386 allRules.emplace_back(std::move(ruleObject));
387 return *ptr;
388 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700389 else if constexpr (NumArgs == 4)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700390 {
391 using RuleT =
392 TaggedRule<std::string, std::string, std::string, std::string>;
393 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
394 RuleT* ptr = ruleObject.get();
395 allRules.emplace_back(std::move(ruleObject));
396 return *ptr;
397 }
398 else
399 {
400 using RuleT = TaggedRule<std::string, std::string, std::string,
401 std::string, std::string>;
402 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
403 RuleT* ptr = ruleObject.get();
404 allRules.emplace_back(std::move(ruleObject));
405 return *ptr;
406 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700407 static_assert(NumArgs <= 5, "Max number of args supported is 5");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700408 }
409
Tanousf00032d2018-11-05 01:18:10 -0300410 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 {
Tanousf00032d2018-11-05 01:18:10 -0300412 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
Tanousf00032d2018-11-05 01:18:10 -0300414 return;
415 }
Ed Tanous759cf102022-07-31 16:36:52 -0700416 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -0700417 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -0300418 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800419 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -0300420 {
421 perMethods[method].rules.emplace_back(ruleObject);
422 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -0700423 rule, static_cast<unsigned>(
424 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -0300425 // directory case:
426 // request to `/about' url matches `/about/' rule
427 if (rule.size() > 2 && rule.back() == '/')
428 {
429 perMethods[method].trie.add(
430 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -0700431 static_cast<unsigned>(perMethods[method].rules.size() -
432 1));
Tanousf00032d2018-11-05 01:18:10 -0300433 }
434 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700436 }
437
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 void validate()
439 {
Tanousf00032d2018-11-05 01:18:10 -0300440 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 {
442 if (rule)
443 {
Tanousf00032d2018-11-05 01:18:10 -0300444 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700446 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700448 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -0300450 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 }
452 }
Tanousf00032d2018-11-05 01:18:10 -0300453 for (PerMethod& perMethod : perMethods)
454 {
455 perMethod.trie.validate();
456 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700457 }
458
Ed Tanous44e45182022-07-26 16:47:23 -0700459 struct FindRoute
460 {
461 BaseRule* rule = nullptr;
Ed Tanous15a42df2023-02-09 18:08:23 -0800462 std::vector<std::string> params;
Ed Tanous44e45182022-07-26 16:47:23 -0700463 };
464
465 struct FindRouteResponse
466 {
467 std::string allowHeader;
468 FindRoute route;
469 };
470
Ed Tanous759cf102022-07-31 16:36:52 -0700471 FindRoute findRouteByIndex(std::string_view url, size_t index) const
472 {
473 FindRoute route;
474 if (index >= perMethods.size())
475 {
Ed Tanous62598e32023-07-17 17:06:25 -0700476 BMCWEB_LOG_CRITICAL("Bad index???");
Ed Tanous759cf102022-07-31 16:36:52 -0700477 return route;
478 }
479 const PerMethod& perMethod = perMethods[index];
Ed Tanousd9e89df2024-03-27 14:08:59 -0700480 Trie::FindResult found = perMethod.trie.find(url);
481 if (found.ruleIndex >= perMethod.rules.size())
Ed Tanous759cf102022-07-31 16:36:52 -0700482 {
483 throw std::runtime_error("Trie internal structure corrupted!");
484 }
485 // Found a 404 route, switch that in
Ed Tanousd9e89df2024-03-27 14:08:59 -0700486 if (found.ruleIndex != 0U)
Ed Tanous759cf102022-07-31 16:36:52 -0700487 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700488 route.rule = perMethod.rules[found.ruleIndex];
489 route.params = std::move(found.params);
Ed Tanous759cf102022-07-31 16:36:52 -0700490 }
491 return route;
492 }
493
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700494 FindRouteResponse findRoute(const Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -0700495 {
496 FindRouteResponse findRoute;
497
Ed Tanous2c9efc32022-07-31 22:08:26 -0700498 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
499 if (!verb)
500 {
501 return findRoute;
502 }
503 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -0700504 // Check to see if this url exists at any verb
505 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
506 perMethodIndex++)
507 {
508 // Make sure it's safe to deference the array at that index
509 static_assert(maxVerbIndex <
510 std::tuple_size_v<decltype(perMethods)>);
Patrick Williams89492a12023-05-10 07:51:34 -0500511 FindRoute route = findRouteByIndex(req.url().encoded_path(),
512 perMethodIndex);
Ed Tanous759cf102022-07-31 16:36:52 -0700513 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -0700514 {
515 continue;
516 }
517 if (!findRoute.allowHeader.empty())
518 {
519 findRoute.allowHeader += ", ";
520 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700521 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
522 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -0700523 if (perMethodIndex == reqMethodIndex)
524 {
Ed Tanous759cf102022-07-31 16:36:52 -0700525 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -0700526 }
527 }
528 return findRoute;
529 }
530
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 template <typename Adaptor>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700532 void handleUpgrade(const std::shared_ptr<Request>& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530533 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
534 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700535 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700536 std::optional<HttpVerb> verb = httpVerbFromBoost(req->method());
Ed Tanous2c9efc32022-07-31 22:08:26 -0700537 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -0700538 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530539 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -0300540 return;
Ed Tanous888880a2020-08-24 13:48:50 -0700541 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700542 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -0300543 Trie& trie = perMethod.trie;
544 std::vector<BaseRule*>& rules = perMethod.rules;
545
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700546 Trie::FindResult found = trie.find(req->url().encoded_path());
Ed Tanousd9e89df2024-03-27 14:08:59 -0700547 unsigned ruleIndex = found.ruleIndex;
Ed Tanouse662eae2022-01-25 10:39:19 -0800548 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700550 BMCWEB_LOG_DEBUG("Cannot match rules {}",
551 req->url().encoded_path());
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530552 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 return;
554 }
555
556 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700557 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700559 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530561 BaseRule& rule = *rules[ruleIndex];
562 size_t methods = rule.getMethods();
563 if ((methods & (1U << static_cast<size_t>(*verb))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
Ed Tanous62598e32023-07-17 17:06:25 -0700565 BMCWEB_LOG_DEBUG(
566 "Rule found but method mismatch: {} with {}({}) / {}",
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700567 req->url().encoded_path(), req->methodString(),
Ed Tanous62598e32023-07-17 17:06:25 -0700568 static_cast<uint32_t>(*verb), methods);
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530569 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 return;
571 }
572
Ed Tanous62598e32023-07-17 17:06:25 -0700573 BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}' {} / {}", rule.rule,
574 static_cast<uint32_t>(*verb), methods);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530576 // TODO(ed) This should be able to use std::bind_front, but it doesn't
577 // appear to work with the std::move on adaptor.
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700578 validatePrivilege(req, asyncResp, rule,
579 [req, &rule, asyncResp,
580 adaptor = std::forward<Adaptor>(adaptor)]() mutable {
581 rule.handleUpgrade(*req, asyncResp, std::move(adaptor));
Patrick Williams5a39f772023-10-20 11:20:21 -0500582 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700583 }
584
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700585 void handle(const std::shared_ptr<Request>& req,
zhanghch058d1b46d2021-04-01 11:18:24 +0800586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700588 std::optional<HttpVerb> verb = httpVerbFromBoost(req->method());
Ed Tanous2c9efc32022-07-31 22:08:26 -0700589 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -0700590 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800591 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -0300592 return;
Ed Tanous888880a2020-08-24 13:48:50 -0700593 }
Ed Tanous44e45182022-07-26 16:47:23 -0700594
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700595 FindRouteResponse foundRoute = findRoute(*req);
Ed Tanous44e45182022-07-26 16:47:23 -0700596
Ed Tanous759cf102022-07-31 16:36:52 -0700597 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -0700598 {
Ed Tanous759cf102022-07-31 16:36:52 -0700599 // Couldn't find a normal route with any verb, try looking for a 404
600 // route
601 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700602 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700603 foundRoute.route = findRouteByIndex(req->url().encoded_path(),
Patrick Williams89492a12023-05-10 07:51:34 -0500604 notFoundIndex);
Ed Tanous759cf102022-07-31 16:36:52 -0700605 }
606 else
607 {
608 // See if we have a method not allowed (405) handler
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700609 foundRoute.route = findRouteByIndex(req->url().encoded_path(),
Ed Tanous39662a32023-02-06 15:09:46 -0800610 methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -0700611 }
612 }
Ed Tanous759cf102022-07-31 16:36:52 -0700613
614 // Fill in the allow header if it's valid
615 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700616 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700617 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -0700618 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -0700619 }
Tanousf00032d2018-11-05 01:18:10 -0300620
Ed Tanous44e45182022-07-26 16:47:23 -0700621 // If we couldn't find a real route or a 404 route, return a generic
622 // response
623 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 {
Ed Tanous44e45182022-07-26 16:47:23 -0700625 if (foundRoute.allowHeader.empty())
626 {
627 asyncResp->res.result(boost::beast::http::status::not_found);
628 }
629 else
Ed Tanous2634dcd2019-03-26 09:28:06 -0700630 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700631 asyncResp->res.result(
632 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -0700633 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 return;
635 }
636
Ed Tanous44e45182022-07-26 16:47:23 -0700637 BaseRule& rule = *foundRoute.route.rule;
Ed Tanous15a42df2023-02-09 18:08:23 -0800638 std::vector<std::string> params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639
Ed Tanous62598e32023-07-17 17:06:25 -0700640 BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule,
641 static_cast<uint32_t>(*verb), rule.getMethods());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700643 if (req->session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +0000644 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700645 rule.handle(*req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +0000646 return;
647 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700648 validatePrivilege(
649 req, asyncResp, rule,
650 [req, asyncResp, &rule, params = std::move(params)]() {
651 rule.handle(*req, asyncResp, params);
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700652 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700653 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700654
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 void debugPrint()
656 {
Ed Tanous271584a2019-07-09 16:24:22 -0700657 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -0300658 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700659 BMCWEB_LOG_DEBUG("{}", httpVerbToString(static_cast<HttpVerb>(i)));
Tanousf00032d2018-11-05 01:18:10 -0300660 perMethods[i].trie.debugPrint();
661 }
Ed Tanous3dac7492017-08-02 13:46:20 -0700662 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700663
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 std::vector<const std::string*> getRoutes(const std::string& parent)
665 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -0300667
668 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 {
Tanousf00032d2018-11-05 01:18:10 -0300670 std::vector<unsigned> x;
671 pm.trie.findRouteIndexes(parent, x);
672 for (unsigned index : x)
673 {
674 ret.push_back(&pm.rules[index]->rule);
675 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 }
677 return ret;
678 }
679
680 private:
Tanousf00032d2018-11-05 01:18:10 -0300681 struct PerMethod
682 {
683 std::vector<BaseRule*> rules;
684 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -0700685 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -0300686 // duplication.
Patrick Williams89492a12023-05-10 07:51:34 -0500687 PerMethod() : rules(1) {}
Tanousf00032d2018-11-05 01:18:10 -0300688 };
Ed Tanous888880a2020-08-24 13:48:50 -0700689
Ed Tanous759cf102022-07-31 16:36:52 -0700690 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -0300691 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700692};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693} // namespace crow