blob: 0059760abe63ff658a58ac57e4793fd939266141 [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 Tanous168e20c2021-12-13 14:39:53 -08005#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06006#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07007#include "http_request.hpp"
8#include "http_response.hpp"
9#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -030010#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053011#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070012#include "utility.hpp"
Ed Tanous3d183202023-03-10 09:21:58 -080013#include "utils/dbus_utils.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070014#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070015#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070016
Ed Tanous88a03c52022-03-14 10:16:07 -070017#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030018#include <boost/container/flat_map.hpp>
Ed Tanous3d183202023-03-10 09:21:58 -080019#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050020
Ed Tanouse0d918b2018-03-27 17:41:04 -070021#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070022#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070023#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070024#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070025#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070026#include <optional>
Ed Tanous7045c8d2017-04-03 10:04:37 -070027#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070028#include <utility>
29#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031namespace crow
32{
Tanousf00032d2018-11-05 01:18:10 -030033
Ed Tanous1abe55e2018-09-05 08:30:59 -070034class BaseRule
35{
36 public:
Ed Tanous4e23a442022-06-06 09:57:26 -070037 explicit BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanous0c0084a2019-10-24 15:57:51 -070040 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070041
Ed Tanousecd6a3a2022-01-07 09:18:40 -080042 BaseRule(const BaseRule&) = delete;
43 BaseRule(BaseRule&&) = delete;
44 BaseRule& operator=(const BaseRule&) = delete;
45 BaseRule& operator=(const BaseRule&&) = delete;
46
Ed Tanous1abe55e2018-09-05 08:30:59 -070047 virtual void validate() = 0;
48 std::unique_ptr<BaseRule> upgrade()
49 {
50 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070051 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070053 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 return {};
55 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070056
Ed Tanous104f09c2022-01-25 09:56:04 -080057 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080058 const std::shared_ptr<bmcweb::AsyncResp>&,
59 const RoutingParams&) = 0;
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +053060#ifndef BMCWEB_ENABLE_SSL
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053061 virtual void
62 handleUpgrade(const Request& /*req*/,
63 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
64 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053066 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 }
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +053068#else
Ed Tanous104f09c2022-01-25 09:56:04 -080069 virtual void handleUpgrade(
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053070 const Request& /*req*/,
71 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -080072 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053074 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070076#endif
77
Ed Tanous9eb808c2022-01-25 10:19:23 -080078 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 {
80 return methodsBitfield;
81 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070082
Tanousf00032d2018-11-05 01:18:10 -030083 bool checkPrivileges(const redfish::Privileges& userPrivileges)
84 {
85 // If there are no privileges assigned, assume no privileges
86 // required
87 if (privilegesSet.empty())
88 {
89 return true;
90 }
91
92 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
93 {
94 if (userPrivileges.isSupersetOf(requiredPrivileges))
95 {
96 return true;
97 }
98 }
99 return false;
100 }
101
Ed Tanous2c9efc32022-07-31 22:08:26 -0700102 size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
Ed Tanous44e45182022-07-26 16:47:23 -0700103 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
Ed Tanous759cf102022-07-31 16:36:52 -0700104 methodNotAllowedIndex,
Ed Tanous44e45182022-07-26 16:47:23 -0700105 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700106
Tanousf00032d2018-11-05 01:18:10 -0300107 std::vector<redfish::Privileges> privilegesSet;
108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 std::string rule;
110 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700111
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500115 template <typename T>
116 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700117};
118
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119namespace detail
120{
121namespace routing_handler_call_helper
122{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500123template <typename T, int Pos>
124struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125{
126 using type = T;
127 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128};
129
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500130template <typename H1>
131struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132{
133 H1& handler;
134 const RoutingParams& params;
135 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700137};
138
139template <typename F, int NInt, int NUint, int NDouble, int NString,
140 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500142{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700143
144template <typename F, int NInt, int NUint, int NDouble, int NString,
145 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700146struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 black_magic::S<Args2...>>
148{
149 void operator()(F cparams)
150 {
151 using pushed = typename black_magic::S<Args2...>::template push_back<
152 CallPair<int64_t, NInt>>;
153 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
154 pushed>()(cparams);
155 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700156};
157
158template <typename F, int NInt, int NUint, int NDouble, int NString,
159 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700160struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
162{
163 void operator()(F cparams)
164 {
165 using pushed = typename black_magic::S<Args2...>::template push_back<
166 CallPair<uint64_t, NUint>>;
167 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
168 pushed>()(cparams);
169 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700170};
171
172template <typename F, int NInt, int NUint, int NDouble, int NString,
173 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700174struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 black_magic::S<Args2...>>
176{
177 void operator()(F cparams)
178 {
179 using pushed = typename black_magic::S<Args2...>::template push_back<
180 CallPair<double, NDouble>>;
181 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
182 pushed>()(cparams);
183 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700184};
185
186template <typename F, int NInt, int NUint, int NDouble, int NString,
187 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700188struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
190{
191 void operator()(F cparams)
192 {
193 using pushed = typename black_magic::S<Args2...>::template push_back<
194 CallPair<std::string, NString>>;
195 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
196 pushed>()(cparams);
197 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700198};
199
200template <typename F, int NInt, int NUint, int NDouble, int NString,
201 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700202struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700203 black_magic::S<Args1...>>
204{
205 void operator()(F cparams)
206 {
207 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800208 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209 cparams.params.template get<typename Args1::type>(Args1::pos)...);
210 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700211};
212
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500213template <typename Func, typename... ArgsWrapped>
214struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215{
216 template <typename... Args>
217 void set(
218 Func f,
219 typename std::enable_if<
220 !std::is_same<
221 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
222 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800223 int>::type /*enable*/
224 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800226 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800227 const Request&,
228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
229 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700230 }
231
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232 template <typename Req, typename... Args>
233 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700235 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500236 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700237
zhanghch058d1b46d2021-04-01 11:18:24 +0800238 void operator()(const Request& req,
239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
240 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800242 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700244
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 Func f;
246 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700247
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 template <typename... Args>
249 void set(
250 Func f,
251 typename std::enable_if<
252 std::is_same<
253 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
254 const Request&>::value &&
255 !std::is_same<typename std::tuple_element<
256 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800257 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800258 int>::type /*enable*/
259 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 {
261 handler = ReqHandlerWrapper<Args...>(std::move(f));
262 /*handler = (
263 [f = std::move(f)]
264 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700265 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 res.end();
267 });*/
268 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700269
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 template <typename... Args>
271 void set(
272 Func f,
273 typename std::enable_if<
274 std::is_same<
275 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
276 const Request&>::value &&
277 std::is_same<typename std::tuple_element<
278 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800279 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800280 int>::type /*enable*/
281 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 {
283 handler = std::move(f);
284 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700285
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500286 template <typename... Args>
287 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800289 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800290 const crow::Request& /*req*/,
291 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700293 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700295
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 template <typename... Args>
297 struct HandlerTypeHelper<const Request&, Args...>
298 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800299 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800300 const crow::Request& /*req*/,
301 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700303 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700305
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800307 struct HandlerTypeHelper<const Request&,
308 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800310 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800311 const crow::Request& /*req*/,
312 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700314 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 };
316
317 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
318
zhanghch058d1b46d2021-04-01 11:18:24 +0800319 void operator()(const Request& req,
320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 const RoutingParams& params)
322 {
323 detail::routing_handler_call_helper::Call<
324 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
325 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
326 black_magic::S<>>()(
327 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800328 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700330};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331} // namespace routing_handler_call_helper
332} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700333
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334class WebSocketRule : public BaseRule
335{
336 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700337
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700339 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500340 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700341
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500343 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700344
Ed Tanous104f09c2022-01-25 09:56:04 -0800345 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800347 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800349 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700351
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530352#ifndef BMCWEB_ENABLE_SSL
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530353 void handleUpgrade(const Request& req,
354 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800355 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 {
Nan Zhou93c02022022-02-24 18:21:07 -0800357 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530358 std::shared_ptr<
359 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
360 myConnection = std::make_shared<
361 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000362 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanous863c1c22022-02-21 21:33:06 -0800363 messageExHandler, closeHandler, errorHandler);
Ratan Gupta02453b12019-10-22 14:43:36 +0530364 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 }
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +0530366#else
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530367 void handleUpgrade(const Request& req,
368 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800369 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
370 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 {
Nan Zhou93c02022022-02-24 18:21:07 -0800372 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800373 std::shared_ptr<crow::websocket::ConnectionImpl<
374 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
375 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
376 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000377 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanous863c1c22022-02-21 21:33:06 -0800378 messageExHandler, closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700379 myConnection->start();
380 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700381#endif
382
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500383 template <typename Func>
384 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 {
386 openHandler = f;
387 return *this;
388 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700389
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500390 template <typename Func>
391 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392 {
393 messageHandler = f;
394 return *this;
395 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700396
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500397 template <typename Func>
Ed Tanous863c1c22022-02-21 21:33:06 -0800398 self_t& onmessageex(Func f)
399 {
400 messageExHandler = f;
401 return *this;
402 }
403
404 template <typename Func>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500405 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 {
407 closeHandler = f;
408 return *this;
409 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700410
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500411 template <typename Func>
412 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
414 errorHandler = f;
415 return *this;
416 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700417
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 protected:
zhanghch0577726382021-10-21 14:07:57 +0800419 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
421 messageHandler;
Ed Tanous863c1c22022-02-21 21:33:06 -0800422 std::function<void(crow::websocket::Connection&, std::string_view,
423 crow::websocket::MessageType type,
424 std::function<void()>&& whenComplete)>
425 messageExHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426 std::function<void(crow::websocket::Connection&, const std::string&)>
427 closeHandler;
428 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700429};
430
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500431template <typename T>
432struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700433{
434 using self_t = T;
435 WebSocketRule& websocket()
436 {
Ed Tanous271584a2019-07-09 16:24:22 -0700437 self_t* self = static_cast<self_t*>(this);
438 WebSocketRule* p = new WebSocketRule(self->rule);
Ed Tanous9d192c72023-04-10 10:20:13 -0700439 p->privilegesSet = self->privilegesSet;
Ed Tanous271584a2019-07-09 16:24:22 -0700440 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 return *p;
442 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700443
Ed Tanous26ccae32023-02-16 10:28:44 -0800444 self_t& name(std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 {
Ed Tanous271584a2019-07-09 16:24:22 -0700446 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700447 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700448 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 self_t& methods(boost::beast::http::verb method)
452 {
Ed Tanous271584a2019-07-09 16:24:22 -0700453 self_t* self = static_cast<self_t*>(this);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700454 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
455 if (verb)
456 {
457 self->methodsBitfield = 1U << static_cast<size_t>(*verb);
458 }
Ed Tanous271584a2019-07-09 16:24:22 -0700459 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700461
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000463 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 {
Ed Tanous271584a2019-07-09 16:24:22 -0700465 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000466 methods(argsMethod...);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700467 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
468 if (verb)
469 {
470 self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
471 }
Ed Tanous271584a2019-07-09 16:24:22 -0700472 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 }
Tanousf00032d2018-11-05 01:18:10 -0300474
Ed Tanous44e45182022-07-26 16:47:23 -0700475 self_t& notFound()
476 {
477 self_t* self = static_cast<self_t*>(this);
478 self->methodsBitfield = 1U << notFoundIndex;
479 return *self;
480 }
481
Ed Tanous759cf102022-07-31 16:36:52 -0700482 self_t& methodNotAllowed()
483 {
484 self_t* self = static_cast<self_t*>(this);
485 self->methodsBitfield = 1U << methodNotAllowedIndex;
486 return *self;
487 }
488
Ed Tanous432a8902021-06-14 15:28:56 -0700489 self_t& privileges(
490 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300491 {
Ed Tanous271584a2019-07-09 16:24:22 -0700492 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700493 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300494 {
Ed Tanous271584a2019-07-09 16:24:22 -0700495 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300496 }
Ed Tanous271584a2019-07-09 16:24:22 -0700497 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300498 }
Ed Tanoused398212021-06-09 17:05:54 -0700499
500 template <size_t N, typename... MethodArgs>
501 self_t& privileges(const std::array<redfish::Privileges, N>& p)
502 {
503 self_t* self = static_cast<self_t*>(this);
504 for (const redfish::Privileges& privilege : p)
505 {
506 self->privilegesSet.emplace_back(privilege);
507 }
508 return *self;
509 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700510};
511
Ed Tanous1abe55e2018-09-05 08:30:59 -0700512class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
513{
514 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700515 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500516 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 void validate() override
519 {
520 if (!erasedHandler)
521 {
522 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
523 "no handler for url " + rule);
524 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700525 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700526
zhanghch058d1b46d2021-04-01 11:18:24 +0800527 void handle(const Request& req,
528 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 const RoutingParams& params) override
530 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800531 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700533
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500534 template <typename Func>
535 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 {
Ed Tanousc867a832022-03-10 14:17:00 -0800537 using boost::callable_traits::args_t;
538 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
539 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
540 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 }
542
543 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500544 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 // enable_if Arg1 != request
546
547 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800548 std::function<void(const Request&,
549 const std::shared_ptr<bmcweb::AsyncResp>&,
550 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800551 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanousc867a832022-03-10 14:17:00 -0800553 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554
555 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700556 black_magic::getParameterTag(rule.c_str()),
557 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 typename function_t::template arg<Indices>...>::value))
559 {
560 throw std::runtime_error("routeDynamic: Handler type is mismatched "
561 "with URL parameters: " +
562 rule);
563 }
564 auto ret = detail::routing_handler_call_helper::Wrapped<
565 Func, typename function_t::template arg<Indices>...>();
566 ret.template set<typename function_t::template arg<Indices>...>(
567 std::move(f));
568 return ret;
569 }
570
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500571 template <typename Func>
572 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 {
574 nameStr = std::move(name);
575 (*this).template operator()<Func>(std::forward(f));
576 }
577
578 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800579 std::function<void(const Request&,
580 const std::shared_ptr<bmcweb::AsyncResp>&,
581 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700583};
584
585template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500586class TaggedRule :
587 public BaseRule,
588 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589{
590 public:
591 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700592
Ed Tanous4e23a442022-06-06 09:57:26 -0700593 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500594 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700595
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 void validate() override
597 {
598 if (!handler)
599 {
600 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
601 "no handler for url " + rule);
602 }
603 }
604
605 template <typename Func>
606 typename std::enable_if<
607 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
608 void>::type
609 operator()(Func&& f)
610 {
611 static_assert(
612 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
613 black_magic::CallHelper<
614 Func, black_magic::S<crow::Request, Args...>>::value,
615 "Handler type is mismatched with URL parameters");
616 static_assert(
617 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
618 "Handler function cannot have void return type; valid return "
619 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500620 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800622 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800623 const Request&,
624 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
625 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 }
627
628 template <typename Func>
629 typename std::enable_if<
630 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700631 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700632 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 void>::type
634 operator()(Func&& f)
635 {
636 static_assert(
637 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
638 black_magic::CallHelper<
639 Func, black_magic::S<crow::Request, Args...>>::value,
640 "Handler type is mismatched with URL parameters");
641 static_assert(
642 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
643 std::declval<Args>()...))>::value,
644 "Handler function cannot have void return type; valid return "
645 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500646 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700647
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800648 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800649 const crow::Request& req,
650 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
651 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700653
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 template <typename Func>
655 typename std::enable_if<
656 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
657 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700658 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 void>::type
660 operator()(Func&& f)
661 {
662 static_assert(
663 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
664 black_magic::CallHelper<
665 Func, black_magic::S<crow::Request, Args...>>::value ||
666 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800667 Func, black_magic::S<crow::Request,
668 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 Args...>>::value,
670 "Handler type is mismatched with URL parameters");
671 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800672 std::is_same<
673 void,
674 decltype(f(std::declval<crow::Request>(),
675 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
676 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300677 "Handler function with response argument should have void "
678 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700680
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800681 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700683
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500684 template <typename Func>
Ed Tanous26ccae32023-02-16 10:28:44 -0800685 void operator()(std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700687 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 (*this).template operator()<Func>(std::forward(f));
689 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700690
zhanghch058d1b46d2021-04-01 11:18:24 +0800691 void handle(const Request& req,
692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 const RoutingParams& params) override
694 {
695 detail::routing_handler_call_helper::Call<
696 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
697 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
698 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800699 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700701
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800703 std::function<void(const crow::Request&,
704 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
705 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700706};
707
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708class Trie
709{
710 public:
711 struct Node
712 {
713 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700714 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
715 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800716 using ChildMap = boost::container::flat_map<
717 std::string, unsigned, std::less<>,
718 std::vector<std::pair<std::string, unsigned>>>;
719 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700720
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 bool isSimpleNode() const
722 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800723 return ruleIndex == 0 &&
724 std::all_of(std::begin(paramChildrens),
725 std::end(paramChildrens),
726 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700727 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700728 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700729
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500731 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732
733 private:
734 void optimizeNode(Node* node)
735 {
Ed Tanous271584a2019-07-09 16:24:22 -0700736 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800738 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700739 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700741 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 Node* child = &nodes[x];
743 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700744 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700746 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700748 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800750 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 {
752 Node* child = &nodes[kv.second];
753 if (!child->isSimpleNode())
754 {
755 mergeWithChild = false;
756 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700757 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 }
759 if (mergeWithChild)
760 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800761 Node::ChildMap merged;
762 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700763 {
764 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800765 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300766 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 {
768 merged[kv.first + childKv.first] = childKv.second;
769 }
770 }
771 node->children = std::move(merged);
772 optimizeNode(node);
773 }
774 else
775 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800776 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 {
778 Node* child = &nodes[kv.second];
779 optimizeNode(child);
780 }
781 }
782 }
783
784 void optimize()
785 {
786 optimizeNode(head());
787 }
788
789 public:
790 void validate()
791 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 optimize();
793 }
794
Ed Tanous81ce6092020-12-17 16:54:55 +0000795 void findRouteIndexes(const std::string& reqUrl,
796 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300797 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 {
799 if (node == nullptr)
800 {
801 node = head();
802 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800803 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 {
805 const std::string& fragment = kv.first;
806 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000807 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 {
809 if (child->ruleIndex != 0 && fragment != "/")
810 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000811 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000813 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700814 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 }
816 else
817 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000818 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 {
Ed Tanous271584a2019-07-09 16:24:22 -0700820 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000821 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700822 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 }
824 }
825 }
826 }
827
828 std::pair<unsigned, RoutingParams>
Ed Tanous26ccae32023-02-16 10:28:44 -0800829 find(std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700830 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 {
832 RoutingParams empty;
833 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700834 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700836 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837
838 unsigned found{};
839 RoutingParams matchParams;
840
841 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700842 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700844 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000845 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700846 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700848 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849
850 auto updateFound =
851 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700852 if (ret.first != 0U && (found == 0U || found > ret.first))
853 {
854 found = ret.first;
855 matchParams = std::move(ret.second);
856 }
857 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858
Ed Tanouse662eae2022-01-25 10:39:19 -0800859 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700860 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000861 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
863 {
Ed Tanous543f4402022-01-06 13:12:53 -0800864 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865 errno = 0;
866 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000867 std::strtoll(reqUrl.data() + pos, &eptr, 10);
868 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 {
870 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000871 std::pair<unsigned, RoutingParams> ret =
872 find(reqUrl,
873 &nodes[node->paramChildrens[static_cast<size_t>(
874 ParamType::INT)]],
875 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 updateFound(ret);
877 params->intParams.pop_back();
878 }
879 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700880 }
881
Ed Tanouse662eae2022-01-25 10:39:19 -0800882 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000884 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 if ((c >= '0' && c <= '9') || c == '+')
886 {
Ed Tanous543f4402022-01-06 13:12:53 -0800887 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888 errno = 0;
889 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000890 std::strtoull(reqUrl.data() + pos, &eptr, 10);
891 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 {
893 params->uintParams.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::UINT)]],
898 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 updateFound(ret);
900 params->uintParams.pop_back();
901 }
902 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700903 }
904
Ed Tanouse662eae2022-01-25 10:39:19 -0800905 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700906 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000907 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
909 {
Ed Tanous543f4402022-01-06 13:12:53 -0800910 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700911 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000912 double value = std::strtod(reqUrl.data() + pos, &eptr);
913 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
915 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000916 std::pair<unsigned, RoutingParams> ret =
917 find(reqUrl,
918 &nodes[node->paramChildrens[static_cast<size_t>(
919 ParamType::DOUBLE)]],
920 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700921 updateFound(ret);
922 params->doubleParams.pop_back();
923 }
924 }
925 }
926
Ed Tanouse662eae2022-01-25 10:39:19 -0800927 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000929 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000930 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000932 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700933 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700935 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700936 }
937
938 if (epos != pos)
939 {
940 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000941 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300942 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000943 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700944 &nodes[node->paramChildrens[static_cast<size_t>(
945 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000946 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947 updateFound(ret);
948 params->stringParams.pop_back();
949 }
950 }
951
Ed Tanouse662eae2022-01-25 10:39:19 -0800952 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700953 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000954 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955
956 if (epos != pos)
957 {
958 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000959 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700960 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000961 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700962 &nodes[node->paramChildrens[static_cast<size_t>(
963 ParamType::PATH)]],
964 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 updateFound(ret);
966 params->stringParams.pop_back();
967 }
968 }
969
Ed Tanousa94ac612022-02-22 11:13:24 -0800970 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971 {
972 const std::string& fragment = kv.first;
973 const Node* child = &nodes[kv.second];
974
Ed Tanous81ce6092020-12-17 16:54:55 +0000975 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700976 {
Tanousf00032d2018-11-05 01:18:10 -0300977 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000978 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 updateFound(ret);
980 }
981 }
982
983 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700984 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985
986 void add(const std::string& url, unsigned ruleIndex)
987 {
Ed Tanous271584a2019-07-09 16:24:22 -0700988 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700989
990 for (unsigned i = 0; i < url.size(); i++)
991 {
992 char c = url[i];
993 if (c == '<')
994 {
Tanousf00032d2018-11-05 01:18:10 -0300995 const static std::array<std::pair<ParamType, std::string>, 7>
996 paramTraits = {{
997 {ParamType::INT, "<int>"},
998 {ParamType::UINT, "<uint>"},
999 {ParamType::DOUBLE, "<float>"},
1000 {ParamType::DOUBLE, "<double>"},
1001 {ParamType::STRING, "<str>"},
1002 {ParamType::STRING, "<string>"},
1003 {ParamType::PATH, "<path>"},
1004 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001005
Tanousf00032d2018-11-05 01:18:10 -03001006 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007 {
Tanousf00032d2018-11-05 01:18:10 -03001008 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001009 {
Ed Tanous271584a2019-07-09 16:24:22 -07001010 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -08001011 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001012 {
Tanousf00032d2018-11-05 01:18:10 -03001013 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -07001014 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001015 }
Ed Tanous271584a2019-07-09 16:24:22 -07001016 idx = nodes[idx].paramChildrens[index];
1017 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 break;
1019 }
1020 }
1021
1022 i--;
1023 }
1024 else
1025 {
1026 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001027 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 {
Tanousf00032d2018-11-05 01:18:10 -03001029 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 nodes[idx].children.emplace(piece, newNodeIdx);
1031 }
1032 idx = nodes[idx].children[piece];
1033 }
1034 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001035 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001036 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001037 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001038 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001039 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001040 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001041
Ed Tanous1abe55e2018-09-05 08:30:59 -07001042 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001043 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044 {
Ed Tanous271584a2019-07-09 16:24:22 -07001045 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001047 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001048 {
1049 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001050 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1051 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052 {
1053 case ParamType::INT:
1054 BMCWEB_LOG_DEBUG << "<int>";
1055 break;
1056 case ParamType::UINT:
1057 BMCWEB_LOG_DEBUG << "<uint>";
1058 break;
1059 case ParamType::DOUBLE:
1060 BMCWEB_LOG_DEBUG << "<float>";
1061 break;
1062 case ParamType::STRING:
1063 BMCWEB_LOG_DEBUG << "<str>";
1064 break;
1065 case ParamType::PATH:
1066 BMCWEB_LOG_DEBUG << "<path>";
1067 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001068 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001069 BMCWEB_LOG_DEBUG << "<ERROR>";
1070 break;
1071 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001072
Ed Tanous1abe55e2018-09-05 08:30:59 -07001073 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1074 }
1075 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001076 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001077 {
1078 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001079 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001080 << kv.first;
1081 debugNodePrint(&nodes[kv.second], level + 1);
1082 }
1083 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001084
Ed Tanous1abe55e2018-09-05 08:30:59 -07001085 public:
1086 void debugPrint()
1087 {
Ed Tanous271584a2019-07-09 16:24:22 -07001088 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001089 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001090
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 private:
1092 const Node* head() const
1093 {
1094 return &nodes.front();
1095 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001096
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097 Node* head()
1098 {
1099 return &nodes.front();
1100 }
1101
1102 unsigned newNode()
1103 {
1104 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001105 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001106 }
1107
1108 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001109};
1110
Ed Tanous1abe55e2018-09-05 08:30:59 -07001111class Router
1112{
1113 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001114 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001115
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116 DynamicRule& newRuleDynamic(const std::string& rule)
1117 {
1118 std::unique_ptr<DynamicRule> ruleObject =
1119 std::make_unique<DynamicRule>(rule);
1120 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001121 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001122
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001124 }
1125
Ed Tanous1abe55e2018-09-05 08:30:59 -07001126 template <uint64_t N>
1127 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1128 newRuleTagged(const std::string& rule)
1129 {
1130 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1131 TaggedRule>;
1132 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1133 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001134 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001135
1136 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001137 }
1138
Tanousf00032d2018-11-05 01:18:10 -03001139 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001140 {
Tanousf00032d2018-11-05 01:18:10 -03001141 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001142 {
Tanousf00032d2018-11-05 01:18:10 -03001143 return;
1144 }
Ed Tanous759cf102022-07-31 16:36:52 -07001145 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001146 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001147 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001148 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001149 {
1150 perMethods[method].rules.emplace_back(ruleObject);
1151 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001152 rule, static_cast<unsigned>(
1153 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001154 // directory case:
1155 // request to `/about' url matches `/about/' rule
1156 if (rule.size() > 2 && rule.back() == '/')
1157 {
1158 perMethods[method].trie.add(
1159 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001160 static_cast<unsigned>(perMethods[method].rules.size() -
1161 1));
Tanousf00032d2018-11-05 01:18:10 -03001162 }
1163 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001165 }
1166
Ed Tanous1abe55e2018-09-05 08:30:59 -07001167 void validate()
1168 {
Tanousf00032d2018-11-05 01:18:10 -03001169 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001170 {
1171 if (rule)
1172 {
Tanousf00032d2018-11-05 01:18:10 -03001173 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001174 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001175 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001176 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001177 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001178 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001179 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 }
1181 }
Tanousf00032d2018-11-05 01:18:10 -03001182 for (PerMethod& perMethod : perMethods)
1183 {
1184 perMethod.trie.validate();
1185 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001186 }
1187
Ed Tanous44e45182022-07-26 16:47:23 -07001188 struct FindRoute
1189 {
1190 BaseRule* rule = nullptr;
1191 RoutingParams params;
1192 };
1193
1194 struct FindRouteResponse
1195 {
1196 std::string allowHeader;
1197 FindRoute route;
1198 };
1199
Ed Tanous759cf102022-07-31 16:36:52 -07001200 FindRoute findRouteByIndex(std::string_view url, size_t index) const
1201 {
1202 FindRoute route;
1203 if (index >= perMethods.size())
1204 {
1205 BMCWEB_LOG_CRITICAL << "Bad index???";
1206 return route;
1207 }
1208 const PerMethod& perMethod = perMethods[index];
1209 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1210 if (found.first >= perMethod.rules.size())
1211 {
1212 throw std::runtime_error("Trie internal structure corrupted!");
1213 }
1214 // Found a 404 route, switch that in
1215 if (found.first != 0U)
1216 {
1217 route.rule = perMethod.rules[found.first];
1218 route.params = std::move(found.second);
1219 }
1220 return route;
1221 }
1222
1223 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -07001224 {
1225 FindRouteResponse findRoute;
1226
Ed Tanous2c9efc32022-07-31 22:08:26 -07001227 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1228 if (!verb)
1229 {
1230 return findRoute;
1231 }
1232 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -07001233 // Check to see if this url exists at any verb
1234 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1235 perMethodIndex++)
1236 {
1237 // Make sure it's safe to deference the array at that index
1238 static_assert(maxVerbIndex <
1239 std::tuple_size_v<decltype(perMethods)>);
Ed Tanous39662a32023-02-06 15:09:46 -08001240 FindRoute route =
1241 findRouteByIndex(req.url().encoded_path(), perMethodIndex);
Ed Tanous759cf102022-07-31 16:36:52 -07001242 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -07001243 {
1244 continue;
1245 }
1246 if (!findRoute.allowHeader.empty())
1247 {
1248 findRoute.allowHeader += ", ";
1249 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001250 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1251 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -07001252 if (perMethodIndex == reqMethodIndex)
1253 {
Ed Tanous759cf102022-07-31 16:36:52 -07001254 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -07001255 }
1256 }
1257 return findRoute;
1258 }
1259
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301260 static bool isUserPrivileged(
1261 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1262 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
Ed Tanouse1f5c162023-03-10 09:02:51 -08001263 {
Ed Tanous3d183202023-03-10 09:21:58 -08001264 std::string userRole{};
1265 const std::string* userRolePtr = nullptr;
1266 const bool* remoteUser = nullptr;
1267 const bool* passwordExpired = nullptr;
1268
1269 const bool success = sdbusplus::unpackPropertiesNoThrow(
1270 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
1271 "UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
1272 "UserPasswordExpired", passwordExpired);
1273
1274 if (!success)
Ed Tanouse1f5c162023-03-10 09:02:51 -08001275 {
Ed Tanous3d183202023-03-10 09:21:58 -08001276 asyncResp->res.result(
1277 boost::beast::http::status::internal_server_error);
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301278 return false;
Ed Tanous3d183202023-03-10 09:21:58 -08001279 }
1280
1281 if (userRolePtr != nullptr)
1282 {
1283 userRole = *userRolePtr;
1284 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1285 << " userRole = " << *userRolePtr;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001286 }
1287
1288 if (remoteUser == nullptr)
1289 {
1290 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1291 asyncResp->res.result(
1292 boost::beast::http::status::internal_server_error);
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301293 return false;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001294 }
Ed Tanous3d183202023-03-10 09:21:58 -08001295 bool expired = false;
1296 if (passwordExpired == nullptr)
Ed Tanouse1f5c162023-03-10 09:02:51 -08001297 {
1298 if (!*remoteUser)
1299 {
1300 BMCWEB_LOG_ERROR
1301 << "UserPasswordExpired property is expected for"
1302 " local user but is missing or wrong type";
1303 asyncResp->res.result(
1304 boost::beast::http::status::internal_server_error);
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301305 return false;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001306 }
Ed Tanous3d183202023-03-10 09:21:58 -08001307 }
1308 else
1309 {
1310 expired = *passwordExpired;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001311 }
1312
1313 // Get the user's privileges from the role
1314 redfish::Privileges userPrivileges =
1315 redfish::getUserPrivileges(userRole);
1316
1317 // Set isConfigureSelfOnly based on D-Bus results. This
1318 // ignores the results from both pamAuthenticateUser and the
1319 // value from any previous use of this session.
Ed Tanous3d183202023-03-10 09:21:58 -08001320 req.session->isConfigureSelfOnly = expired;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001321
1322 // Modify privileges if isConfigureSelfOnly.
1323 if (req.session->isConfigureSelfOnly)
1324 {
1325 // Remove all privileges except ConfigureSelf
1326 userPrivileges = userPrivileges.intersection(
1327 redfish::Privileges{"ConfigureSelf"});
1328 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1329 }
1330
1331 if (!rule.checkPrivileges(userPrivileges))
1332 {
1333 asyncResp->res.result(boost::beast::http::status::forbidden);
1334 if (req.session->isConfigureSelfOnly)
1335 {
1336 redfish::messages::passwordChangeRequired(
1337 asyncResp->res, crow::utility::urlFromPieces(
1338 "redfish", "v1", "AccountService",
1339 "Accounts", req.session->username));
1340 }
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301341 return false;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001342 }
1343
1344 req.userRole = userRole;
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301345
1346 return true;
1347 }
1348
1349 template <typename CallbackFn>
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001350 void afterGetUserInfo(Request& req,
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301351 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1352 BaseRule& rule, CallbackFn&& callback,
1353 const boost::system::error_code& ec,
1354 const dbus::utility::DBusPropertiesMap& userInfoMap)
1355 {
1356 if (ec)
1357 {
1358 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1359 asyncResp->res.result(
1360 boost::beast::http::status::internal_server_error);
1361 return;
1362 }
1363
1364 if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap))
1365 {
1366 // User is not privileged
1367 BMCWEB_LOG_ERROR << "Insufficient Privilege";
1368 asyncResp->res.result(boost::beast::http::status::forbidden);
1369 return;
1370 }
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001371 callback(req);
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301372 }
1373
1374 template <typename CallbackFn>
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001375 void validatePrivilege(Request& req,
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301376 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1377 BaseRule& rule, CallbackFn&& callback)
1378 {
Ed Tanous915d2d42023-03-15 13:09:34 -07001379 if (req.session == nullptr)
1380 {
1381 return;
1382 }
1383 std::string username = req.session->username;
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301384 crow::connections::systemBus->async_method_call(
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001385 [this, &req, asyncResp, &rule,
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301386 callback(std::forward<CallbackFn>(callback))](
1387 const boost::system::error_code& ec,
1388 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001389 afterGetUserInfo(req, asyncResp, rule,
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301390 std::forward<CallbackFn>(callback), ec,
1391 userInfoMap);
1392 },
1393 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous915d2d42023-03-15 13:09:34 -07001394 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
Ed Tanouse1f5c162023-03-10 09:02:51 -08001395 }
1396
Ed Tanous1abe55e2018-09-05 08:30:59 -07001397 template <typename Adaptor>
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301398 void handleUpgrade(Request& req,
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301399 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1400 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001401 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001402 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1403 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001404 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301405 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001406 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001407 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001408 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -03001409 Trie& trie = perMethod.trie;
1410 std::vector<BaseRule*>& rules = perMethod.rules;
1411
Ed Tanous39662a32023-02-06 15:09:46 -08001412 const std::pair<unsigned, RoutingParams>& found =
1413 trie.find(req.url().encoded_path());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001414 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001415 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 {
Ed Tanous39662a32023-02-06 15:09:46 -08001417 BMCWEB_LOG_DEBUG << "Cannot match rules "
1418 << req.url().encoded_path();
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301419 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420 return;
1421 }
1422
1423 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001424 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001425 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001426 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001427
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301428 BaseRule& rule = *rules[ruleIndex];
1429 size_t methods = rule.getMethods();
1430 if ((methods & (1U << static_cast<size_t>(*verb))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001431 {
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301432 BMCWEB_LOG_DEBUG
1433 << "Rule found but method mismatch: "
1434 << req.url().encoded_path() << " with " << req.methodString()
1435 << "(" << static_cast<uint32_t>(*verb) << ") / " << methods;
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301436 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437 return;
1438 }
1439
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301440 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rule.rule << "' "
1441 << static_cast<uint32_t>(*verb) << " / " << methods;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001442
P Dheeraj Srujan Kumar7e9093e2021-09-18 01:19:04 +05301443 // TODO(ed) This should be able to use std::bind_front, but it doesn't
1444 // appear to work with the std::move on adaptor.
Ed Tanous915d2d42023-03-15 13:09:34 -07001445 validatePrivilege(
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001446 req, asyncResp, rule,
Ed Tanous915d2d42023-03-15 13:09:34 -07001447 [&rule, asyncResp, adaptor(std::forward<Adaptor>(adaptor))](
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001448 Request& thisReq) mutable {
Ed Tanous915d2d42023-03-15 13:09:34 -07001449 rule.handleUpgrade(thisReq, asyncResp, std::move(adaptor));
1450 });
Ed Tanous7045c8d2017-04-03 10:04:37 -07001451 }
1452
zhanghch058d1b46d2021-04-01 11:18:24 +08001453 void handle(Request& req,
1454 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001456 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1457 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001458 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001459 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001460 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001461 }
Ed Tanous44e45182022-07-26 16:47:23 -07001462
1463 FindRouteResponse foundRoute = findRoute(req);
1464
Ed Tanous759cf102022-07-31 16:36:52 -07001465 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -07001466 {
Ed Tanous759cf102022-07-31 16:36:52 -07001467 // Couldn't find a normal route with any verb, try looking for a 404
1468 // route
1469 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001470 {
Ed Tanous39662a32023-02-06 15:09:46 -08001471 foundRoute.route =
1472 findRouteByIndex(req.url().encoded_path(), notFoundIndex);
Ed Tanous759cf102022-07-31 16:36:52 -07001473 }
1474 else
1475 {
1476 // See if we have a method not allowed (405) handler
Ed Tanous39662a32023-02-06 15:09:46 -08001477 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
1478 methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -07001479 }
1480 }
Ed Tanous759cf102022-07-31 16:36:52 -07001481
1482 // Fill in the allow header if it's valid
1483 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001484 {
Ed Tanous759cf102022-07-31 16:36:52 -07001485
Ed Tanous88a03c52022-03-14 10:16:07 -07001486 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001487 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001488 }
Tanousf00032d2018-11-05 01:18:10 -03001489
Ed Tanous44e45182022-07-26 16:47:23 -07001490 // If we couldn't find a real route or a 404 route, return a generic
1491 // response
1492 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001493 {
Ed Tanous44e45182022-07-26 16:47:23 -07001494 if (foundRoute.allowHeader.empty())
1495 {
1496 asyncResp->res.result(boost::beast::http::status::not_found);
1497 }
1498 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001499 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001500 asyncResp->res.result(
1501 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001502 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001503 return;
1504 }
1505
Ed Tanous44e45182022-07-26 16:47:23 -07001506 BaseRule& rule = *foundRoute.route.rule;
1507 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508
Ed Tanous44e45182022-07-26 16:47:23 -07001509 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Snehalatha Venkatesh1c99da02022-12-27 06:45:35 +00001510 << static_cast<uint32_t>(*verb) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001511 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001512
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001513 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001514 {
Ed Tanous44e45182022-07-26 16:47:23 -07001515 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001516 return;
1517 }
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001518 validatePrivilege(req, asyncResp, rule,
1519 [&rule, asyncResp, params](Request& thisReq) mutable {
Ed Tanous915d2d42023-03-15 13:09:34 -07001520 rule.handle(thisReq, asyncResp, params);
Jonathan Domand3c8ce62023-03-21 18:17:06 -07001521 });
Ed Tanous7045c8d2017-04-03 10:04:37 -07001522 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001523
Ed Tanous1abe55e2018-09-05 08:30:59 -07001524 void debugPrint()
1525 {
Ed Tanous271584a2019-07-09 16:24:22 -07001526 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001527 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001528 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1529 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001530 perMethods[i].trie.debugPrint();
1531 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001532 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001533
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 std::vector<const std::string*> getRoutes(const std::string& parent)
1535 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001536 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001537
1538 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539 {
Tanousf00032d2018-11-05 01:18:10 -03001540 std::vector<unsigned> x;
1541 pm.trie.findRouteIndexes(parent, x);
1542 for (unsigned index : x)
1543 {
1544 ret.push_back(&pm.rules[index]->rule);
1545 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001546 }
1547 return ret;
1548 }
1549
1550 private:
Tanousf00032d2018-11-05 01:18:10 -03001551 struct PerMethod
1552 {
1553 std::vector<BaseRule*> rules;
1554 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001555 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001556 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001557 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001558 {}
Tanousf00032d2018-11-05 01:18:10 -03001559 };
Ed Tanous888880a2020-08-24 13:48:50 -07001560
Ed Tanous759cf102022-07-31 16:36:52 -07001561 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001562 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001563};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001564} // namespace crow