blob: 010ee2dfdcb7db0a1e4eeefc0fdfb0a56f945dca [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous7045c8d2017-04-03 10:04:37 -07003#pragma once
4
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "async_resp.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -07006#include "dbus_privileges.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06008#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07009#include "http_request.hpp"
10#include "http_response.hpp"
11#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -030012#include "privileges.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070013#include "routing/baserule.hpp"
14#include "routing/dynamicrule.hpp"
15#include "routing/sserule.hpp"
16#include "routing/taggedrule.hpp"
17#include "routing/websocketrule.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053018#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070019#include "utility.hpp"
Ed Tanous3d183202023-03-10 09:21:58 -080020#include "utils/dbus_utils.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070021#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070022#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070023
Tanousf00032d2018-11-05 01:18:10 -030024#include <boost/container/flat_map.hpp>
Ed Tanousd9e89df2024-03-27 14:08:59 -070025#include <boost/container/small_vector.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
Ed Tanousd9e89df2024-03-27 14:08:59 -070027#include <algorithm>
Ed Tanouse0d918b2018-03-27 17:41:04 -070028#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070029#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070030#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070031#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070032#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070033#include <optional>
Ed Tanousa3b9eb92024-06-03 08:39:37 -070034#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070035#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070036#include <utility>
37#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070038
Ed Tanous1abe55e2018-09-05 08:30:59 -070039namespace crow
40{
Tanousf00032d2018-11-05 01:18:10 -030041
Ed Tanous1abe55e2018-09-05 08:30:59 -070042class Trie
43{
44 public:
45 struct Node
46 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070047 unsigned ruleIndex = 0U;
48
49 size_t stringParamChild = 0U;
50 size_t pathParamChild = 0U;
51
Ed Tanousa94ac612022-02-22 11:13:24 -080052 using ChildMap = boost::container::flat_map<
53 std::string, unsigned, std::less<>,
Ed Tanousd9e89df2024-03-27 14:08:59 -070054 boost::container::small_vector<std::pair<std::string, unsigned>,
55 1>>;
Ed Tanousa94ac612022-02-22 11:13:24 -080056 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -070057
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 bool isSimpleNode() const
59 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070060 return ruleIndex == 0 && stringParamChild == 0 &&
61 pathParamChild == 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -070062 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070063 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070064
Patrick Williams89492a12023-05-10 07:51:34 -050065 Trie() : nodes(1) {}
Ed Tanous1abe55e2018-09-05 08:30:59 -070066
67 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -070068 void optimizeNode(Node& node)
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070070 if (node.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070072 optimizeNode(nodes[node.stringParamChild]);
Ed Tanous7045c8d2017-04-03 10:04:37 -070073 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070074 if (node.pathParamChild != 0U)
75 {
76 optimizeNode(nodes[node.pathParamChild]);
77 }
78
79 if (node.children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -070080 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -070082 }
Ed Tanousd9e89df2024-03-27 14:08:59 -070083 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -070084 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070085 bool didMerge = false;
86 Node::ChildMap merged;
87 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 {
Ed Tanousd9e89df2024-03-27 14:08:59 -070089 Node& child = nodes[kv.second];
90 if (child.isSimpleNode())
91 {
92 for (const Node::ChildMap::value_type& childKv :
93 child.children)
94 {
95 merged[kv.first + childKv.first] = childKv.second;
96 didMerge = true;
97 }
98 }
99 else
100 {
101 merged[kv.first] = kv.second;
102 }
103 }
104 node.children = std::move(merged);
105 if (!didMerge)
106 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700110
111 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700113 optimizeNode(nodes[kv.second]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 }
115 }
116
117 void optimize()
118 {
119 optimizeNode(head());
120 }
121
122 public:
123 void validate()
124 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125 optimize();
126 }
127
Ed Tanousd9e89df2024-03-27 14:08:59 -0700128 void findRouteIndexesHelper(std::string_view reqUrl,
129 std::vector<unsigned>& routeIndexes,
130 const Node& node) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700132 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133 {
134 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700135 const Node& child = nodes[kv.second];
136 if (reqUrl.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700138 if (child.ruleIndex != 0 && fragment != "/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700140 routeIndexes.push_back(child.ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700142 findRouteIndexesHelper(reqUrl, routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 }
144 else
145 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700146 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700148 findRouteIndexesHelper(reqUrl.substr(fragment.size()),
149 routeIndexes, child);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 }
151 }
152 }
153 }
154
Ed Tanousd9e89df2024-03-27 14:08:59 -0700155 void findRouteIndexes(const std::string& reqUrl,
156 std::vector<unsigned>& routeIndexes) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700158 findRouteIndexesHelper(reqUrl, routeIndexes, head());
159 }
160
161 struct FindResult
162 {
163 unsigned ruleIndex;
164 std::vector<std::string> params;
165 };
166
167 private:
168 FindResult findHelper(const std::string_view reqUrl, const Node& node,
169 std::vector<std::string>& params) const
170 {
171 if (reqUrl.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700172 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700173 return {node.ruleIndex, params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700174 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175
Ed Tanousd9e89df2024-03-27 14:08:59 -0700176 if (node.stringParamChild != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700177 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700178 size_t epos = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000179 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000181 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700182 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700184 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 }
186
Ed Tanousd9e89df2024-03-27 14:08:59 -0700187 if (epos != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700189 params.emplace_back(reqUrl.substr(0, epos));
190 FindResult ret = findHelper(
191 reqUrl.substr(epos), nodes[node.stringParamChild], params);
192 if (ret.ruleIndex != 0U)
193 {
194 return {ret.ruleIndex, std::move(ret.params)};
195 }
196 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 }
198 }
199
Ed Tanousd9e89df2024-03-27 14:08:59 -0700200 if (node.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700202 params.emplace_back(reqUrl);
203 FindResult ret = findHelper("", nodes[node.pathParamChild], params);
204 if (ret.ruleIndex != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700206 return {ret.ruleIndex, std::move(ret.params)};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700208 params.pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209 }
210
Ed Tanousd9e89df2024-03-27 14:08:59 -0700211 for (const Node::ChildMap::value_type& kv : node.children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212 {
213 const std::string& fragment = kv.first;
Ed Tanousd9e89df2024-03-27 14:08:59 -0700214 const Node& child = nodes[kv.second];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215
Ed Tanousd9e89df2024-03-27 14:08:59 -0700216 if (reqUrl.starts_with(fragment))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400218 FindResult ret =
219 findHelper(reqUrl.substr(fragment.size()), child, params);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700220 if (ret.ruleIndex != 0U)
221 {
222 return {ret.ruleIndex, std::move(ret.params)};
223 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 }
225 }
226
Ed Tanousd9e89df2024-03-27 14:08:59 -0700227 return {0U, std::vector<std::string>()};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700228 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229
Ed Tanousd9e89df2024-03-27 14:08:59 -0700230 public:
231 FindResult find(const std::string_view reqUrl) const
232 {
233 std::vector<std::string> start;
234 return findHelper(reqUrl, head(), start);
235 }
236
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700237 void add(std::string_view urlIn, unsigned ruleIndex)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 {
Ed Tanous271584a2019-07-09 16:24:22 -0700239 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700240
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700241 std::string_view url = urlIn;
242
Ed Tanousd9e89df2024-03-27 14:08:59 -0700243 while (!url.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700245 char c = url[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 if (c == '<')
247 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700248 bool found = false;
249 for (const std::string_view str1 :
250 {"<str>", "<string>", "<path>"})
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700252 if (!url.starts_with(str1))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700254 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700256 found = true;
257 Node& node = nodes[idx];
258 size_t* param = &node.stringParamChild;
259 if (str1 == "<path>")
260 {
261 param = &node.pathParamChild;
262 }
263 if (*param == 0U)
264 {
265 *param = newNode();
266 }
267 idx = *param;
268
269 url.remove_prefix(str1.size());
270 break;
271 }
272 if (found)
273 {
274 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 }
276
Manojkiran Edaefff2b52024-06-18 18:01:46 +0530277 BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700278 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700280 std::string piece(&c, 1);
281 if (!nodes[idx].children.contains(piece))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700283 unsigned newNodeIdx = newNode();
284 nodes[idx].children.emplace(piece, newNodeIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700286 idx = nodes[idx].children[piece];
287 url.remove_prefix(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700289 Node& node = nodes[idx];
290 if (node.ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700291 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700292 BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700293 throw std::runtime_error(
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700294 std::format("handler already exists for \"{}\"", urlIn));
Ed Tanous3174e4d2020-10-07 11:41:22 -0700295 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700296 node.ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700297 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700298
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700300 void debugNodePrint(Node& n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700302 std::string spaces(level, ' ');
303 if (n.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700305 BMCWEB_LOG_DEBUG("{}<str>", spaces);
306 debugNodePrint(nodes[n.stringParamChild], level + 5);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700308 if (n.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700310 BMCWEB_LOG_DEBUG("{} <path>", spaces);
311 debugNodePrint(nodes[n.pathParamChild], level + 6);
312 }
313 for (const Node::ChildMap::value_type& kv : n.children)
314 {
315 BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
316 debugNodePrint(nodes[kv.second], level + kv.first.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 }
318 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700319
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320 public:
321 void debugPrint()
322 {
Ed Tanous271584a2019-07-09 16:24:22 -0700323 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700325
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700327 const Node& head() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700329 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700331
Ed Tanousd9e89df2024-03-27 14:08:59 -0700332 Node& head()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700334 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700335 }
336
337 unsigned newNode()
338 {
339 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -0700340 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341 }
342
343 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700344};
345
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346class Router
347{
348 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -0700349 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 DynamicRule& newRuleDynamic(const std::string& rule)
352 {
353 std::unique_ptr<DynamicRule> ruleObject =
354 std::make_unique<DynamicRule>(rule);
355 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -0300356 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700357
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700359 }
360
Ed Tanousd9e89df2024-03-27 14:08:59 -0700361 template <uint64_t NumArgs>
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700362 auto& newRuleTagged(const std::string& rule)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700363 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700364 if constexpr (NumArgs == 0)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700365 {
366 using RuleT = TaggedRule<>;
367 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
368 RuleT* ptr = ruleObject.get();
369 allRules.emplace_back(std::move(ruleObject));
370 return *ptr;
371 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700372 else if constexpr (NumArgs == 1)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700373 {
374 using RuleT = TaggedRule<std::string>;
375 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
376 RuleT* ptr = ruleObject.get();
377 allRules.emplace_back(std::move(ruleObject));
378 return *ptr;
379 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700380 else if constexpr (NumArgs == 2)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700381 {
382 using RuleT = TaggedRule<std::string, std::string>;
383 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
384 RuleT* ptr = ruleObject.get();
385 allRules.emplace_back(std::move(ruleObject));
386 return *ptr;
387 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700388 else if constexpr (NumArgs == 3)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700389 {
390 using RuleT = TaggedRule<std::string, std::string, std::string>;
391 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
392 RuleT* ptr = ruleObject.get();
393 allRules.emplace_back(std::move(ruleObject));
394 return *ptr;
395 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700396 else if constexpr (NumArgs == 4)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700397 {
398 using RuleT =
399 TaggedRule<std::string, std::string, std::string, std::string>;
400 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
401 RuleT* ptr = ruleObject.get();
402 allRules.emplace_back(std::move(ruleObject));
403 return *ptr;
404 }
405 else
406 {
407 using RuleT = TaggedRule<std::string, std::string, std::string,
408 std::string, std::string>;
409 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
410 RuleT* ptr = ruleObject.get();
411 allRules.emplace_back(std::move(ruleObject));
412 return *ptr;
413 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700414 static_assert(NumArgs <= 5, "Max number of args supported is 5");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700415 }
416
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700417 struct PerMethod
418 {
419 std::vector<BaseRule*> rules;
420 Trie trie;
421 // rule index 0 has special meaning; preallocate it to avoid
422 // duplication.
423 PerMethod() : rules(1) {}
424
425 void internalAdd(std::string_view rule, BaseRule* ruleObject)
426 {
427 rules.emplace_back(ruleObject);
428 trie.add(rule, static_cast<unsigned>(rules.size() - 1U));
429 // directory case:
430 // request to `/about' url matches `/about/' rule
431 if (rule.size() > 2 && rule.back() == '/')
432 {
433 trie.add(rule.substr(0, rule.size() - 1),
434 static_cast<unsigned>(rules.size() - 1));
435 }
436 }
437 };
438
Tanousf00032d2018-11-05 01:18:10 -0300439 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 {
Tanousf00032d2018-11-05 01:18:10 -0300441 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 {
Tanousf00032d2018-11-05 01:18:10 -0300443 return;
444 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700445 for (size_t method = 0; method <= maxVerbIndex; method++)
Tanousf00032d2018-11-05 01:18:10 -0300446 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700447 size_t methodBit = 1 << method;
Ed Tanouse662eae2022-01-25 10:39:19 -0800448 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -0300449 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700450 perMethods[method].internalAdd(rule, ruleObject);
Tanousf00032d2018-11-05 01:18:10 -0300451 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700453
454 if (ruleObject->isNotFound)
455 {
456 notFoundRoutes.internalAdd(rule, ruleObject);
457 }
458
459 if (ruleObject->isMethodNotAllowed)
460 {
461 methodNotAllowedRoutes.internalAdd(rule, ruleObject);
462 }
463
464 if (ruleObject->isUpgrade)
465 {
466 upgradeRoutes.internalAdd(rule, ruleObject);
467 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700468 }
469
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 void validate()
471 {
Tanousf00032d2018-11-05 01:18:10 -0300472 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 {
474 if (rule)
475 {
Tanousf00032d2018-11-05 01:18:10 -0300476 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700478 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700479 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700480 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -0300482 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 }
484 }
Tanousf00032d2018-11-05 01:18:10 -0300485 for (PerMethod& perMethod : perMethods)
486 {
487 perMethod.trie.validate();
488 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700489 }
490
Ed Tanous44e45182022-07-26 16:47:23 -0700491 struct FindRoute
492 {
493 BaseRule* rule = nullptr;
Ed Tanous15a42df2023-02-09 18:08:23 -0800494 std::vector<std::string> params;
Ed Tanous44e45182022-07-26 16:47:23 -0700495 };
496
497 struct FindRouteResponse
498 {
499 std::string allowHeader;
500 FindRoute route;
501 };
502
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700503 static FindRoute findRouteByPerMethod(std::string_view url,
504 const PerMethod& perMethod)
Ed Tanous759cf102022-07-31 16:36:52 -0700505 {
506 FindRoute route;
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700507
Ed Tanousd9e89df2024-03-27 14:08:59 -0700508 Trie::FindResult found = perMethod.trie.find(url);
509 if (found.ruleIndex >= perMethod.rules.size())
Ed Tanous759cf102022-07-31 16:36:52 -0700510 {
511 throw std::runtime_error("Trie internal structure corrupted!");
512 }
513 // Found a 404 route, switch that in
Ed Tanousd9e89df2024-03-27 14:08:59 -0700514 if (found.ruleIndex != 0U)
Ed Tanous759cf102022-07-31 16:36:52 -0700515 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700516 route.rule = perMethod.rules[found.ruleIndex];
517 route.params = std::move(found.params);
Ed Tanous759cf102022-07-31 16:36:52 -0700518 }
519 return route;
520 }
521
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700522 FindRouteResponse findRoute(const Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -0700523 {
524 FindRouteResponse findRoute;
525
Ed Tanous44e45182022-07-26 16:47:23 -0700526 // Check to see if this url exists at any verb
527 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
528 perMethodIndex++)
529 {
530 // Make sure it's safe to deference the array at that index
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400531 static_assert(
532 maxVerbIndex < std::tuple_size_v<decltype(perMethods)>);
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700533 FindRoute route = findRouteByPerMethod(req.url().encoded_path(),
534 perMethods[perMethodIndex]);
Ed Tanous759cf102022-07-31 16:36:52 -0700535 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -0700536 {
537 continue;
538 }
539 if (!findRoute.allowHeader.empty())
540 {
541 findRoute.allowHeader += ", ";
542 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700543 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
544 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -0700545 }
Ed Tanous50bfc912024-07-29 14:20:50 -0700546
547 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
548 if (!verb)
549 {
550 return findRoute;
551 }
552 size_t reqMethodIndex = static_cast<size_t>(*verb);
553 if (reqMethodIndex >= perMethods.size())
554 {
555 return findRoute;
556 }
557
558 FindRoute route = findRouteByPerMethod(req.url().encoded_path(),
559 perMethods[reqMethodIndex]);
560 if (route.rule != nullptr)
561 {
562 findRoute.route = route;
563 }
564
Ed Tanous44e45182022-07-26 16:47:23 -0700565 return findRoute;
566 }
567
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 template <typename Adaptor>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700569 void handleUpgrade(const std::shared_ptr<Request>& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530570 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700573 PerMethod& perMethod = upgradeRoutes;
Tanousf00032d2018-11-05 01:18:10 -0300574 Trie& trie = perMethod.trie;
575 std::vector<BaseRule*>& rules = perMethod.rules;
576
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700577 Trie::FindResult found = trie.find(req->url().encoded_path());
Ed Tanousd9e89df2024-03-27 14:08:59 -0700578 unsigned ruleIndex = found.ruleIndex;
Ed Tanouse662eae2022-01-25 10:39:19 -0800579 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700581 BMCWEB_LOG_DEBUG("Cannot match rules {}",
582 req->url().encoded_path());
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530583 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 return;
585 }
586
587 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700588 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700590 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530592 BaseRule& rule = *rules[ruleIndex];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700594 BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}'", rule.rule);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530596 // TODO(ed) This should be able to use std::bind_front, but it doesn't
597 // appear to work with the std::move on adaptor.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400598 validatePrivilege(
599 req, asyncResp, rule,
600 [req, &rule, asyncResp,
601 adaptor = std::forward<Adaptor>(adaptor)]() mutable {
602 rule.handleUpgrade(*req, asyncResp, std::move(adaptor));
603 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700604 }
605
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700606 void handle(const std::shared_ptr<Request>& req,
zhanghch058d1b46d2021-04-01 11:18:24 +0800607 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700609 FindRouteResponse foundRoute = findRoute(*req);
Ed Tanous44e45182022-07-26 16:47:23 -0700610
Ed Tanous759cf102022-07-31 16:36:52 -0700611 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -0700612 {
Ed Tanous759cf102022-07-31 16:36:52 -0700613 // Couldn't find a normal route with any verb, try looking for a 404
614 // route
615 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700616 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700617 foundRoute.route = findRouteByPerMethod(
618 req->url().encoded_path(), notFoundRoutes);
Ed Tanous759cf102022-07-31 16:36:52 -0700619 }
620 else
621 {
622 // See if we have a method not allowed (405) handler
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700623 foundRoute.route = findRouteByPerMethod(
624 req->url().encoded_path(), methodNotAllowedRoutes);
Ed Tanous44e45182022-07-26 16:47:23 -0700625 }
626 }
Ed Tanous759cf102022-07-31 16:36:52 -0700627
628 // Fill in the allow header if it's valid
629 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700630 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700631 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -0700632 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -0700633 }
Tanousf00032d2018-11-05 01:18:10 -0300634
Ed Tanous44e45182022-07-26 16:47:23 -0700635 // If we couldn't find a real route or a 404 route, return a generic
636 // response
637 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 {
Ed Tanous44e45182022-07-26 16:47:23 -0700639 if (foundRoute.allowHeader.empty())
640 {
641 asyncResp->res.result(boost::beast::http::status::not_found);
642 }
643 else
Ed Tanous2634dcd2019-03-26 09:28:06 -0700644 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700645 asyncResp->res.result(
646 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -0700647 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 return;
649 }
650
Ed Tanous44e45182022-07-26 16:47:23 -0700651 BaseRule& rule = *foundRoute.route.rule;
Ed Tanous15a42df2023-02-09 18:08:23 -0800652 std::vector<std::string> params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653
Ed Tanous62598e32023-07-17 17:06:25 -0700654 BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule,
Ed Tanous50bfc912024-07-29 14:20:50 -0700655 req->methodString(), rule.getMethods());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700657 if (req->session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +0000658 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700659 rule.handle(*req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +0000660 return;
661 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700662 validatePrivilege(
663 req, asyncResp, rule,
664 [req, asyncResp, &rule, params = std::move(params)]() {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400665 rule.handle(*req, asyncResp, params);
666 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700667 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700668
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 void debugPrint()
670 {
Ed Tanous271584a2019-07-09 16:24:22 -0700671 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -0300672 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700673 BMCWEB_LOG_DEBUG("{}", httpVerbToString(static_cast<HttpVerb>(i)));
Tanousf00032d2018-11-05 01:18:10 -0300674 perMethods[i].trie.debugPrint();
675 }
Ed Tanous3dac7492017-08-02 13:46:20 -0700676 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700677
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 std::vector<const std::string*> getRoutes(const std::string& parent)
679 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -0300681
682 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
Tanousf00032d2018-11-05 01:18:10 -0300684 std::vector<unsigned> x;
685 pm.trie.findRouteIndexes(parent, x);
686 for (unsigned index : x)
687 {
688 ret.push_back(&pm.rules[index]->rule);
689 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 }
691 return ret;
692 }
693
694 private:
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700695 std::array<PerMethod, static_cast<size_t>(HttpVerb::Max)> perMethods;
Ed Tanous888880a2020-08-24 13:48:50 -0700696
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700697 PerMethod notFoundRoutes;
698 PerMethod upgradeRoutes;
699 PerMethod methodNotAllowedRoutes;
700
Tanousf00032d2018-11-05 01:18:10 -0300701 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700702};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703} // namespace crow