blob: 6c393ab33bab28bdd75c401aaacf6d9fe0f25c6c [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "common.hpp"
Ed Tanous168e20c2021-12-13 14:39:53 -08004#include "dbus_utility.hpp"
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06005#include "error_messages.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include "http_request.hpp"
7#include "http_response.hpp"
8#include "logging.hpp"
Tanousf00032d2018-11-05 01:18:10 -03009#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +053010#include "sessions.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070011#include "utility.hpp"
Ed Tanous2c9efc32022-07-31 22:08:26 -070012#include "verb.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -070013#include "websocket.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070014
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +020015#include <async_resp.hpp>
Ed Tanous88a03c52022-03-14 10:16:07 -070016#include <boost/beast/ssl/ssl_stream.hpp>
Tanousf00032d2018-11-05 01:18:10 -030017#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050018
Ed Tanouse0d918b2018-03-27 17:41:04 -070019#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070020#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070021#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070022#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <memory>
Ed Tanous2c9efc32022-07-31 22:08:26 -070024#include <optional>
Ed Tanous7045c8d2017-04-03 10:04:37 -070025#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070026#include <utility>
27#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Tanousf00032d2018-11-05 01:18:10 -030031
Ed Tanous2c9efc32022-07-31 22:08:26 -070032static constexpr size_t maxVerbIndex = static_cast<size_t>(HttpVerb::Max) - 1U;
Ed Tanous44e45182022-07-26 16:47:23 -070033
34// MaxVerb + 1 is designated as the "not found" verb. It is done this way
35// to keep the BaseRule as a single bitfield (thus keeping the struct small)
36// while still having a way to declare a route a "not found" route.
37static constexpr const size_t notFoundIndex = maxVerbIndex + 1;
Ed Tanous759cf102022-07-31 16:36:52 -070038static constexpr const size_t methodNotAllowedIndex = notFoundIndex + 1;
Ed Tanous44e45182022-07-26 16:47:23 -070039
Ed Tanous1abe55e2018-09-05 08:30:59 -070040class BaseRule
41{
42 public:
Ed Tanous4e23a442022-06-06 09:57:26 -070043 explicit BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070045
Ed Tanous0c0084a2019-10-24 15:57:51 -070046 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070047
Ed Tanousecd6a3a2022-01-07 09:18:40 -080048 BaseRule(const BaseRule&) = delete;
49 BaseRule(BaseRule&&) = delete;
50 BaseRule& operator=(const BaseRule&) = delete;
51 BaseRule& operator=(const BaseRule&&) = delete;
52
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 virtual void validate() = 0;
54 std::unique_ptr<BaseRule> upgrade()
55 {
56 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070057 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070059 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 return {};
61 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070062
Ed Tanous104f09c2022-01-25 09:56:04 -080063 virtual void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +080064 const std::shared_ptr<bmcweb::AsyncResp>&,
65 const RoutingParams&) = 0;
Ed Tanous104f09c2022-01-25 09:56:04 -080066 virtual void handleUpgrade(const Request& /*req*/, Response& res,
67 boost::asio::ip::tcp::socket&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070069 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 res.end();
71 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070072#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -080073 virtual void handleUpgrade(
74 const Request& /*req*/, Response& res,
75 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&& /*adaptor*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070077 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 res.end();
79 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070080#endif
81
Ed Tanous9eb808c2022-01-25 10:19:23 -080082 size_t getMethods() const
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 {
84 return methodsBitfield;
85 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070086
Tanousf00032d2018-11-05 01:18:10 -030087 bool checkPrivileges(const redfish::Privileges& userPrivileges)
88 {
89 // If there are no privileges assigned, assume no privileges
90 // required
91 if (privilegesSet.empty())
92 {
93 return true;
94 }
95
96 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
97 {
98 if (userPrivileges.isSupersetOf(requiredPrivileges))
99 {
100 return true;
101 }
102 }
103 return false;
104 }
105
Ed Tanous2c9efc32022-07-31 22:08:26 -0700106 size_t methodsBitfield{1 << static_cast<size_t>(HttpVerb::Get)};
Ed Tanous44e45182022-07-26 16:47:23 -0700107 static_assert(std::numeric_limits<decltype(methodsBitfield)>::digits >
Ed Tanous759cf102022-07-31 16:36:52 -0700108 methodNotAllowedIndex,
Ed Tanous44e45182022-07-26 16:47:23 -0700109 "Not enough bits to store bitfield");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700110
Tanousf00032d2018-11-05 01:18:10 -0300111 std::vector<redfish::Privileges> privilegesSet;
112
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 std::string rule;
114 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700115
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700117
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500119 template <typename T>
120 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700121};
122
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123namespace detail
124{
125namespace routing_handler_call_helper
126{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500127template <typename T, int Pos>
128struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129{
130 using type = T;
131 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700132};
133
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134template <typename H1>
135struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136{
137 H1& handler;
138 const RoutingParams& params;
139 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700141};
142
143template <typename F, int NInt, int NUint, int NDouble, int NString,
144 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700147
148template <typename F, int NInt, int NUint, int NDouble, int NString,
149 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700150struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 black_magic::S<Args2...>>
152{
153 void operator()(F cparams)
154 {
155 using pushed = typename black_magic::S<Args2...>::template push_back<
156 CallPair<int64_t, NInt>>;
157 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
158 pushed>()(cparams);
159 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700160};
161
162template <typename F, int NInt, int NUint, int NDouble, int NString,
163 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700164struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
166{
167 void operator()(F cparams)
168 {
169 using pushed = typename black_magic::S<Args2...>::template push_back<
170 CallPair<uint64_t, NUint>>;
171 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
172 pushed>()(cparams);
173 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700174};
175
176template <typename F, int NInt, int NUint, int NDouble, int NString,
177 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700178struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 black_magic::S<Args2...>>
180{
181 void operator()(F cparams)
182 {
183 using pushed = typename black_magic::S<Args2...>::template push_back<
184 CallPair<double, NDouble>>;
185 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
186 pushed>()(cparams);
187 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700188};
189
190template <typename F, int NInt, int NUint, int NDouble, int NString,
191 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700192struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
194{
195 void operator()(F cparams)
196 {
197 using pushed = typename black_magic::S<Args2...>::template push_back<
198 CallPair<std::string, NString>>;
199 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
200 pushed>()(cparams);
201 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700202};
203
204template <typename F, int NInt, int NUint, int NDouble, int NString,
205 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700206struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 black_magic::S<Args1...>>
208{
209 void operator()(F cparams)
210 {
211 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800212 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 cparams.params.template get<typename Args1::type>(Args1::pos)...);
214 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700215};
216
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500217template <typename Func, typename... ArgsWrapped>
218struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219{
220 template <typename... Args>
221 void set(
222 Func f,
223 typename std::enable_if<
224 !std::is_same<
225 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
226 const Request&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800227 int>::type /*enable*/
228 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800230 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800231 const Request&,
232 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
233 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700234 }
235
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500236 template <typename Req, typename... Args>
237 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 {
Ed Tanous4e23a442022-06-06 09:57:26 -0700239 explicit ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500240 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700241
zhanghch058d1b46d2021-04-01 11:18:24 +0800242 void operator()(const Request& req,
243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
244 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800246 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700248
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 Func f;
250 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700251
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 template <typename... Args>
253 void set(
254 Func f,
255 typename std::enable_if<
256 std::is_same<
257 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
258 const Request&>::value &&
259 !std::is_same<typename std::tuple_element<
260 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800261 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800262 int>::type /*enable*/
263 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264 {
265 handler = ReqHandlerWrapper<Args...>(std::move(f));
266 /*handler = (
267 [f = std::move(f)]
268 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700269 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 res.end();
271 });*/
272 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 template <typename... Args>
275 void set(
276 Func f,
277 typename std::enable_if<
278 std::is_same<
279 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
280 const Request&>::value &&
281 std::is_same<typename std::tuple_element<
282 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800283 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous104f09c2022-01-25 09:56:04 -0800284 int>::type /*enable*/
285 = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700286 {
287 handler = std::move(f);
288 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700289
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500290 template <typename... Args>
291 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800293 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800294 const crow::Request& /*req*/,
295 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700297 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700299
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 template <typename... Args>
301 struct HandlerTypeHelper<const Request&, Args...>
302 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800303 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800304 const crow::Request& /*req*/,
305 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700307 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700309
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800311 struct HandlerTypeHelper<const Request&,
312 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800314 using type = std::function<void(
Ed Tanous104f09c2022-01-25 09:56:04 -0800315 const crow::Request& /*req*/,
316 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700318 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 };
320
321 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
322
zhanghch058d1b46d2021-04-01 11:18:24 +0800323 void operator()(const Request& req,
324 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 const RoutingParams& params)
326 {
327 detail::routing_handler_call_helper::Call<
328 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
329 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
330 black_magic::S<>>()(
331 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800332 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700334};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700335} // namespace routing_handler_call_helper
336} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700337
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338class WebSocketRule : public BaseRule
339{
340 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700341
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700343 explicit WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500344 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700345
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500347 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700348
Ed Tanous104f09c2022-01-25 09:56:04 -0800349 void handle(const Request& /*req*/,
zhanghch058d1b46d2021-04-01 11:18:24 +0800350 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous104f09c2022-01-25 09:56:04 -0800351 const RoutingParams& /*params*/) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800353 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700355
Ed Tanous104f09c2022-01-25 09:56:04 -0800356 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800357 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 {
Nan Zhou93c02022022-02-24 18:21:07 -0800359 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ratan Gupta02453b12019-10-22 14:43:36 +0530360 std::shared_ptr<
361 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
362 myConnection = std::make_shared<
363 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000364 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530365 closeHandler, errorHandler);
366 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700367 }
368#ifdef BMCWEB_ENABLE_SSL
Ed Tanous104f09c2022-01-25 09:56:04 -0800369 void handleUpgrade(const Request& req, Response& /*res*/,
Ed Tanousceac6f72018-12-02 11:58:47 -0800370 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
371 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 {
Nan Zhou93c02022022-02-24 18:21:07 -0800373 BMCWEB_LOG_DEBUG << "Websocket handles upgrade";
Ed Tanousceac6f72018-12-02 11:58:47 -0800374 std::shared_ptr<crow::websocket::ConnectionImpl<
375 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
376 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
377 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000378 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800379 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700380 myConnection->start();
381 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700382#endif
383
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500384 template <typename Func>
385 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700386 {
387 openHandler = f;
388 return *this;
389 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700390
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500391 template <typename Func>
392 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 {
394 messageHandler = f;
395 return *this;
396 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700397
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500398 template <typename Func>
399 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400 {
401 closeHandler = f;
402 return *this;
403 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700404
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500405 template <typename Func>
406 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 {
408 errorHandler = f;
409 return *this;
410 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700411
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 protected:
zhanghch0577726382021-10-21 14:07:57 +0800413 std::function<void(crow::websocket::Connection&)> openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
415 messageHandler;
416 std::function<void(crow::websocket::Connection&, const std::string&)>
417 closeHandler;
418 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700419};
420
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500421template <typename T>
422struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423{
424 using self_t = T;
425 WebSocketRule& websocket()
426 {
Ed Tanous271584a2019-07-09 16:24:22 -0700427 self_t* self = static_cast<self_t*>(this);
428 WebSocketRule* p = new WebSocketRule(self->rule);
429 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 return *p;
431 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700432
Ed Tanousf23b7292020-10-15 09:41:17 -0700433 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 {
Ed Tanous271584a2019-07-09 16:24:22 -0700435 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700436 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700437 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700439
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 self_t& methods(boost::beast::http::verb method)
441 {
Ed Tanous271584a2019-07-09 16:24:22 -0700442 self_t* self = static_cast<self_t*>(this);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700443 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
444 if (verb)
445 {
446 self->methodsBitfield = 1U << static_cast<size_t>(*verb);
447 }
Ed Tanous271584a2019-07-09 16:24:22 -0700448 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700450
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000452 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700453 {
Ed Tanous271584a2019-07-09 16:24:22 -0700454 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000455 methods(argsMethod...);
Ed Tanous2c9efc32022-07-31 22:08:26 -0700456 std::optional<HttpVerb> verb = httpVerbFromBoost(method);
457 if (verb)
458 {
459 self->methodsBitfield |= 1U << static_cast<size_t>(*verb);
460 }
Ed Tanous271584a2019-07-09 16:24:22 -0700461 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 }
Tanousf00032d2018-11-05 01:18:10 -0300463
Ed Tanous44e45182022-07-26 16:47:23 -0700464 self_t& notFound()
465 {
466 self_t* self = static_cast<self_t*>(this);
467 self->methodsBitfield = 1U << notFoundIndex;
468 return *self;
469 }
470
Ed Tanous759cf102022-07-31 16:36:52 -0700471 self_t& methodNotAllowed()
472 {
473 self_t* self = static_cast<self_t*>(this);
474 self->methodsBitfield = 1U << methodNotAllowedIndex;
475 return *self;
476 }
477
Ed Tanous432a8902021-06-14 15:28:56 -0700478 self_t& privileges(
479 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300480 {
Ed Tanous271584a2019-07-09 16:24:22 -0700481 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700482 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300483 {
Ed Tanous271584a2019-07-09 16:24:22 -0700484 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300485 }
Ed Tanous271584a2019-07-09 16:24:22 -0700486 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300487 }
Ed Tanoused398212021-06-09 17:05:54 -0700488
489 template <size_t N, typename... MethodArgs>
490 self_t& privileges(const std::array<redfish::Privileges, N>& p)
491 {
492 self_t* self = static_cast<self_t*>(this);
493 for (const redfish::Privileges& privilege : p)
494 {
495 self->privilegesSet.emplace_back(privilege);
496 }
497 return *self;
498 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700499};
500
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
502{
503 public:
Ed Tanous4e23a442022-06-06 09:57:26 -0700504 explicit DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500505 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700506
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 void validate() override
508 {
509 if (!erasedHandler)
510 {
511 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
512 "no handler for url " + rule);
513 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700514 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700515
zhanghch058d1b46d2021-04-01 11:18:24 +0800516 void handle(const Request& req,
517 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 const RoutingParams& params) override
519 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800520 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700522
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500523 template <typename Func>
524 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525 {
Ed Tanousc867a832022-03-10 14:17:00 -0800526 using boost::callable_traits::args_t;
527 constexpr size_t arity = std::tuple_size<args_t<Func>>::value;
528 constexpr auto is = std::make_integer_sequence<unsigned, arity>{};
529 erasedHandler = wrap(std::move(f), is);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 }
531
532 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500533 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700534 // enable_if Arg1 != request
535
536 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800537 std::function<void(const Request&,
538 const std::shared_ptr<bmcweb::AsyncResp>&,
539 const RoutingParams&)>
Ed Tanous104f09c2022-01-25 09:56:04 -0800540 wrap(Func f, std::integer_sequence<unsigned, Indices...> /*is*/)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 {
Ed Tanousc867a832022-03-10 14:17:00 -0800542 using function_t = crow::utility::FunctionTraits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543
544 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700545 black_magic::getParameterTag(rule.c_str()),
546 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 typename function_t::template arg<Indices>...>::value))
548 {
549 throw std::runtime_error("routeDynamic: Handler type is mismatched "
550 "with URL parameters: " +
551 rule);
552 }
553 auto ret = detail::routing_handler_call_helper::Wrapped<
554 Func, typename function_t::template arg<Indices>...>();
555 ret.template set<typename function_t::template arg<Indices>...>(
556 std::move(f));
557 return ret;
558 }
559
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500560 template <typename Func>
561 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
563 nameStr = std::move(name);
564 (*this).template operator()<Func>(std::forward(f));
565 }
566
567 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800568 std::function<void(const Request&,
569 const std::shared_ptr<bmcweb::AsyncResp>&,
570 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700572};
573
574template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575class TaggedRule :
576 public BaseRule,
577 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578{
579 public:
580 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700581
Ed Tanous4e23a442022-06-06 09:57:26 -0700582 explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500583 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700584
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 void validate() override
586 {
587 if (!handler)
588 {
589 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
590 "no handler for url " + rule);
591 }
592 }
593
594 template <typename Func>
595 typename std::enable_if<
596 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
597 void>::type
598 operator()(Func&& f)
599 {
600 static_assert(
601 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
602 black_magic::CallHelper<
603 Func, black_magic::S<crow::Request, Args...>>::value,
604 "Handler type is mismatched with URL parameters");
605 static_assert(
606 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
607 "Handler function cannot have void return type; valid return "
608 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500609 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800611 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800612 const Request&,
613 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
614 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 }
616
617 template <typename Func>
618 typename std::enable_if<
619 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700620 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700621 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 void>::type
623 operator()(Func&& f)
624 {
625 static_assert(
626 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
627 black_magic::CallHelper<
628 Func, black_magic::S<crow::Request, Args...>>::value,
629 "Handler type is mismatched with URL parameters");
630 static_assert(
631 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
632 std::declval<Args>()...))>::value,
633 "Handler function cannot have void return type; valid return "
634 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500635 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700636
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800637 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800638 const crow::Request& req,
639 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
640 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700642
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 template <typename Func>
644 typename std::enable_if<
645 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
646 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700647 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 void>::type
649 operator()(Func&& f)
650 {
651 static_assert(
652 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
653 black_magic::CallHelper<
654 Func, black_magic::S<crow::Request, Args...>>::value ||
655 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800656 Func, black_magic::S<crow::Request,
657 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 Args...>>::value,
659 "Handler type is mismatched with URL parameters");
660 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800661 std::is_same<
662 void,
663 decltype(f(std::declval<crow::Request>(),
664 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
665 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300666 "Handler function with response argument should have void "
667 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700669
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800670 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700672
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500673 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700674 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700676 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 (*this).template operator()<Func>(std::forward(f));
678 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700679
zhanghch058d1b46d2021-04-01 11:18:24 +0800680 void handle(const Request& req,
681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 const RoutingParams& params) override
683 {
684 detail::routing_handler_call_helper::Call<
685 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
686 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
687 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800688 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700690
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800692 std::function<void(const crow::Request&,
693 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
694 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700695};
696
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697class Trie
698{
699 public:
700 struct Node
701 {
702 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700703 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
704 paramChildrens{};
Ed Tanousa94ac612022-02-22 11:13:24 -0800705 using ChildMap = boost::container::flat_map<
706 std::string, unsigned, std::less<>,
707 std::vector<std::pair<std::string, unsigned>>>;
708 ChildMap children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700709
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 bool isSimpleNode() const
711 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800712 return ruleIndex == 0 &&
713 std::all_of(std::begin(paramChildrens),
714 std::end(paramChildrens),
715 [](size_t x) { return x == 0U; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700716 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700717 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700718
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500720 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721
722 private:
723 void optimizeNode(Node* node)
724 {
Ed Tanous271584a2019-07-09 16:24:22 -0700725 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 {
Ed Tanousdbb59d42022-01-25 11:09:55 -0800727 if (x == 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700728 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700730 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 Node* child = &nodes[x];
732 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700733 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700735 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700737 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 bool mergeWithChild = true;
Ed Tanousa94ac612022-02-22 11:13:24 -0800739 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 {
741 Node* child = &nodes[kv.second];
742 if (!child->isSimpleNode())
743 {
744 mergeWithChild = false;
745 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700746 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 }
748 if (mergeWithChild)
749 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800750 Node::ChildMap merged;
751 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 {
753 Node* child = &nodes[kv.second];
Ed Tanousa94ac612022-02-22 11:13:24 -0800754 for (const Node::ChildMap::value_type& childKv :
Tanousf00032d2018-11-05 01:18:10 -0300755 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 {
757 merged[kv.first + childKv.first] = childKv.second;
758 }
759 }
760 node->children = std::move(merged);
761 optimizeNode(node);
762 }
763 else
764 {
Ed Tanousa94ac612022-02-22 11:13:24 -0800765 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 {
767 Node* child = &nodes[kv.second];
768 optimizeNode(child);
769 }
770 }
771 }
772
773 void optimize()
774 {
775 optimizeNode(head());
776 }
777
778 public:
779 void validate()
780 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 optimize();
782 }
783
Ed Tanous81ce6092020-12-17 16:54:55 +0000784 void findRouteIndexes(const std::string& reqUrl,
785 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300786 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 {
788 if (node == nullptr)
789 {
790 node = head();
791 }
Ed Tanousa94ac612022-02-22 11:13:24 -0800792 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 {
794 const std::string& fragment = kv.first;
795 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000796 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 {
798 if (child->ruleIndex != 0 && fragment != "/")
799 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000800 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000802 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700803 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 }
805 else
806 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000807 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 {
Ed Tanous271584a2019-07-09 16:24:22 -0700809 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000810 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700811 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 }
813 }
814 }
815 }
816
817 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000818 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700819 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 {
821 RoutingParams empty;
822 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700823 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700825 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826
827 unsigned found{};
828 RoutingParams matchParams;
829
830 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700831 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700833 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000834 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700835 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700837 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838
839 auto updateFound =
840 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700841 if (ret.first != 0U && (found == 0U || found > ret.first))
842 {
843 found = ret.first;
844 matchParams = std::move(ret.second);
845 }
846 };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847
Ed Tanouse662eae2022-01-25 10:39:19 -0800848 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000850 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
852 {
Ed Tanous543f4402022-01-06 13:12:53 -0800853 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 errno = 0;
855 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000856 std::strtoll(reqUrl.data() + pos, &eptr, 10);
857 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 {
859 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000860 std::pair<unsigned, RoutingParams> ret =
861 find(reqUrl,
862 &nodes[node->paramChildrens[static_cast<size_t>(
863 ParamType::INT)]],
864 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865 updateFound(ret);
866 params->intParams.pop_back();
867 }
868 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700869 }
870
Ed Tanouse662eae2022-01-25 10:39:19 -0800871 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000873 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 if ((c >= '0' && c <= '9') || c == '+')
875 {
Ed Tanous543f4402022-01-06 13:12:53 -0800876 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877 errno = 0;
878 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000879 std::strtoull(reqUrl.data() + pos, &eptr, 10);
880 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 {
882 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000883 std::pair<unsigned, RoutingParams> ret =
884 find(reqUrl,
885 &nodes[node->paramChildrens[static_cast<size_t>(
886 ParamType::UINT)]],
887 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888 updateFound(ret);
889 params->uintParams.pop_back();
890 }
891 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700892 }
893
Ed Tanouse662eae2022-01-25 10:39:19 -0800894 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000896 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
898 {
Ed Tanous543f4402022-01-06 13:12:53 -0800899 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000901 double value = std::strtod(reqUrl.data() + pos, &eptr);
902 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 {
904 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000905 std::pair<unsigned, RoutingParams> ret =
906 find(reqUrl,
907 &nodes[node->paramChildrens[static_cast<size_t>(
908 ParamType::DOUBLE)]],
909 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910 updateFound(ret);
911 params->doubleParams.pop_back();
912 }
913 }
914 }
915
Ed Tanouse662eae2022-01-25 10:39:19 -0800916 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000918 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000919 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000921 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700922 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700923 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700924 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 }
926
927 if (epos != pos)
928 {
929 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000930 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300931 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000932 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700933 &nodes[node->paramChildrens[static_cast<size_t>(
934 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000935 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700936 updateFound(ret);
937 params->stringParams.pop_back();
938 }
939 }
940
Ed Tanouse662eae2022-01-25 10:39:19 -0800941 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700942 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000943 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944
945 if (epos != pos)
946 {
947 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000948 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700949 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000950 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700951 &nodes[node->paramChildrens[static_cast<size_t>(
952 ParamType::PATH)]],
953 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954 updateFound(ret);
955 params->stringParams.pop_back();
956 }
957 }
958
Ed Tanousa94ac612022-02-22 11:13:24 -0800959 for (const Node::ChildMap::value_type& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700960 {
961 const std::string& fragment = kv.first;
962 const Node* child = &nodes[kv.second];
963
Ed Tanous81ce6092020-12-17 16:54:55 +0000964 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 {
Tanousf00032d2018-11-05 01:18:10 -0300966 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000967 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968 updateFound(ret);
969 }
970 }
971
972 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700973 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700974
975 void add(const std::string& url, unsigned ruleIndex)
976 {
Ed Tanous271584a2019-07-09 16:24:22 -0700977 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700978
979 for (unsigned i = 0; i < url.size(); i++)
980 {
981 char c = url[i];
982 if (c == '<')
983 {
Tanousf00032d2018-11-05 01:18:10 -0300984 const static std::array<std::pair<ParamType, std::string>, 7>
985 paramTraits = {{
986 {ParamType::INT, "<int>"},
987 {ParamType::UINT, "<uint>"},
988 {ParamType::DOUBLE, "<float>"},
989 {ParamType::DOUBLE, "<double>"},
990 {ParamType::STRING, "<str>"},
991 {ParamType::STRING, "<string>"},
992 {ParamType::PATH, "<path>"},
993 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994
Tanousf00032d2018-11-05 01:18:10 -0300995 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 {
Tanousf00032d2018-11-05 01:18:10 -0300997 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 {
Ed Tanous271584a2019-07-09 16:24:22 -0700999 size_t index = static_cast<size_t>(x.first);
Ed Tanouse662eae2022-01-25 10:39:19 -08001000 if (nodes[idx].paramChildrens[index] == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 {
Tanousf00032d2018-11-05 01:18:10 -03001002 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -07001003 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001004 }
Ed Tanous271584a2019-07-09 16:24:22 -07001005 idx = nodes[idx].paramChildrens[index];
1006 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007 break;
1008 }
1009 }
1010
1011 i--;
1012 }
1013 else
1014 {
1015 std::string piece(&c, 1);
Ed Tanouse662eae2022-01-25 10:39:19 -08001016 if (nodes[idx].children.count(piece) == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 {
Tanousf00032d2018-11-05 01:18:10 -03001018 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001019 nodes[idx].children.emplace(piece, newNodeIdx);
1020 }
1021 idx = nodes[idx].children[piece];
1022 }
1023 }
Ed Tanouse662eae2022-01-25 10:39:19 -08001024 if (nodes[idx].ruleIndex != 0U)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001025 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001027 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001029 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001030
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 private:
Ed Tanous271584a2019-07-09 16:24:22 -07001032 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 {
Ed Tanous271584a2019-07-09 16:24:22 -07001034 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001035 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001036 if (n->paramChildrens[i] != 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001037 {
1038 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -07001039 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1040 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041 {
1042 case ParamType::INT:
1043 BMCWEB_LOG_DEBUG << "<int>";
1044 break;
1045 case ParamType::UINT:
1046 BMCWEB_LOG_DEBUG << "<uint>";
1047 break;
1048 case ParamType::DOUBLE:
1049 BMCWEB_LOG_DEBUG << "<float>";
1050 break;
1051 case ParamType::STRING:
1052 BMCWEB_LOG_DEBUG << "<str>";
1053 break;
1054 case ParamType::PATH:
1055 BMCWEB_LOG_DEBUG << "<path>";
1056 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001057 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058 BMCWEB_LOG_DEBUG << "<ERROR>";
1059 break;
1060 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001061
Ed Tanous1abe55e2018-09-05 08:30:59 -07001062 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1063 }
1064 }
Ed Tanousa94ac612022-02-22 11:13:24 -08001065 for (const Node::ChildMap::value_type& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 {
1067 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001068 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001069 << kv.first;
1070 debugNodePrint(&nodes[kv.second], level + 1);
1071 }
1072 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001073
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 public:
1075 void debugPrint()
1076 {
Ed Tanous271584a2019-07-09 16:24:22 -07001077 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001079
Ed Tanous1abe55e2018-09-05 08:30:59 -07001080 private:
1081 const Node* head() const
1082 {
1083 return &nodes.front();
1084 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001085
Ed Tanous1abe55e2018-09-05 08:30:59 -07001086 Node* head()
1087 {
1088 return &nodes.front();
1089 }
1090
1091 unsigned newNode()
1092 {
1093 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001094 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 }
1096
1097 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001098};
1099
Ed Tanous1abe55e2018-09-05 08:30:59 -07001100class Router
1101{
1102 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001103 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001104
Ed Tanous1abe55e2018-09-05 08:30:59 -07001105 DynamicRule& newRuleDynamic(const std::string& rule)
1106 {
1107 std::unique_ptr<DynamicRule> ruleObject =
1108 std::make_unique<DynamicRule>(rule);
1109 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001110 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001111
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001113 }
1114
Ed Tanous1abe55e2018-09-05 08:30:59 -07001115 template <uint64_t N>
1116 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1117 newRuleTagged(const std::string& rule)
1118 {
1119 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1120 TaggedRule>;
1121 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1122 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001123 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001124
1125 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001126 }
1127
Tanousf00032d2018-11-05 01:18:10 -03001128 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 {
Tanousf00032d2018-11-05 01:18:10 -03001130 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001131 {
Tanousf00032d2018-11-05 01:18:10 -03001132 return;
1133 }
Ed Tanous759cf102022-07-31 16:36:52 -07001134 for (size_t method = 0, methodBit = 1; method <= methodNotAllowedIndex;
Ed Tanous2c70f802020-09-28 14:29:23 -07001135 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001136 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001137 if ((ruleObject->methodsBitfield & methodBit) > 0U)
Tanousf00032d2018-11-05 01:18:10 -03001138 {
1139 perMethods[method].rules.emplace_back(ruleObject);
1140 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001141 rule, static_cast<unsigned>(
1142 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001143 // directory case:
1144 // request to `/about' url matches `/about/' rule
1145 if (rule.size() > 2 && rule.back() == '/')
1146 {
1147 perMethods[method].trie.add(
1148 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001149 static_cast<unsigned>(perMethods[method].rules.size() -
1150 1));
Tanousf00032d2018-11-05 01:18:10 -03001151 }
1152 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001154 }
1155
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 void validate()
1157 {
Tanousf00032d2018-11-05 01:18:10 -03001158 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001159 {
1160 if (rule)
1161 {
Tanousf00032d2018-11-05 01:18:10 -03001162 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001164 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001166 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001167 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001168 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169 }
1170 }
Tanousf00032d2018-11-05 01:18:10 -03001171 for (PerMethod& perMethod : perMethods)
1172 {
1173 perMethod.trie.validate();
1174 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001175 }
1176
Ed Tanous44e45182022-07-26 16:47:23 -07001177 struct FindRoute
1178 {
1179 BaseRule* rule = nullptr;
1180 RoutingParams params;
1181 };
1182
1183 struct FindRouteResponse
1184 {
1185 std::string allowHeader;
1186 FindRoute route;
1187 };
1188
Ed Tanous759cf102022-07-31 16:36:52 -07001189 FindRoute findRouteByIndex(std::string_view url, size_t index) const
1190 {
1191 FindRoute route;
1192 if (index >= perMethods.size())
1193 {
1194 BMCWEB_LOG_CRITICAL << "Bad index???";
1195 return route;
1196 }
1197 const PerMethod& perMethod = perMethods[index];
1198 std::pair<unsigned, RoutingParams> found = perMethod.trie.find(url);
1199 if (found.first >= perMethod.rules.size())
1200 {
1201 throw std::runtime_error("Trie internal structure corrupted!");
1202 }
1203 // Found a 404 route, switch that in
1204 if (found.first != 0U)
1205 {
1206 route.rule = perMethod.rules[found.first];
1207 route.params = std::move(found.second);
1208 }
1209 return route;
1210 }
1211
1212 FindRouteResponse findRoute(Request& req) const
Ed Tanous44e45182022-07-26 16:47:23 -07001213 {
1214 FindRouteResponse findRoute;
1215
Ed Tanous2c9efc32022-07-31 22:08:26 -07001216 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1217 if (!verb)
1218 {
1219 return findRoute;
1220 }
1221 size_t reqMethodIndex = static_cast<size_t>(*verb);
Ed Tanous44e45182022-07-26 16:47:23 -07001222 // Check to see if this url exists at any verb
1223 for (size_t perMethodIndex = 0; perMethodIndex <= maxVerbIndex;
1224 perMethodIndex++)
1225 {
1226 // Make sure it's safe to deference the array at that index
1227 static_assert(maxVerbIndex <
1228 std::tuple_size_v<decltype(perMethods)>);
Ed Tanous759cf102022-07-31 16:36:52 -07001229 FindRoute route = findRouteByIndex(req.url, perMethodIndex);
1230 if (route.rule == nullptr)
Ed Tanous44e45182022-07-26 16:47:23 -07001231 {
1232 continue;
1233 }
1234 if (!findRoute.allowHeader.empty())
1235 {
1236 findRoute.allowHeader += ", ";
1237 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001238 HttpVerb thisVerb = static_cast<HttpVerb>(perMethodIndex);
1239 findRoute.allowHeader += httpVerbToString(thisVerb);
Ed Tanous44e45182022-07-26 16:47:23 -07001240 if (perMethodIndex == reqMethodIndex)
1241 {
Ed Tanous759cf102022-07-31 16:36:52 -07001242 findRoute.route = route;
Ed Tanous44e45182022-07-26 16:47:23 -07001243 }
1244 }
1245 return findRoute;
1246 }
1247
Ed Tanous1abe55e2018-09-05 08:30:59 -07001248 template <typename Adaptor>
1249 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1250 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001251 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1252 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001253 {
1254 res.result(boost::beast::http::status::not_found);
1255 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001256 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001257 }
Ed Tanous2c9efc32022-07-31 22:08:26 -07001258 PerMethod& perMethod = perMethods[static_cast<size_t>(*verb)];
Tanousf00032d2018-11-05 01:18:10 -03001259 Trie& trie = perMethod.trie;
1260 std::vector<BaseRule*>& rules = perMethod.rules;
1261
1262 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001263 unsigned ruleIndex = found.first;
Ed Tanouse662eae2022-01-25 10:39:19 -08001264 if (ruleIndex == 0U)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001265 {
1266 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001267 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268 res.end();
1269 return;
1270 }
1271
1272 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001273 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001275 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276
Ed Tanous271584a2019-07-09 16:24:22 -07001277 if ((rules[ruleIndex]->getMethods() &
1278 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 {
1280 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1281 << " with " << req.methodString() << "("
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();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001284 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001285 res.end();
1286 return;
1287 }
1288
1289 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001290 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 << rules[ruleIndex]->getMethods();
1292
1293 // any uncaught exceptions become 500s
1294 try
1295 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -08001296 rules[ruleIndex]->handleUpgrade(req, res,
1297 std::forward<Adaptor>(adaptor));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 }
Patrick Williamsc5967042021-10-06 12:39:54 -05001299 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001300 {
1301 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001302 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001303 res.end();
1304 return;
1305 }
1306 catch (...)
1307 {
1308 BMCWEB_LOG_ERROR
1309 << "An uncaught exception occurred. The type was unknown "
1310 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001311 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001312 res.end();
1313 return;
1314 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001315 }
1316
zhanghch058d1b46d2021-04-01 11:18:24 +08001317 void handle(Request& req,
1318 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001319 {
Ed Tanous2c9efc32022-07-31 22:08:26 -07001320 std::optional<HttpVerb> verb = httpVerbFromBoost(req.method());
1321 if (!verb || static_cast<size_t>(*verb) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001322 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001323 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001324 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001325 }
Ed Tanous44e45182022-07-26 16:47:23 -07001326
1327 FindRouteResponse foundRoute = findRoute(req);
1328
Ed Tanous759cf102022-07-31 16:36:52 -07001329 if (foundRoute.route.rule == nullptr)
Ed Tanous88a03c52022-03-14 10:16:07 -07001330 {
Ed Tanous759cf102022-07-31 16:36:52 -07001331 // Couldn't find a normal route with any verb, try looking for a 404
1332 // route
1333 if (foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001334 {
Ed Tanous759cf102022-07-31 16:36:52 -07001335 foundRoute.route = findRouteByIndex(req.url, notFoundIndex);
1336 }
1337 else
1338 {
1339 // See if we have a method not allowed (405) handler
1340 foundRoute.route =
1341 findRouteByIndex(req.url, methodNotAllowedIndex);
Ed Tanous44e45182022-07-26 16:47:23 -07001342 }
1343 }
Ed Tanous759cf102022-07-31 16:36:52 -07001344
1345 // Fill in the allow header if it's valid
1346 if (!foundRoute.allowHeader.empty())
Ed Tanous44e45182022-07-26 16:47:23 -07001347 {
Ed Tanous759cf102022-07-31 16:36:52 -07001348
Ed Tanous88a03c52022-03-14 10:16:07 -07001349 asyncResp->res.addHeader(boost::beast::http::field::allow,
Ed Tanous44e45182022-07-26 16:47:23 -07001350 foundRoute.allowHeader);
Ed Tanous88a03c52022-03-14 10:16:07 -07001351 }
Tanousf00032d2018-11-05 01:18:10 -03001352
Ed Tanous44e45182022-07-26 16:47:23 -07001353 // If we couldn't find a real route or a 404 route, return a generic
1354 // response
1355 if (foundRoute.route.rule == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001356 {
Ed Tanous44e45182022-07-26 16:47:23 -07001357 if (foundRoute.allowHeader.empty())
1358 {
1359 asyncResp->res.result(boost::beast::http::status::not_found);
1360 }
1361 else
Ed Tanous2634dcd2019-03-26 09:28:06 -07001362 {
Ed Tanous88a03c52022-03-14 10:16:07 -07001363 asyncResp->res.result(
1364 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001365 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001366 return;
1367 }
1368
Ed Tanous44e45182022-07-26 16:47:23 -07001369 BaseRule& rule = *foundRoute.route.rule;
1370 RoutingParams params = std::move(foundRoute.route.params);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001371
Ed Tanous44e45182022-07-26 16:47:23 -07001372 BMCWEB_LOG_DEBUG << "Matched rule '" << rule.rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001373 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous44e45182022-07-26 16:47:23 -07001374 << rule.getMethods();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001375
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001376 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001377 {
Ed Tanous44e45182022-07-26 16:47:23 -07001378 rule.handle(req, asyncResp, params);
James Feist7166bf02019-12-10 16:52:14 +00001379 return;
1380 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001381
1382 crow::connections::systemBus->async_method_call(
Ed Tanous44e45182022-07-26 16:47:23 -07001383 [&req, asyncResp, &rule,
1384 params](const boost::system::error_code ec,
1385 const dbus::utility::DBusPropertiesMap& userInfoMap) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001386 if (ec)
1387 {
1388 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1389 asyncResp->res.result(
1390 boost::beast::http::status::internal_server_error);
1391 return;
1392 }
1393 std::string userRole{};
1394 const bool* remoteUser = nullptr;
1395 std::optional<bool> passwordExpired;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001396
Ed Tanous002d39b2022-05-31 08:59:27 -07001397 for (const auto& userInfo : userInfoMap)
1398 {
1399 if (userInfo.first == "UserPrivilege")
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001400 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001401 const std::string* userRolePtr =
1402 std::get_if<std::string>(&userInfo.second);
1403 if (userRolePtr == nullptr)
Ed Tanousb9d36b42022-02-26 21:42:46 -08001404 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001405 continue;
Ed Tanousb9d36b42022-02-26 21:42:46 -08001406 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001407 userRole = *userRolePtr;
1408 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1409 << " userRole = " << *userRolePtr;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001410 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001411 else if (userInfo.first == "RemoteUser")
1412 {
1413 remoteUser = std::get_if<bool>(&userInfo.second);
1414 }
1415 else if (userInfo.first == "UserPasswordExpired")
1416 {
1417 const bool* passwordExpiredPtr =
1418 std::get_if<bool>(&userInfo.second);
1419 if (passwordExpiredPtr == nullptr)
1420 {
1421 continue;
1422 }
1423 passwordExpired = *passwordExpiredPtr;
1424 }
1425 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001426
Ed Tanous002d39b2022-05-31 08:59:27 -07001427 if (remoteUser == nullptr)
1428 {
1429 BMCWEB_LOG_ERROR << "RemoteUser property missing or wrong type";
1430 asyncResp->res.result(
1431 boost::beast::http::status::internal_server_error);
1432 return;
1433 }
1434
1435 if (passwordExpired == std::nullopt)
1436 {
1437 if (!*remoteUser)
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001438 {
1439 BMCWEB_LOG_ERROR
Ed Tanous002d39b2022-05-31 08:59:27 -07001440 << "UserPasswordExpired property is expected for"
1441 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001442 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001443 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001444 return;
1445 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001446 passwordExpired = false;
1447 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001448
Nan Zhouac6250a2022-08-09 20:15:44 +00001449 // Get the user's privileges from the role
Ed Tanous002d39b2022-05-31 08:59:27 -07001450 redfish::Privileges userPrivileges =
1451 redfish::getUserPrivileges(userRole);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001452
Ed Tanous002d39b2022-05-31 08:59:27 -07001453 // Set isConfigureSelfOnly based on D-Bus results. This
1454 // ignores the results from both pamAuthenticateUser and the
1455 // value from any previous use of this session.
1456 req.session->isConfigureSelfOnly = *passwordExpired;
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001457
Nan Zhouac6250a2022-08-09 20:15:44 +00001458 // Modify privileges if isConfigureSelfOnly.
Ed Tanous002d39b2022-05-31 08:59:27 -07001459 if (req.session->isConfigureSelfOnly)
1460 {
Nan Zhouac6250a2022-08-09 20:15:44 +00001461 // Remove all privileges except ConfigureSelf
Ed Tanous002d39b2022-05-31 08:59:27 -07001462 userPrivileges = userPrivileges.intersection(
1463 redfish::Privileges{"ConfigureSelf"});
1464 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1465 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001466
Ed Tanous44e45182022-07-26 16:47:23 -07001467 if (!rule.checkPrivileges(userPrivileges))
Ed Tanous002d39b2022-05-31 08:59:27 -07001468 {
1469 asyncResp->res.result(boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001470 if (req.session->isConfigureSelfOnly)
1471 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001472 redfish::messages::passwordChangeRequired(
1473 asyncResp->res, crow::utility::urlFromPieces(
1474 "redfish", "v1", "AccountService",
1475 "Accounts", req.session->username));
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001476 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001477 return;
1478 }
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001479
Ed Tanous002d39b2022-05-31 08:59:27 -07001480 req.userRole = userRole;
Ed Tanous44e45182022-07-26 16:47:23 -07001481 rule.handle(req, asyncResp, params);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001482 },
1483 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1484 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1485 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001486 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001487
Ed Tanous1abe55e2018-09-05 08:30:59 -07001488 void debugPrint()
1489 {
Ed Tanous271584a2019-07-09 16:24:22 -07001490 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001491 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001492 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1493 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001494 perMethods[i].trie.debugPrint();
1495 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001496 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001497
Ed Tanous1abe55e2018-09-05 08:30:59 -07001498 std::vector<const std::string*> getRoutes(const std::string& parent)
1499 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001500 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001501
1502 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001503 {
Tanousf00032d2018-11-05 01:18:10 -03001504 std::vector<unsigned> x;
1505 pm.trie.findRouteIndexes(parent, x);
1506 for (unsigned index : x)
1507 {
1508 ret.push_back(&pm.rules[index]->rule);
1509 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510 }
1511 return ret;
1512 }
1513
1514 private:
Tanousf00032d2018-11-05 01:18:10 -03001515 struct PerMethod
1516 {
1517 std::vector<BaseRule*> rules;
1518 Trie trie;
Ed Tanous313a3c22022-03-14 09:27:38 -07001519 // rule index 0 has special meaning; preallocate it to avoid
Tanousf00032d2018-11-05 01:18:10 -03001520 // duplication.
Ed Tanous313a3c22022-03-14 09:27:38 -07001521 PerMethod() : rules(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001522 {}
Tanousf00032d2018-11-05 01:18:10 -03001523 };
Ed Tanous888880a2020-08-24 13:48:50 -07001524
Ed Tanous759cf102022-07-31 16:36:52 -07001525 std::array<PerMethod, methodNotAllowedIndex + 1> perMethods;
Tanousf00032d2018-11-05 01:18:10 -03001526 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001527};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001528} // namespace crow