blob: 40218fbf6833109d39fc80ffa965e611b7dc24e8 [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 Kumara9f076e2021-10-18 22:45:37 +053060 virtual void
61 handleUpgrade(const Request& /*req*/,
62 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
63 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070064 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053065 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070067#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080068 virtual void handleUpgrade(
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053069 const Request& /*req*/,
70 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -080071 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +053073 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070075#endif
76
Ed Tanous9eb808c2022-01-25 10:19:23 -080077 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 {
79 return methodsBitfield;
80 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070081
Tanousf00032d2018-11-05 01:18:10 -030082 bool checkPrivileges(const redfish::Privileges& userPrivileges)
83 {
84 // If there are no privileges assigned, assume no privileges
85 // required
86 if (privilegesSet.empty())
87 {
88 return true;
89 }
90
91 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
92 {
93 if (userPrivileges.isSupersetOf(requiredPrivileges))
94 {
95 return true;
96 }
97 }
98 return false;
99 }
100
Ed Tanous2c9efc32022-07-31 22:08:26 -0700101 size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
Ed Tanous44e45182022-07-26 16:47:23 -0700102 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
Ed Tanous759cf102022-07-31 16:36:52 -0700103 methodNotAllowedIndex,
Ed Tanous44e45182022-07-26 16:47:23 -0700104 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105
Tanousf00032d2018-11-05 01:18:10 -0300106 std::vector<redfish::Privileges> privilegesSet;
107
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::string rule;
109 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700112
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500114 template <typename T>
115 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700116};
117
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118namespace detail
119{
120namespace routing_handler_call_helper
121{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500122template <typename T, int Pos>
123struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124{
125 using type = T;
126 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700127};
128
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500129template <typename H1>
130struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131{
132 H1& handler;
133 const RoutingParams& params;
134 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800135 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700136};
137
138template <typename F, int NInt, int NUint, int NDouble, int NString,
139 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500141{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700142
143template <typename F, int NInt, int NUint, int NDouble, int NString,
144 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700145struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700146 black_magic::S<Args2...>>
147{
148 void operator()(F cparams)
149 {
150 using pushed = typename black_magic::S<Args2...>::template push_back<
151 CallPair<int64_t, NInt>>;
152 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
153 pushed>()(cparams);
154 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700155};
156
157template <typename F, int NInt, int NUint, int NDouble, int NString,
158 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700159struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
161{
162 void operator()(F cparams)
163 {
164 using pushed = typename black_magic::S<Args2...>::template push_back<
165 CallPair<uint64_t, NUint>>;
166 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
167 pushed>()(cparams);
168 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700169};
170
171template <typename F, int NInt, int NUint, int NDouble, int NString,
172 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700173struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 black_magic::S<Args2...>>
175{
176 void operator()(F cparams)
177 {
178 using pushed = typename black_magic::S<Args2...>::template push_back<
179 CallPair<double, NDouble>>;
180 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
181 pushed>()(cparams);
182 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700183};
184
185template <typename F, int NInt, int NUint, int NDouble, int NString,
186 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700187struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
189{
190 void operator()(F cparams)
191 {
192 using pushed = typename black_magic::S<Args2...>::template push_back<
193 CallPair<std::string, NString>>;
194 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
195 pushed>()(cparams);
196 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700197};
198
199template <typename F, int NInt, int NUint, int NDouble, int NString,
200 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700201struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700202 black_magic::S<Args1...>>
203{
204 void operator()(F cparams)
205 {
206 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800207 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208 cparams.params.template get<typename Args1::type>(Args1::pos)...);
209 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700210};
211
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212template <typename Func, typename... ArgsWrapped>
213struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214{
215 template <typename... Args>
216 void set(
217 Func f,
218 typename std::enable_if<
219 !std::is_same<
220 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
221 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800222 int>::type /*enable*/
223 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800225 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800226 const Request&,
227 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
228 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229 }
230
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500231 template <typename Req, typename... Args>
232 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700234 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700236
zhanghch058d1b46d2021-04-01 11:18:24 +0800237 void operator()(const Request& req,
238 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
239 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700240 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800241 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700243
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 Func f;
245 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700246
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 template <typename... Args>
248 void set(
249 Func f,
250 typename std::enable_if<
251 std::is_same<
252 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
253 const Request&>::value &&
254 !std::is_same<typename std::tuple_element<
255 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800256 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800257 int>::type /*enable*/
258 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 {
260 handler = ReqHandlerWrapper<Args...>(std::move(f));
261 /*handler = (
262 [f = std::move(f)]
263 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700264 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 res.end();
266 });*/
267 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700268
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 template <typename... Args>
270 void set(
271 Func f,
272 typename std::enable_if<
273 std::is_same<
274 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
275 const Request&>::value &&
276 std::is_same<typename std::tuple_element<
277 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800278 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800279 int>::type /*enable*/
280 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 {
282 handler = std::move(f);
283 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700284
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500285 template <typename... Args>
286 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800288 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800289 const crow::Request& /*req*/,
290 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700292 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700294
Ed Tanous1abe55e2018-09-05 08:30:59 -0700295 template <typename... Args>
296 struct HandlerTypeHelper<const Request&, Args...>
297 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800298 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800299 const crow::Request& /*req*/,
300 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700302 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700304
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800306 struct HandlerTypeHelper<const Request&,
307 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800309 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800310 const crow::Request& /*req*/,
311 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700313 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314 };
315
316 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
317
zhanghch058d1b46d2021-04-01 11:18:24 +0800318 void operator()(const Request& req,
319 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320 const RoutingParams& params)
321 {
322 detail::routing_handler_call_helper::Call<
323 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
324 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
325 black_magic::S<>>()(
326 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800327 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330} // namespace routing_handler_call_helper
331} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700332
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333class WebSocketRule : public BaseRule
334{
335 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700336
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700338 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700340
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500342 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700343
Ed Tanous104f09c2022-01-25 09:56:04 -0800344 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800345 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800346 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700347 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800348 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530351 void handleUpgrade(const Request& req,
352 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800353 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 {
Nan Zhou93c02022022-02-24 18:21:07 -0800355 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530356 std::shared_ptr<
357 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
358 myConnection = std::make_shared<
359 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000360 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530361 closeHandler, errorHandler);
362 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700363 }
364#ifdef BMCWEB_ENABLE_SSL
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +0530365 void handleUpgrade(const Request& req,
366 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800367 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
368 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700369 {
Nan Zhou93c02022022-02-24 18:21:07 -0800370 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800371 std::shared_ptr<crow::websocket::ConnectionImpl<
372 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
373 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
374 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000375 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800376 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 myConnection->start();
378 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700379#endif
380
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500381 template <typename Func>
382 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700383 {
384 openHandler = f;
385 return *this;
386 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700387
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 template <typename Func>
389 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700390 {
391 messageHandler = 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& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397 {
398 closeHandler = 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& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
405 errorHandler = f;
406 return *this;
407 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700408
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 protected:
zhanghch0577726382021-10-21 14:07:57 +0800410 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
412 messageHandler;
413 std::function<void(crow::websocket::Connection&, const std::string&)>
414 closeHandler;
415 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700416};
417
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500418template <typename T>
419struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420{
421 using self_t = T;
422 WebSocketRule& websocket()
423 {
Ed Tanous271584a2019-07-09 16:24:22 -0700424 self_t* self = static_cast<self_t*>(this);
425 WebSocketRule* p = new WebSocketRule(self->rule);
426 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700427 return *p;
428 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700429
Ed Tanous26ccae32023-02-16 10:28:44 -0800430 self_t& name(std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 {
Ed Tanous271584a2019-07-09 16:24:22 -0700432 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700433 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700434 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700436
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 self_t& methods(boost::beast::http::verb method)
438 {
Ed Tanous271584a2019-07-09 16:24:22 -0700439 self_t* self = static_cast<self_t*>(this);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700440 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
441 if (verb)
442 {
443 self->methodsBitfield = 1U << static_cast<size_t>(*verb);
444 }
Ed Tanous271584a2019-07-09 16:24:22 -0700445 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000449 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 {
Ed Tanous271584a2019-07-09 16:24:22 -0700451 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000452 methods(argsMethod...);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700453 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
454 if (verb)
455 {
456 self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
457 }
Ed Tanous271584a2019-07-09 16:24:22 -0700458 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 }
Tanousf00032d2018-11-05 01:18:10 -0300460
Ed Tanous44e45182022-07-26 16:47:23 -0700461 self_t& notFound()
462 {
463 self_t* self = static_cast<self_t*>(this);
464 self->methodsBitfield = 1U << notFoundIndex;
465 return *self;
466 }
467
Ed Tanous759cf102022-07-31 16:36:52 -0700468 self_t& methodNotAllowed()
469 {
470 self_t* self = static_cast<self_t*>(this);
471 self->methodsBitfield = 1U << methodNotAllowedIndex;
472 return *self;
473 }
474
Ed Tanous432a8902021-06-14 15:28:56 -0700475 self_t& privileges(
476 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300477 {
Ed Tanous271584a2019-07-09 16:24:22 -0700478 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700479 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300480 {
Ed Tanous271584a2019-07-09 16:24:22 -0700481 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300482 }
Ed Tanous271584a2019-07-09 16:24:22 -0700483 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300484 }
Ed Tanoused398212021-06-09 17:05:54 -0700485
486 template <size_t N, typename... MethodArgs>
487 self_t& privileges(const std::array<redfish::Privileges, N>& p)
488 {
489 self_t* self = static_cast<self_t*>(this);
490 for (const redfish::Privileges& privilege : p)
491 {
492 self->privilegesSet.emplace_back(privilege);
493 }
494 return *self;
495 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700496};
497
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
499{
500 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700501 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500502 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700503
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 void validate() override
505 {
506 if (!erasedHandler)
507 {
508 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
509 "no handler for url " + rule);
510 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700511 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700512
zhanghch058d1b46d2021-04-01 11:18:24 +0800513 void handle(const Request& req,
514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 const RoutingParams& params) override
516 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800517 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700519
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500520 template <typename Func>
521 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
Ed Tanousc867a832022-03-10 14:17:00 -0800523 using boost::callable_traits::args_t;
524 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
525 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
526 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 }
528
529 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500530 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 // enable_if Arg1 != request
532
533 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800534 std::function<void(const Request&,
535 const std::shared_ptr<bmcweb::AsyncResp>&,
536 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800537 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 {
Ed Tanousc867a832022-03-10 14:17:00 -0800539 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540
541 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700542 black_magic::getParameterTag(rule.c_str()),
543 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 typename function_t::template arg<Indices>...>::value))
545 {
546 throw std::runtime_error("routeDynamic: Handler type is mismatched "
547 "with URL parameters: " +
548 rule);
549 }
550 auto ret = detail::routing_handler_call_helper::Wrapped<
551 Func, typename function_t::template arg<Indices>...>();
552 ret.template set<typename function_t::template arg<Indices>...>(
553 std::move(f));
554 return ret;
555 }
556
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500557 template <typename Func>
558 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 {
560 nameStr = std::move(name);
561 (*this).template operator()<Func>(std::forward(f));
562 }
563
564 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800565 std::function<void(const Request&,
566 const std::shared_ptr<bmcweb::AsyncResp>&,
567 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700569};
570
571template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500572class TaggedRule :
573 public BaseRule,
574 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575{
576 public:
577 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700578
Ed Tanous4e23a442022-06-06 09:57:26 -0700579 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500580 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700581
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 void validate() override
583 {
584 if (!handler)
585 {
586 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
587 "no handler for url " + rule);
588 }
589 }
590
591 template <typename Func>
592 typename std::enable_if<
593 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
594 void>::type
595 operator()(Func&& f)
596 {
597 static_assert(
598 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
599 black_magic::CallHelper<
600 Func, black_magic::S<crow::Request, Args...>>::value,
601 "Handler type is mismatched with URL parameters");
602 static_assert(
603 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
604 "Handler function cannot have void return type; valid return "
605 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500606 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800608 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800609 const Request&,
610 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
611 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 }
613
614 template <typename Func>
615 typename std::enable_if<
616 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700617 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700618 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 void>::type
620 operator()(Func&& f)
621 {
622 static_assert(
623 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
624 black_magic::CallHelper<
625 Func, black_magic::S<crow::Request, Args...>>::value,
626 "Handler type is mismatched with URL parameters");
627 static_assert(
628 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
629 std::declval<Args>()...))>::value,
630 "Handler function cannot have void return type; valid return "
631 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500632 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700633
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800634 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800635 const crow::Request& req,
636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700639
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 template <typename Func>
641 typename std::enable_if<
642 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
643 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700644 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 void>::type
646 operator()(Func&& f)
647 {
648 static_assert(
649 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
650 black_magic::CallHelper<
651 Func, black_magic::S<crow::Request, Args...>>::value ||
652 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800653 Func, black_magic::S<crow::Request,
654 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 Args...>>::value,
656 "Handler type is mismatched with URL parameters");
657 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800658 std::is_same<
659 void,
660 decltype(f(std::declval<crow::Request>(),
661 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
662 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300663 "Handler function with response argument should have void "
664 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700666
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800667 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700669
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500670 template <typename Func>
Ed Tanous26ccae32023-02-16 10:28:44 -0800671 void operator()(std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700673 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 (*this).template operator()<Func>(std::forward(f));
675 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700676
zhanghch058d1b46d2021-04-01 11:18:24 +0800677 void handle(const Request& req,
678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 const RoutingParams& params) override
680 {
681 detail::routing_handler_call_helper::Call<
682 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
683 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
684 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800685 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700687
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800689 std::function<void(const crow::Request&,
690 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
691 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700692};
693
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694class Trie
695{
696 public:
697 struct Node
698 {
699 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700700 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
701 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800702 using ChildMap = boost::container::flat_map<
703 std::string, unsigned, std::less<>,
704 std::vector<std::pair<std::string, unsigned>>>;
705 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700706
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 bool isSimpleNode() const
708 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800709 return ruleIndex == 0 &&
710 std::all_of(std::begin(paramChildrens),
711 std::end(paramChildrens),
712 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700713 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700714 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700715
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500717 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718
719 private:
720 void optimizeNode(Node* node)
721 {
Ed Tanous271584a2019-07-09 16:24:22 -0700722 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800724 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700725 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700727 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 Node* child = &nodes[x];
729 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700730 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700732 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700734 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800736 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 {
738 Node* child = &nodes[kv.second];
739 if (!child->isSimpleNode())
740 {
741 mergeWithChild = false;
742 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700743 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 }
745 if (mergeWithChild)
746 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800747 Node::ChildMap merged;
748 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 {
750 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800751 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300752 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 {
754 merged[kv.first + childKv.first] = childKv.second;
755 }
756 }
757 node->children = std::move(merged);
758 optimizeNode(node);
759 }
760 else
761 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800762 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700763 {
764 Node* child = &nodes[kv.second];
765 optimizeNode(child);
766 }
767 }
768 }
769
770 void optimize()
771 {
772 optimizeNode(head());
773 }
774
775 public:
776 void validate()
777 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 optimize();
779 }
780
Ed Tanous81ce6092020-12-17 16:54:55 +0000781 void findRouteIndexes(const std::string& reqUrl,
782 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300783 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 {
785 if (node == nullptr)
786 {
787 node = head();
788 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800789 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
791 const std::string& fragment = kv.first;
792 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000793 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 {
795 if (child->ruleIndex != 0 && fragment != "/")
796 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000797 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000799 findRouteIndexes(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 else
803 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000804 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 {
Ed Tanous271584a2019-07-09 16:24:22 -0700806 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000807 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700808 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 }
810 }
811 }
812 }
813
814 std::pair<unsigned, RoutingParams>
Ed Tanous26ccae32023-02-16 10:28:44 -0800815 find(std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700816 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 {
818 RoutingParams empty;
819 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700820 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700822 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823
824 unsigned found{};
825 RoutingParams matchParams;
826
827 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700828 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700830 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000831 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700832 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700834 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835
836 auto updateFound =
837 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 if (ret.first != 0U && (found == 0U || found > ret.first))
839 {
840 found = ret.first;
841 matchParams = std::move(ret.second);
842 }
843 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844
Ed Tanouse662eae2022-01-25 10:39:19 -0800845 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000847 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
849 {
Ed Tanous543f4402022-01-06 13:12:53 -0800850 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 errno = 0;
852 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000853 std::strtoll(reqUrl.data() + pos, &eptr, 10);
854 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 {
856 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000857 std::pair<unsigned, RoutingParams> ret =
858 find(reqUrl,
859 &nodes[node->paramChildrens[static_cast<size_t>(
860 ParamType::INT)]],
861 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 updateFound(ret);
863 params->intParams.pop_back();
864 }
865 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700866 }
867
Ed Tanouse662eae2022-01-25 10:39:19 -0800868 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000870 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 if ((c >= '0' && c <= '9') || c == '+')
872 {
Ed Tanous543f4402022-01-06 13:12:53 -0800873 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 errno = 0;
875 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000876 std::strtoull(reqUrl.data() + pos, &eptr, 10);
877 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 {
879 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000880 std::pair<unsigned, RoutingParams> ret =
881 find(reqUrl,
882 &nodes[node->paramChildrens[static_cast<size_t>(
883 ParamType::UINT)]],
884 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 updateFound(ret);
886 params->uintParams.pop_back();
887 }
888 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700889 }
890
Ed Tanouse662eae2022-01-25 10:39:19 -0800891 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000893 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
895 {
Ed Tanous543f4402022-01-06 13:12:53 -0800896 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000898 double value = std::strtod(reqUrl.data() + pos, &eptr);
899 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 {
901 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000902 std::pair<unsigned, RoutingParams> ret =
903 find(reqUrl,
904 &nodes[node->paramChildrens[static_cast<size_t>(
905 ParamType::DOUBLE)]],
906 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 updateFound(ret);
908 params->doubleParams.pop_back();
909 }
910 }
911 }
912
Ed Tanouse662eae2022-01-25 10:39:19 -0800913 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000915 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000916 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000918 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700919 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700921 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700922 }
923
924 if (epos != pos)
925 {
926 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000927 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300928 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000929 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700930 &nodes[node->paramChildrens[static_cast<size_t>(
931 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000932 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933 updateFound(ret);
934 params->stringParams.pop_back();
935 }
936 }
937
Ed Tanouse662eae2022-01-25 10:39:19 -0800938 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000940 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941
942 if (epos != pos)
943 {
944 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000945 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700946 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000947 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700948 &nodes[node->paramChildrens[static_cast<size_t>(
949 ParamType::PATH)]],
950 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 updateFound(ret);
952 params->stringParams.pop_back();
953 }
954 }
955
Ed Tanousa94ac612022-02-22 11:13:24 -0800956 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 {
958 const std::string& fragment = kv.first;
959 const Node* child = &nodes[kv.second];
960
Ed Tanous81ce6092020-12-17 16:54:55 +0000961 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962 {
Tanousf00032d2018-11-05 01:18:10 -0300963 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000964 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 updateFound(ret);
966 }
967 }
968
969 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700970 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971
972 void add(const std::string& url, unsigned ruleIndex)
973 {
Ed Tanous271584a2019-07-09 16:24:22 -0700974 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700975
976 for (unsigned i = 0; i < url.size(); i++)
977 {
978 char c = url[i];
979 if (c == '<')
980 {
Tanousf00032d2018-11-05 01:18:10 -0300981 const static std::array<std::pair<ParamType, std::string>, 7>
982 paramTraits = {{
983 {ParamType::INT, "<int>"},
984 {ParamType::UINT, "<uint>"},
985 {ParamType::DOUBLE, "<float>"},
986 {ParamType::DOUBLE, "<double>"},
987 {ParamType::STRING, "<str>"},
988 {ParamType::STRING, "<string>"},
989 {ParamType::PATH, "<path>"},
990 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700991
Tanousf00032d2018-11-05 01:18:10 -0300992 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 {
Tanousf00032d2018-11-05 01:18:10 -0300994 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700995 {
Ed Tanous271584a2019-07-09 16:24:22 -0700996 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800997 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 {
Tanousf00032d2018-11-05 01:18:10 -0300999 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -07001000 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 }
Ed Tanous271584a2019-07-09 16:24:22 -07001002 idx = nodes[idx].paramChildrens[index];
1003 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001004 break;
1005 }
1006 }
1007
1008 i--;
1009 }
1010 else
1011 {
1012 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001013 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 {
Tanousf00032d2018-11-05 01:18:10 -03001015 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 nodes[idx].children.emplace(piece, newNodeIdx);
1017 }
1018 idx = nodes[idx].children[piece];
1019 }
1020 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001021 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001022 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001023 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001024 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001025 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001026 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001029 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 {
Ed Tanous271584a2019-07-09 16:24:22 -07001031 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001033 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 {
1035 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001036 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1037 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001038 {
1039 case ParamType::INT:
1040 BMCWEB_LOG_DEBUG << "<int>";
1041 break;
1042 case ParamType::UINT:
1043 BMCWEB_LOG_DEBUG << "<uint>";
1044 break;
1045 case ParamType::DOUBLE:
1046 BMCWEB_LOG_DEBUG << "<float>";
1047 break;
1048 case ParamType::STRING:
1049 BMCWEB_LOG_DEBUG << "<str>";
1050 break;
1051 case ParamType::PATH:
1052 BMCWEB_LOG_DEBUG << "<path>";
1053 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001054 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055 BMCWEB_LOG_DEBUG << "<ERROR>";
1056 break;
1057 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001058
Ed Tanous1abe55e2018-09-05 08:30:59 -07001059 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1060 }
1061 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001062 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 {
1064 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001065 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 << kv.first;
1067 debugNodePrint(&nodes[kv.second], level + 1);
1068 }
1069 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001070
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 public:
1072 void debugPrint()
1073 {
Ed Tanous271584a2019-07-09 16:24:22 -07001074 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001076
Ed Tanous1abe55e2018-09-05 08:30:59 -07001077 private:
1078 const Node* head() const
1079 {
1080 return &nodes.front();
1081 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001082
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 Node* head()
1084 {
1085 return &nodes.front();
1086 }
1087
1088 unsigned newNode()
1089 {
1090 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001091 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001092 }
1093
1094 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001095};
1096
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097class Router
1098{
1099 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001100 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001101
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 DynamicRule& newRuleDynamic(const std::string& rule)
1103 {
1104 std::unique_ptr<DynamicRule> ruleObject =
1105 std::make_unique<DynamicRule>(rule);
1106 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001107 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001108
Ed Tanous1abe55e2018-09-05 08:30:59 -07001109 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001110 }
1111
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 template <uint64_t N>
1113 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1114 newRuleTagged(const std::string& rule)
1115 {
1116 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1117 TaggedRule>;
1118 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1119 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001120 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001121
1122 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001123 }
1124
Tanousf00032d2018-11-05 01:18:10 -03001125 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001126 {
Tanousf00032d2018-11-05 01:18:10 -03001127 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001128 {
Tanousf00032d2018-11-05 01:18:10 -03001129 return;
1130 }
Ed Tanous759cf102022-07-31 16:36:52 -07001131 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001132 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001133 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001134 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001135 {
1136 perMethods[method].rules.emplace_back(ruleObject);
1137 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001138 rule, static_cast<unsigned>(
1139 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001140 // directory case:
1141 // request to `/about' url matches `/about/' rule
1142 if (rule.size() > 2 && rule.back() == '/')
1143 {
1144 perMethods[method].trie.add(
1145 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001146 static_cast<unsigned>(perMethods[method].rules.size() -
1147 1));
Tanousf00032d2018-11-05 01:18:10 -03001148 }
1149 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001151 }
1152
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 void validate()
1154 {
Tanousf00032d2018-11-05 01:18:10 -03001155 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 {
1157 if (rule)
1158 {
Tanousf00032d2018-11-05 01:18:10 -03001159 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001160 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001161 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001162 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001163 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001165 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001166 }
1167 }
Tanousf00032d2018-11-05 01:18:10 -03001168 for (PerMethod& perMethod : perMethods)
1169 {
1170 perMethod.trie.validate();
1171 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001172 }
1173
Ed Tanous44e45182022-07-26 16:47:23 -07001174 struct FindRoute
1175 {
1176 BaseRule* rule = nullptr;
1177 RoutingParams params;
1178 };
1179
1180 struct FindRouteResponse
1181 {
1182 std::string allowHeader;
1183 FindRoute route;
1184 };
1185
Ed Tanous759cf102022-07-31 16:36:52 -07001186 FindRoute findRouteByIndex(std::string_view url, size_t index) const
1187 {
1188 FindRoute route;
1189 if (index >= perMethods.size())
1190 {
1191 BMCWEB_LOG_CRITICAL << "Bad index???";
1192 return route;
1193 }
1194 const PerMethod& perMethod = perMethods[index];
1195 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1196 if (found.first >= perMethod.rules.size())
1197 {
1198 throw std::runtime_error("Trie internal structure corrupted!");
1199 }
1200 // Found a 404 route, switch that in
1201 if (found.first != 0U)
1202 {
1203 route.rule = perMethod.rules[found.first];
1204 route.params = std::move(found.second);
1205 }
1206 return route;
1207 }
1208
1209 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -07001210 {
1211 FindRouteResponse findRoute;
1212
Ed Tanous2c9efc32022-07-31 22:08:26 -07001213 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1214 if (!verb)
1215 {
1216 return findRoute;
1217 }
1218 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -07001219 // Check to see if this url exists at any verb
1220 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1221 perMethodIndex++)
1222 {
1223 // Make sure it's safe to deference the array at that index
1224 static_assert(maxVerbIndex <
1225 std::tuple_size_v<decltype(perMethods)>);
Ed Tanous39662a32023-02-06 15:09:46 -08001226 FindRoute route =
1227 findRouteByIndex(req.url().encoded_path(), perMethodIndex);
Ed Tanous759cf102022-07-31 16:36:52 -07001228 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -07001229 {
1230 continue;
1231 }
1232 if (!findRoute.allowHeader.empty())
1233 {
1234 findRoute.allowHeader += ", ";
1235 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001236 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1237 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -07001238 if (perMethodIndex == reqMethodIndex)
1239 {
Ed Tanous759cf102022-07-31 16:36:52 -07001240 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -07001241 }
1242 }
1243 return findRoute;
1244 }
1245
Ed Tanouse1f5c162023-03-10 09:02:51 -08001246 static void
1247 isUserPrivileged(const boost::system::error_code& ec, Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 BaseRule& rule, const RoutingParams& params,
1250 const dbus::utility::DBusPropertiesMap& userInfoMap)
1251 {
1252 if (ec)
1253 {
1254 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1255 asyncResp->res.result(
1256 boost::beast::http::status::internal_server_error);
1257 return;
1258 }
Ed Tanouse1f5c162023-03-10 09:02:51 -08001259
Ed Tanous3d183202023-03-10 09:21:58 -08001260 std::string userRole{};
1261 const std::string* userRolePtr = nullptr;
1262 const bool* remoteUser = nullptr;
1263 const bool* passwordExpired = nullptr;
1264
1265 const bool success = sdbusplus::unpackPropertiesNoThrow(
1266 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
1267 "UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
1268 "UserPasswordExpired", passwordExpired);
1269
1270 if (!success)
Ed Tanouse1f5c162023-03-10 09:02:51 -08001271 {
Ed Tanous3d183202023-03-10 09:21:58 -08001272 asyncResp->res.result(
1273 boost::beast::http::status::internal_server_error);
1274 return;
1275 }
1276
1277 if (userRolePtr != nullptr)
1278 {
1279 userRole = *userRolePtr;
1280 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1281 << " userRole = " << *userRolePtr;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001282 }
1283
1284 if (remoteUser == nullptr)
1285 {
1286 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1287 asyncResp->res.result(
1288 boost::beast::http::status::internal_server_error);
1289 return;
1290 }
Ed Tanous3d183202023-03-10 09:21:58 -08001291 bool expired = false;
1292 if (passwordExpired == nullptr)
Ed Tanouse1f5c162023-03-10 09:02:51 -08001293 {
1294 if (!*remoteUser)
1295 {
1296 BMCWEB_LOG_ERROR
1297 << "UserPasswordExpired property is expected for"
1298 " local user but is missing or wrong type";
1299 asyncResp->res.result(
1300 boost::beast::http::status::internal_server_error);
1301 return;
1302 }
Ed Tanous3d183202023-03-10 09:21:58 -08001303 }
1304 else
1305 {
1306 expired = *passwordExpired;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001307 }
1308
1309 // Get the user's privileges from the role
1310 redfish::Privileges userPrivileges =
1311 redfish::getUserPrivileges(userRole);
1312
1313 // Set isConfigureSelfOnly based on D-Bus results. This
1314 // ignores the results from both pamAuthenticateUser and the
1315 // value from any previous use of this session.
Ed Tanous3d183202023-03-10 09:21:58 -08001316 req.session->isConfigureSelfOnly = expired;
Ed Tanouse1f5c162023-03-10 09:02:51 -08001317
1318 // Modify privileges if isConfigureSelfOnly.
1319 if (req.session->isConfigureSelfOnly)
1320 {
1321 // Remove all privileges except ConfigureSelf
1322 userPrivileges = userPrivileges.intersection(
1323 redfish::Privileges{"ConfigureSelf"});
1324 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1325 }
1326
1327 if (!rule.checkPrivileges(userPrivileges))
1328 {
1329 asyncResp->res.result(boost::beast::http::status::forbidden);
1330 if (req.session->isConfigureSelfOnly)
1331 {
1332 redfish::messages::passwordChangeRequired(
1333 asyncResp->res, crow::utility::urlFromPieces(
1334 "redfish", "v1", "AccountService",
1335 "Accounts", req.session->username));
1336 }
1337 return;
1338 }
1339
1340 req.userRole = userRole;
1341 rule.handle(req, asyncResp, params);
1342 }
1343
Ed Tanous1abe55e2018-09-05 08:30:59 -07001344 template <typename Adaptor>
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301345 void handleUpgrade(const Request& req,
1346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1347 Adaptor&& adaptor)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001349 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1350 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001351 {
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301352 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001353 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001354 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001355 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -03001356 Trie& trie = perMethod.trie;
1357 std::vector<BaseRule*>& rules = perMethod.rules;
1358
Ed Tanous39662a32023-02-06 15:09:46 -08001359 const std::pair<unsigned, RoutingParams>& found =
1360 trie.find(req.url().encoded_path());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001361 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001362 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001363 {
Ed Tanous39662a32023-02-06 15:09:46 -08001364 BMCWEB_LOG_DEBUG << "Cannot match rules "
1365 << req.url().encoded_path();
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301366 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001367 return;
1368 }
1369
1370 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001371 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001372 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001373 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001374
Ed Tanous271584a2019-07-09 16:24:22 -07001375 if ((rules[ruleIndex]->getMethods() &
Snehalatha Venkatesh1c99da02022-12-27 06:45:35 +00001376 (1U << static_cast<size_t>(*verb))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001377 {
Ed Tanous39662a32023-02-06 15:09:46 -08001378 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: "
1379 << req.url().encoded_path() << " with "
1380 << req.methodString() << "("
Snehalatha Venkatesh1c99da02022-12-27 06:45:35 +00001381 << static_cast<uint32_t>(*verb) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 << rules[ruleIndex]->getMethods();
P Dheeraj Srujan Kumara9f076e2021-10-18 22:45:37 +05301383 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001384 return;
1385 }
1386
1387 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Snehalatha Venkatesh1c99da02022-12-27 06:45:35 +00001388 << "' " << static_cast<uint32_t>(*verb) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001389 << rules[ruleIndex]->getMethods();
1390
Ed Tanous4f10f7e2023-03-10 09:57:18 -08001391 rules[ruleIndex]->handleUpgrade(req, asyncResp,
1392 std::forward<Adaptor>(adaptor));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001393 }
1394
zhanghch058d1b46d2021-04-01 11:18:24 +08001395 void handle(Request& req,
1396 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001397 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001398 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1399 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001400 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001401 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001402 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001403 }
Ed Tanous44e45182022-07-26 16:47:23 -07001404
1405 FindRouteResponse foundRoute = findRoute(req);
1406
Ed Tanous759cf102022-07-31 16:36:52 -07001407 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -07001408 {
Ed Tanous759cf102022-07-31 16:36:52 -07001409 // Couldn't find a normal route with any verb, try looking for a 404
1410 // route
1411 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001412 {
Ed Tanous39662a32023-02-06 15:09:46 -08001413 foundRoute.route =
1414 findRouteByIndex(req.url().encoded_path(), notFoundIndex);
Ed Tanous759cf102022-07-31 16:36:52 -07001415 }
1416 else
1417 {
1418 // See if we have a method not allowed (405) handler
Ed Tanous39662a32023-02-06 15:09:46 -08001419 foundRoute.route = findRouteByIndex(req.url().encoded_path(),
1420 methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -07001421 }
1422 }
Ed Tanous759cf102022-07-31 16:36:52 -07001423
1424 // Fill in the allow header if it's valid
1425 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001426 {
Ed Tanous759cf102022-07-31 16:36:52 -07001427
Ed Tanous88a03c52022-03-14 10:16:07 -07001428 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001429 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001430 }
Tanousf00032d2018-11-05 01:18:10 -03001431
Ed Tanous44e45182022-07-26 16:47:23 -07001432 // If we couldn't find a real route or a 404 route, return a generic
1433 // response
1434 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001435 {
Ed Tanous44e45182022-07-26 16:47:23 -07001436 if (foundRoute.allowHeader.empty())
1437 {
1438 asyncResp->res.result(boost::beast::http::status::not_found);
1439 }
1440 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001441 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001442 asyncResp->res.result(
1443 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001444 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001445 return;
1446 }
1447
Ed Tanous44e45182022-07-26 16:47:23 -07001448 BaseRule& rule = *foundRoute.route.rule;
1449 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450
Ed Tanous44e45182022-07-26 16:47:23 -07001451 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Snehalatha Venkatesh1c99da02022-12-27 06:45:35 +00001452 << static_cast<uint32_t>(*verb) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001453 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001454
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001455 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001456 {
Ed Tanous44e45182022-07-26 16:47:23 -07001457 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001458 return;
1459 }
Ed Tanous2d6cb562022-07-07 20:44:54 -07001460 std::string username = req.session->username;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001461
1462 crow::connections::systemBus->async_method_call(
Ed Tanous2d6cb562022-07-07 20:44:54 -07001463 [req{std::move(req)}, asyncResp, &rule, params](
Ed Tanouse1f5c162023-03-10 09:02:51 -08001464
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001465 const boost::system::error_code& ec,
Ed Tanouse1f5c162023-03-10 09:02:51 -08001466 const dbus::utility::DBusPropertiesMap& userInfoMap
Ed Tanousb9d36b42022-02-26 21:42:46 -08001467
Ed Tanouse1f5c162023-03-10 09:02:51 -08001468 ) mutable {
1469 isUserPrivileged(ec, req, asyncResp, rule, params, userInfoMap);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001470 },
1471 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous2d6cb562022-07-07 20:44:54 -07001472 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001473 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001474
Ed Tanous1abe55e2018-09-05 08:30:59 -07001475 void debugPrint()
1476 {
Ed Tanous271584a2019-07-09 16:24:22 -07001477 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001478 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001479 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1480 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001481 perMethods[i].trie.debugPrint();
1482 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001483 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001484
Ed Tanous1abe55e2018-09-05 08:30:59 -07001485 std::vector<const std::string*> getRoutes(const std::string& parent)
1486 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001488
1489 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 {
Tanousf00032d2018-11-05 01:18:10 -03001491 std::vector<unsigned> x;
1492 pm.trie.findRouteIndexes(parent, x);
1493 for (unsigned index : x)
1494 {
1495 ret.push_back(&pm.rules[index]->rule);
1496 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497 }
1498 return ret;
1499 }
1500
1501 private:
Tanousf00032d2018-11-05 01:18:10 -03001502 struct PerMethod
1503 {
1504 std::vector<BaseRule*> rules;
1505 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001506 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001507 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001508 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001509 {}
Tanousf00032d2018-11-05 01:18:10 -03001510 };
Ed Tanous888880a2020-08-24 13:48:50 -07001511
Ed Tanous759cf102022-07-31 16:36:52 -07001512 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001513 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001514};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515} // namespace crow