blob: 870a62c9ed47a1ed5ec8fea33918a562c8051f70 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "common.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08004#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06005#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include "http_request.hpp"
7#include "http_response.hpp"
8#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -03009#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053010#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "utility.hpp"
12#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070013
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020014#include <async_resp.hpp>
Ed Tanous88a03c52022-03-14 10:16:07 -070015#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030016#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017
Ed Tanouse0d918b2018-03-27 17:41:04 -070018#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070019#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070020#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070021#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070022#include <memory>
23#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <utility>
25#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Tanousf00032d2018-11-05 01:18:10 -030029
Ed Tanous44e45182022-07-26 16:47:23 -070030// Note, this is an imperfect abstraction. There are a lot of verbs that we
31// use memory for, but are basically unused by most implementations.
32// Ideally we would have a list of verbs that we do use, and only index in
33// to a smaller array of those, but that would require a translation from
34// boost::beast::http::verb, to the bmcweb index.
35static constexpr size_t maxVerbIndex =
36 static_cast<size_t>(boost::beast::http::verb::patch);
37
38// MaxVerb + 1 is designated as the "not found" verb. It is done this way
39// to keep the BaseRule as a single bitfield (thus keeping the struct small)
40// while still having a way to declare a route a "not found" route.
41static constexpr const size_t notFoundIndex = maxVerbIndex + 1;
Ed Tanous759cf102022-07-31 16:36:52 -070042static constexpr const size_t methodNotAllowedIndex = notFoundIndex + 1;
Ed Tanous44e45182022-07-26 16:47:23 -070043
Ed Tanous1abe55e2018-09-05 08:30:59 -070044class BaseRule
45{
46 public:
Ed Tanous4e23a442022-06-06 09:57:26 -070047 explicit BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050048 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070049
Ed Tanous0c0084a2019-10-24 15:57:51 -070050 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070051
Ed Tanousecd6a3a2022-01-07 09:18:40 -080052 BaseRule(const BaseRule&) = delete;
53 BaseRule(BaseRule&&) = delete;
54 BaseRule& operator=(const BaseRule&) = delete;
55 BaseRule& operator=(const BaseRule&&) = delete;
56
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 virtual void validate() = 0;
58 std::unique_ptr<BaseRule> upgrade()
59 {
60 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070061 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070063 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070064 return {};
65 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070066
Ed Tanous104f09c2022-01-25 09:56:04 -080067 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080068 const std::shared_ptr<bmcweb::AsyncResp>&,
69 const RoutingParams&) = 0;
Ed Tanous104f09c2022-01-25 09:56:04 -080070 virtual void handleUpgrade(const Request& /*req*/, Response& res,
71 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070073 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 res.end();
75 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070076#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080077 virtual void handleUpgrade(
78 const Request& /*req*/, Response& res,
79 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070081 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 res.end();
83 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070084#endif
85
Ed Tanous9eb808c2022-01-25 10:19:23 -080086 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 {
88 return methodsBitfield;
89 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070090
Tanousf00032d2018-11-05 01:18:10 -030091 bool checkPrivileges(const redfish::Privileges& userPrivileges)
92 {
93 // If there are no privileges assigned, assume no privileges
94 // required
95 if (privilegesSet.empty())
96 {
97 return true;
98 }
99
100 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
101 {
102 if (userPrivileges.isSupersetOf(requiredPrivileges))
103 {
104 return true;
105 }
106 }
107 return false;
108 }
109
Ed Tanous271584a2019-07-09 16:24:22 -0700110 size_t methodsBitfield{
111 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous44e45182022-07-26 16:47:23 -0700112 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
Ed Tanous759cf102022-07-31 16:36:52 -0700113 methodNotAllowedIndex,
Ed Tanous44e45182022-07-26 16:47:23 -0700114 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700115
Tanousf00032d2018-11-05 01:18:10 -0300116 std::vector<redfish::Privileges> privilegesSet;
117
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 std::string rule;
119 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700120
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700122
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500124 template <typename T>
125 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126};
127
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128namespace detail
129{
130namespace routing_handler_call_helper
131{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500132template <typename T, int Pos>
133struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134{
135 using type = T;
136 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700137};
138
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139template <typename H1>
140struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141{
142 H1& handler;
143 const RoutingParams& params;
144 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800145 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700146};
147
148template <typename F, int NInt, int NUint, int NDouble, int NString,
149 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500151{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700152
153template <typename F, int NInt, int NUint, int NDouble, int NString,
154 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700155struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 black_magic::S<Args2...>>
157{
158 void operator()(F cparams)
159 {
160 using pushed = typename black_magic::S<Args2...>::template push_back<
161 CallPair<int64_t, NInt>>;
162 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
163 pushed>()(cparams);
164 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700165};
166
167template <typename F, int NInt, int NUint, int NDouble, int NString,
168 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700169struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
171{
172 void operator()(F cparams)
173 {
174 using pushed = typename black_magic::S<Args2...>::template push_back<
175 CallPair<uint64_t, NUint>>;
176 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
177 pushed>()(cparams);
178 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700179};
180
181template <typename F, int NInt, int NUint, int NDouble, int NString,
182 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700183struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 black_magic::S<Args2...>>
185{
186 void operator()(F cparams)
187 {
188 using pushed = typename black_magic::S<Args2...>::template push_back<
189 CallPair<double, NDouble>>;
190 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
191 pushed>()(cparams);
192 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700193};
194
195template <typename F, int NInt, int NUint, int NDouble, int NString,
196 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700197struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
199{
200 void operator()(F cparams)
201 {
202 using pushed = typename black_magic::S<Args2...>::template push_back<
203 CallPair<std::string, NString>>;
204 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
205 pushed>()(cparams);
206 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700207};
208
209template <typename F, int NInt, int NUint, int NDouble, int NString,
210 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700211struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212 black_magic::S<Args1...>>
213{
214 void operator()(F cparams)
215 {
216 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800217 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700218 cparams.params.template get<typename Args1::type>(Args1::pos)...);
219 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700220};
221
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500222template <typename Func, typename... ArgsWrapped>
223struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224{
225 template <typename... Args>
226 void set(
227 Func f,
228 typename std::enable_if<
229 !std::is_same<
230 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
231 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800232 int>::type /*enable*/
233 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800235 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800236 const Request&,
237 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
238 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700239 }
240
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500241 template <typename Req, typename... Args>
242 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700244 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500245 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700246
zhanghch058d1b46d2021-04-01 11:18:24 +0800247 void operator()(const Request& req,
248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
249 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800251 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700253
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 Func f;
255 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700256
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 template <typename... Args>
258 void set(
259 Func f,
260 typename std::enable_if<
261 std::is_same<
262 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
263 const Request&>::value &&
264 !std::is_same<typename std::tuple_element<
265 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800266 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800267 int>::type /*enable*/
268 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 {
270 handler = ReqHandlerWrapper<Args...>(std::move(f));
271 /*handler = (
272 [f = std::move(f)]
273 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700274 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 res.end();
276 });*/
277 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700278
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 template <typename... Args>
280 void set(
281 Func f,
282 typename std::enable_if<
283 std::is_same<
284 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
285 const Request&>::value &&
286 std::is_same<typename std::tuple_element<
287 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800288 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800289 int>::type /*enable*/
290 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 {
292 handler = std::move(f);
293 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700294
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500295 template <typename... Args>
296 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800298 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800299 const crow::Request& /*req*/,
300 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700302 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700304
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 template <typename... Args>
306 struct HandlerTypeHelper<const Request&, Args...>
307 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800308 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800309 const crow::Request& /*req*/,
310 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700312 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700314
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800316 struct HandlerTypeHelper<const Request&,
317 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800319 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800320 const crow::Request& /*req*/,
321 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700322 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700323 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 };
325
326 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
327
zhanghch058d1b46d2021-04-01 11:18:24 +0800328 void operator()(const Request& req,
329 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 const RoutingParams& params)
331 {
332 detail::routing_handler_call_helper::Call<
333 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
334 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
335 black_magic::S<>>()(
336 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800337 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700339};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340} // namespace routing_handler_call_helper
341} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700342
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343class WebSocketRule : public BaseRule
344{
345 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700346
Ed Tanous1abe55e2018-09-05 08:30:59 -0700347 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700348 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500349 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500352 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700353
Ed Tanous104f09c2022-01-25 09:56:04 -0800354 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800355 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800356 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800358 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700360
Ed Tanous104f09c2022-01-25 09:56:04 -0800361 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800362 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700363 {
Nan Zhou93c02022022-02-24 18:21:07 -0800364 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530365 std::shared_ptr<
366 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
367 myConnection = std::make_shared<
368 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000369 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530370 closeHandler, errorHandler);
371 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 }
373#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -0800374 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800375 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
376 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 {
Nan Zhou93c02022022-02-24 18:21:07 -0800378 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800379 std::shared_ptr<crow::websocket::ConnectionImpl<
380 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
381 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
382 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000383 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800384 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 myConnection->start();
386 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700387#endif
388
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500389 template <typename Func>
390 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700391 {
392 openHandler = f;
393 return *this;
394 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700395
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500396 template <typename Func>
397 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398 {
399 messageHandler = f;
400 return *this;
401 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700402
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500403 template <typename Func>
404 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 {
406 closeHandler = f;
407 return *this;
408 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700409
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500410 template <typename Func>
411 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 {
413 errorHandler = f;
414 return *this;
415 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700416
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 protected:
zhanghch0577726382021-10-21 14:07:57 +0800418 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
420 messageHandler;
421 std::function<void(crow::websocket::Connection&, const std::string&)>
422 closeHandler;
423 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700424};
425
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500426template <typename T>
427struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428{
429 using self_t = T;
430 WebSocketRule& websocket()
431 {
Ed Tanous271584a2019-07-09 16:24:22 -0700432 self_t* self = static_cast<self_t*>(this);
433 WebSocketRule* p = new WebSocketRule(self->rule);
434 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 return *p;
436 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700437
Ed Tanousf23b7292020-10-15 09:41:17 -0700438 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 {
Ed Tanous271584a2019-07-09 16:24:22 -0700440 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700441 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700442 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700444
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 self_t& methods(boost::beast::http::verb method)
446 {
Ed Tanous271584a2019-07-09 16:24:22 -0700447 self_t* self = static_cast<self_t*>(this);
448 self->methodsBitfield = 1U << static_cast<size_t>(method);
449 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700451
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000453 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 {
Ed Tanous271584a2019-07-09 16:24:22 -0700455 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000456 methods(argsMethod...);
Ed Tanous271584a2019-07-09 16:24:22 -0700457 self->methodsBitfield |= 1U << static_cast<size_t>(method);
458 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 }
Tanousf00032d2018-11-05 01:18:10 -0300460
Ed Tanous44e45182022-07-26 16:47:23 -0700461 self_t& notFound()
462 {
463 self_t* self = static_cast<self_t*>(this);
464 self->methodsBitfield = 1U << notFoundIndex;
465 return *self;
466 }
467
Ed Tanous759cf102022-07-31 16:36:52 -0700468 self_t& methodNotAllowed()
469 {
470 self_t* self = static_cast<self_t*>(this);
471 self->methodsBitfield = 1U << methodNotAllowedIndex;
472 return *self;
473 }
474
Ed Tanous432a8902021-06-14 15:28:56 -0700475 self_t& privileges(
476 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300477 {
Ed Tanous271584a2019-07-09 16:24:22 -0700478 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700479 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300480 {
Ed Tanous271584a2019-07-09 16:24:22 -0700481 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300482 }
Ed Tanous271584a2019-07-09 16:24:22 -0700483 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300484 }
Ed Tanoused398212021-06-09 17:05:54 -0700485
486 template <size_t N, typename... MethodArgs>
487 self_t& privileges(const std::array<redfish::Privileges, N>& p)
488 {
489 self_t* self = static_cast<self_t*>(this);
490 for (const redfish::Privileges& privilege : p)
491 {
492 self->privilegesSet.emplace_back(privilege);
493 }
494 return *self;
495 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700496};
497
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
499{
500 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700501 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500502 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700503
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 void validate() override
505 {
506 if (!erasedHandler)
507 {
508 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
509 "no handler for url " + rule);
510 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700511 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700512
zhanghch058d1b46d2021-04-01 11:18:24 +0800513 void handle(const Request& req,
514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 const RoutingParams& params) override
516 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800517 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700519
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500520 template <typename Func>
521 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
Ed Tanousc867a832022-03-10 14:17:00 -0800523 using boost::callable_traits::args_t;
524 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
525 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
526 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 }
528
529 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500530 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 // enable_if Arg1 != request
532
533 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800534 std::function<void(const Request&,
535 const std::shared_ptr<bmcweb::AsyncResp>&,
536 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800537 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 {
Ed Tanousc867a832022-03-10 14:17:00 -0800539 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540
541 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700542 black_magic::getParameterTag(rule.c_str()),
543 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 typename function_t::template arg<Indices>...>::value))
545 {
546 throw std::runtime_error("routeDynamic: Handler type is mismatched "
547 "with URL parameters: " +
548 rule);
549 }
550 auto ret = detail::routing_handler_call_helper::Wrapped<
551 Func, typename function_t::template arg<Indices>...>();
552 ret.template set<typename function_t::template arg<Indices>...>(
553 std::move(f));
554 return ret;
555 }
556
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500557 template <typename Func>
558 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 {
560 nameStr = std::move(name);
561 (*this).template operator()<Func>(std::forward(f));
562 }
563
564 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800565 std::function<void(const Request&,
566 const std::shared_ptr<bmcweb::AsyncResp>&,
567 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700569};
570
571template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500572class TaggedRule :
573 public BaseRule,
574 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575{
576 public:
577 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700578
Ed Tanous4e23a442022-06-06 09:57:26 -0700579 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500580 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700581
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 void validate() override
583 {
584 if (!handler)
585 {
586 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
587 "no handler for url " + rule);
588 }
589 }
590
591 template <typename Func>
592 typename std::enable_if<
593 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
594 void>::type
595 operator()(Func&& f)
596 {
597 static_assert(
598 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
599 black_magic::CallHelper<
600 Func, black_magic::S<crow::Request, Args...>>::value,
601 "Handler type is mismatched with URL parameters");
602 static_assert(
603 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
604 "Handler function cannot have void return type; valid return "
605 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500606 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800608 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800609 const Request&,
610 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
611 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 }
613
614 template <typename Func>
615 typename std::enable_if<
616 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700617 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700618 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 void>::type
620 operator()(Func&& f)
621 {
622 static_assert(
623 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
624 black_magic::CallHelper<
625 Func, black_magic::S<crow::Request, Args...>>::value,
626 "Handler type is mismatched with URL parameters");
627 static_assert(
628 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
629 std::declval<Args>()...))>::value,
630 "Handler function cannot have void return type; valid return "
631 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500632 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700633
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800634 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800635 const crow::Request& req,
636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700639
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 template <typename Func>
641 typename std::enable_if<
642 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
643 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700644 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 void>::type
646 operator()(Func&& f)
647 {
648 static_assert(
649 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
650 black_magic::CallHelper<
651 Func, black_magic::S<crow::Request, Args...>>::value ||
652 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800653 Func, black_magic::S<crow::Request,
654 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 Args...>>::value,
656 "Handler type is mismatched with URL parameters");
657 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800658 std::is_same<
659 void,
660 decltype(f(std::declval<crow::Request>(),
661 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
662 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300663 "Handler function with response argument should have void "
664 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700666
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800667 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700669
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500670 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700671 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700673 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 (*this).template operator()<Func>(std::forward(f));
675 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700676
zhanghch058d1b46d2021-04-01 11:18:24 +0800677 void handle(const Request& req,
678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 const RoutingParams& params) override
680 {
681 detail::routing_handler_call_helper::Call<
682 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
683 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
684 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800685 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700687
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800689 std::function<void(const crow::Request&,
690 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
691 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700692};
693
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694class Trie
695{
696 public:
697 struct Node
698 {
699 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700700 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
701 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800702 using ChildMap = boost::container::flat_map<
703 std::string, unsigned, std::less<>,
704 std::vector<std::pair<std::string, unsigned>>>;
705 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700706
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 bool isSimpleNode() const
708 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800709 return ruleIndex == 0 &&
710 std::all_of(std::begin(paramChildrens),
711 std::end(paramChildrens),
712 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700713 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700714 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700715
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500717 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718
719 private:
720 void optimizeNode(Node* node)
721 {
Ed Tanous271584a2019-07-09 16:24:22 -0700722 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800724 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700725 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700727 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 Node* child = &nodes[x];
729 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700730 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700732 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700734 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800736 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 {
738 Node* child = &nodes[kv.second];
739 if (!child->isSimpleNode())
740 {
741 mergeWithChild = false;
742 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700743 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 }
745 if (mergeWithChild)
746 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800747 Node::ChildMap merged;
748 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 {
750 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800751 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300752 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 {
754 merged[kv.first + childKv.first] = childKv.second;
755 }
756 }
757 node->children = std::move(merged);
758 optimizeNode(node);
759 }
760 else
761 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800762 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700763 {
764 Node* child = &nodes[kv.second];
765 optimizeNode(child);
766 }
767 }
768 }
769
770 void optimize()
771 {
772 optimizeNode(head());
773 }
774
775 public:
776 void validate()
777 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 optimize();
779 }
780
Ed Tanous81ce6092020-12-17 16:54:55 +0000781 void findRouteIndexes(const std::string& reqUrl,
782 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300783 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 {
785 if (node == nullptr)
786 {
787 node = head();
788 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800789 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
791 const std::string& fragment = kv.first;
792 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000793 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 {
795 if (child->ruleIndex != 0 && fragment != "/")
796 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000797 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000799 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700800 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 }
802 else
803 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000804 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 {
Ed Tanous271584a2019-07-09 16:24:22 -0700806 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000807 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700808 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 }
810 }
811 }
812 }
813
814 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000815 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700816 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 {
818 RoutingParams empty;
819 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700820 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700822 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823
824 unsigned found{};
825 RoutingParams matchParams;
826
827 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700828 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700830 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000831 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700832 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700834 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835
836 auto updateFound =
837 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 if (ret.first != 0U && (found == 0U || found > ret.first))
839 {
840 found = ret.first;
841 matchParams = std::move(ret.second);
842 }
843 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844
Ed Tanouse662eae2022-01-25 10:39:19 -0800845 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000847 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
849 {
Ed Tanous543f4402022-01-06 13:12:53 -0800850 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 errno = 0;
852 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000853 std::strtoll(reqUrl.data() + pos, &eptr, 10);
854 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 {
856 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000857 std::pair<unsigned, RoutingParams> ret =
858 find(reqUrl,
859 &nodes[node->paramChildrens[static_cast<size_t>(
860 ParamType::INT)]],
861 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 updateFound(ret);
863 params->intParams.pop_back();
864 }
865 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700866 }
867
Ed Tanouse662eae2022-01-25 10:39:19 -0800868 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000870 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 if ((c >= '0' && c <= '9') || c == '+')
872 {
Ed Tanous543f4402022-01-06 13:12:53 -0800873 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 errno = 0;
875 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000876 std::strtoull(reqUrl.data() + pos, &eptr, 10);
877 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 {
879 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000880 std::pair<unsigned, RoutingParams> ret =
881 find(reqUrl,
882 &nodes[node->paramChildrens[static_cast<size_t>(
883 ParamType::UINT)]],
884 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 updateFound(ret);
886 params->uintParams.pop_back();
887 }
888 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700889 }
890
Ed Tanouse662eae2022-01-25 10:39:19 -0800891 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000893 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
895 {
Ed Tanous543f4402022-01-06 13:12:53 -0800896 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000898 double value = std::strtod(reqUrl.data() + pos, &eptr);
899 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 {
901 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000902 std::pair<unsigned, RoutingParams> ret =
903 find(reqUrl,
904 &nodes[node->paramChildrens[static_cast<size_t>(
905 ParamType::DOUBLE)]],
906 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 updateFound(ret);
908 params->doubleParams.pop_back();
909 }
910 }
911 }
912
Ed Tanouse662eae2022-01-25 10:39:19 -0800913 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000915 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000916 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000918 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700919 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700921 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700922 }
923
924 if (epos != pos)
925 {
926 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000927 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300928 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000929 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700930 &nodes[node->paramChildrens[static_cast<size_t>(
931 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000932 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933 updateFound(ret);
934 params->stringParams.pop_back();
935 }
936 }
937
Ed Tanouse662eae2022-01-25 10:39:19 -0800938 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000940 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941
942 if (epos != pos)
943 {
944 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000945 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700946 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000947 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700948 &nodes[node->paramChildrens[static_cast<size_t>(
949 ParamType::PATH)]],
950 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 updateFound(ret);
952 params->stringParams.pop_back();
953 }
954 }
955
Ed Tanousa94ac612022-02-22 11:13:24 -0800956 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 {
958 const std::string& fragment = kv.first;
959 const Node* child = &nodes[kv.second];
960
Ed Tanous81ce6092020-12-17 16:54:55 +0000961 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962 {
Tanousf00032d2018-11-05 01:18:10 -0300963 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000964 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 updateFound(ret);
966 }
967 }
968
969 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700970 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971
972 void add(const std::string& url, unsigned ruleIndex)
973 {
Ed Tanous271584a2019-07-09 16:24:22 -0700974 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700975
976 for (unsigned i = 0; i < url.size(); i++)
977 {
978 char c = url[i];
979 if (c == '<')
980 {
Tanousf00032d2018-11-05 01:18:10 -0300981 const static std::array<std::pair<ParamType, std::string>, 7>
982 paramTraits = {{
983 {ParamType::INT, "<int>"},
984 {ParamType::UINT, "<uint>"},
985 {ParamType::DOUBLE, "<float>"},
986 {ParamType::DOUBLE, "<double>"},
987 {ParamType::STRING, "<str>"},
988 {ParamType::STRING, "<string>"},
989 {ParamType::PATH, "<path>"},
990 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700991
Tanousf00032d2018-11-05 01:18:10 -0300992 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 {
Tanousf00032d2018-11-05 01:18:10 -0300994 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700995 {
Ed Tanous271584a2019-07-09 16:24:22 -0700996 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -0800997 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 {
Tanousf00032d2018-11-05 01:18:10 -0300999 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -07001000 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 }
Ed Tanous271584a2019-07-09 16:24:22 -07001002 idx = nodes[idx].paramChildrens[index];
1003 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001004 break;
1005 }
1006 }
1007
1008 i--;
1009 }
1010 else
1011 {
1012 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001013 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 {
Tanousf00032d2018-11-05 01:18:10 -03001015 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 nodes[idx].children.emplace(piece, newNodeIdx);
1017 }
1018 idx = nodes[idx].children[piece];
1019 }
1020 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001021 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001022 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001023 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001024 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001025 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001026 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001029 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 {
Ed Tanous271584a2019-07-09 16:24:22 -07001031 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001033 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 {
1035 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001036 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1037 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001038 {
1039 case ParamType::INT:
1040 BMCWEB_LOG_DEBUG << "<int>";
1041 break;
1042 case ParamType::UINT:
1043 BMCWEB_LOG_DEBUG << "<uint>";
1044 break;
1045 case ParamType::DOUBLE:
1046 BMCWEB_LOG_DEBUG << "<float>";
1047 break;
1048 case ParamType::STRING:
1049 BMCWEB_LOG_DEBUG << "<str>";
1050 break;
1051 case ParamType::PATH:
1052 BMCWEB_LOG_DEBUG << "<path>";
1053 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001054 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055 BMCWEB_LOG_DEBUG << "<ERROR>";
1056 break;
1057 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001058
Ed Tanous1abe55e2018-09-05 08:30:59 -07001059 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1060 }
1061 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001062 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 {
1064 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001065 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 << kv.first;
1067 debugNodePrint(&nodes[kv.second], level + 1);
1068 }
1069 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001070
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 public:
1072 void debugPrint()
1073 {
Ed Tanous271584a2019-07-09 16:24:22 -07001074 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001076
Ed Tanous1abe55e2018-09-05 08:30:59 -07001077 private:
1078 const Node* head() const
1079 {
1080 return &nodes.front();
1081 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001082
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 Node* head()
1084 {
1085 return &nodes.front();
1086 }
1087
1088 unsigned newNode()
1089 {
1090 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001091 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001092 }
1093
1094 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001095};
1096
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097class Router
1098{
1099 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001100 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001101
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 DynamicRule& newRuleDynamic(const std::string& rule)
1103 {
1104 std::unique_ptr<DynamicRule> ruleObject =
1105 std::make_unique<DynamicRule>(rule);
1106 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001107 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001108
Ed Tanous1abe55e2018-09-05 08:30:59 -07001109 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001110 }
1111
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 template <uint64_t N>
1113 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1114 newRuleTagged(const std::string& rule)
1115 {
1116 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1117 TaggedRule>;
1118 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1119 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001120 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001121
1122 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001123 }
1124
Tanousf00032d2018-11-05 01:18:10 -03001125 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001126 {
Tanousf00032d2018-11-05 01:18:10 -03001127 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001128 {
Tanousf00032d2018-11-05 01:18:10 -03001129 return;
1130 }
Ed Tanous759cf102022-07-31 16:36:52 -07001131 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001132 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001133 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001134 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001135 {
1136 perMethods[method].rules.emplace_back(ruleObject);
1137 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001138 rule, static_cast<unsigned>(
1139 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001140 // directory case:
1141 // request to `/about' url matches `/about/' rule
1142 if (rule.size() > 2 && rule.back() == '/')
1143 {
1144 perMethods[method].trie.add(
1145 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001146 static_cast<unsigned>(perMethods[method].rules.size() -
1147 1));
Tanousf00032d2018-11-05 01:18:10 -03001148 }
1149 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001151 }
1152
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 void validate()
1154 {
Tanousf00032d2018-11-05 01:18:10 -03001155 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 {
1157 if (rule)
1158 {
Tanousf00032d2018-11-05 01:18:10 -03001159 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001160 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001161 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001162 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001163 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001165 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001166 }
1167 }
Tanousf00032d2018-11-05 01:18:10 -03001168 for (PerMethod& perMethod : perMethods)
1169 {
1170 perMethod.trie.validate();
1171 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001172 }
1173
Ed Tanous44e45182022-07-26 16:47:23 -07001174 struct FindRoute
1175 {
1176 BaseRule* rule = nullptr;
1177 RoutingParams params;
1178 };
1179
1180 struct FindRouteResponse
1181 {
1182 std::string allowHeader;
1183 FindRoute route;
1184 };
1185
Ed Tanous759cf102022-07-31 16:36:52 -07001186 FindRoute findRouteByIndex(std::string_view url, size_t index) const
1187 {
1188 FindRoute route;
1189 if (index >= perMethods.size())
1190 {
1191 BMCWEB_LOG_CRITICAL << "Bad index???";
1192 return route;
1193 }
1194 const PerMethod& perMethod = perMethods[index];
1195 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1196 if (found.first >= perMethod.rules.size())
1197 {
1198 throw std::runtime_error("Trie internal structure corrupted!");
1199 }
1200 // Found a 404 route, switch that in
1201 if (found.first != 0U)
1202 {
1203 route.rule = perMethod.rules[found.first];
1204 route.params = std::move(found.second);
1205 }
1206 return route;
1207 }
1208
1209 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -07001210 {
1211 FindRouteResponse findRoute;
1212
1213 size_t reqMethodIndex = static_cast<size_t>(req.method());
1214 // 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 }
1230 findRoute.allowHeader += boost::beast::http::to_string(
1231 static_cast<boost::beast::http::verb>(perMethodIndex));
1232 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 Tanous271584a2019-07-09 16:24:22 -07001243 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001244 {
1245 res.result(boost::beast::http::status::not_found);
1246 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001247 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001248 }
Tanousf00032d2018-11-05 01:18:10 -03001249
Ed Tanous271584a2019-07-09 16:24:22 -07001250 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
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 Tanous271584a2019-07-09 16:24:22 -07001312 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001313 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001314 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001315 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001316 }
Ed Tanous44e45182022-07-26 16:47:23 -07001317
1318 FindRouteResponse foundRoute = findRoute(req);
1319
Ed Tanous759cf102022-07-31 16:36:52 -07001320 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -07001321 {
Ed Tanous759cf102022-07-31 16:36:52 -07001322 // Couldn't find a normal route with any verb, try looking for a 404
1323 // route
1324 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001325 {
Ed Tanous759cf102022-07-31 16:36:52 -07001326 foundRoute.route = findRouteByIndex(req.url, notFoundIndex);
1327 }
1328 else
1329 {
1330 // See if we have a method not allowed (405) handler
1331 foundRoute.route =
1332 findRouteByIndex(req.url, methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -07001333 }
1334 }
Ed Tanous759cf102022-07-31 16:36:52 -07001335
1336 // Fill in the allow header if it's valid
1337 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001338 {
Ed Tanous759cf102022-07-31 16:36:52 -07001339
Ed Tanous88a03c52022-03-14 10:16:07 -07001340 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001341 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001342 }
Tanousf00032d2018-11-05 01:18:10 -03001343
Ed Tanous44e45182022-07-26 16:47:23 -07001344 // If we couldn't find a real route or a 404 route, return a generic
1345 // response
1346 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 {
Ed Tanous44e45182022-07-26 16:47:23 -07001348 if (foundRoute.allowHeader.empty())
1349 {
1350 asyncResp->res.result(boost::beast::http::status::not_found);
1351 }
1352 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001353 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001354 asyncResp->res.result(
1355 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001356 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001357 return;
1358 }
1359
Ed Tanous44e45182022-07-26 16:47:23 -07001360 BaseRule& rule = *foundRoute.route.rule;
1361 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001362
Ed Tanous44e45182022-07-26 16:47:23 -07001363 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001364 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001365 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001366
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001367 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001368 {
Ed Tanous44e45182022-07-26 16:47:23 -07001369 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001370 return;
1371 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001372
1373 crow::connections::systemBus->async_method_call(
Ed Tanous44e45182022-07-26 16:47:23 -07001374 [&req, asyncResp, &rule,
1375 params](const boost::system::error_code ec,
1376 const dbus::utility::DBusPropertiesMap& userInfoMap) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001377 if (ec)
1378 {
1379 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1380 asyncResp->res.result(
1381 boost::beast::http::status::internal_server_error);
1382 return;
1383 }
1384 std::string userRole{};
1385 const bool* remoteUser = nullptr;
1386 std::optional<bool> passwordExpired;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001387
Ed Tanous002d39b2022-05-31 08:59:27 -07001388 for (const auto& userInfo : userInfoMap)
1389 {
1390 if (userInfo.first == "UserPrivilege")
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001391 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001392 const std::string* userRolePtr =
1393 std::get_if<std::string>(&userInfo.second);
1394 if (userRolePtr == nullptr)
Ed Tanousb9d36b42022-02-26 21:42:46 -08001395 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001396 continue;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001397 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001398 userRole = *userRolePtr;
1399 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1400 << " userRole = " << *userRolePtr;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001401 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001402 else if (userInfo.first == "RemoteUser")
1403 {
1404 remoteUser = std::get_if<bool>(&userInfo.second);
1405 }
1406 else if (userInfo.first == "UserPasswordExpired")
1407 {
1408 const bool* passwordExpiredPtr =
1409 std::get_if<bool>(&userInfo.second);
1410 if (passwordExpiredPtr == nullptr)
1411 {
1412 continue;
1413 }
1414 passwordExpired = *passwordExpiredPtr;
1415 }
1416 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001417
Ed Tanous002d39b2022-05-31 08:59:27 -07001418 if (remoteUser == nullptr)
1419 {
1420 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1421 asyncResp->res.result(
1422 boost::beast::http::status::internal_server_error);
1423 return;
1424 }
1425
1426 if (passwordExpired == std::nullopt)
1427 {
1428 if (!*remoteUser)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001429 {
1430 BMCWEB_LOG_ERROR
Ed Tanous002d39b2022-05-31 08:59:27 -07001431 << "UserPasswordExpired property is expected for"
1432 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001433 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001434 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001435 return;
1436 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001437 passwordExpired = false;
1438 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001439
Nan Zhouac6250a2022-08-09 20:15:44 +00001440 // Get the user's privileges from the role
Ed Tanous002d39b2022-05-31 08:59:27 -07001441 redfish::Privileges userPrivileges =
1442 redfish::getUserPrivileges(userRole);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001443
Ed Tanous002d39b2022-05-31 08:59:27 -07001444 // Set isConfigureSelfOnly based on D-Bus results. This
1445 // ignores the results from both pamAuthenticateUser and the
1446 // value from any previous use of this session.
1447 req.session->isConfigureSelfOnly = *passwordExpired;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001448
Nan Zhouac6250a2022-08-09 20:15:44 +00001449 // Modify privileges if isConfigureSelfOnly.
Ed Tanous002d39b2022-05-31 08:59:27 -07001450 if (req.session->isConfigureSelfOnly)
1451 {
Nan Zhouac6250a2022-08-09 20:15:44 +00001452 // Remove all privileges except ConfigureSelf
Ed Tanous002d39b2022-05-31 08:59:27 -07001453 userPrivileges = userPrivileges.intersection(
1454 redfish::Privileges{"ConfigureSelf"});
1455 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1456 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001457
Ed Tanous44e45182022-07-26 16:47:23 -07001458 if (!rule.checkPrivileges(userPrivileges))
Ed Tanous002d39b2022-05-31 08:59:27 -07001459 {
1460 asyncResp->res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001461 if (req.session->isConfigureSelfOnly)
1462 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001463 redfish::messages::passwordChangeRequired(
1464 asyncResp->res, crow::utility::urlFromPieces(
1465 "redfish", "v1", "AccountService",
1466 "Accounts", req.session->username));
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001467 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001468 return;
1469 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001470
Ed Tanous002d39b2022-05-31 08:59:27 -07001471 req.userRole = userRole;
Ed Tanous44e45182022-07-26 16:47:23 -07001472 rule.handle(req, asyncResp, params);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001473 },
1474 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1475 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1476 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001477 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001478
Ed Tanous1abe55e2018-09-05 08:30:59 -07001479 void debugPrint()
1480 {
Ed Tanous271584a2019-07-09 16:24:22 -07001481 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001482 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001483 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1484 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001485 perMethods[i].trie.debugPrint();
1486 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001487 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001488
Ed Tanous1abe55e2018-09-05 08:30:59 -07001489 std::vector<const std::string*> getRoutes(const std::string& parent)
1490 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001491 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001492
1493 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001494 {
Tanousf00032d2018-11-05 01:18:10 -03001495 std::vector<unsigned> x;
1496 pm.trie.findRouteIndexes(parent, x);
1497 for (unsigned index : x)
1498 {
1499 ret.push_back(&pm.rules[index]->rule);
1500 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001501 }
1502 return ret;
1503 }
1504
1505 private:
Tanousf00032d2018-11-05 01:18:10 -03001506 struct PerMethod
1507 {
1508 std::vector<BaseRule*> rules;
1509 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001510 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001511 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001512 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001513 {}
Tanousf00032d2018-11-05 01:18:10 -03001514 };
Ed Tanous888880a2020-08-24 13:48:50 -07001515
Ed Tanous759cf102022-07-31 16:36:52 -07001516 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001517 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001518};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519} // namespace crow