blob: ed1b7e489bdc222077913575da29780ab35e7275 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "common.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08004#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06005#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include "http_request.hpp"
7#include "http_response.hpp"
8#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -03009#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053010#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "utility.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070012#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070013#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070014
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020015#include <async_resp.hpp>
Ed Tanous88a03c52022-03-14 10:16:07 -070016#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030017#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050018
Ed Tanouse0d918b2018-03-27 17:41:04 -070019#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070020#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070021#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070022#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070024#include <optional>
Ed Tanous7045c8d2017-04-03 10:04:37 -070025#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070026#include <utility>
27#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Tanousf00032d2018-11-05 01:18:10 -030031
Ed Tanous1abe55e2018-09-05 08:30:59 -070032class BaseRule
33{
34 public:
Ed Tanous4e23a442022-06-06 09:57:26 -070035 explicit BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070037
Ed Tanous0c0084a2019-10-24 15:57:51 -070038 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanousecd6a3a2022-01-07 09:18:40 -080040 BaseRule(const BaseRule&) = delete;
41 BaseRule(BaseRule&&) = delete;
42 BaseRule& operator=(const BaseRule&) = delete;
43 BaseRule& operator=(const BaseRule&&) = delete;
44
Ed Tanous1abe55e2018-09-05 08:30:59 -070045 virtual void validate() = 0;
46 std::unique_ptr<BaseRule> upgrade()
47 {
48 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070049 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070051 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 return {};
53 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070054
Ed Tanous104f09c2022-01-25 09:56:04 -080055 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080056 const std::shared_ptr<bmcweb::AsyncResp>&,
57 const RoutingParams&) = 0;
Ed Tanous104f09c2022-01-25 09:56:04 -080058 virtual void handleUpgrade(const Request& /*req*/, Response& res,
59 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070061 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 res.end();
63 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070064#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080065 virtual void handleUpgrade(
66 const Request& /*req*/, Response& res,
67 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070069 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 res.end();
71 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070072#endif
73
Ed Tanous9eb808c2022-01-25 10:19:23 -080074 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 {
76 return methodsBitfield;
77 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070078
Tanousf00032d2018-11-05 01:18:10 -030079 bool checkPrivileges(const redfish::Privileges& userPrivileges)
80 {
81 // If there are no privileges assigned, assume no privileges
82 // required
83 if (privilegesSet.empty())
84 {
85 return true;
86 }
87
88 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
89 {
90 if (userPrivileges.isSupersetOf(requiredPrivileges))
91 {
92 return true;
93 }
94 }
95 return false;
96 }
97
Ed Tanous2c9efc32022-07-31 22:08:26 -070098 size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
Ed Tanous44e45182022-07-26 16:47:23 -070099 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
Ed Tanous759cf102022-07-31 16:36:52 -0700100 methodNotAllowedIndex,
Ed Tanous44e45182022-07-26 16:47:23 -0700101 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700102
Tanousf00032d2018-11-05 01:18:10 -0300103 std::vector<redfish::Privileges> privilegesSet;
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 std::string rule;
106 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700107
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700109
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500111 template <typename T>
112 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113};
114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115namespace detail
116{
117namespace routing_handler_call_helper
118{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500119template <typename T, int Pos>
120struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121{
122 using type = T;
123 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700124};
125
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500126template <typename H1>
127struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128{
129 H1& handler;
130 const RoutingParams& params;
131 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800132 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700133};
134
135template <typename F, int NInt, int NUint, int NDouble, int NString,
136 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500138{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700139
140template <typename F, int NInt, int NUint, int NDouble, int NString,
141 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700142struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 black_magic::S<Args2...>>
144{
145 void operator()(F cparams)
146 {
147 using pushed = typename black_magic::S<Args2...>::template push_back<
148 CallPair<int64_t, NInt>>;
149 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
150 pushed>()(cparams);
151 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700152};
153
154template <typename F, int NInt, int NUint, int NDouble, int NString,
155 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700156struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
158{
159 void operator()(F cparams)
160 {
161 using pushed = typename black_magic::S<Args2...>::template push_back<
162 CallPair<uint64_t, NUint>>;
163 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
164 pushed>()(cparams);
165 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700166};
167
168template <typename F, int NInt, int NUint, int NDouble, int NString,
169 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700170struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 black_magic::S<Args2...>>
172{
173 void operator()(F cparams)
174 {
175 using pushed = typename black_magic::S<Args2...>::template push_back<
176 CallPair<double, NDouble>>;
177 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
178 pushed>()(cparams);
179 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700180};
181
182template <typename F, int NInt, int NUint, int NDouble, int NString,
183 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700184struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
186{
187 void operator()(F cparams)
188 {
189 using pushed = typename black_magic::S<Args2...>::template push_back<
190 CallPair<std::string, NString>>;
191 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
192 pushed>()(cparams);
193 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700194};
195
196template <typename F, int NInt, int NUint, int NDouble, int NString,
197 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700198struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 black_magic::S<Args1...>>
200{
201 void operator()(F cparams)
202 {
203 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800204 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205 cparams.params.template get<typename Args1::type>(Args1::pos)...);
206 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700207};
208
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500209template <typename Func, typename... ArgsWrapped>
210struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211{
212 template <typename... Args>
213 void set(
214 Func f,
215 typename std::enable_if<
216 !std::is_same<
217 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
218 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800219 int>::type /*enable*/
220 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800222 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800223 const Request&,
224 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
225 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700226 }
227
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500228 template <typename Req, typename... Args>
229 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700231 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700233
zhanghch058d1b46d2021-04-01 11:18:24 +0800234 void operator()(const Request& req,
235 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
236 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700237 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800238 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700239 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700240
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 Func f;
242 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700243
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 template <typename... Args>
245 void set(
246 Func f,
247 typename std::enable_if<
248 std::is_same<
249 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
250 const Request&>::value &&
251 !std::is_same<typename std::tuple_element<
252 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800253 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800254 int>::type /*enable*/
255 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 {
257 handler = ReqHandlerWrapper<Args...>(std::move(f));
258 /*handler = (
259 [f = std::move(f)]
260 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700261 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700262 res.end();
263 });*/
264 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700265
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 template <typename... Args>
267 void set(
268 Func f,
269 typename std::enable_if<
270 std::is_same<
271 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
272 const Request&>::value &&
273 std::is_same<typename std::tuple_element<
274 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800275 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800276 int>::type /*enable*/
277 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278 {
279 handler = std::move(f);
280 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700281
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282 template <typename... Args>
283 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800285 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800286 const crow::Request& /*req*/,
287 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700289 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700291
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 template <typename... Args>
293 struct HandlerTypeHelper<const Request&, Args...>
294 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800295 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800296 const crow::Request& /*req*/,
297 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700299 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700301
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800303 struct HandlerTypeHelper<const Request&,
304 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800306 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800307 const crow::Request& /*req*/,
308 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700310 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 };
312
313 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
314
zhanghch058d1b46d2021-04-01 11:18:24 +0800315 void operator()(const Request& req,
316 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 const RoutingParams& params)
318 {
319 detail::routing_handler_call_helper::Call<
320 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
321 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
322 black_magic::S<>>()(
323 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800324 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700326};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327} // namespace routing_handler_call_helper
328} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330class WebSocketRule : public BaseRule
331{
332 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700333
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700335 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500336 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700337
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700340
Ed Tanous104f09c2022-01-25 09:56:04 -0800341 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800342 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800343 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800345 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700347
Ed Tanous104f09c2022-01-25 09:56:04 -0800348 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800349 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 {
Nan Zhou93c02022022-02-24 18:21:07 -0800351 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530352 std::shared_ptr<
353 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
354 myConnection = std::make_shared<
355 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000356 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530357 closeHandler, errorHandler);
358 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 }
360#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -0800361 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800362 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
363 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 {
Nan Zhou93c02022022-02-24 18:21:07 -0800365 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800366 std::shared_ptr<crow::websocket::ConnectionImpl<
367 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
368 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
369 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000370 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800371 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 myConnection->start();
373 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700374#endif
375
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500376 template <typename Func>
377 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700378 {
379 openHandler = f;
380 return *this;
381 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700382
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500383 template <typename Func>
384 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 {
386 messageHandler = 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& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392 {
393 closeHandler = f;
394 return *this;
395 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700396
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500397 template <typename Func>
398 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 {
400 errorHandler = f;
401 return *this;
402 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700403
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 protected:
zhanghch0577726382021-10-21 14:07:57 +0800405 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
407 messageHandler;
408 std::function<void(crow::websocket::Connection&, const std::string&)>
409 closeHandler;
410 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700411};
412
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500413template <typename T>
414struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415{
416 using self_t = T;
417 WebSocketRule& websocket()
418 {
Ed Tanous271584a2019-07-09 16:24:22 -0700419 self_t* self = static_cast<self_t*>(this);
420 WebSocketRule* p = new WebSocketRule(self->rule);
421 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 return *p;
423 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700424
Ed Tanousf23b7292020-10-15 09:41:17 -0700425 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426 {
Ed Tanous271584a2019-07-09 16:24:22 -0700427 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700428 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700429 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700431
Ed Tanous1abe55e2018-09-05 08:30:59 -0700432 self_t& methods(boost::beast::http::verb method)
433 {
Ed Tanous271584a2019-07-09 16:24:22 -0700434 self_t* self = static_cast<self_t*>(this);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700435 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
436 if (verb)
437 {
438 self->methodsBitfield = 1U << static_cast<size_t>(*verb);
439 }
Ed Tanous271584a2019-07-09 16:24:22 -0700440 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700442
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000444 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
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 Tanous81ce6092020-12-17 16:54:55 +0000447 methods(argsMethod...);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700448 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
449 if (verb)
450 {
451 self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
452 }
Ed Tanous271584a2019-07-09 16:24:22 -0700453 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 }
Tanousf00032d2018-11-05 01:18:10 -0300455
Ed Tanous44e45182022-07-26 16:47:23 -0700456 self_t& notFound()
457 {
458 self_t* self = static_cast<self_t*>(this);
459 self->methodsBitfield = 1U << notFoundIndex;
460 return *self;
461 }
462
Ed Tanous759cf102022-07-31 16:36:52 -0700463 self_t& methodNotAllowed()
464 {
465 self_t* self = static_cast<self_t*>(this);
466 self->methodsBitfield = 1U << methodNotAllowedIndex;
467 return *self;
468 }
469
Ed Tanous432a8902021-06-14 15:28:56 -0700470 self_t& privileges(
471 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300472 {
Ed Tanous271584a2019-07-09 16:24:22 -0700473 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700474 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300475 {
Ed Tanous271584a2019-07-09 16:24:22 -0700476 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300477 }
Ed Tanous271584a2019-07-09 16:24:22 -0700478 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300479 }
Ed Tanoused398212021-06-09 17:05:54 -0700480
481 template <size_t N, typename... MethodArgs>
482 self_t& privileges(const std::array<redfish::Privileges, N>& p)
483 {
484 self_t* self = static_cast<self_t*>(this);
485 for (const redfish::Privileges& privilege : p)
486 {
487 self->privilegesSet.emplace_back(privilege);
488 }
489 return *self;
490 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700491};
492
Ed Tanous1abe55e2018-09-05 08:30:59 -0700493class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
494{
495 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700496 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500497 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700498
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 void validate() override
500 {
501 if (!erasedHandler)
502 {
503 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
504 "no handler for url " + rule);
505 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700506 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700507
zhanghch058d1b46d2021-04-01 11:18:24 +0800508 void handle(const Request& req,
509 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 const RoutingParams& params) override
511 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800512 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700514
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500515 template <typename Func>
516 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 {
Ed Tanousc867a832022-03-10 14:17:00 -0800518 using boost::callable_traits::args_t;
519 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
520 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
521 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 }
523
524 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500525 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700526 // enable_if Arg1 != request
527
528 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800529 std::function<void(const Request&,
530 const std::shared_ptr<bmcweb::AsyncResp>&,
531 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800532 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 {
Ed Tanousc867a832022-03-10 14:17:00 -0800534 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700535
536 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700537 black_magic::getParameterTag(rule.c_str()),
538 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 typename function_t::template arg<Indices>...>::value))
540 {
541 throw std::runtime_error("routeDynamic: Handler type is mismatched "
542 "with URL parameters: " +
543 rule);
544 }
545 auto ret = detail::routing_handler_call_helper::Wrapped<
546 Func, typename function_t::template arg<Indices>...>();
547 ret.template set<typename function_t::template arg<Indices>...>(
548 std::move(f));
549 return ret;
550 }
551
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500552 template <typename Func>
553 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 {
555 nameStr = std::move(name);
556 (*this).template operator()<Func>(std::forward(f));
557 }
558
559 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800560 std::function<void(const Request&,
561 const std::shared_ptr<bmcweb::AsyncResp>&,
562 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700564};
565
566template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500567class TaggedRule :
568 public BaseRule,
569 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570{
571 public:
572 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700573
Ed Tanous4e23a442022-06-06 09:57:26 -0700574 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700576
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 void validate() override
578 {
579 if (!handler)
580 {
581 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
582 "no handler for url " + rule);
583 }
584 }
585
586 template <typename Func>
587 typename std::enable_if<
588 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
589 void>::type
590 operator()(Func&& f)
591 {
592 static_assert(
593 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
594 black_magic::CallHelper<
595 Func, black_magic::S<crow::Request, Args...>>::value,
596 "Handler type is mismatched with URL parameters");
597 static_assert(
598 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
599 "Handler function cannot have void return type; valid return "
600 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500601 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800603 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800604 const Request&,
605 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
606 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 }
608
609 template <typename Func>
610 typename std::enable_if<
611 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700612 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700613 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 void>::type
615 operator()(Func&& f)
616 {
617 static_assert(
618 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
619 black_magic::CallHelper<
620 Func, black_magic::S<crow::Request, Args...>>::value,
621 "Handler type is mismatched with URL parameters");
622 static_assert(
623 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
624 std::declval<Args>()...))>::value,
625 "Handler function cannot have void return type; valid return "
626 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500627 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700628
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800629 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800630 const crow::Request& req,
631 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
632 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700634
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 template <typename Func>
636 typename std::enable_if<
637 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
638 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700639 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 void>::type
641 operator()(Func&& f)
642 {
643 static_assert(
644 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
645 black_magic::CallHelper<
646 Func, black_magic::S<crow::Request, Args...>>::value ||
647 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800648 Func, black_magic::S<crow::Request,
649 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 Args...>>::value,
651 "Handler type is mismatched with URL parameters");
652 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800653 std::is_same<
654 void,
655 decltype(f(std::declval<crow::Request>(),
656 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
657 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300658 "Handler function with response argument should have void "
659 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700661
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800662 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700664
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500665 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700666 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700668 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 (*this).template operator()<Func>(std::forward(f));
670 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700671
zhanghch058d1b46d2021-04-01 11:18:24 +0800672 void handle(const Request& req,
673 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 const RoutingParams& params) override
675 {
676 detail::routing_handler_call_helper::Call<
677 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
678 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
679 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800680 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700682
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800684 std::function<void(const crow::Request&,
685 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
686 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700687};
688
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689class Trie
690{
691 public:
692 struct Node
693 {
694 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700695 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
696 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800697 using ChildMap = boost::container::flat_map<
698 std::string, unsigned, std::less<>,
699 std::vector<std::pair<std::string, unsigned>>>;
700 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700701
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 bool isSimpleNode() const
703 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800704 return ruleIndex == 0 &&
705 std::all_of(std::begin(paramChildrens),
706 std::end(paramChildrens),
707 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700708 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700709 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700710
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500712 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713
714 private:
715 void optimizeNode(Node* node)
716 {
Ed Tanous271584a2019-07-09 16:24:22 -0700717 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800719 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700720 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700722 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 Node* child = &nodes[x];
724 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700725 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700727 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700729 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800731 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 {
733 Node* child = &nodes[kv.second];
734 if (!child->isSimpleNode())
735 {
736 mergeWithChild = false;
737 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700738 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 }
740 if (mergeWithChild)
741 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800742 Node::ChildMap merged;
743 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 {
745 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800746 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300747 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 {
749 merged[kv.first + childKv.first] = childKv.second;
750 }
751 }
752 node->children = std::move(merged);
753 optimizeNode(node);
754 }
755 else
756 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800757 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 {
759 Node* child = &nodes[kv.second];
760 optimizeNode(child);
761 }
762 }
763 }
764
765 void optimize()
766 {
767 optimizeNode(head());
768 }
769
770 public:
771 void validate()
772 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 optimize();
774 }
775
Ed Tanous81ce6092020-12-17 16:54:55 +0000776 void findRouteIndexes(const std::string& reqUrl,
777 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300778 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
780 if (node == nullptr)
781 {
782 node = head();
783 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800784 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
786 const std::string& fragment = kv.first;
787 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000788 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 {
790 if (child->ruleIndex != 0 && fragment != "/")
791 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000792 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000794 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700795 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 }
797 else
798 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000799 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 {
Ed Tanous271584a2019-07-09 16:24:22 -0700801 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000802 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700803 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 }
805 }
806 }
807 }
808
809 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000810 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700811 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 {
813 RoutingParams empty;
814 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700815 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818
819 unsigned found{};
820 RoutingParams matchParams;
821
822 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700823 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700825 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000826 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700827 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700829 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830
831 auto updateFound =
832 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700833 if (ret.first != 0U && (found == 0U || found > ret.first))
834 {
835 found = ret.first;
836 matchParams = std::move(ret.second);
837 }
838 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839
Ed Tanouse662eae2022-01-25 10:39:19 -0800840 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000842 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
844 {
Ed Tanous543f4402022-01-06 13:12:53 -0800845 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 errno = 0;
847 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000848 std::strtoll(reqUrl.data() + pos, &eptr, 10);
849 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700850 {
851 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000852 std::pair<unsigned, RoutingParams> ret =
853 find(reqUrl,
854 &nodes[node->paramChildrens[static_cast<size_t>(
855 ParamType::INT)]],
856 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 updateFound(ret);
858 params->intParams.pop_back();
859 }
860 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700861 }
862
Ed Tanouse662eae2022-01-25 10:39:19 -0800863 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700864 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000865 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 if ((c >= '0' && c <= '9') || c == '+')
867 {
Ed Tanous543f4402022-01-06 13:12:53 -0800868 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 errno = 0;
870 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000871 std::strtoull(reqUrl.data() + pos, &eptr, 10);
872 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873 {
874 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000875 std::pair<unsigned, RoutingParams> ret =
876 find(reqUrl,
877 &nodes[node->paramChildrens[static_cast<size_t>(
878 ParamType::UINT)]],
879 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 updateFound(ret);
881 params->uintParams.pop_back();
882 }
883 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700884 }
885
Ed Tanouse662eae2022-01-25 10:39:19 -0800886 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700887 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000888 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
890 {
Ed Tanous543f4402022-01-06 13:12:53 -0800891 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000893 double value = std::strtod(reqUrl.data() + pos, &eptr);
894 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 {
896 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000897 std::pair<unsigned, RoutingParams> ret =
898 find(reqUrl,
899 &nodes[node->paramChildrens[static_cast<size_t>(
900 ParamType::DOUBLE)]],
901 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 updateFound(ret);
903 params->doubleParams.pop_back();
904 }
905 }
906 }
907
Ed Tanouse662eae2022-01-25 10:39:19 -0800908 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000910 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000911 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700912 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000913 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700914 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700916 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 }
918
919 if (epos != pos)
920 {
921 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000922 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300923 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000924 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700925 &nodes[node->paramChildrens[static_cast<size_t>(
926 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000927 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 updateFound(ret);
929 params->stringParams.pop_back();
930 }
931 }
932
Ed Tanouse662eae2022-01-25 10:39:19 -0800933 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000935 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700936
937 if (epos != pos)
938 {
939 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000940 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700941 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000942 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700943 &nodes[node->paramChildrens[static_cast<size_t>(
944 ParamType::PATH)]],
945 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700946 updateFound(ret);
947 params->stringParams.pop_back();
948 }
949 }
950
Ed Tanousa94ac612022-02-22 11:13:24 -0800951 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 {
953 const std::string& fragment = kv.first;
954 const Node* child = &nodes[kv.second];
955
Ed Tanous81ce6092020-12-17 16:54:55 +0000956 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 {
Tanousf00032d2018-11-05 01:18:10 -0300958 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000959 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700960 updateFound(ret);
961 }
962 }
963
964 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700965 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700966
967 void add(const std::string& url, unsigned ruleIndex)
968 {
Ed Tanous271584a2019-07-09 16:24:22 -0700969 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970
971 for (unsigned i = 0; i < url.size(); i++)
972 {
973 char c = url[i];
974 if (c == '<')
975 {
Tanousf00032d2018-11-05 01:18:10 -0300976 const static std::array<std::pair<ParamType, std::string>, 7>
977 paramTraits = {{
978 {ParamType::INT, "<int>"},
979 {ParamType::UINT, "<uint>"},
980 {ParamType::DOUBLE, "<float>"},
981 {ParamType::DOUBLE, "<double>"},
982 {ParamType::STRING, "<str>"},
983 {ParamType::STRING, "<string>"},
984 {ParamType::PATH, "<path>"},
985 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986
Tanousf00032d2018-11-05 01:18:10 -0300987 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700988 {
Tanousf00032d2018-11-05 01:18:10 -0300989 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 {
Ed Tanous271584a2019-07-09 16:24:22 -0700991 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800992 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 {
Tanousf00032d2018-11-05 01:18:10 -0300994 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700995 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 }
Ed Tanous271584a2019-07-09 16:24:22 -0700997 idx = nodes[idx].paramChildrens[index];
998 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 break;
1000 }
1001 }
1002
1003 i--;
1004 }
1005 else
1006 {
1007 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001008 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001009 {
Tanousf00032d2018-11-05 01:18:10 -03001010 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001011 nodes[idx].children.emplace(piece, newNodeIdx);
1012 }
1013 idx = nodes[idx].children[piece];
1014 }
1015 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001016 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001017 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001019 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001021 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001022
Ed Tanous1abe55e2018-09-05 08:30:59 -07001023 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001024 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001025 {
Ed Tanous271584a2019-07-09 16:24:22 -07001026 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001027 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001028 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 {
1030 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001031 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1032 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 {
1034 case ParamType::INT:
1035 BMCWEB_LOG_DEBUG << "<int>";
1036 break;
1037 case ParamType::UINT:
1038 BMCWEB_LOG_DEBUG << "<uint>";
1039 break;
1040 case ParamType::DOUBLE:
1041 BMCWEB_LOG_DEBUG << "<float>";
1042 break;
1043 case ParamType::STRING:
1044 BMCWEB_LOG_DEBUG << "<str>";
1045 break;
1046 case ParamType::PATH:
1047 BMCWEB_LOG_DEBUG << "<path>";
1048 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001049 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001050 BMCWEB_LOG_DEBUG << "<ERROR>";
1051 break;
1052 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001053
Ed Tanous1abe55e2018-09-05 08:30:59 -07001054 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1055 }
1056 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001057 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058 {
1059 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001060 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001061 << kv.first;
1062 debugNodePrint(&nodes[kv.second], level + 1);
1063 }
1064 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001065
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 public:
1067 void debugPrint()
1068 {
Ed Tanous271584a2019-07-09 16:24:22 -07001069 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001070 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001071
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 private:
1073 const Node* head() const
1074 {
1075 return &nodes.front();
1076 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001077
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 Node* head()
1079 {
1080 return &nodes.front();
1081 }
1082
1083 unsigned newNode()
1084 {
1085 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001086 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 }
1088
1089 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001090};
1091
Ed Tanous1abe55e2018-09-05 08:30:59 -07001092class Router
1093{
1094 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001095 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001096
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097 DynamicRule& newRuleDynamic(const std::string& rule)
1098 {
1099 std::unique_ptr<DynamicRule> ruleObject =
1100 std::make_unique<DynamicRule>(rule);
1101 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001102 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001103
Ed Tanous1abe55e2018-09-05 08:30:59 -07001104 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001105 }
1106
Ed Tanous1abe55e2018-09-05 08:30:59 -07001107 template <uint64_t N>
1108 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1109 newRuleTagged(const std::string& rule)
1110 {
1111 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1112 TaggedRule>;
1113 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1114 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001115 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116
1117 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001118 }
1119
Tanousf00032d2018-11-05 01:18:10 -03001120 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001121 {
Tanousf00032d2018-11-05 01:18:10 -03001122 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123 {
Tanousf00032d2018-11-05 01:18:10 -03001124 return;
1125 }
Ed Tanous759cf102022-07-31 16:36:52 -07001126 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001127 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001128 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001129 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001130 {
1131 perMethods[method].rules.emplace_back(ruleObject);
1132 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001133 rule, static_cast<unsigned>(
1134 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001135 // directory case:
1136 // request to `/about' url matches `/about/' rule
1137 if (rule.size() > 2 && rule.back() == '/')
1138 {
1139 perMethods[method].trie.add(
1140 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001141 static_cast<unsigned>(perMethods[method].rules.size() -
1142 1));
Tanousf00032d2018-11-05 01:18:10 -03001143 }
1144 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001145 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001146 }
1147
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 void validate()
1149 {
Tanousf00032d2018-11-05 01:18:10 -03001150 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001151 {
1152 if (rule)
1153 {
Tanousf00032d2018-11-05 01:18:10 -03001154 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001155 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001156 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001157 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001158 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001159 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001160 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001161 }
1162 }
Tanousf00032d2018-11-05 01:18:10 -03001163 for (PerMethod& perMethod : perMethods)
1164 {
1165 perMethod.trie.validate();
1166 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001167 }
1168
Ed Tanous44e45182022-07-26 16:47:23 -07001169 struct FindRoute
1170 {
1171 BaseRule* rule = nullptr;
1172 RoutingParams params;
1173 };
1174
1175 struct FindRouteResponse
1176 {
1177 std::string allowHeader;
1178 FindRoute route;
1179 };
1180
Ed Tanous759cf102022-07-31 16:36:52 -07001181 FindRoute findRouteByIndex(std::string_view url, size_t index) const
1182 {
1183 FindRoute route;
1184 if (index >= perMethods.size())
1185 {
1186 BMCWEB_LOG_CRITICAL << "Bad index???";
1187 return route;
1188 }
1189 const PerMethod& perMethod = perMethods[index];
1190 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1191 if (found.first >= perMethod.rules.size())
1192 {
1193 throw std::runtime_error("Trie internal structure corrupted!");
1194 }
1195 // Found a 404 route, switch that in
1196 if (found.first != 0U)
1197 {
1198 route.rule = perMethod.rules[found.first];
1199 route.params = std::move(found.second);
1200 }
1201 return route;
1202 }
1203
1204 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -07001205 {
1206 FindRouteResponse findRoute;
1207
Ed Tanous2c9efc32022-07-31 22:08:26 -07001208 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1209 if (!verb)
1210 {
1211 return findRoute;
1212 }
1213 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -07001214 // Check to see if this url exists at any verb
1215 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1216 perMethodIndex++)
1217 {
1218 // Make sure it's safe to deference the array at that index
1219 static_assert(maxVerbIndex <
1220 std::tuple_size_v<decltype(perMethods)>);
Ed Tanous759cf102022-07-31 16:36:52 -07001221 FindRoute route = findRouteByIndex(req.url, perMethodIndex);
1222 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -07001223 {
1224 continue;
1225 }
1226 if (!findRoute.allowHeader.empty())
1227 {
1228 findRoute.allowHeader += ", ";
1229 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001230 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1231 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -07001232 if (perMethodIndex == reqMethodIndex)
1233 {
Ed Tanous759cf102022-07-31 16:36:52 -07001234 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -07001235 }
1236 }
1237 return findRoute;
1238 }
1239
Ed Tanous1abe55e2018-09-05 08:30:59 -07001240 template <typename Adaptor>
1241 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1242 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001243 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1244 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001245 {
1246 res.result(boost::beast::http::status::not_found);
1247 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001248 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001249 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001250 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -03001251 Trie& trie = perMethod.trie;
1252 std::vector<BaseRule*>& rules = perMethod.rules;
1253
1254 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001255 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001256 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001257 {
1258 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001259 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 res.end();
1261 return;
1262 }
1263
1264 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001265 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001266 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001267 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268
Ed Tanous271584a2019-07-09 16:24:22 -07001269 if ((rules[ruleIndex]->getMethods() &
1270 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001271 {
1272 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1273 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001274 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001275 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001276 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001277 res.end();
1278 return;
1279 }
1280
1281 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001282 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001283 << rules[ruleIndex]->getMethods();
1284
1285 // any uncaught exceptions become 500s
1286 try
1287 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -08001288 rules[ruleIndex]->handleUpgrade(req, res,
1289 std::forward<Adaptor>(adaptor));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 }
Patrick Williamsc5967042021-10-06 12:39:54 -05001291 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001292 {
1293 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001294 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001295 res.end();
1296 return;
1297 }
1298 catch (...)
1299 {
1300 BMCWEB_LOG_ERROR
1301 << "An uncaught exception occurred. The type was unknown "
1302 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001303 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304 res.end();
1305 return;
1306 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001307 }
1308
zhanghch058d1b46d2021-04-01 11:18:24 +08001309 void handle(Request& req,
1310 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001311 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001312 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1313 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001314 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001315 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001316 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001317 }
Ed Tanous44e45182022-07-26 16:47:23 -07001318
1319 FindRouteResponse foundRoute = findRoute(req);
1320
Ed Tanous759cf102022-07-31 16:36:52 -07001321 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -07001322 {
Ed Tanous759cf102022-07-31 16:36:52 -07001323 // Couldn't find a normal route with any verb, try looking for a 404
1324 // route
1325 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001326 {
Ed Tanous759cf102022-07-31 16:36:52 -07001327 foundRoute.route = findRouteByIndex(req.url, notFoundIndex);
1328 }
1329 else
1330 {
1331 // See if we have a method not allowed (405) handler
1332 foundRoute.route =
1333 findRouteByIndex(req.url, methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -07001334 }
1335 }
Ed Tanous759cf102022-07-31 16:36:52 -07001336
1337 // Fill in the allow header if it's valid
1338 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001339 {
Ed Tanous759cf102022-07-31 16:36:52 -07001340
Ed Tanous88a03c52022-03-14 10:16:07 -07001341 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001342 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001343 }
Tanousf00032d2018-11-05 01:18:10 -03001344
Ed Tanous44e45182022-07-26 16:47:23 -07001345 // If we couldn't find a real route or a 404 route, return a generic
1346 // response
1347 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 {
Ed Tanous44e45182022-07-26 16:47:23 -07001349 if (foundRoute.allowHeader.empty())
1350 {
1351 asyncResp->res.result(boost::beast::http::status::not_found);
1352 }
1353 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001354 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001355 asyncResp->res.result(
1356 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001357 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001358 return;
1359 }
1360
Ed Tanous44e45182022-07-26 16:47:23 -07001361 BaseRule& rule = *foundRoute.route.rule;
1362 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001363
Ed Tanous44e45182022-07-26 16:47:23 -07001364 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001365 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001366 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001367
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001368 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001369 {
Ed Tanous44e45182022-07-26 16:47:23 -07001370 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001371 return;
1372 }
Ed Tanous2d6cb562022-07-07 20:44:54 -07001373 std::string username = req.session->username;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001374
1375 crow::connections::systemBus->async_method_call(
Ed Tanous2d6cb562022-07-07 20:44:54 -07001376 [req{std::move(req)}, asyncResp, &rule, params](
1377 const boost::system::error_code ec,
1378 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -07001379 if (ec)
1380 {
1381 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1382 asyncResp->res.result(
1383 boost::beast::http::status::internal_server_error);
1384 return;
1385 }
1386 std::string userRole{};
1387 const bool* remoteUser = nullptr;
1388 std::optional<bool> passwordExpired;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001389
Ed Tanous002d39b2022-05-31 08:59:27 -07001390 for (const auto& userInfo : userInfoMap)
1391 {
1392 if (userInfo.first == "UserPrivilege")
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001393 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001394 const std::string* userRolePtr =
1395 std::get_if<std::string>(&userInfo.second);
1396 if (userRolePtr == nullptr)
Ed Tanousb9d36b42022-02-26 21:42:46 -08001397 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001398 continue;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001399 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001400 userRole = *userRolePtr;
1401 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1402 << " userRole = " << *userRolePtr;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001403 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001404 else if (userInfo.first == "RemoteUser")
1405 {
1406 remoteUser = std::get_if<bool>(&userInfo.second);
1407 }
1408 else if (userInfo.first == "UserPasswordExpired")
1409 {
1410 const bool* passwordExpiredPtr =
1411 std::get_if<bool>(&userInfo.second);
1412 if (passwordExpiredPtr == nullptr)
1413 {
1414 continue;
1415 }
1416 passwordExpired = *passwordExpiredPtr;
1417 }
1418 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001419
Ed Tanous002d39b2022-05-31 08:59:27 -07001420 if (remoteUser == nullptr)
1421 {
1422 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1423 asyncResp->res.result(
1424 boost::beast::http::status::internal_server_error);
1425 return;
1426 }
1427
1428 if (passwordExpired == std::nullopt)
1429 {
1430 if (!*remoteUser)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001431 {
1432 BMCWEB_LOG_ERROR
Ed Tanous002d39b2022-05-31 08:59:27 -07001433 << "UserPasswordExpired property is expected for"
1434 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001435 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001436 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001437 return;
1438 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001439 passwordExpired = false;
1440 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001441
Nan Zhouac6250a2022-08-09 20:15:44 +00001442 // Get the user's privileges from the role
Ed Tanous002d39b2022-05-31 08:59:27 -07001443 redfish::Privileges userPrivileges =
1444 redfish::getUserPrivileges(userRole);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001445
Ed Tanous002d39b2022-05-31 08:59:27 -07001446 // Set isConfigureSelfOnly based on D-Bus results. This
1447 // ignores the results from both pamAuthenticateUser and the
1448 // value from any previous use of this session.
1449 req.session->isConfigureSelfOnly = *passwordExpired;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001450
Nan Zhouac6250a2022-08-09 20:15:44 +00001451 // Modify privileges if isConfigureSelfOnly.
Ed Tanous002d39b2022-05-31 08:59:27 -07001452 if (req.session->isConfigureSelfOnly)
1453 {
Nan Zhouac6250a2022-08-09 20:15:44 +00001454 // Remove all privileges except ConfigureSelf
Ed Tanous002d39b2022-05-31 08:59:27 -07001455 userPrivileges = userPrivileges.intersection(
1456 redfish::Privileges{"ConfigureSelf"});
1457 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1458 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001459
Ed Tanous44e45182022-07-26 16:47:23 -07001460 if (!rule.checkPrivileges(userPrivileges))
Ed Tanous002d39b2022-05-31 08:59:27 -07001461 {
1462 asyncResp->res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001463 if (req.session->isConfigureSelfOnly)
1464 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001465 redfish::messages::passwordChangeRequired(
1466 asyncResp->res, crow::utility::urlFromPieces(
1467 "redfish", "v1", "AccountService",
1468 "Accounts", req.session->username));
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001469 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001470 return;
1471 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001472
Ed Tanous002d39b2022-05-31 08:59:27 -07001473 req.userRole = userRole;
Ed Tanous44e45182022-07-26 16:47:23 -07001474 rule.handle(req, asyncResp, params);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001475 },
1476 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous2d6cb562022-07-07 20:44:54 -07001477 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001478 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001479
Ed Tanous1abe55e2018-09-05 08:30:59 -07001480 void debugPrint()
1481 {
Ed Tanous271584a2019-07-09 16:24:22 -07001482 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001483 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001484 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1485 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001486 perMethods[i].trie.debugPrint();
1487 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001488 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001489
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 std::vector<const std::string*> getRoutes(const std::string& parent)
1491 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001492 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001493
1494 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001495 {
Tanousf00032d2018-11-05 01:18:10 -03001496 std::vector<unsigned> x;
1497 pm.trie.findRouteIndexes(parent, x);
1498 for (unsigned index : x)
1499 {
1500 ret.push_back(&pm.rules[index]->rule);
1501 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 }
1503 return ret;
1504 }
1505
1506 private:
Tanousf00032d2018-11-05 01:18:10 -03001507 struct PerMethod
1508 {
1509 std::vector<BaseRule*> rules;
1510 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001511 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001512 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001513 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001514 {}
Tanousf00032d2018-11-05 01:18:10 -03001515 };
Ed Tanous888880a2020-08-24 13:48:50 -07001516
Ed Tanous759cf102022-07-31 16:36:52 -07001517 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001518 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001519};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001520} // namespace crow