blob: 562abaf06c289ab170613824da3333a04dafc109 [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 Tanous04e438c2020-10-03 08:06:26 -07004#include "common.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -07005#include "dbus_privileges.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08006#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06007#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07008#include "http_request.hpp"
9#include "http_response.hpp"
10#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -030011#include "privileges.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070012#include "routing/baserule.hpp"
13#include "routing/dynamicrule.hpp"
14#include "routing/sserule.hpp"
15#include "routing/taggedrule.hpp"
16#include "routing/websocketrule.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053017#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070018#include "utility.hpp"
Ed Tanous3d183202023-03-10 09:21:58 -080019#include "utils/dbus_utils.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070020#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070021#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070022
Ed Tanous88a03c52022-03-14 10:16:07 -070023#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030024#include <boost/container/flat_map.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070025#include <boost/url/format.hpp>
Ed Tanous3d183202023-03-10 09:21:58 -080026#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027
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 Tanous7045c8d2017-04-03 10:04:37 -070034#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070035#include <utility>
36#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070037
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace crow
39{
Tanousf00032d2018-11-05 01:18:10 -030040
Ed Tanous1abe55e2018-09-05 08:30:59 -070041class Trie
42{
43 public:
44 struct Node
45 {
46 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -070047 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
48 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -080049 using ChildMap = boost::container::flat_map<
50 std::string, unsigned, std::less<>,
51 std::vector<std::pair<std::string, unsigned>>>;
52 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -070053
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 bool isSimpleNode() const
55 {
Ed Tanouse662eae2022-01-25 10:39:19 -080056 return ruleIndex == 0 &&
57 std::all_of(std::begin(paramChildrens),
58 std::end(paramChildrens),
59 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -070060 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070061 };
Ed Tanous7045c8d2017-04-03 10:04:37 -070062
Patrick Williams89492a12023-05-10 07:51:34 -050063 Trie() : nodes(1) {}
Ed Tanous1abe55e2018-09-05 08:30:59 -070064
65 private:
66 void optimizeNode(Node* node)
67 {
Ed Tanous271584a2019-07-09 16:24:22 -070068 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 {
Ed Tanousdbb59d42022-01-25 11:09:55 -080070 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -070071 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -070073 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 Node* child = &nodes[x];
75 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -070076 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -070078 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -070080 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -080082 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 {
84 Node* child = &nodes[kv.second];
85 if (!child->isSimpleNode())
86 {
87 mergeWithChild = false;
88 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -070089 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 }
91 if (mergeWithChild)
92 {
Ed Tanousa94ac612022-02-22 11:13:24 -080093 Node::ChildMap merged;
94 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 {
96 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -080097 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -030098 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 {
100 merged[kv.first + childKv.first] = childKv.second;
101 }
102 }
103 node->children = std::move(merged);
104 optimizeNode(node);
105 }
106 else
107 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800108 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
110 Node* child = &nodes[kv.second];
111 optimizeNode(child);
112 }
113 }
114 }
115
116 void optimize()
117 {
118 optimizeNode(head());
119 }
120
121 public:
122 void validate()
123 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 optimize();
125 }
126
Ed Tanous81ce6092020-12-17 16:54:55 +0000127 void findRouteIndexes(const std::string& reqUrl,
128 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300129 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
131 if (node == nullptr)
132 {
133 node = head();
134 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800135 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 {
137 const std::string& fragment = kv.first;
138 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000139 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140 {
141 if (child->ruleIndex != 0 && fragment != "/")
142 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000143 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000145 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700146 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 }
148 else
149 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000150 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 {
Ed Tanous271584a2019-07-09 16:24:22 -0700152 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000153 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700154 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 }
156 }
157 }
158 }
159
Ed Tanous15a42df2023-02-09 18:08:23 -0800160 std::pair<unsigned, std::vector<std::string>>
161 find(const std::string_view reqUrl, const Node* node = nullptr,
162 size_t pos = 0, std::vector<std::string>* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800164 std::vector<std::string> empty;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700166 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700168 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169
170 unsigned found{};
Ed Tanous15a42df2023-02-09 18:08:23 -0800171 std::vector<std::string> matchParams;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172
173 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700174 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700176 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000177 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700178 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700180 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181
182 auto updateFound =
Ed Tanous15a42df2023-02-09 18:08:23 -0800183 [&found,
184 &matchParams](std::pair<unsigned, std::vector<std::string>>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700185 if (ret.first != 0U && (found == 0U || found > ret.first))
186 {
187 found = ret.first;
188 matchParams = std::move(ret.second);
189 }
190 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700191
Ed Tanouse662eae2022-01-25 10:39:19 -0800192 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000194 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000195 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000197 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700198 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700200 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 }
202
203 if (epos != pos)
204 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800205 params->emplace_back(reqUrl.substr(pos, epos - pos));
206 std::pair<unsigned, std::vector<std::string>> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000207 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700208 &nodes[node->paramChildrens[static_cast<size_t>(
209 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000210 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211 updateFound(ret);
Ed Tanous15a42df2023-02-09 18:08:23 -0800212 params->pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 }
214 }
215
Ed Tanouse662eae2022-01-25 10:39:19 -0800216 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000218 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219
220 if (epos != pos)
221 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800222 params->emplace_back(reqUrl.substr(pos, epos - pos));
223 std::pair<unsigned, std::vector<std::string>> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000224 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700225 &nodes[node->paramChildrens[static_cast<size_t>(
226 ParamType::PATH)]],
227 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 updateFound(ret);
Ed Tanous15a42df2023-02-09 18:08:23 -0800229 params->pop_back();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 }
231 }
232
Ed Tanousa94ac612022-02-22 11:13:24 -0800233 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 {
235 const std::string& fragment = kv.first;
236 const Node* child = &nodes[kv.second];
237
Ed Tanous81ce6092020-12-17 16:54:55 +0000238 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700239 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800240 std::pair<unsigned, std::vector<std::string>> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000241 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 updateFound(ret);
243 }
244 }
245
246 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700247 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248
249 void add(const std::string& url, unsigned ruleIndex)
250 {
Ed Tanous271584a2019-07-09 16:24:22 -0700251 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252
253 for (unsigned i = 0; i < url.size(); i++)
254 {
255 char c = url[i];
256 if (c == '<')
257 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800258 constexpr static std::array<
259 std::pair<ParamType, std::string_view>, 3>
Tanousf00032d2018-11-05 01:18:10 -0300260 paramTraits = {{
Tanousf00032d2018-11-05 01:18:10 -0300261 {ParamType::STRING, "<str>"},
262 {ParamType::STRING, "<string>"},
263 {ParamType::PATH, "<path>"},
264 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265
Ed Tanous15a42df2023-02-09 18:08:23 -0800266 for (const std::pair<ParamType, std::string_view>& x :
267 paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 {
Tanousf00032d2018-11-05 01:18:10 -0300269 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 {
Ed Tanous271584a2019-07-09 16:24:22 -0700271 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800272 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 {
Tanousf00032d2018-11-05 01:18:10 -0300274 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700275 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 }
Ed Tanous271584a2019-07-09 16:24:22 -0700277 idx = nodes[idx].paramChildrens[index];
278 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 break;
280 }
281 }
282
283 i--;
284 }
285 else
286 {
287 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -0800288 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 {
Tanousf00032d2018-11-05 01:18:10 -0300290 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 nodes[idx].children.emplace(piece, newNodeIdx);
292 }
293 idx = nodes[idx].children[piece];
294 }
295 }
Ed Tanouse662eae2022-01-25 10:39:19 -0800296 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700297 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700299 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700301 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700302
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700304 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 {
Ed Tanous271584a2019-07-09 16:24:22 -0700306 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800308 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 {
Ed Tanous62598e32023-07-17 17:06:25 -0700310 BMCWEB_LOG_DEBUG(
311 "{}({}{}",
312 std::string(2U * level,
313 ' ') /*, n->paramChildrens[i], ") "*/);
Ed Tanous271584a2019-07-09 16:24:22 -0700314 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 case ParamType::STRING:
Ed Tanous62598e32023-07-17 17:06:25 -0700317 BMCWEB_LOG_DEBUG("<str>");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 break;
319 case ParamType::PATH:
Ed Tanous62598e32023-07-17 17:06:25 -0700320 BMCWEB_LOG_DEBUG("<path>");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 break;
Ed Tanous23a21a12020-07-25 04:45:05 +0000322 case ParamType::MAX:
Ed Tanous62598e32023-07-17 17:06:25 -0700323 BMCWEB_LOG_DEBUG("<ERROR>");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 break;
325 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700326
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
328 }
329 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800330 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 {
Ed Tanous62598e32023-07-17 17:06:25 -0700332 BMCWEB_LOG_DEBUG("{}({}{}{}",
333 std::string(2U * level, ' ') /*, kv.second, ") "*/,
334 kv.first);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700335 debugNodePrint(&nodes[kv.second], level + 1);
336 }
337 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700338
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339 public:
340 void debugPrint()
341 {
Ed Tanous271584a2019-07-09 16:24:22 -0700342 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700344
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 private:
346 const Node* head() const
347 {
348 return &nodes.front();
349 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 Node* head()
352 {
353 return &nodes.front();
354 }
355
356 unsigned newNode()
357 {
358 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -0700359 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 }
361
362 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700363};
364
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365class Router
366{
367 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -0700368 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700369
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 DynamicRule& newRuleDynamic(const std::string& rule)
371 {
372 std::unique_ptr<DynamicRule> ruleObject =
373 std::make_unique<DynamicRule>(rule);
374 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -0300375 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700376
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700378 }
379
Ed Tanous1abe55e2018-09-05 08:30:59 -0700380 template <uint64_t N>
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700381 auto& newRuleTagged(const std::string& rule)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 {
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700383 constexpr size_t numArgs = utility::numArgsFromTag(N);
384 if constexpr (numArgs == 0)
385 {
386 using RuleT = TaggedRule<>;
387 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
388 RuleT* ptr = ruleObject.get();
389 allRules.emplace_back(std::move(ruleObject));
390 return *ptr;
391 }
392 else if constexpr (numArgs == 1)
393 {
394 using RuleT = TaggedRule<std::string>;
395 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
396 RuleT* ptr = ruleObject.get();
397 allRules.emplace_back(std::move(ruleObject));
398 return *ptr;
399 }
400 else if constexpr (numArgs == 2)
401 {
402 using RuleT = TaggedRule<std::string, std::string>;
403 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
404 RuleT* ptr = ruleObject.get();
405 allRules.emplace_back(std::move(ruleObject));
406 return *ptr;
407 }
408 else if constexpr (numArgs == 3)
409 {
410 using RuleT = TaggedRule<std::string, std::string, std::string>;
411 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
412 RuleT* ptr = ruleObject.get();
413 allRules.emplace_back(std::move(ruleObject));
414 return *ptr;
415 }
416 else if constexpr (numArgs == 4)
417 {
418 using RuleT =
419 TaggedRule<std::string, std::string, std::string, std::string>;
420 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
421 RuleT* ptr = ruleObject.get();
422 allRules.emplace_back(std::move(ruleObject));
423 return *ptr;
424 }
425 else
426 {
427 using RuleT = TaggedRule<std::string, std::string, std::string,
428 std::string, std::string>;
429 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
430 RuleT* ptr = ruleObject.get();
431 allRules.emplace_back(std::move(ruleObject));
432 return *ptr;
433 }
Ed Tanous8983cf52024-03-20 09:30:12 -0700434 static_assert(numArgs <= 5, "Max number of args supported is 5");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700435 }
436
Tanousf00032d2018-11-05 01:18:10 -0300437 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
Tanousf00032d2018-11-05 01:18:10 -0300439 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 {
Tanousf00032d2018-11-05 01:18:10 -0300441 return;
442 }
Ed Tanous759cf102022-07-31 16:36:52 -0700443 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -0700444 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -0300445 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800446 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -0300447 {
448 perMethods[method].rules.emplace_back(ruleObject);
449 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -0700450 rule, static_cast<unsigned>(
451 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -0300452 // directory case:
453 // request to `/about' url matches `/about/' rule
454 if (rule.size() > 2 && rule.back() == '/')
455 {
456 perMethods[method].trie.add(
457 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -0700458 static_cast<unsigned>(perMethods[method].rules.size() -
459 1));
Tanousf00032d2018-11-05 01:18:10 -0300460 }
461 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700463 }
464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 void validate()
466 {
Tanousf00032d2018-11-05 01:18:10 -0300467 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 {
469 if (rule)
470 {
Tanousf00032d2018-11-05 01:18:10 -0300471 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700472 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700473 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700475 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -0300477 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 }
479 }
Tanousf00032d2018-11-05 01:18:10 -0300480 for (PerMethod& perMethod : perMethods)
481 {
482 perMethod.trie.validate();
483 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700484 }
485
Ed Tanous44e45182022-07-26 16:47:23 -0700486 struct FindRoute
487 {
488 BaseRule* rule = nullptr;
Ed Tanous15a42df2023-02-09 18:08:23 -0800489 std::vector<std::string> params;
Ed Tanous44e45182022-07-26 16:47:23 -0700490 };
491
492 struct FindRouteResponse
493 {
494 std::string allowHeader;
495 FindRoute route;
496 };
497
Ed Tanous759cf102022-07-31 16:36:52 -0700498 FindRoute findRouteByIndex(std::string_view url, size_t index) const
499 {
500 FindRoute route;
501 if (index >= perMethods.size())
502 {
Ed Tanous62598e32023-07-17 17:06:25 -0700503 BMCWEB_LOG_CRITICAL("Bad index???");
Ed Tanous759cf102022-07-31 16:36:52 -0700504 return route;
505 }
506 const PerMethod& perMethod = perMethods[index];
Ed Tanous15a42df2023-02-09 18:08:23 -0800507 std::pair<unsigned, std::vector<std::string>> found =
508 perMethod.trie.find(url);
Ed Tanous759cf102022-07-31 16:36:52 -0700509 if (found.first >= perMethod.rules.size())
510 {
511 throw std::runtime_error("Trie internal structure corrupted!");
512 }
513 // Found a 404 route, switch that in
514 if (found.first != 0U)
515 {
516 route.rule = perMethod.rules[found.first];
517 route.params = std::move(found.second);
518 }
519 return route;
520 }
521
522 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -0700523 {
524 FindRouteResponse findRoute;
525
Ed Tanous2c9efc32022-07-31 22:08:26 -0700526 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
527 if (!verb)
528 {
529 return findRoute;
530 }
531 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -0700532 // Check to see if this url exists at any verb
533 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
534 perMethodIndex++)
535 {
536 // Make sure it's safe to deference the array at that index
537 static_assert(maxVerbIndex <
538 std::tuple_size_v<decltype(perMethods)>);
Patrick Williams89492a12023-05-10 07:51:34 -0500539 FindRoute route = findRouteByIndex(req.url().encoded_path(),
540 perMethodIndex);
Ed Tanous759cf102022-07-31 16:36:52 -0700541 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -0700542 {
543 continue;
544 }
545 if (!findRoute.allowHeader.empty())
546 {
547 findRoute.allowHeader += ", ";
548 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700549 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
550 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -0700551 if (perMethodIndex == reqMethodIndex)
552 {
Ed Tanous759cf102022-07-31 16:36:52 -0700553 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -0700554 }
555 }
556 return findRoute;
557 }
558
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 template <typename Adaptor>
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530560 void handleUpgrade(Request& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530561 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
562 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
Ed Tanous2c9efc32022-07-31 22:08:26 -0700564 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
565 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -0700566 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530567 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -0300568 return;
Ed Tanous888880a2020-08-24 13:48:50 -0700569 }
Ed Tanous2c9efc32022-07-31 22:08:26 -0700570 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -0300571 Trie& trie = perMethod.trie;
572 std::vector<BaseRule*>& rules = perMethod.rules;
573
Ed Tanous15a42df2023-02-09 18:08:23 -0800574 const std::pair<unsigned, std::vector<std::string>>& found =
Ed Tanous39662a32023-02-06 15:09:46 -0800575 trie.find(req.url().encoded_path());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -0800577 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
Ed Tanous62598e32023-07-17 17:06:25 -0700579 BMCWEB_LOG_DEBUG("Cannot match rules {}", 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];
590 size_t methods = rule.getMethods();
591 if ((methods & (1U << static_cast<size_t>(*verb))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 {
Ed Tanous62598e32023-07-17 17:06:25 -0700593 BMCWEB_LOG_DEBUG(
594 "Rule found but method mismatch: {} with {}({}) / {}",
595 req.url().encoded_path(), req.methodString(),
596 static_cast<uint32_t>(*verb), methods);
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530597 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 return;
599 }
600
Ed Tanous62598e32023-07-17 17:06:25 -0700601 BMCWEB_LOG_DEBUG("Matched rule (upgrade) '{}' {} / {}", rule.rule,
602 static_cast<uint32_t>(*verb), methods);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530604 // TODO(ed) This should be able to use std::bind_front, but it doesn't
605 // appear to work with the std::move on adaptor.
Ed Tanous915d2d42023-03-15 13:09:34 -0700606 validatePrivilege(
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700607 req, asyncResp, rule,
Ed Tanous915d2d42023-03-15 13:09:34 -0700608 [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))](
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700609 Request& thisReq) mutable {
Ed Tanous915d2d42023-03-15 13:09:34 -0700610 rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor));
Patrick Williams5a39f772023-10-20 11:20:21 -0500611 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700612 }
613
zhanghch058d1b46d2021-04-01 11:18:24 +0800614 void handle(Request& req,
615 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
Ed Tanous2c9efc32022-07-31 22:08:26 -0700617 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
618 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -0700619 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800620 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -0300621 return;
Ed Tanous888880a2020-08-24 13:48:50 -0700622 }
Ed Tanous44e45182022-07-26 16:47:23 -0700623
624 FindRouteResponse foundRoute = findRoute(req);
625
Ed Tanous759cf102022-07-31 16:36:52 -0700626 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -0700627 {
Ed Tanous759cf102022-07-31 16:36:52 -0700628 // Couldn't find a normal route with any verb, try looking for a 404
629 // route
630 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700631 {
Patrick Williams89492a12023-05-10 07:51:34 -0500632 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
633 notFoundIndex);
Ed Tanous759cf102022-07-31 16:36:52 -0700634 }
635 else
636 {
637 // See if we have a method not allowed (405) handler
Ed Tanous39662a32023-02-06 15:09:46 -0800638 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
639 methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -0700640 }
641 }
Ed Tanous759cf102022-07-31 16:36:52 -0700642
643 // Fill in the allow header if it's valid
644 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -0700645 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700646 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -0700647 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -0700648 }
Tanousf00032d2018-11-05 01:18:10 -0300649
Ed Tanous44e45182022-07-26 16:47:23 -0700650 // If we couldn't find a real route or a 404 route, return a generic
651 // response
652 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 {
Ed Tanous44e45182022-07-26 16:47:23 -0700654 if (foundRoute.allowHeader.empty())
655 {
656 asyncResp->res.result(boost::beast::http::status::not_found);
657 }
658 else
Ed Tanous2634dcd2019-03-26 09:28:06 -0700659 {
Ed Tanous88a03c52022-03-14 10:16:07 -0700660 asyncResp->res.result(
661 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -0700662 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 return;
664 }
665
Ed Tanous44e45182022-07-26 16:47:23 -0700666 BaseRule& rule = *foundRoute.route.rule;
Ed Tanous15a42df2023-02-09 18:08:23 -0800667 std::vector<std::string> params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668
Ed Tanous62598e32023-07-17 17:06:25 -0700669 BMCWEB_LOG_DEBUG("Matched rule '{}' {} / {}", rule.rule,
670 static_cast<uint32_t>(*verb), rule.getMethods());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -0600672 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +0000673 {
Ed Tanous44e45182022-07-26 16:47:23 -0700674 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +0000675 return;
676 }
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700677 validatePrivilege(req, asyncResp, rule,
678 [&rule, asyncResp, params](Request& thisReq) mutable {
Ed Tanous915d2d42023-03-15 13:09:34 -0700679 rule.handle(thisReq, asyncResp, params);
Jonathan Domand3c8ce62023-03-21 18:17:06 -0700680 });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700681 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700682
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 void debugPrint()
684 {
Ed Tanous271584a2019-07-09 16:24:22 -0700685 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -0300686 {
Ed Tanous62598e32023-07-17 17:06:25 -0700687 BMCWEB_LOG_DEBUG("{}",
688 boost::beast::http::to_string(
689 static_cast<boost::beast::http::verb>(i)));
Tanousf00032d2018-11-05 01:18:10 -0300690 perMethods[i].trie.debugPrint();
691 }
Ed Tanous3dac7492017-08-02 13:46:20 -0700692 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700693
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 std::vector<const std::string*> getRoutes(const std::string& parent)
695 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -0300697
698 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 {
Tanousf00032d2018-11-05 01:18:10 -0300700 std::vector<unsigned> x;
701 pm.trie.findRouteIndexes(parent, x);
702 for (unsigned index : x)
703 {
704 ret.push_back(&pm.rules[index]->rule);
705 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 }
707 return ret;
708 }
709
710 private:
Tanousf00032d2018-11-05 01:18:10 -0300711 struct PerMethod
712 {
713 std::vector<BaseRule*> rules;
714 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -0700715 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -0300716 // duplication.
Patrick Williams89492a12023-05-10 07:51:34 -0500717 PerMethod() : rules(1) {}
Tanousf00032d2018-11-05 01:18:10 -0300718 };
Ed Tanous888880a2020-08-24 13:48:50 -0700719
Ed Tanous759cf102022-07-31 16:36:52 -0700720 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -0300721 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700722};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723} // namespace crow