blob: 7bac2834375a618232c65d0d2aae8b07b08aec20 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "common.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08004#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06005#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include "http_request.hpp"
7#include "http_response.hpp"
8#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -03009#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053010#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "utility.hpp"
12#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070013
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020014#include <async_resp.hpp>
Ed Tanous88a03c52022-03-14 10:16:07 -070015#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030016#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017
Ed Tanouse0d918b2018-03-27 17:41:04 -070018#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070019#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070020#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070021#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070022#include <memory>
23#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <utility>
25#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Tanousf00032d2018-11-05 01:18:10 -030029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030class BaseRule
31{
32 public:
Ed Tanousf23b7292020-10-15 09:41:17 -070033 BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070035
Ed Tanous0c0084a2019-10-24 15:57:51 -070036 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070037
Ed Tanousecd6a3a2022-01-07 09:18:40 -080038 BaseRule(const BaseRule&) = delete;
39 BaseRule(BaseRule&&) = delete;
40 BaseRule& operator=(const BaseRule&) = delete;
41 BaseRule& operator=(const BaseRule&&) = delete;
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043 virtual void validate() = 0;
44 std::unique_ptr<BaseRule> upgrade()
45 {
46 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070047 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070049 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 return {};
51 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070052
Ed Tanous104f09c2022-01-25 09:56:04 -080053 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080054 const std::shared_ptr<bmcweb::AsyncResp>&,
55 const RoutingParams&) = 0;
Ed Tanous104f09c2022-01-25 09:56:04 -080056 virtual void handleUpgrade(const Request& /*req*/, Response& res,
57 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070059 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 res.end();
61 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070062#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080063 virtual void handleUpgrade(
64 const Request& /*req*/, Response& res,
65 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070067 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 res.end();
69 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070070#endif
71
Ed Tanous9eb808c2022-01-25 10:19:23 -080072 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 {
74 return methodsBitfield;
75 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070076
Tanousf00032d2018-11-05 01:18:10 -030077 bool checkPrivileges(const redfish::Privileges& userPrivileges)
78 {
79 // If there are no privileges assigned, assume no privileges
80 // required
81 if (privilegesSet.empty())
82 {
83 return true;
84 }
85
86 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
87 {
88 if (userPrivileges.isSupersetOf(requiredPrivileges))
89 {
90 return true;
91 }
92 }
93 return false;
94 }
95
Ed Tanous271584a2019-07-09 16:24:22 -070096 size_t methodsBitfield{
97 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070098
Tanousf00032d2018-11-05 01:18:10 -030099 std::vector<redfish::Privileges> privilegesSet;
100
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 std::string rule;
102 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500107 template <typename T>
108 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700109};
110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111namespace detail
112{
113namespace routing_handler_call_helper
114{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500115template <typename T, int Pos>
116struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117{
118 using type = T;
119 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700120};
121
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500122template <typename H1>
123struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124{
125 H1& handler;
126 const RoutingParams& params;
127 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800128 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700129};
130
131template <typename F, int NInt, int NUint, int NDouble, int NString,
132 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700135
136template <typename F, int NInt, int NUint, int NDouble, int NString,
137 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700138struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 black_magic::S<Args2...>>
140{
141 void operator()(F cparams)
142 {
143 using pushed = typename black_magic::S<Args2...>::template push_back<
144 CallPair<int64_t, NInt>>;
145 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
146 pushed>()(cparams);
147 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700148};
149
150template <typename F, int NInt, int NUint, int NDouble, int NString,
151 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700152struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
154{
155 void operator()(F cparams)
156 {
157 using pushed = typename black_magic::S<Args2...>::template push_back<
158 CallPair<uint64_t, NUint>>;
159 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
160 pushed>()(cparams);
161 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700162};
163
164template <typename F, int NInt, int NUint, int NDouble, int NString,
165 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700166struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 black_magic::S<Args2...>>
168{
169 void operator()(F cparams)
170 {
171 using pushed = typename black_magic::S<Args2...>::template push_back<
172 CallPair<double, NDouble>>;
173 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
174 pushed>()(cparams);
175 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700176};
177
178template <typename F, int NInt, int NUint, int NDouble, int NString,
179 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700180struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
182{
183 void operator()(F cparams)
184 {
185 using pushed = typename black_magic::S<Args2...>::template push_back<
186 CallPair<std::string, NString>>;
187 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
188 pushed>()(cparams);
189 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700190};
191
192template <typename F, int NInt, int NUint, int NDouble, int NString,
193 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700194struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 black_magic::S<Args1...>>
196{
197 void operator()(F cparams)
198 {
199 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800200 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 cparams.params.template get<typename Args1::type>(Args1::pos)...);
202 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700203};
204
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205template <typename Func, typename... ArgsWrapped>
206struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207{
208 template <typename... Args>
209 void set(
210 Func f,
211 typename std::enable_if<
212 !std::is_same<
213 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
214 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800215 int>::type /*enable*/
216 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800218 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800219 const Request&,
220 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
221 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700222 }
223
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224 template <typename Req, typename... Args>
225 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000227 ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500228 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229
zhanghch058d1b46d2021-04-01 11:18:24 +0800230 void operator()(const Request& req,
231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
232 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800234 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700236
Ed Tanous1abe55e2018-09-05 08:30:59 -0700237 Func f;
238 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700239
Ed Tanous1abe55e2018-09-05 08:30:59 -0700240 template <typename... Args>
241 void set(
242 Func f,
243 typename std::enable_if<
244 std::is_same<
245 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
246 const Request&>::value &&
247 !std::is_same<typename std::tuple_element<
248 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800249 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800250 int>::type /*enable*/
251 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 {
253 handler = ReqHandlerWrapper<Args...>(std::move(f));
254 /*handler = (
255 [f = std::move(f)]
256 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700257 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 res.end();
259 });*/
260 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700261
Ed Tanous1abe55e2018-09-05 08:30:59 -0700262 template <typename... Args>
263 void set(
264 Func f,
265 typename std::enable_if<
266 std::is_same<
267 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
268 const Request&>::value &&
269 std::is_same<typename std::tuple_element<
270 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800271 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800272 int>::type /*enable*/
273 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 {
275 handler = std::move(f);
276 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700277
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500278 template <typename... Args>
279 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800281 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800282 const crow::Request& /*req*/,
283 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700285 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700286 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700287
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 template <typename... Args>
289 struct HandlerTypeHelper<const Request&, Args...>
290 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800291 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800292 const crow::Request& /*req*/,
293 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700295 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700297
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800299 struct HandlerTypeHelper<const Request&,
300 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800302 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800303 const crow::Request& /*req*/,
304 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700306 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 };
308
309 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
310
zhanghch058d1b46d2021-04-01 11:18:24 +0800311 void operator()(const Request& req,
312 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 const RoutingParams& params)
314 {
315 detail::routing_handler_call_helper::Call<
316 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
317 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
318 black_magic::S<>>()(
319 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800320 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700322};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323} // namespace routing_handler_call_helper
324} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700325
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326class WebSocketRule : public BaseRule
327{
328 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 public:
Ed Tanousf23b7292020-10-15 09:41:17 -0700331 WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500332 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700333
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500335 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700336
Ed Tanous104f09c2022-01-25 09:56:04 -0800337 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800339 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800341 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700343
Ed Tanous104f09c2022-01-25 09:56:04 -0800344 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800345 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 {
Nan Zhou93c02022022-02-24 18:21:07 -0800347 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530348 std::shared_ptr<
349 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
350 myConnection = std::make_shared<
351 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000352 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530353 closeHandler, errorHandler);
354 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 }
356#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -0800357 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800358 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
359 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 {
Nan Zhou93c02022022-02-24 18:21:07 -0800361 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800362 std::shared_ptr<crow::websocket::ConnectionImpl<
363 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
364 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
365 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000366 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800367 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368 myConnection->start();
369 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700370#endif
371
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500372 template <typename Func>
373 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700374 {
375 openHandler = f;
376 return *this;
377 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700378
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500379 template <typename Func>
380 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700381 {
382 messageHandler = f;
383 return *this;
384 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700385
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500386 template <typename Func>
387 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388 {
389 closeHandler = f;
390 return *this;
391 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700392
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500393 template <typename Func>
394 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700395 {
396 errorHandler = f;
397 return *this;
398 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700399
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400 protected:
zhanghch0577726382021-10-21 14:07:57 +0800401 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
403 messageHandler;
404 std::function<void(crow::websocket::Connection&, const std::string&)>
405 closeHandler;
406 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700407};
408
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500409template <typename T>
410struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411{
412 using self_t = T;
413 WebSocketRule& websocket()
414 {
Ed Tanous271584a2019-07-09 16:24:22 -0700415 self_t* self = static_cast<self_t*>(this);
416 WebSocketRule* p = new WebSocketRule(self->rule);
417 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 return *p;
419 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700420
Ed Tanousf23b7292020-10-15 09:41:17 -0700421 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
Ed Tanous271584a2019-07-09 16:24:22 -0700423 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700424 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700425 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700427
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 self_t& methods(boost::beast::http::verb method)
429 {
Ed Tanous271584a2019-07-09 16:24:22 -0700430 self_t* self = static_cast<self_t*>(this);
431 self->methodsBitfield = 1U << static_cast<size_t>(method);
432 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700433 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700434
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000436 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 {
Ed Tanous271584a2019-07-09 16:24:22 -0700438 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000439 methods(argsMethod...);
Ed Tanous271584a2019-07-09 16:24:22 -0700440 self->methodsBitfield |= 1U << static_cast<size_t>(method);
441 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 }
Tanousf00032d2018-11-05 01:18:10 -0300443
Ed Tanous432a8902021-06-14 15:28:56 -0700444 self_t& privileges(
445 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300446 {
Ed Tanous271584a2019-07-09 16:24:22 -0700447 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700448 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300449 {
Ed Tanous271584a2019-07-09 16:24:22 -0700450 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300451 }
Ed Tanous271584a2019-07-09 16:24:22 -0700452 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300453 }
Ed Tanoused398212021-06-09 17:05:54 -0700454
455 template <size_t N, typename... MethodArgs>
456 self_t& privileges(const std::array<redfish::Privileges, N>& p)
457 {
458 self_t* self = static_cast<self_t*>(this);
459 for (const redfish::Privileges& privilege : p)
460 {
461 self->privilegesSet.emplace_back(privilege);
462 }
463 return *self;
464 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700465};
466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
468{
469 public:
Ed Tanousf23b7292020-10-15 09:41:17 -0700470 DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500471 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 void validate() override
474 {
475 if (!erasedHandler)
476 {
477 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
478 "no handler for url " + rule);
479 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700480 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700481
zhanghch058d1b46d2021-04-01 11:18:24 +0800482 void handle(const Request& req,
483 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 const RoutingParams& params) override
485 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800486 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700488
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500489 template <typename Func>
490 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700491 {
Ed Tanousc867a832022-03-10 14:17:00 -0800492 using boost::callable_traits::args_t;
493 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
494 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
495 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 }
497
498 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500499 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 // enable_if Arg1 != request
501
502 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800503 std::function<void(const Request&,
504 const std::shared_ptr<bmcweb::AsyncResp>&,
505 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800506 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 {
Ed Tanousc867a832022-03-10 14:17:00 -0800508 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509
510 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700511 black_magic::getParameterTag(rule.c_str()),
512 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 typename function_t::template arg<Indices>...>::value))
514 {
515 throw std::runtime_error("routeDynamic: Handler type is mismatched "
516 "with URL parameters: " +
517 rule);
518 }
519 auto ret = detail::routing_handler_call_helper::Wrapped<
520 Func, typename function_t::template arg<Indices>...>();
521 ret.template set<typename function_t::template arg<Indices>...>(
522 std::move(f));
523 return ret;
524 }
525
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500526 template <typename Func>
527 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 {
529 nameStr = std::move(name);
530 (*this).template operator()<Func>(std::forward(f));
531 }
532
533 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800534 std::function<void(const Request&,
535 const std::shared_ptr<bmcweb::AsyncResp>&,
536 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700538};
539
540template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500541class TaggedRule :
542 public BaseRule,
543 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544{
545 public:
546 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700547
Ed Tanousf23b7292020-10-15 09:41:17 -0700548 TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500549 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700550
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 void validate() override
552 {
553 if (!handler)
554 {
555 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
556 "no handler for url " + rule);
557 }
558 }
559
560 template <typename Func>
561 typename std::enable_if<
562 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
563 void>::type
564 operator()(Func&& f)
565 {
566 static_assert(
567 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
568 black_magic::CallHelper<
569 Func, black_magic::S<crow::Request, Args...>>::value,
570 "Handler type is mismatched with URL parameters");
571 static_assert(
572 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
573 "Handler function cannot have void return type; valid return "
574 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500575 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800577 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800578 const Request&,
579 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
580 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 }
582
583 template <typename Func>
584 typename std::enable_if<
585 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700586 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700587 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 void>::type
589 operator()(Func&& f)
590 {
591 static_assert(
592 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
593 black_magic::CallHelper<
594 Func, black_magic::S<crow::Request, Args...>>::value,
595 "Handler type is mismatched with URL parameters");
596 static_assert(
597 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
598 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 Tanous7045c8d2017-04-03 10:04:37 -0700602
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800603 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800604 const crow::Request& req,
605 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
606 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700608
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 template <typename Func>
610 typename std::enable_if<
611 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
612 !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 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800622 Func, black_magic::S<crow::Request,
623 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 Args...>>::value,
625 "Handler type is mismatched with URL parameters");
626 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800627 std::is_same<
628 void,
629 decltype(f(std::declval<crow::Request>(),
630 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
631 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300632 "Handler function with response argument should have void "
633 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700635
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800636 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700638
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500639 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700640 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700642 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 (*this).template operator()<Func>(std::forward(f));
644 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700645
zhanghch058d1b46d2021-04-01 11:18:24 +0800646 void handle(const Request& req,
647 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 const RoutingParams& params) override
649 {
650 detail::routing_handler_call_helper::Call<
651 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
652 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
653 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800654 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700656
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800658 std::function<void(const crow::Request&,
659 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
660 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700661};
662
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663class Trie
664{
665 public:
666 struct Node
667 {
668 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700669 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
670 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800671 using ChildMap = boost::container::flat_map<
672 std::string, unsigned, std::less<>,
673 std::vector<std::pair<std::string, unsigned>>>;
674 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700675
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 bool isSimpleNode() const
677 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800678 return ruleIndex == 0 &&
679 std::all_of(std::begin(paramChildrens),
680 std::end(paramChildrens),
681 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700682 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700683 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700684
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500686 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687
688 private:
689 void optimizeNode(Node* node)
690 {
Ed Tanous271584a2019-07-09 16:24:22 -0700691 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800693 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700694 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700696 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 Node* child = &nodes[x];
698 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700699 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700701 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700703 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800705 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
707 Node* child = &nodes[kv.second];
708 if (!child->isSimpleNode())
709 {
710 mergeWithChild = false;
711 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700712 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 }
714 if (mergeWithChild)
715 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800716 Node::ChildMap merged;
717 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 {
719 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800720 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300721 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 {
723 merged[kv.first + childKv.first] = childKv.second;
724 }
725 }
726 node->children = std::move(merged);
727 optimizeNode(node);
728 }
729 else
730 {
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 optimizeNode(child);
735 }
736 }
737 }
738
739 void optimize()
740 {
741 optimizeNode(head());
742 }
743
744 public:
745 void validate()
746 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 optimize();
748 }
749
Ed Tanous81ce6092020-12-17 16:54:55 +0000750 void findRouteIndexes(const std::string& reqUrl,
751 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300752 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 {
754 if (node == nullptr)
755 {
756 node = head();
757 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800758 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 {
760 const std::string& fragment = kv.first;
761 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000762 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700763 {
764 if (child->ruleIndex != 0 && fragment != "/")
765 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000766 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000768 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700769 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 }
771 else
772 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000773 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 {
Ed Tanous271584a2019-07-09 16:24:22 -0700775 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000776 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700777 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 }
779 }
780 }
781 }
782
783 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000784 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700785 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 {
787 RoutingParams empty;
788 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700789 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700791 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792
793 unsigned found{};
794 RoutingParams matchParams;
795
796 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700797 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700799 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000800 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700801 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700803 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804
805 auto updateFound =
806 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanouse662eae2022-01-25 10:39:19 -0800807 if (ret.first != 0U && (found == 0U || found > ret.first))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 {
809 found = ret.first;
810 matchParams = std::move(ret.second);
811 }
812 };
813
Ed Tanouse662eae2022-01-25 10:39:19 -0800814 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000816 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
818 {
Ed Tanous543f4402022-01-06 13:12:53 -0800819 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 errno = 0;
821 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000822 std::strtoll(reqUrl.data() + pos, &eptr, 10);
823 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 {
825 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000826 std::pair<unsigned, RoutingParams> ret =
827 find(reqUrl,
828 &nodes[node->paramChildrens[static_cast<size_t>(
829 ParamType::INT)]],
830 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 updateFound(ret);
832 params->intParams.pop_back();
833 }
834 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700835 }
836
Ed Tanouse662eae2022-01-25 10:39:19 -0800837 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000839 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700840 if ((c >= '0' && c <= '9') || c == '+')
841 {
Ed Tanous543f4402022-01-06 13:12:53 -0800842 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 errno = 0;
844 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000845 std::strtoull(reqUrl.data() + pos, &eptr, 10);
846 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 {
848 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000849 std::pair<unsigned, RoutingParams> ret =
850 find(reqUrl,
851 &nodes[node->paramChildrens[static_cast<size_t>(
852 ParamType::UINT)]],
853 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 updateFound(ret);
855 params->uintParams.pop_back();
856 }
857 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700858 }
859
Ed Tanouse662eae2022-01-25 10:39:19 -0800860 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000862 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
864 {
Ed Tanous543f4402022-01-06 13:12:53 -0800865 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000867 double value = std::strtod(reqUrl.data() + pos, &eptr);
868 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 {
870 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000871 std::pair<unsigned, RoutingParams> ret =
872 find(reqUrl,
873 &nodes[node->paramChildrens[static_cast<size_t>(
874 ParamType::DOUBLE)]],
875 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 updateFound(ret);
877 params->doubleParams.pop_back();
878 }
879 }
880 }
881
Ed Tanouse662eae2022-01-25 10:39:19 -0800882 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000884 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000885 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000887 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700888 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700890 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 }
892
893 if (epos != pos)
894 {
895 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000896 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300897 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000898 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700899 &nodes[node->paramChildrens[static_cast<size_t>(
900 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000901 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 updateFound(ret);
903 params->stringParams.pop_back();
904 }
905 }
906
Ed Tanouse662eae2022-01-25 10:39:19 -0800907 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000909 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910
911 if (epos != pos)
912 {
913 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000914 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700915 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000916 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700917 &nodes[node->paramChildrens[static_cast<size_t>(
918 ParamType::PATH)]],
919 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 updateFound(ret);
921 params->stringParams.pop_back();
922 }
923 }
924
Ed Tanousa94ac612022-02-22 11:13:24 -0800925 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 {
927 const std::string& fragment = kv.first;
928 const Node* child = &nodes[kv.second];
929
Ed Tanous81ce6092020-12-17 16:54:55 +0000930 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
Tanousf00032d2018-11-05 01:18:10 -0300932 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000933 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 updateFound(ret);
935 }
936 }
937
938 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700939 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940
941 void add(const std::string& url, unsigned ruleIndex)
942 {
Ed Tanous271584a2019-07-09 16:24:22 -0700943 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944
945 for (unsigned i = 0; i < url.size(); i++)
946 {
947 char c = url[i];
948 if (c == '<')
949 {
Tanousf00032d2018-11-05 01:18:10 -0300950 const static std::array<std::pair<ParamType, std::string>, 7>
951 paramTraits = {{
952 {ParamType::INT, "<int>"},
953 {ParamType::UINT, "<uint>"},
954 {ParamType::DOUBLE, "<float>"},
955 {ParamType::DOUBLE, "<double>"},
956 {ParamType::STRING, "<str>"},
957 {ParamType::STRING, "<string>"},
958 {ParamType::PATH, "<path>"},
959 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700960
Tanousf00032d2018-11-05 01:18:10 -0300961 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962 {
Tanousf00032d2018-11-05 01:18:10 -0300963 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700964 {
Ed Tanous271584a2019-07-09 16:24:22 -0700965 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800966 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 {
Tanousf00032d2018-11-05 01:18:10 -0300968 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700969 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970 }
Ed Tanous271584a2019-07-09 16:24:22 -0700971 idx = nodes[idx].paramChildrens[index];
972 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700973 break;
974 }
975 }
976
977 i--;
978 }
979 else
980 {
981 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -0800982 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700983 {
Tanousf00032d2018-11-05 01:18:10 -0300984 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 nodes[idx].children.emplace(piece, newNodeIdx);
986 }
987 idx = nodes[idx].children[piece];
988 }
989 }
Ed Tanouse662eae2022-01-25 10:39:19 -0800990 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700991 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700993 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700995 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700996
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700998 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 {
Ed Tanous271584a2019-07-09 16:24:22 -07001000 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001002 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001003 {
1004 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001005 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1006 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007 {
1008 case ParamType::INT:
1009 BMCWEB_LOG_DEBUG << "<int>";
1010 break;
1011 case ParamType::UINT:
1012 BMCWEB_LOG_DEBUG << "<uint>";
1013 break;
1014 case ParamType::DOUBLE:
1015 BMCWEB_LOG_DEBUG << "<float>";
1016 break;
1017 case ParamType::STRING:
1018 BMCWEB_LOG_DEBUG << "<str>";
1019 break;
1020 case ParamType::PATH:
1021 BMCWEB_LOG_DEBUG << "<path>";
1022 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001023 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 BMCWEB_LOG_DEBUG << "<ERROR>";
1025 break;
1026 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1029 }
1030 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001031 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 {
1033 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001034 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001035 << kv.first;
1036 debugNodePrint(&nodes[kv.second], level + 1);
1037 }
1038 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001039
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 public:
1041 void debugPrint()
1042 {
Ed Tanous271584a2019-07-09 16:24:22 -07001043 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001045
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046 private:
1047 const Node* head() const
1048 {
1049 return &nodes.front();
1050 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001051
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052 Node* head()
1053 {
1054 return &nodes.front();
1055 }
1056
1057 unsigned newNode()
1058 {
1059 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001060 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001061 }
1062
1063 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001064};
1065
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066class Router
1067{
1068 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001069 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001070
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 DynamicRule& newRuleDynamic(const std::string& rule)
1072 {
1073 std::unique_ptr<DynamicRule> ruleObject =
1074 std::make_unique<DynamicRule>(rule);
1075 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001076 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001077
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001079 }
1080
Ed Tanous1abe55e2018-09-05 08:30:59 -07001081 template <uint64_t N>
1082 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1083 newRuleTagged(const std::string& rule)
1084 {
1085 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1086 TaggedRule>;
1087 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1088 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001089 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001090
1091 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001092 }
1093
Tanousf00032d2018-11-05 01:18:10 -03001094 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 {
Tanousf00032d2018-11-05 01:18:10 -03001096 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097 {
Tanousf00032d2018-11-05 01:18:10 -03001098 return;
1099 }
Ed Tanous888880a2020-08-24 13:48:50 -07001100 for (size_t method = 0, methodBit = 1; method < maxHttpVerbCount;
Ed Tanous2c70f802020-09-28 14:29:23 -07001101 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001102 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001103 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001104 {
1105 perMethods[method].rules.emplace_back(ruleObject);
1106 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001107 rule, static_cast<unsigned>(
1108 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001109 // directory case:
1110 // request to `/about' url matches `/about/' rule
1111 if (rule.size() > 2 && rule.back() == '/')
1112 {
1113 perMethods[method].trie.add(
1114 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001115 static_cast<unsigned>(perMethods[method].rules.size() -
1116 1));
Tanousf00032d2018-11-05 01:18:10 -03001117 }
1118 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001119 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001120 }
1121
Ed Tanous1abe55e2018-09-05 08:30:59 -07001122 void validate()
1123 {
Tanousf00032d2018-11-05 01:18:10 -03001124 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001125 {
1126 if (rule)
1127 {
Tanousf00032d2018-11-05 01:18:10 -03001128 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001130 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001131 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001132 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001133 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001134 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001135 }
1136 }
Tanousf00032d2018-11-05 01:18:10 -03001137 for (PerMethod& perMethod : perMethods)
1138 {
1139 perMethod.trie.validate();
1140 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001141 }
1142
Ed Tanous1abe55e2018-09-05 08:30:59 -07001143 template <typename Adaptor>
1144 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1145 {
Ed Tanous271584a2019-07-09 16:24:22 -07001146 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001147 {
1148 res.result(boost::beast::http::status::not_found);
1149 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001150 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001151 }
Tanousf00032d2018-11-05 01:18:10 -03001152
Ed Tanous271584a2019-07-09 16:24:22 -07001153 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001154 Trie& trie = perMethod.trie;
1155 std::vector<BaseRule*>& rules = perMethod.rules;
1156
1157 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001159 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001160 {
1161 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001162 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163 res.end();
1164 return;
1165 }
1166
1167 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001168 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001170 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001171
Ed Tanous271584a2019-07-09 16:24:22 -07001172 if ((rules[ruleIndex]->getMethods() &
1173 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001174 {
1175 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1176 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001177 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001178 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001179 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 res.end();
1181 return;
1182 }
1183
1184 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001185 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001186 << rules[ruleIndex]->getMethods();
1187
1188 // any uncaught exceptions become 500s
1189 try
1190 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -08001191 rules[ruleIndex]->handleUpgrade(req, res,
1192 std::forward<Adaptor>(adaptor));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001193 }
Patrick Williamsc5967042021-10-06 12:39:54 -05001194 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001195 {
1196 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001197 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198 res.end();
1199 return;
1200 }
1201 catch (...)
1202 {
1203 BMCWEB_LOG_ERROR
1204 << "An uncaught exception occurred. The type was unknown "
1205 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001206 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001207 res.end();
1208 return;
1209 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001210 }
1211
Ed Tanous88a03c52022-03-14 10:16:07 -07001212 std::string buildAllowHeader(Request& req)
1213 {
1214 std::string allowHeader;
1215 // Check to see if this url exists at any verb
1216 for (size_t perMethodIndex = 0; perMethodIndex < perMethods.size();
1217 perMethodIndex++)
1218 {
1219 const PerMethod& p = perMethods[perMethodIndex];
1220 const std::pair<unsigned, RoutingParams>& found2 =
1221 p.trie.find(req.url);
1222 if (found2.first == 0)
1223 {
1224 continue;
1225 }
1226 if (!allowHeader.empty())
1227 {
1228 allowHeader += ", ";
1229 }
1230 allowHeader += boost::beast::http::to_string(
1231 static_cast<boost::beast::http::verb>(perMethodIndex));
1232 }
1233 return allowHeader;
1234 }
1235
zhanghch058d1b46d2021-04-01 11:18:24 +08001236 void handle(Request& req,
1237 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001238 {
Ed Tanous271584a2019-07-09 16:24:22 -07001239 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001240 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001241 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001242 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001243 }
Ed Tanous271584a2019-07-09 16:24:22 -07001244 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001245 Trie& trie = perMethod.trie;
1246 std::vector<BaseRule*>& rules = perMethod.rules;
Ed Tanous88a03c52022-03-14 10:16:07 -07001247 std::string allowHeader = buildAllowHeader(req);
1248 if (!allowHeader.empty())
1249 {
1250 asyncResp->res.addHeader(boost::beast::http::field::allow,
1251 allowHeader);
1252
1253 // If this is a header request, we're done.
1254 if (req.method() == boost::beast::http::verb::head)
1255 {
1256 return;
1257 }
1258 }
Tanousf00032d2018-11-05 01:18:10 -03001259
1260 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001261
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001263 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001265 if (!allowHeader.empty())
Ed Tanous2634dcd2019-03-26 09:28:06 -07001266 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001267 asyncResp->res.result(
1268 boost::beast::http::status::method_not_allowed);
1269 return;
Ed Tanous2634dcd2019-03-26 09:28:06 -07001270 }
Ed Tanous88a03c52022-03-14 10:16:07 -07001271
Ed Tanous1abe55e2018-09-05 08:30:59 -07001272 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
zhanghch058d1b46d2021-04-01 11:18:24 +08001273 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274 return;
1275 }
1276
1277 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001278 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001280 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001281
Ed Tanous271584a2019-07-09 16:24:22 -07001282 if ((rules[ruleIndex]->getMethods() &
1283 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284 {
1285 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1286 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001287 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001288 << rules[ruleIndex]->getMethods();
zhanghch058d1b46d2021-04-01 11:18:24 +08001289 asyncResp->res.result(
1290 boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 return;
1292 }
1293
1294 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001295 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001296 << rules[ruleIndex]->getMethods();
1297
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001298 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001299 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001300 rules[ruleIndex]->handle(req, asyncResp, found.second);
James Feist7166bf02019-12-10 16:52:14 +00001301 return;
1302 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001303
1304 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -08001305 [&req, asyncResp, &rules, ruleIndex,
1306 found](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001307 const dbus::utility::DBusPropertiesMap& userInfoMap) {
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001308 if (ec)
1309 {
1310 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
zhanghch058d1b46d2021-04-01 11:18:24 +08001311 asyncResp->res.result(
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001312 boost::beast::http::status::internal_server_error);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001313 return;
1314 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001315 std::string userRole{};
Ed Tanousb9d36b42022-02-26 21:42:46 -08001316 const bool* remoteUser = nullptr;
1317 std::optional<bool> passwordExpired;
1318
1319 for (const auto& userInfo : userInfoMap)
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001320 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001321 if (userInfo.first == "UserPrivilege")
1322 {
1323 const std::string* userRolePtr =
1324 std::get_if<std::string>(&userInfo.second);
1325 if (userRolePtr == nullptr)
1326 {
1327 continue;
1328 }
1329 userRole = *userRolePtr;
1330 BMCWEB_LOG_DEBUG
1331 << "userName = " << req.session->username
1332 << " userRole = " << *userRolePtr;
1333 }
1334 else if (userInfo.first == "RemoteUser")
1335 {
1336 remoteUser = std::get_if<bool>(&userInfo.second);
1337 }
1338 else if (userInfo.first == "UserPasswordExpired")
1339 {
1340 const bool* passwordExpiredPtr =
1341 std::get_if<bool>(&userInfo.second);
1342 if (passwordExpiredPtr == nullptr)
1343 {
1344 continue;
1345 }
1346 passwordExpired = *passwordExpiredPtr;
1347 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001348 }
1349
Ed Tanousb9d36b42022-02-26 21:42:46 -08001350 if (remoteUser == nullptr)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001351 {
1352 BMCWEB_LOG_ERROR
1353 << "RemoteUser property missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001354 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001355 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001356 return;
1357 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001358
Ed Tanousb9d36b42022-02-26 21:42:46 -08001359 if (passwordExpired == std::nullopt)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001360 {
Ed Tanousb9d36b42022-02-26 21:42:46 -08001361 if (!*remoteUser)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001362 {
1363 BMCWEB_LOG_ERROR
1364 << "UserPasswordExpired property is expected for"
1365 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001366 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001367 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001368 return;
1369 }
Ed Tanousb9d36b42022-02-26 21:42:46 -08001370 passwordExpired = false;
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001371 }
1372
Ed Tanous23a21a12020-07-25 04:45:05 +00001373 // Get the userprivileges from the role
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001374 redfish::Privileges userPrivileges =
1375 redfish::getUserPrivileges(userRole);
1376
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001377 // Set isConfigureSelfOnly based on D-Bus results. This
1378 // ignores the results from both pamAuthenticateUser and the
1379 // value from any previous use of this session.
Ed Tanousb9d36b42022-02-26 21:42:46 -08001380 req.session->isConfigureSelfOnly = *passwordExpired;
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001381
Ed Tanous23a21a12020-07-25 04:45:05 +00001382 // Modifyprivileges if isConfigureSelfOnly.
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001383 if (req.session->isConfigureSelfOnly)
1384 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001385 // Remove allprivileges except ConfigureSelf
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001386 userPrivileges = userPrivileges.intersection(
1387 redfish::Privileges{"ConfigureSelf"});
1388 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1389 }
1390
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001391 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1392 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001393 asyncResp->res.result(
1394 boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001395 if (req.session->isConfigureSelfOnly)
1396 {
1397 redfish::messages::passwordChangeRequired(
zhanghch058d1b46d2021-04-01 11:18:24 +08001398 asyncResp->res,
Ed Tanousace85d62021-10-26 12:45:59 -07001399 crow::utility::urlFromPieces(
1400 "redfish", "v1", "AccountService", "Accounts",
1401 req.session->username));
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001402 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001403 return;
1404 }
1405
1406 req.userRole = userRole;
zhanghch058d1b46d2021-04-01 11:18:24 +08001407 rules[ruleIndex]->handle(req, asyncResp, found.second);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001408 },
1409 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1410 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1411 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001412 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001413
Ed Tanous1abe55e2018-09-05 08:30:59 -07001414 void debugPrint()
1415 {
Ed Tanous271584a2019-07-09 16:24:22 -07001416 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001417 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001418 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1419 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001420 perMethods[i].trie.debugPrint();
1421 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001422 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001423
Ed Tanous1abe55e2018-09-05 08:30:59 -07001424 std::vector<const std::string*> getRoutes(const std::string& parent)
1425 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001426 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001427
1428 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001429 {
Tanousf00032d2018-11-05 01:18:10 -03001430 std::vector<unsigned> x;
1431 pm.trie.findRouteIndexes(parent, x);
1432 for (unsigned index : x)
1433 {
1434 ret.push_back(&pm.rules[index]->rule);
1435 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001436 }
1437 return ret;
1438 }
1439
1440 private:
Tanousf00032d2018-11-05 01:18:10 -03001441 struct PerMethod
1442 {
1443 std::vector<BaseRule*> rules;
1444 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001445 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001446 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001447 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001448 {}
Tanousf00032d2018-11-05 01:18:10 -03001449 };
Ed Tanous888880a2020-08-24 13:48:50 -07001450
1451 const static size_t maxHttpVerbCount =
Ed Tanouscc090442020-10-07 08:20:50 -07001452 static_cast<size_t>(boost::beast::http::verb::unlink);
Ed Tanous888880a2020-08-24 13:48:50 -07001453
Tanousf00032d2018-11-05 01:18:10 -03001454 std::array<PerMethod, maxHttpVerbCount> perMethods;
1455 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001456};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457} // namespace crow