blob: 8852b304ab31682424a34ed5b19939897156b23a [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "common.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08004#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06005#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include "http_request.hpp"
7#include "http_response.hpp"
8#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -03009#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053010#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "utility.hpp"
12#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070013
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020014#include <async_resp.hpp>
Ed Tanous88a03c52022-03-14 10:16:07 -070015#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030016#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017
Ed Tanouse0d918b2018-03-27 17:41:04 -070018#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070019#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070020#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070021#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070022#include <memory>
23#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <utility>
25#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Tanousf00032d2018-11-05 01:18:10 -030029
Ed Tanous44e45182022-07-26 16:47:23 -070030// Note, this is an imperfect abstraction. There are a lot of verbs that we
31// use memory for, but are basically unused by most implementations.
32// Ideally we would have a list of verbs that we do use, and only index in
33// to a smaller array of those, but that would require a translation from
34// boost::beast::http::verb, to the bmcweb index.
35static constexpr size_t maxVerbIndex =
36 static_cast<size_t>(boost::beast::http::verb::patch);
37
38// MaxVerb + 1 is designated as the "not found" verb. It is done this way
39// to keep the BaseRule as a single bitfield (thus keeping the struct small)
40// while still having a way to declare a route a "not found" route.
41static constexpr const size_t notFoundIndex = maxVerbIndex + 1;
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043class BaseRule
44{
45 public:
Ed Tanous4e23a442022-06-06 09:57:26 -070046 explicit BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070048
Ed Tanous0c0084a2019-10-24 15:57:51 -070049 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070050
Ed Tanousecd6a3a2022-01-07 09:18:40 -080051 BaseRule(const BaseRule&) = delete;
52 BaseRule(BaseRule&&) = delete;
53 BaseRule& operator=(const BaseRule&) = delete;
54 BaseRule& operator=(const BaseRule&&) = delete;
55
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 virtual void validate() = 0;
57 std::unique_ptr<BaseRule> upgrade()
58 {
59 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070060 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070062 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 return {};
64 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070065
Ed Tanous104f09c2022-01-25 09:56:04 -080066 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080067 const std::shared_ptr<bmcweb::AsyncResp>&,
68 const RoutingParams&) = 0;
Ed Tanous104f09c2022-01-25 09:56:04 -080069 virtual void handleUpgrade(const Request& /*req*/, Response& res,
70 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070072 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 res.end();
74 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070075#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080076 virtual void handleUpgrade(
77 const Request& /*req*/, Response& res,
78 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070080 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 res.end();
82 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070083#endif
84
Ed Tanous9eb808c2022-01-25 10:19:23 -080085 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070086 {
87 return methodsBitfield;
88 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070089
Tanousf00032d2018-11-05 01:18:10 -030090 bool checkPrivileges(const redfish::Privileges& userPrivileges)
91 {
92 // If there are no privileges assigned, assume no privileges
93 // required
94 if (privilegesSet.empty())
95 {
96 return true;
97 }
98
99 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
100 {
101 if (userPrivileges.isSupersetOf(requiredPrivileges))
102 {
103 return true;
104 }
105 }
106 return false;
107 }
108
Ed Tanous271584a2019-07-09 16:24:22 -0700109 size_t methodsBitfield{
110 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous44e45182022-07-26 16:47:23 -0700111 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
112 notFoundIndex,
113 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114
Tanousf00032d2018-11-05 01:18:10 -0300115 std::vector<redfish::Privileges> privilegesSet;
116
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 std::string rule;
118 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700119
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700121
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500123 template <typename T>
124 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700125};
126
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127namespace detail
128{
129namespace routing_handler_call_helper
130{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500131template <typename T, int Pos>
132struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133{
134 using type = T;
135 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700136};
137
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500138template <typename H1>
139struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140{
141 H1& handler;
142 const RoutingParams& params;
143 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800144 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700145};
146
147template <typename F, int NInt, int NUint, int NDouble, int NString,
148 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700151
152template <typename F, int NInt, int NUint, int NDouble, int NString,
153 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700154struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 black_magic::S<Args2...>>
156{
157 void operator()(F cparams)
158 {
159 using pushed = typename black_magic::S<Args2...>::template push_back<
160 CallPair<int64_t, NInt>>;
161 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
162 pushed>()(cparams);
163 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700164};
165
166template <typename F, int NInt, int NUint, int NDouble, int NString,
167 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700168struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
170{
171 void operator()(F cparams)
172 {
173 using pushed = typename black_magic::S<Args2...>::template push_back<
174 CallPair<uint64_t, NUint>>;
175 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
176 pushed>()(cparams);
177 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700178};
179
180template <typename F, int NInt, int NUint, int NDouble, int NString,
181 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700182struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 black_magic::S<Args2...>>
184{
185 void operator()(F cparams)
186 {
187 using pushed = typename black_magic::S<Args2...>::template push_back<
188 CallPair<double, NDouble>>;
189 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
190 pushed>()(cparams);
191 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700192};
193
194template <typename F, int NInt, int NUint, int NDouble, int NString,
195 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700196struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
198{
199 void operator()(F cparams)
200 {
201 using pushed = typename black_magic::S<Args2...>::template push_back<
202 CallPair<std::string, NString>>;
203 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
204 pushed>()(cparams);
205 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700206};
207
208template <typename F, int NInt, int NUint, int NDouble, int NString,
209 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700210struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211 black_magic::S<Args1...>>
212{
213 void operator()(F cparams)
214 {
215 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800216 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 cparams.params.template get<typename Args1::type>(Args1::pos)...);
218 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700219};
220
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221template <typename Func, typename... ArgsWrapped>
222struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223{
224 template <typename... Args>
225 void set(
226 Func f,
227 typename std::enable_if<
228 !std::is_same<
229 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
230 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800231 int>::type /*enable*/
232 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800234 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800235 const Request&,
236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
237 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700238 }
239
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500240 template <typename Req, typename... Args>
241 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700243 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700245
zhanghch058d1b46d2021-04-01 11:18:24 +0800246 void operator()(const Request& req,
247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
248 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800250 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700252
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 Func f;
254 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700255
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 template <typename... Args>
257 void set(
258 Func f,
259 typename std::enable_if<
260 std::is_same<
261 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
262 const Request&>::value &&
263 !std::is_same<typename std::tuple_element<
264 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800265 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800266 int>::type /*enable*/
267 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 {
269 handler = ReqHandlerWrapper<Args...>(std::move(f));
270 /*handler = (
271 [f = std::move(f)]
272 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700273 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 res.end();
275 });*/
276 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700277
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278 template <typename... Args>
279 void set(
280 Func f,
281 typename std::enable_if<
282 std::is_same<
283 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
284 const Request&>::value &&
285 std::is_same<typename std::tuple_element<
286 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800287 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800288 int>::type /*enable*/
289 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 {
291 handler = std::move(f);
292 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700293
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500294 template <typename... Args>
295 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800297 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800298 const crow::Request& /*req*/,
299 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700301 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700303
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 template <typename... Args>
305 struct HandlerTypeHelper<const Request&, Args...>
306 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800307 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800308 const crow::Request& /*req*/,
309 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700311 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700313
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800315 struct HandlerTypeHelper<const Request&,
316 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800318 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800319 const crow::Request& /*req*/,
320 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700322 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323 };
324
325 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
326
zhanghch058d1b46d2021-04-01 11:18:24 +0800327 void operator()(const Request& req,
328 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 const RoutingParams& params)
330 {
331 detail::routing_handler_call_helper::Call<
332 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
333 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
334 black_magic::S<>>()(
335 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800336 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700338};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339} // namespace routing_handler_call_helper
340} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700341
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342class WebSocketRule : public BaseRule
343{
344 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700345
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700347 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500348 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700349
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500351 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700352
Ed Tanous104f09c2022-01-25 09:56:04 -0800353 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800354 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800355 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800357 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700359
Ed Tanous104f09c2022-01-25 09:56:04 -0800360 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800361 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362 {
Nan Zhou93c02022022-02-24 18:21:07 -0800363 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530364 std::shared_ptr<
365 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
366 myConnection = std::make_shared<
367 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000368 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530369 closeHandler, errorHandler);
370 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 }
372#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -0800373 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800374 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
375 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376 {
Nan Zhou93c02022022-02-24 18:21:07 -0800377 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800378 std::shared_ptr<crow::websocket::ConnectionImpl<
379 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
380 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
381 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000382 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800383 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 myConnection->start();
385 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700386#endif
387
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 template <typename Func>
389 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700390 {
391 openHandler = f;
392 return *this;
393 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700394
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500395 template <typename Func>
396 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397 {
398 messageHandler = f;
399 return *this;
400 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700401
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500402 template <typename Func>
403 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
405 closeHandler = f;
406 return *this;
407 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700408
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500409 template <typename Func>
410 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 {
412 errorHandler = f;
413 return *this;
414 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700415
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 protected:
zhanghch0577726382021-10-21 14:07:57 +0800417 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
419 messageHandler;
420 std::function<void(crow::websocket::Connection&, const std::string&)>
421 closeHandler;
422 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700423};
424
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500425template <typename T>
426struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700427{
428 using self_t = T;
429 WebSocketRule& websocket()
430 {
Ed Tanous271584a2019-07-09 16:24:22 -0700431 self_t* self = static_cast<self_t*>(this);
432 WebSocketRule* p = new WebSocketRule(self->rule);
433 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 return *p;
435 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700436
Ed Tanousf23b7292020-10-15 09:41:17 -0700437 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
Ed Tanous271584a2019-07-09 16:24:22 -0700439 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700440 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700441 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700443
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 self_t& methods(boost::beast::http::verb method)
445 {
Ed Tanous271584a2019-07-09 16:24:22 -0700446 self_t* self = static_cast<self_t*>(this);
447 self->methodsBitfield = 1U << static_cast<size_t>(method);
448 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700450
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000452 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700453 {
Ed Tanous271584a2019-07-09 16:24:22 -0700454 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000455 methods(argsMethod...);
Ed Tanous271584a2019-07-09 16:24:22 -0700456 self->methodsBitfield |= 1U << static_cast<size_t>(method);
457 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 }
Tanousf00032d2018-11-05 01:18:10 -0300459
Ed Tanous44e45182022-07-26 16:47:23 -0700460 self_t& notFound()
461 {
462 self_t* self = static_cast<self_t*>(this);
463 self->methodsBitfield = 1U << notFoundIndex;
464 return *self;
465 }
466
Ed Tanous432a8902021-06-14 15:28:56 -0700467 self_t& privileges(
468 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300469 {
Ed Tanous271584a2019-07-09 16:24:22 -0700470 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700471 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300472 {
Ed Tanous271584a2019-07-09 16:24:22 -0700473 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300474 }
Ed Tanous271584a2019-07-09 16:24:22 -0700475 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300476 }
Ed Tanoused398212021-06-09 17:05:54 -0700477
478 template <size_t N, typename... MethodArgs>
479 self_t& privileges(const std::array<redfish::Privileges, N>& p)
480 {
481 self_t* self = static_cast<self_t*>(this);
482 for (const redfish::Privileges& privilege : p)
483 {
484 self->privilegesSet.emplace_back(privilege);
485 }
486 return *self;
487 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700488};
489
Ed Tanous1abe55e2018-09-05 08:30:59 -0700490class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
491{
492 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700493 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500494 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700495
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 void validate() override
497 {
498 if (!erasedHandler)
499 {
500 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
501 "no handler for url " + rule);
502 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700503 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700504
zhanghch058d1b46d2021-04-01 11:18:24 +0800505 void handle(const Request& req,
506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 const RoutingParams& params) override
508 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800509 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700511
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500512 template <typename Func>
513 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 {
Ed Tanousc867a832022-03-10 14:17:00 -0800515 using boost::callable_traits::args_t;
516 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
517 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
518 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 }
520
521 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500522 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 // enable_if Arg1 != request
524
525 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800526 std::function<void(const Request&,
527 const std::shared_ptr<bmcweb::AsyncResp>&,
528 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800529 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 {
Ed Tanousc867a832022-03-10 14:17:00 -0800531 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532
533 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700534 black_magic::getParameterTag(rule.c_str()),
535 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 typename function_t::template arg<Indices>...>::value))
537 {
538 throw std::runtime_error("routeDynamic: Handler type is mismatched "
539 "with URL parameters: " +
540 rule);
541 }
542 auto ret = detail::routing_handler_call_helper::Wrapped<
543 Func, typename function_t::template arg<Indices>...>();
544 ret.template set<typename function_t::template arg<Indices>...>(
545 std::move(f));
546 return ret;
547 }
548
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500549 template <typename Func>
550 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
552 nameStr = std::move(name);
553 (*this).template operator()<Func>(std::forward(f));
554 }
555
556 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800557 std::function<void(const Request&,
558 const std::shared_ptr<bmcweb::AsyncResp>&,
559 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700561};
562
563template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500564class TaggedRule :
565 public BaseRule,
566 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567{
568 public:
569 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700570
Ed Tanous4e23a442022-06-06 09:57:26 -0700571 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500572 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700573
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 void validate() override
575 {
576 if (!handler)
577 {
578 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
579 "no handler for url " + rule);
580 }
581 }
582
583 template <typename Func>
584 typename std::enable_if<
585 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
586 void>::type
587 operator()(Func&& f)
588 {
589 static_assert(
590 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
591 black_magic::CallHelper<
592 Func, black_magic::S<crow::Request, Args...>>::value,
593 "Handler type is mismatched with URL parameters");
594 static_assert(
595 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
596 "Handler function cannot have void return type; valid return "
597 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500598 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800600 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800601 const Request&,
602 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
603 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 }
605
606 template <typename Func>
607 typename std::enable_if<
608 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700609 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700610 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700611 void>::type
612 operator()(Func&& f)
613 {
614 static_assert(
615 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
616 black_magic::CallHelper<
617 Func, black_magic::S<crow::Request, Args...>>::value,
618 "Handler type is mismatched with URL parameters");
619 static_assert(
620 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
621 std::declval<Args>()...))>::value,
622 "Handler function cannot have void return type; valid return "
623 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500624 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700625
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800626 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800627 const crow::Request& req,
628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
629 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700631
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 template <typename Func>
633 typename std::enable_if<
634 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
635 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700636 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 void>::type
638 operator()(Func&& f)
639 {
640 static_assert(
641 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
642 black_magic::CallHelper<
643 Func, black_magic::S<crow::Request, Args...>>::value ||
644 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800645 Func, black_magic::S<crow::Request,
646 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 Args...>>::value,
648 "Handler type is mismatched with URL parameters");
649 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800650 std::is_same<
651 void,
652 decltype(f(std::declval<crow::Request>(),
653 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
654 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300655 "Handler function with response argument should have void "
656 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700658
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800659 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700661
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500662 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700663 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700665 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 (*this).template operator()<Func>(std::forward(f));
667 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700668
zhanghch058d1b46d2021-04-01 11:18:24 +0800669 void handle(const Request& req,
670 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 const RoutingParams& params) override
672 {
673 detail::routing_handler_call_helper::Call<
674 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
675 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
676 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800677 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700679
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800681 std::function<void(const crow::Request&,
682 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
683 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700684};
685
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686class Trie
687{
688 public:
689 struct Node
690 {
691 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700692 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
693 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800694 using ChildMap = boost::container::flat_map<
695 std::string, unsigned, std::less<>,
696 std::vector<std::pair<std::string, unsigned>>>;
697 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700698
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 bool isSimpleNode() const
700 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800701 return ruleIndex == 0 &&
702 std::all_of(std::begin(paramChildrens),
703 std::end(paramChildrens),
704 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700705 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700706 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700707
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500709 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710
711 private:
712 void optimizeNode(Node* node)
713 {
Ed Tanous271584a2019-07-09 16:24:22 -0700714 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800716 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700717 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700719 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 Node* child = &nodes[x];
721 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700722 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700724 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700726 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800728 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 {
730 Node* child = &nodes[kv.second];
731 if (!child->isSimpleNode())
732 {
733 mergeWithChild = false;
734 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700735 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 }
737 if (mergeWithChild)
738 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800739 Node::ChildMap merged;
740 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 {
742 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800743 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300744 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
746 merged[kv.first + childKv.first] = childKv.second;
747 }
748 }
749 node->children = std::move(merged);
750 optimizeNode(node);
751 }
752 else
753 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800754 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 {
756 Node* child = &nodes[kv.second];
757 optimizeNode(child);
758 }
759 }
760 }
761
762 void optimize()
763 {
764 optimizeNode(head());
765 }
766
767 public:
768 void validate()
769 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 optimize();
771 }
772
Ed Tanous81ce6092020-12-17 16:54:55 +0000773 void findRouteIndexes(const std::string& reqUrl,
774 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300775 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 {
777 if (node == nullptr)
778 {
779 node = head();
780 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800781 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 {
783 const std::string& fragment = kv.first;
784 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000785 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 {
787 if (child->ruleIndex != 0 && fragment != "/")
788 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000789 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000791 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700792 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 }
794 else
795 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000796 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 {
Ed Tanous271584a2019-07-09 16:24:22 -0700798 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000799 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700800 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 }
802 }
803 }
804 }
805
806 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000807 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700808 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 {
810 RoutingParams empty;
811 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700812 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700814 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815
816 unsigned found{};
817 RoutingParams matchParams;
818
819 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700820 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700822 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000823 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700824 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700826 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827
828 auto updateFound =
829 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700830 if (ret.first != 0U && (found == 0U || found > ret.first))
831 {
832 found = ret.first;
833 matchParams = std::move(ret.second);
834 }
835 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836
Ed Tanouse662eae2022-01-25 10:39:19 -0800837 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000839 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700840 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
841 {
Ed Tanous543f4402022-01-06 13:12:53 -0800842 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 errno = 0;
844 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000845 std::strtoll(reqUrl.data() + pos, &eptr, 10);
846 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 {
848 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000849 std::pair<unsigned, RoutingParams> ret =
850 find(reqUrl,
851 &nodes[node->paramChildrens[static_cast<size_t>(
852 ParamType::INT)]],
853 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 updateFound(ret);
855 params->intParams.pop_back();
856 }
857 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700858 }
859
Ed Tanouse662eae2022-01-25 10:39:19 -0800860 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000862 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 if ((c >= '0' && c <= '9') || c == '+')
864 {
Ed Tanous543f4402022-01-06 13:12:53 -0800865 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 errno = 0;
867 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000868 std::strtoull(reqUrl.data() + pos, &eptr, 10);
869 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 {
871 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000872 std::pair<unsigned, RoutingParams> ret =
873 find(reqUrl,
874 &nodes[node->paramChildrens[static_cast<size_t>(
875 ParamType::UINT)]],
876 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877 updateFound(ret);
878 params->uintParams.pop_back();
879 }
880 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700881 }
882
Ed Tanouse662eae2022-01-25 10:39:19 -0800883 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000885 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
887 {
Ed Tanous543f4402022-01-06 13:12:53 -0800888 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000890 double value = std::strtod(reqUrl.data() + pos, &eptr);
891 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 {
893 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000894 std::pair<unsigned, RoutingParams> ret =
895 find(reqUrl,
896 &nodes[node->paramChildrens[static_cast<size_t>(
897 ParamType::DOUBLE)]],
898 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 updateFound(ret);
900 params->doubleParams.pop_back();
901 }
902 }
903 }
904
Ed Tanouse662eae2022-01-25 10:39:19 -0800905 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700906 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000907 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000908 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000910 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700911 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700912 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700913 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 }
915
916 if (epos != pos)
917 {
918 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000919 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300920 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000921 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700922 &nodes[node->paramChildrens[static_cast<size_t>(
923 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000924 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 updateFound(ret);
926 params->stringParams.pop_back();
927 }
928 }
929
Ed Tanouse662eae2022-01-25 10:39:19 -0800930 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000932 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933
934 if (epos != pos)
935 {
936 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000937 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700938 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000939 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700940 &nodes[node->paramChildrens[static_cast<size_t>(
941 ParamType::PATH)]],
942 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700943 updateFound(ret);
944 params->stringParams.pop_back();
945 }
946 }
947
Ed Tanousa94ac612022-02-22 11:13:24 -0800948 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 {
950 const std::string& fragment = kv.first;
951 const Node* child = &nodes[kv.second];
952
Ed Tanous81ce6092020-12-17 16:54:55 +0000953 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954 {
Tanousf00032d2018-11-05 01:18:10 -0300955 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000956 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 updateFound(ret);
958 }
959 }
960
961 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700962 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700963
964 void add(const std::string& url, unsigned ruleIndex)
965 {
Ed Tanous271584a2019-07-09 16:24:22 -0700966 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967
968 for (unsigned i = 0; i < url.size(); i++)
969 {
970 char c = url[i];
971 if (c == '<')
972 {
Tanousf00032d2018-11-05 01:18:10 -0300973 const static std::array<std::pair<ParamType, std::string>, 7>
974 paramTraits = {{
975 {ParamType::INT, "<int>"},
976 {ParamType::UINT, "<uint>"},
977 {ParamType::DOUBLE, "<float>"},
978 {ParamType::DOUBLE, "<double>"},
979 {ParamType::STRING, "<str>"},
980 {ParamType::STRING, "<string>"},
981 {ParamType::PATH, "<path>"},
982 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700983
Tanousf00032d2018-11-05 01:18:10 -0300984 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 {
Tanousf00032d2018-11-05 01:18:10 -0300986 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700987 {
Ed Tanous271584a2019-07-09 16:24:22 -0700988 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800989 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 {
Tanousf00032d2018-11-05 01:18:10 -0300991 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700992 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 }
Ed Tanous271584a2019-07-09 16:24:22 -0700994 idx = nodes[idx].paramChildrens[index];
995 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 break;
997 }
998 }
999
1000 i--;
1001 }
1002 else
1003 {
1004 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001005 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 {
Tanousf00032d2018-11-05 01:18:10 -03001007 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001008 nodes[idx].children.emplace(piece, newNodeIdx);
1009 }
1010 idx = nodes[idx].children[piece];
1011 }
1012 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001013 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001014 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001015 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001016 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001018 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001019
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001021 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001022 {
Ed Tanous271584a2019-07-09 16:24:22 -07001023 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001025 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 {
1027 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001028 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1029 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 {
1031 case ParamType::INT:
1032 BMCWEB_LOG_DEBUG << "<int>";
1033 break;
1034 case ParamType::UINT:
1035 BMCWEB_LOG_DEBUG << "<uint>";
1036 break;
1037 case ParamType::DOUBLE:
1038 BMCWEB_LOG_DEBUG << "<float>";
1039 break;
1040 case ParamType::STRING:
1041 BMCWEB_LOG_DEBUG << "<str>";
1042 break;
1043 case ParamType::PATH:
1044 BMCWEB_LOG_DEBUG << "<path>";
1045 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001046 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001047 BMCWEB_LOG_DEBUG << "<ERROR>";
1048 break;
1049 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001050
Ed Tanous1abe55e2018-09-05 08:30:59 -07001051 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1052 }
1053 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001054 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055 {
1056 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001057 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058 << kv.first;
1059 debugNodePrint(&nodes[kv.second], level + 1);
1060 }
1061 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001062
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 public:
1064 void debugPrint()
1065 {
Ed Tanous271584a2019-07-09 16:24:22 -07001066 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001067 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001068
Ed Tanous1abe55e2018-09-05 08:30:59 -07001069 private:
1070 const Node* head() const
1071 {
1072 return &nodes.front();
1073 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001074
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 Node* head()
1076 {
1077 return &nodes.front();
1078 }
1079
1080 unsigned newNode()
1081 {
1082 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001083 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 }
1085
1086 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001087};
1088
Ed Tanous1abe55e2018-09-05 08:30:59 -07001089class Router
1090{
1091 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001092 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001093
Ed Tanous1abe55e2018-09-05 08:30:59 -07001094 DynamicRule& newRuleDynamic(const std::string& rule)
1095 {
1096 std::unique_ptr<DynamicRule> ruleObject =
1097 std::make_unique<DynamicRule>(rule);
1098 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001099 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001100
Ed Tanous1abe55e2018-09-05 08:30:59 -07001101 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001102 }
1103
Ed Tanous1abe55e2018-09-05 08:30:59 -07001104 template <uint64_t N>
1105 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1106 newRuleTagged(const std::string& rule)
1107 {
1108 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1109 TaggedRule>;
1110 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1111 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001112 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001113
1114 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001115 }
1116
Tanousf00032d2018-11-05 01:18:10 -03001117 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001118 {
Tanousf00032d2018-11-05 01:18:10 -03001119 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001120 {
Tanousf00032d2018-11-05 01:18:10 -03001121 return;
1122 }
Ed Tanous44e45182022-07-26 16:47:23 -07001123 for (size_t method = 0, methodBit = 1; method <= notFoundIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001124 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001125 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001126 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001127 {
1128 perMethods[method].rules.emplace_back(ruleObject);
1129 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001130 rule, static_cast<unsigned>(
1131 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001132 // directory case:
1133 // request to `/about' url matches `/about/' rule
1134 if (rule.size() > 2 && rule.back() == '/')
1135 {
1136 perMethods[method].trie.add(
1137 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001138 static_cast<unsigned>(perMethods[method].rules.size() -
1139 1));
Tanousf00032d2018-11-05 01:18:10 -03001140 }
1141 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001142 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001143 }
1144
Ed Tanous1abe55e2018-09-05 08:30:59 -07001145 void validate()
1146 {
Tanousf00032d2018-11-05 01:18:10 -03001147 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 {
1149 if (rule)
1150 {
Tanousf00032d2018-11-05 01:18:10 -03001151 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001152 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001153 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001154 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001155 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001157 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158 }
1159 }
Tanousf00032d2018-11-05 01:18:10 -03001160 for (PerMethod& perMethod : perMethods)
1161 {
1162 perMethod.trie.validate();
1163 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001164 }
1165
Ed Tanous44e45182022-07-26 16:47:23 -07001166 struct FindRoute
1167 {
1168 BaseRule* rule = nullptr;
1169 RoutingParams params;
1170 };
1171
1172 struct FindRouteResponse
1173 {
1174 std::string allowHeader;
1175 FindRoute route;
1176 };
1177
1178 FindRouteResponse findRoute(const Request& req) const
1179 {
1180 FindRouteResponse findRoute;
1181
1182 size_t reqMethodIndex = static_cast<size_t>(req.method());
1183 // Check to see if this url exists at any verb
1184 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1185 perMethodIndex++)
1186 {
1187 // Make sure it's safe to deference the array at that index
1188 static_assert(maxVerbIndex <
1189 std::tuple_size_v<decltype(perMethods)>);
1190
1191 const PerMethod& perMethod = perMethods[perMethodIndex];
1192 std::pair<unsigned, RoutingParams> found2 =
1193 perMethod.trie.find(req.url);
1194 if (found2.first == 0)
1195 {
1196 continue;
1197 }
1198 if (!findRoute.allowHeader.empty())
1199 {
1200 findRoute.allowHeader += ", ";
1201 }
1202 findRoute.allowHeader += boost::beast::http::to_string(
1203 static_cast<boost::beast::http::verb>(perMethodIndex));
1204 if (perMethodIndex == reqMethodIndex)
1205 {
1206 findRoute.route.rule = perMethod.rules[found2.first];
1207 findRoute.route.params = std::move(found2.second);
1208 }
1209 }
1210 return findRoute;
1211 }
1212
Ed Tanous1abe55e2018-09-05 08:30:59 -07001213 template <typename Adaptor>
1214 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1215 {
Ed Tanous271584a2019-07-09 16:24:22 -07001216 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001217 {
1218 res.result(boost::beast::http::status::not_found);
1219 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001220 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001221 }
Tanousf00032d2018-11-05 01:18:10 -03001222
Ed Tanous271584a2019-07-09 16:24:22 -07001223 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001224 Trie& trie = perMethod.trie;
1225 std::vector<BaseRule*>& rules = perMethod.rules;
1226
1227 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001228 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001229 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001230 {
1231 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001232 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001233 res.end();
1234 return;
1235 }
1236
1237 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001238 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001239 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001240 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001241
Ed Tanous271584a2019-07-09 16:24:22 -07001242 if ((rules[ruleIndex]->getMethods() &
1243 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001244 {
1245 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1246 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001247 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001248 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001249 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001250 res.end();
1251 return;
1252 }
1253
1254 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001255 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 << rules[ruleIndex]->getMethods();
1257
1258 // any uncaught exceptions become 500s
1259 try
1260 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -08001261 rules[ruleIndex]->handleUpgrade(req, res,
1262 std::forward<Adaptor>(adaptor));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001263 }
Patrick Williamsc5967042021-10-06 12:39:54 -05001264 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001265 {
1266 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001267 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268 res.end();
1269 return;
1270 }
1271 catch (...)
1272 {
1273 BMCWEB_LOG_ERROR
1274 << "An uncaught exception occurred. The type was unknown "
1275 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001276 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001277 res.end();
1278 return;
1279 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001280 }
1281
zhanghch058d1b46d2021-04-01 11:18:24 +08001282 void handle(Request& req,
1283 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284 {
Ed Tanous271584a2019-07-09 16:24:22 -07001285 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001286 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001287 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001288 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001289 }
Ed Tanous44e45182022-07-26 16:47:23 -07001290
1291 FindRouteResponse foundRoute = findRoute(req);
1292
1293 // Couldn't find a normal route with any verb, try looking for a 404
1294 // route
1295 if (foundRoute.allowHeader.empty())
Ed Tanous88a03c52022-03-14 10:16:07 -07001296 {
Ed Tanous44e45182022-07-26 16:47:23 -07001297 if (foundRoute.route.rule == nullptr)
1298 {
1299 const PerMethod& perMethod = perMethods[notFoundIndex];
1300 std::pair<unsigned, RoutingParams> found =
1301 perMethod.trie.find(req.url);
1302 if (found.first >= perMethod.rules.size())
1303 {
1304 throw std::runtime_error(
1305 "Trie internal structure corrupted!");
1306 }
1307 // Found a 404 route, switch that in
1308 if (found.first != 0U)
1309 {
1310 foundRoute.route.rule = perMethod.rules[found.first];
1311 foundRoute.route.params = std::move(found.second);
1312 }
1313 }
1314 }
1315 else
1316 {
1317 // Found at least one valid route, fill in the allow header
Ed Tanous88a03c52022-03-14 10:16:07 -07001318 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001319 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001320 }
Tanousf00032d2018-11-05 01:18:10 -03001321
Ed Tanous44e45182022-07-26 16:47:23 -07001322 // If we couldn't find a real route or a 404 route, return a generic
1323 // response
1324 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325 {
Ed Tanous44e45182022-07-26 16:47:23 -07001326 if (foundRoute.allowHeader.empty())
1327 {
1328 asyncResp->res.result(boost::beast::http::status::not_found);
1329 }
1330 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001331 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001332 asyncResp->res.result(
1333 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001334 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001335 return;
1336 }
1337
Ed Tanous44e45182022-07-26 16:47:23 -07001338 BaseRule& rule = *foundRoute.route.rule;
1339 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340
Ed Tanous44e45182022-07-26 16:47:23 -07001341 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001342 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001343 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001344
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001345 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001346 {
Ed Tanous44e45182022-07-26 16:47:23 -07001347 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001348 return;
1349 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001350
1351 crow::connections::systemBus->async_method_call(
Ed Tanous44e45182022-07-26 16:47:23 -07001352 [&req, asyncResp, &rule,
1353 params](const boost::system::error_code ec,
1354 const dbus::utility::DBusPropertiesMap& userInfoMap) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001355 if (ec)
1356 {
1357 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1358 asyncResp->res.result(
1359 boost::beast::http::status::internal_server_error);
1360 return;
1361 }
1362 std::string userRole{};
1363 const bool* remoteUser = nullptr;
1364 std::optional<bool> passwordExpired;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001365
Ed Tanous002d39b2022-05-31 08:59:27 -07001366 for (const auto& userInfo : userInfoMap)
1367 {
1368 if (userInfo.first == "UserPrivilege")
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001369 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001370 const std::string* userRolePtr =
1371 std::get_if<std::string>(&userInfo.second);
1372 if (userRolePtr == nullptr)
Ed Tanousb9d36b42022-02-26 21:42:46 -08001373 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001374 continue;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001375 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001376 userRole = *userRolePtr;
1377 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1378 << " userRole = " << *userRolePtr;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001379 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001380 else if (userInfo.first == "RemoteUser")
1381 {
1382 remoteUser = std::get_if<bool>(&userInfo.second);
1383 }
1384 else if (userInfo.first == "UserPasswordExpired")
1385 {
1386 const bool* passwordExpiredPtr =
1387 std::get_if<bool>(&userInfo.second);
1388 if (passwordExpiredPtr == nullptr)
1389 {
1390 continue;
1391 }
1392 passwordExpired = *passwordExpiredPtr;
1393 }
1394 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001395
Ed Tanous002d39b2022-05-31 08:59:27 -07001396 if (remoteUser == nullptr)
1397 {
1398 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1399 asyncResp->res.result(
1400 boost::beast::http::status::internal_server_error);
1401 return;
1402 }
1403
1404 if (passwordExpired == std::nullopt)
1405 {
1406 if (!*remoteUser)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001407 {
1408 BMCWEB_LOG_ERROR
Ed Tanous002d39b2022-05-31 08:59:27 -07001409 << "UserPasswordExpired property is expected for"
1410 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001411 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001412 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001413 return;
1414 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001415 passwordExpired = false;
1416 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001417
Nan Zhouac6250a2022-08-09 20:15:44 +00001418 // Get the user's privileges from the role
Ed Tanous002d39b2022-05-31 08:59:27 -07001419 redfish::Privileges userPrivileges =
1420 redfish::getUserPrivileges(userRole);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001421
Ed Tanous002d39b2022-05-31 08:59:27 -07001422 // Set isConfigureSelfOnly based on D-Bus results. This
1423 // ignores the results from both pamAuthenticateUser and the
1424 // value from any previous use of this session.
1425 req.session->isConfigureSelfOnly = *passwordExpired;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001426
Nan Zhouac6250a2022-08-09 20:15:44 +00001427 // Modify privileges if isConfigureSelfOnly.
Ed Tanous002d39b2022-05-31 08:59:27 -07001428 if (req.session->isConfigureSelfOnly)
1429 {
Nan Zhouac6250a2022-08-09 20:15:44 +00001430 // Remove all privileges except ConfigureSelf
Ed Tanous002d39b2022-05-31 08:59:27 -07001431 userPrivileges = userPrivileges.intersection(
1432 redfish::Privileges{"ConfigureSelf"});
1433 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1434 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001435
Ed Tanous44e45182022-07-26 16:47:23 -07001436 if (!rule.checkPrivileges(userPrivileges))
Ed Tanous002d39b2022-05-31 08:59:27 -07001437 {
1438 asyncResp->res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001439 if (req.session->isConfigureSelfOnly)
1440 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001441 redfish::messages::passwordChangeRequired(
1442 asyncResp->res, crow::utility::urlFromPieces(
1443 "redfish", "v1", "AccountService",
1444 "Accounts", req.session->username));
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001445 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001446 return;
1447 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001448
Ed Tanous002d39b2022-05-31 08:59:27 -07001449 req.userRole = userRole;
Ed Tanous44e45182022-07-26 16:47:23 -07001450 rule.handle(req, asyncResp, params);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001451 },
1452 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1453 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1454 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001455 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001456
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457 void debugPrint()
1458 {
Ed Tanous271584a2019-07-09 16:24:22 -07001459 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001460 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001461 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1462 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001463 perMethods[i].trie.debugPrint();
1464 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001465 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001466
Ed Tanous1abe55e2018-09-05 08:30:59 -07001467 std::vector<const std::string*> getRoutes(const std::string& parent)
1468 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001470
1471 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 {
Tanousf00032d2018-11-05 01:18:10 -03001473 std::vector<unsigned> x;
1474 pm.trie.findRouteIndexes(parent, x);
1475 for (unsigned index : x)
1476 {
1477 ret.push_back(&pm.rules[index]->rule);
1478 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001479 }
1480 return ret;
1481 }
1482
1483 private:
Tanousf00032d2018-11-05 01:18:10 -03001484 struct PerMethod
1485 {
1486 std::vector<BaseRule*> rules;
1487 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001488 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001489 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001490 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001491 {}
Tanousf00032d2018-11-05 01:18:10 -03001492 };
Ed Tanous888880a2020-08-24 13:48:50 -07001493
Ed Tanous44e45182022-07-26 16:47:23 -07001494 std::array<PerMethod, notFoundIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001495 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001496};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497} // namespace crow