blob: 6c3993914dcefb1b32a282ab36d50d412bb3317b [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 Tanous04e438c2020-10-03 08:06:26 -07007#include "http_request.hpp"
8#include "http_response.hpp"
9#include "logging.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070010#include "routing/baserule.hpp"
11#include "routing/dynamicrule.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070012#include "routing/taggedrule.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070013#include "verb.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070014
Ed Tanousd7857202025-01-28 15:32:26 -080015#include <boost/beast/http/field.hpp>
16#include <boost/beast/http/status.hpp>
Tanousf00032d2018-11-05 01:18:10 -030017#include <boost/container/flat_map.hpp>
Ed Tanousd9e89df2024-03-27 14:08:59 -070018#include <boost/container/small_vector.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019
Ed Tanousd9e89df2024-03-27 14:08:59 -070020#include <algorithm>
Ed Tanousd7857202025-01-28 15:32:26 -080021#include <array>
Ed Tanouse0d918b2018-03-27 17:41:04 -070022#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070024#include <cstdlib>
Ed Tanousd7857202025-01-28 15:32:26 -080025#include <format>
26#include <functional>
Ed Tanous7045c8d2017-04-03 10:04:37 -070027#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070028#include <optional>
Ed Tanousd7857202025-01-28 15:32:26 -080029#include <stdexcept>
30#include <string>
Ed Tanousa3b9eb92024-06-03 08:39:37 -070031#include <string_view>
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 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400215 FindResult ret =
216 findHelper(reqUrl.substr(fragment.size()), child, params);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700217 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
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700234 void add(std::string_view urlIn, 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 Tanousa3b9eb92024-06-03 08:39:37 -0700238 std::string_view url = urlIn;
239
Ed Tanousd9e89df2024-03-27 14:08:59 -0700240 while (!url.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700242 char c = url[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 if (c == '<')
244 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700245 bool found = false;
246 for (const std::string_view str1 :
247 {"<str>", "<string>", "<path>"})
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700249 if (!url.starts_with(str1))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700251 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700253 found = true;
254 Node& node = nodes[idx];
255 size_t* param = &node.stringParamChild;
256 if (str1 == "<path>")
257 {
258 param = &node.pathParamChild;
259 }
260 if (*param == 0U)
261 {
262 *param = newNode();
263 }
264 idx = *param;
265
266 url.remove_prefix(str1.size());
267 break;
268 }
269 if (found)
270 {
271 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273
Manojkiran Edaefff2b52024-06-18 18:01:46 +0530274 BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700275 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700277 std::string piece(&c, 1);
278 if (!nodes[idx].children.contains(piece))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700280 unsigned newNodeIdx = newNode();
281 nodes[idx].children.emplace(piece, newNodeIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700283 idx = nodes[idx].children[piece];
284 url.remove_prefix(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700286 Node& node = nodes[idx];
287 if (node.ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700288 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700289 BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn);
Ed Tanousd9e89df2024-03-27 14:08:59 -0700290 throw std::runtime_error(
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700291 std::format("handler already exists for \"{}\"", urlIn));
Ed Tanous3174e4d2020-10-07 11:41:22 -0700292 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700293 node.ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700294 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700295
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700297 void debugNodePrint(Node& n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700299 std::string spaces(level, ' ');
300 if (n.stringParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700302 BMCWEB_LOG_DEBUG("{}<str>", spaces);
303 debugNodePrint(nodes[n.stringParamChild], level + 5);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700305 if (n.pathParamChild != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700307 BMCWEB_LOG_DEBUG("{} <path>", spaces);
308 debugNodePrint(nodes[n.pathParamChild], level + 6);
309 }
310 for (const Node::ChildMap::value_type& kv : n.children)
311 {
312 BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
313 debugNodePrint(nodes[kv.second], level + kv.first.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314 }
315 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700316
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 public:
318 void debugPrint()
319 {
Ed Tanous271584a2019-07-09 16:24:22 -0700320 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700322
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323 private:
Ed Tanousd9e89df2024-03-27 14:08:59 -0700324 const Node& head() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700326 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700328
Ed Tanousd9e89df2024-03-27 14:08:59 -0700329 Node& head()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700331 return nodes.front();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 }
333
334 unsigned newNode()
335 {
336 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -0700337 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
339
340 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700341};
342
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343class Router
344{
345 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -0700346 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700347
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 DynamicRule& newRuleDynamic(const std::string& rule)
349 {
350 std::unique_ptr<DynamicRule> ruleObject =
351 std::make_unique<DynamicRule>(rule);
352 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -0300353 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700354
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700356 }
357
Ed Tanousd9e89df2024-03-27 14:08:59 -0700358 template <uint64_t NumArgs>
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700359 auto& newRuleTagged(const std::string& rule)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700361 if constexpr (NumArgs == 0)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700362 {
363 using RuleT = TaggedRule<>;
364 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
365 RuleT* ptr = ruleObject.get();
366 allRules.emplace_back(std::move(ruleObject));
367 return *ptr;
368 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700369 else if constexpr (NumArgs == 1)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700370 {
371 using RuleT = TaggedRule<std::string>;
372 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
373 RuleT* ptr = ruleObject.get();
374 allRules.emplace_back(std::move(ruleObject));
375 return *ptr;
376 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700377 else if constexpr (NumArgs == 2)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700378 {
379 using RuleT = TaggedRule<std::string, std::string>;
380 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
381 RuleT* ptr = ruleObject.get();
382 allRules.emplace_back(std::move(ruleObject));
383 return *ptr;
384 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700385 else if constexpr (NumArgs == 3)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700386 {
387 using RuleT = TaggedRule<std::string, std::string, std::string>;
388 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
389 RuleT* ptr = ruleObject.get();
390 allRules.emplace_back(std::move(ruleObject));
391 return *ptr;
392 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700393 else if constexpr (NumArgs == 4)
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700394 {
395 using RuleT =
396 TaggedRule<std::string, std::string, std::string, std::string>;
397 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
398 RuleT* ptr = ruleObject.get();
399 allRules.emplace_back(std::move(ruleObject));
400 return *ptr;
401 }
402 else
403 {
404 using RuleT = TaggedRule<std::string, std::string, std::string,
405 std::string, std::string>;
406 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
407 RuleT* ptr = ruleObject.get();
408 allRules.emplace_back(std::move(ruleObject));
409 return *ptr;
410 }
Ed Tanousd9e89df2024-03-27 14:08:59 -0700411 static_assert(NumArgs <= 5, "Max number of args supported is 5");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700412 }
413
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700414 struct PerMethod
415 {
416 std::vector<BaseRule*> rules;
417 Trie trie;
418 // rule index 0 has special meaning; preallocate it to avoid
419 // duplication.
420 PerMethod() : rules(1) {}
421
422 void internalAdd(std::string_view rule, BaseRule* ruleObject)
423 {
424 rules.emplace_back(ruleObject);
425 trie.add(rule, static_cast<unsigned>(rules.size() - 1U));
426 // directory case:
427 // request to `/about' url matches `/about/' rule
428 if (rule.size() > 2 && rule.back() == '/')
429 {
430 trie.add(rule.substr(0, rule.size() - 1),
431 static_cast<unsigned>(rules.size() - 1));
432 }
433 }
434 };
435
Tanousf00032d2018-11-05 01:18:10 -0300436 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 {
Tanousf00032d2018-11-05 01:18:10 -0300438 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 {
Tanousf00032d2018-11-05 01:18:10 -0300440 return;
441 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700442 for (size_t method = 0; method <= maxVerbIndex; method++)
Tanousf00032d2018-11-05 01:18:10 -0300443 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700444 size_t methodBit = 1 << method;
Ed Tanouse662eae2022-01-25 10:39:19 -0800445 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -0300446 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700447 perMethods[method].internalAdd(rule, ruleObject);
Tanousf00032d2018-11-05 01:18:10 -0300448 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700450
451 if (ruleObject->isNotFound)
452 {
453 notFoundRoutes.internalAdd(rule, ruleObject);
454 }
455
456 if (ruleObject->isMethodNotAllowed)
457 {
458 methodNotAllowedRoutes.internalAdd(rule, ruleObject);
459 }
460
461 if (ruleObject->isUpgrade)
462 {
463 upgradeRoutes.internalAdd(rule, ruleObject);
464 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700465 }
466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 void validate()
468 {
Tanousf00032d2018-11-05 01:18:10 -0300469 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 {
471 if (rule)
472 {
Tanousf00032d2018-11-05 01:18:10 -0300473 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700475 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700477 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -0300479 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 }
481 }
Tanousf00032d2018-11-05 01:18:10 -0300482 for (PerMethod& perMethod : perMethods)
483 {
484 perMethod.trie.validate();
485 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700486 }
487
Ed Tanous44e45182022-07-26 16:47:23 -0700488 struct FindRoute
489 {
490 BaseRule* rule = nullptr;
Ed Tanous15a42df2023-02-09 18:08:23 -0800491 std::vector<std::string> params;
Ed Tanous44e45182022-07-26 16:47:23 -0700492 };
493
494 struct FindRouteResponse
495 {
496 std::string allowHeader;
497 FindRoute route;
498 };
499
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700500 static FindRoute findRouteByPerMethod(std::string_view url,
501 const PerMethod& perMethod)
Ed Tanous759cf102022-07-31 16:36:52 -0700502 {
503 FindRoute route;
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700504
Ed Tanousd9e89df2024-03-27 14:08:59 -0700505 Trie::FindResult found = perMethod.trie.find(url);
506 if (found.ruleIndex >= perMethod.rules.size())
Ed Tanous759cf102022-07-31 16:36:52 -0700507 {
508 throw std::runtime_error("Trie internal structure corrupted!");
509 }
510 // Found a 404 route, switch that in
Ed Tanousd9e89df2024-03-27 14:08:59 -0700511 if (found.ruleIndex != 0U)
Ed Tanous759cf102022-07-31 16:36:52 -0700512 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700513 route.rule = perMethod.rules[found.ruleIndex];
514 route.params = std::move(found.params);
Ed Tanous759cf102022-07-31 16:36:52 -0700515 }
516 return route;
517 }
518
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700519 FindRouteResponse findRoute(const Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -0700520 {
521 FindRouteResponse findRoute;
522
Ed Tanous44e45182022-07-26 16:47:23 -0700523 // Check to see if this url exists at any verb
524 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
525 perMethodIndex++)
526 {
527 // Make sure it's safe to deference the array at that index
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400528 static_assert(
529 maxVerbIndex < std::tuple_size_v<decltype(perMethods)>);
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700530 FindRoute route = findRouteByPerMethod(req.url().encoded_path(),
531 perMethods[perMethodIndex]);
Ed Tanous759cf102022-07-31 16:36:52 -0700532 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -0700533 {
534 continue;
535 }
536 if (!findRoute.allowHeader.empty())
537 {
538 findRoute.allowHeader += ", ";
539 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700540 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
541 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -0700542 }
Ed Tanous50bfc912024-07-29 14:20:50 -0700543
544 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
545 if (!verb)
546 {
547 return findRoute;
548 }
549 size_t reqMethodIndex = static_cast<size_t>(*verb);
550 if (reqMethodIndex >= perMethods.size())
551 {
552 return findRoute;
553 }
554
555 FindRoute route = findRouteByPerMethod(req.url().encoded_path(),
556 perMethods[reqMethodIndex]);
557 if (route.rule != nullptr)
558 {
559 findRoute.route = route;
560 }
561
Ed Tanous44e45182022-07-26 16:47:23 -0700562 return findRoute;
563 }
564
Ed Tanous1abe55e2018-09-05 08:30:59 -0700565 template <typename Adaptor>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700566 void handleUpgrade(const std::shared_ptr<Request>& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
568 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700570 PerMethod& perMethod = upgradeRoutes;
Tanousf00032d2018-11-05 01:18:10 -0300571 Trie& trie = perMethod.trie;
572 std::vector<BaseRule*>& rules = perMethod.rules;
573
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700574 Trie::FindResult found = trie.find(req->url().encoded_path());
Ed Tanousd9e89df2024-03-27 14:08:59 -0700575 unsigned ruleIndex = found.ruleIndex;
Ed Tanouse662eae2022-01-25 10:39:19 -0800576 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700578 BMCWEB_LOG_DEBUG("Cannot match rules {}",
579 req->url().encoded_path());
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530580 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 return;
582 }
583
584 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700585 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -0700587 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530589 BaseRule& rule = *rules[ruleIndex];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700591 BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}'", rule.rule);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530593 // TODO(ed) This should be able to use std::bind_front, but it doesn't
594 // appear to work with the std::move on adaptor.
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400595 validatePrivilege(
596 req, asyncResp, rule,
597 [req, &rule, asyncResp,
598 adaptor = std::forward<Adaptor>(adaptor)]() mutable {
599 rule.handleUpgrade(*req, asyncResp, std::move(adaptor));
600 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700601 }
602
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700603 void handle(const std::shared_ptr<Request>& req,
zhanghch058d1b46d2021-04-01 11:18:24 +0800604 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700606 FindRouteResponse foundRoute = findRoute(*req);
Ed Tanous44e45182022-07-26 16:47:23 -0700607
Ed Tanous759cf102022-07-31 16:36:52 -0700608 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -0700609 {
Ed Tanous759cf102022-07-31 16:36:52 -0700610 // Couldn't find a normal route with any verb, try looking for a 404
611 // route
612 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700613 {
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700614 foundRoute.route = findRouteByPerMethod(
615 req->url().encoded_path(), notFoundRoutes);
Ed Tanous759cf102022-07-31 16:36:52 -0700616 }
617 else
618 {
619 // See if we have a method not allowed (405) handler
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700620 foundRoute.route = findRouteByPerMethod(
621 req->url().encoded_path(), methodNotAllowedRoutes);
Ed Tanous44e45182022-07-26 16:47:23 -0700622 }
623 }
Ed Tanous759cf102022-07-31 16:36:52 -0700624
625 // Fill in the allow header if it's valid
626 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700627 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700628 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -0700629 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -0700630 }
Tanousf00032d2018-11-05 01:18:10 -0300631
Ed Tanous44e45182022-07-26 16:47:23 -0700632 // If we couldn't find a real route or a 404 route, return a generic
633 // response
634 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 {
Ed Tanous44e45182022-07-26 16:47:23 -0700636 if (foundRoute.allowHeader.empty())
637 {
638 asyncResp->res.result(boost::beast::http::status::not_found);
639 }
640 else
Ed Tanous2634dcd2019-03-26 09:28:06 -0700641 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700642 asyncResp->res.result(
643 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -0700644 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 return;
646 }
647
Ed Tanous44e45182022-07-26 16:47:23 -0700648 BaseRule& rule = *foundRoute.route.rule;
Ed Tanous15a42df2023-02-09 18:08:23 -0800649 std::vector<std::string> params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650
Ed Tanous62598e32023-07-17 17:06:25 -0700651 BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule,
Ed Tanous50bfc912024-07-29 14:20:50 -0700652 req->methodString(), rule.getMethods());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700654 if (req->session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +0000655 {
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700656 rule.handle(*req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +0000657 return;
658 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700659 validatePrivilege(
660 req, asyncResp, rule,
661 [req, asyncResp, &rule, params = std::move(params)]() {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400662 rule.handle(*req, asyncResp, params);
663 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700664 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700665
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 void debugPrint()
667 {
Ed Tanous271584a2019-07-09 16:24:22 -0700668 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -0300669 {
Ed Tanousd9e89df2024-03-27 14:08:59 -0700670 BMCWEB_LOG_DEBUG("{}", httpVerbToString(static_cast<HttpVerb>(i)));
Tanousf00032d2018-11-05 01:18:10 -0300671 perMethods[i].trie.debugPrint();
672 }
Ed Tanous3dac7492017-08-02 13:46:20 -0700673 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700674
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 std::vector<const std::string*> getRoutes(const std::string& parent)
676 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -0300678
679 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 {
Tanousf00032d2018-11-05 01:18:10 -0300681 std::vector<unsigned> x;
682 pm.trie.findRouteIndexes(parent, x);
683 for (unsigned index : x)
684 {
685 ret.push_back(&pm.rules[index]->rule);
686 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 }
688 return ret;
689 }
690
691 private:
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700692 std::array<PerMethod, static_cast<size_t>(HttpVerb::Max)> perMethods;
Ed Tanous888880a2020-08-24 13:48:50 -0700693
Ed Tanousa3b9eb92024-06-03 08:39:37 -0700694 PerMethod notFoundRoutes;
695 PerMethod upgradeRoutes;
696 PerMethod methodNotAllowedRoutes;
697
Tanousf00032d2018-11-05 01:18:10 -0300698 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700699};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700} // namespace crow