blob: a7c0ced992905e44eac73b7eb8c805f5a93dd2f3 [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>
Tanousf00032d2018-11-05 01:18:10 -030015#include <boost/container/flat_map.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050016
Ed Tanouse0d918b2018-03-27 17:41:04 -070017#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070019#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070020#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070021#include <memory>
22#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <utility>
24#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace crow
27{
Tanousf00032d2018-11-05 01:18:10 -030028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029class BaseRule
30{
31 public:
Ed Tanousf23b7292020-10-15 09:41:17 -070032 BaseRule(const std::string& thisRule) : rule(thisRule)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050033 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070034
Ed Tanous0c0084a2019-10-24 15:57:51 -070035 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070036
Ed Tanousecd6a3a2022-01-07 09:18:40 -080037 BaseRule(const BaseRule&) = delete;
38 BaseRule(BaseRule&&) = delete;
39 BaseRule& operator=(const BaseRule&) = delete;
40 BaseRule& operator=(const BaseRule&&) = delete;
41
Ed Tanous1abe55e2018-09-05 08:30:59 -070042 virtual void validate() = 0;
43 std::unique_ptr<BaseRule> upgrade()
44 {
45 if (ruleToUpgrade)
Ed Tanous3174e4d2020-10-07 11:41:22 -070046 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070047 return std::move(ruleToUpgrade);
Ed Tanous3174e4d2020-10-07 11:41:22 -070048 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 return {};
50 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070051
zhanghch058d1b46d2021-04-01 11:18:24 +080052 virtual void handle(const Request&,
53 const std::shared_ptr<bmcweb::AsyncResp>&,
54 const RoutingParams&) = 0;
Ed Tanousceac6f72018-12-02 11:58:47 -080055 virtual void handleUpgrade(const Request&, Response& res,
56 boost::asio::ip::tcp::socket&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070058 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 res.end();
60 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070061#ifdef BMCWEB_ENABLE_SSL
Ed Tanousceac6f72018-12-02 11:58:47 -080062 virtual void
63 handleUpgrade(const Request&, Response& res,
64 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070066 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 res.end();
68 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070069#endif
70
Ed Tanous271584a2019-07-09 16:24:22 -070071 size_t getMethods()
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 {
73 return methodsBitfield;
74 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070075
Tanousf00032d2018-11-05 01:18:10 -030076 bool checkPrivileges(const redfish::Privileges& userPrivileges)
77 {
78 // If there are no privileges assigned, assume no privileges
79 // required
80 if (privilegesSet.empty())
81 {
82 return true;
83 }
84
85 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
86 {
87 if (userPrivileges.isSupersetOf(requiredPrivileges))
88 {
89 return true;
90 }
91 }
92 return false;
93 }
94
Ed Tanous271584a2019-07-09 16:24:22 -070095 size_t methodsBitfield{
96 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070097
Tanousf00032d2018-11-05 01:18:10 -030098 std::vector<redfish::Privileges> privilegesSet;
99
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 std::string rule;
101 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700102
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 friend class Router;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500106 template <typename T>
107 friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108};
109
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110namespace detail
111{
112namespace routing_handler_call_helper
113{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500114template <typename T, int Pos>
115struct CallPair
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116{
117 using type = T;
118 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700119};
120
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500121template <typename H1>
122struct CallParams
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123{
124 H1& handler;
125 const RoutingParams& params;
126 const Request& req;
zhanghch058d1b46d2021-04-01 11:18:24 +0800127 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128};
129
130template <typename F, int NInt, int NUint, int NDouble, int NString,
131 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132struct Call
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500133{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700134
135template <typename F, int NInt, int NUint, int NDouble, int NString,
136 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700137struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700138 black_magic::S<Args2...>>
139{
140 void operator()(F cparams)
141 {
142 using pushed = typename black_magic::S<Args2...>::template push_back<
143 CallPair<int64_t, NInt>>;
144 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
145 pushed>()(cparams);
146 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700147};
148
149template <typename F, int NInt, int NUint, int NDouble, int NString,
150 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700151struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
153{
154 void operator()(F cparams)
155 {
156 using pushed = typename black_magic::S<Args2...>::template push_back<
157 CallPair<uint64_t, NUint>>;
158 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
159 pushed>()(cparams);
160 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700161};
162
163template <typename F, int NInt, int NUint, int NDouble, int NString,
164 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700165struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 black_magic::S<Args2...>>
167{
168 void operator()(F cparams)
169 {
170 using pushed = typename black_magic::S<Args2...>::template push_back<
171 CallPair<double, NDouble>>;
172 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
173 pushed>()(cparams);
174 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700175};
176
177template <typename F, int NInt, int NUint, int NDouble, int NString,
178 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700179struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
181{
182 void operator()(F cparams)
183 {
184 using pushed = typename black_magic::S<Args2...>::template push_back<
185 CallPair<std::string, NString>>;
186 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
187 pushed>()(cparams);
188 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700189};
190
191template <typename F, int NInt, int NUint, int NDouble, int NString,
192 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700193struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 black_magic::S<Args1...>>
195{
196 void operator()(F cparams)
197 {
198 cparams.handler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800199 cparams.req, cparams.asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 cparams.params.template get<typename Args1::type>(Args1::pos)...);
201 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700202};
203
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204template <typename Func, typename... ArgsWrapped>
205struct Wrapped
Ed Tanous1abe55e2018-09-05 08:30:59 -0700206{
207 template <typename... Args>
208 void set(
209 Func f,
210 typename std::enable_if<
211 !std::is_same<
212 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
213 const Request&>::value,
214 int>::type = 0)
215 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800216 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800217 const Request&,
218 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
219 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700220 }
221
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500222 template <typename Req, typename... Args>
223 struct ReqHandlerWrapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000225 ReqHandlerWrapper(Func fIn) : f(std::move(fIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500226 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700227
zhanghch058d1b46d2021-04-01 11:18:24 +0800228 void operator()(const Request& req,
229 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
230 Args... args)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800232 asyncResp->res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700234
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 Func f;
236 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700237
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 template <typename... Args>
239 void set(
240 Func f,
241 typename std::enable_if<
242 std::is_same<
243 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
244 const Request&>::value &&
245 !std::is_same<typename std::tuple_element<
246 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800247 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 int>::type = 0)
249 {
250 handler = ReqHandlerWrapper<Args...>(std::move(f));
251 /*handler = (
252 [f = std::move(f)]
253 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700254 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 res.end();
256 });*/
257 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700258
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 template <typename... Args>
260 void set(
261 Func f,
262 typename std::enable_if<
263 std::is_same<
264 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
265 const Request&>::value &&
266 std::is_same<typename std::tuple_element<
267 1, std::tuple<Args..., void, void>>::type,
zhanghch058d1b46d2021-04-01 11:18:24 +0800268 const std::shared_ptr<bmcweb::AsyncResp>&>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 int>::type = 0)
270 {
271 handler = std::move(f);
272 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700273
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500274 template <typename... Args>
275 struct HandlerTypeHelper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800277 using type = std::function<void(
278 const crow::Request&, const std::shared_ptr<bmcweb::AsyncResp>&,
279 Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700281 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 template <typename... Args>
285 struct HandlerTypeHelper<const Request&, Args...>
286 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800287 using type = std::function<void(
288 const crow::Request&, const std::shared_ptr<bmcweb::AsyncResp>&,
289 Args...)>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 using args_type =
Ed Tanous988403c2020-08-24 11:29:49 -0700291 black_magic::S<typename black_magic::PromoteT<Args>...>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700293
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 template <typename... Args>
zhanghch058d1b46d2021-04-01 11:18:24 +0800295 struct HandlerTypeHelper<const Request&,
296 const std::shared_ptr<bmcweb::AsyncResp>&, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800298 using type = std::function<void(
299 const crow::Request&, const std::shared_ptr<bmcweb::AsyncResp>&,
300 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 };
304
305 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
306
zhanghch058d1b46d2021-04-01 11:18:24 +0800307 void operator()(const Request& req,
308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 const RoutingParams& params)
310 {
311 detail::routing_handler_call_helper::Call<
312 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
313 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
314 black_magic::S<>>()(
315 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800316 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700318};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319} // namespace routing_handler_call_helper
320} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700321
Ed Tanous1abe55e2018-09-05 08:30:59 -0700322class WebSocketRule : public BaseRule
323{
324 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700325
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326 public:
Ed Tanousf23b7292020-10-15 09:41:17 -0700327 WebSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500328 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700329
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 void validate() override
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500331 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700332
zhanghch058d1b46d2021-04-01 11:18:24 +0800333 void handle(const Request&,
334 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
335 const RoutingParams&) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800337 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700339
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000340 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800341 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530343 std::shared_ptr<
344 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
345 myConnection = std::make_shared<
346 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000347 req, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530348 closeHandler, errorHandler);
349 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 }
351#ifdef BMCWEB_ENABLE_SSL
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000352 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800353 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
354 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800356 std::shared_ptr<crow::websocket::ConnectionImpl<
357 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
358 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
359 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Jan Sowinskiee52ae12020-01-09 16:28:32 +0000360 req, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800361 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362 myConnection->start();
363 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700364#endif
365
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500366 template <typename Func>
367 self_t& onopen(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368 {
369 openHandler = f;
370 return *this;
371 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700372
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500373 template <typename Func>
374 self_t& onmessage(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700375 {
376 messageHandler = f;
377 return *this;
378 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700379
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500380 template <typename Func>
381 self_t& onclose(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 {
383 closeHandler = f;
384 return *this;
385 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700386
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500387 template <typename Func>
388 self_t& onerror(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 {
390 errorHandler = f;
391 return *this;
392 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700393
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394 protected:
Gunnar Millsccd584f2021-11-16 11:36:33 -0600395 std::function<void(crow::websocket::Connection&,
396 std::shared_ptr<bmcweb::AsyncResp>)>
397 openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
399 messageHandler;
400 std::function<void(crow::websocket::Connection&, const std::string&)>
401 closeHandler;
402 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700403};
404
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500405template <typename T>
406struct RuleParameterTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407{
408 using self_t = T;
409 WebSocketRule& websocket()
410 {
Ed Tanous271584a2019-07-09 16:24:22 -0700411 self_t* self = static_cast<self_t*>(this);
412 WebSocketRule* p = new WebSocketRule(self->rule);
413 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 return *p;
415 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700416
Ed Tanousf23b7292020-10-15 09:41:17 -0700417 self_t& name(const std::string_view name) noexcept
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 {
Ed Tanous271584a2019-07-09 16:24:22 -0700419 self_t* self = static_cast<self_t*>(this);
Ed Tanousf23b7292020-10-15 09:41:17 -0700420 self->nameStr = name;
Ed Tanous271584a2019-07-09 16:24:22 -0700421 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700423
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 self_t& methods(boost::beast::http::verb method)
425 {
Ed Tanous271584a2019-07-09 16:24:22 -0700426 self_t* self = static_cast<self_t*>(this);
427 self->methodsBitfield = 1U << static_cast<size_t>(method);
428 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700430
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 template <typename... MethodArgs>
Ed Tanous81ce6092020-12-17 16:54:55 +0000432 self_t& methods(boost::beast::http::verb method, MethodArgs... argsMethod)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700433 {
Ed Tanous271584a2019-07-09 16:24:22 -0700434 self_t* self = static_cast<self_t*>(this);
Ed Tanous81ce6092020-12-17 16:54:55 +0000435 methods(argsMethod...);
Ed Tanous271584a2019-07-09 16:24:22 -0700436 self->methodsBitfield |= 1U << static_cast<size_t>(method);
437 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 }
Tanousf00032d2018-11-05 01:18:10 -0300439
Ed Tanous432a8902021-06-14 15:28:56 -0700440 self_t& privileges(
441 const std::initializer_list<std::initializer_list<const char*>>& p)
Tanousf00032d2018-11-05 01:18:10 -0300442 {
Ed Tanous271584a2019-07-09 16:24:22 -0700443 self_t* self = static_cast<self_t*>(this);
Ed Tanous432a8902021-06-14 15:28:56 -0700444 for (const std::initializer_list<const char*>& privilege : p)
Tanousf00032d2018-11-05 01:18:10 -0300445 {
Ed Tanous271584a2019-07-09 16:24:22 -0700446 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300447 }
Ed Tanous271584a2019-07-09 16:24:22 -0700448 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300449 }
Ed Tanoused398212021-06-09 17:05:54 -0700450
451 template <size_t N, typename... MethodArgs>
452 self_t& privileges(const std::array<redfish::Privileges, N>& p)
453 {
454 self_t* self = static_cast<self_t*>(this);
455 for (const redfish::Privileges& privilege : p)
456 {
457 self->privilegesSet.emplace_back(privilege);
458 }
459 return *self;
460 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700461};
462
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
464{
465 public:
Ed Tanousf23b7292020-10-15 09:41:17 -0700466 DynamicRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500467 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700468
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 void validate() override
470 {
471 if (!erasedHandler)
472 {
473 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
474 "no handler for url " + rule);
475 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700476 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700477
zhanghch058d1b46d2021-04-01 11:18:24 +0800478 void handle(const Request& req,
479 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 const RoutingParams& params) override
481 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800482 erasedHandler(req, asyncResp, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700484
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500485 template <typename Func>
486 void operator()(Func f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 {
488 using function_t = utility::function_traits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 erasedHandler =
Ed Tanous988403c2020-08-24 11:29:49 -0700490 wrap(std::move(f),
491 std::make_integer_sequence<unsigned, function_t::arity>{});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 }
493
494 // enable_if Arg1 == request && Arg2 == Response
Gunnar Mills6be0e402020-07-08 13:21:51 -0500495 // enable_if Arg1 == request && Arg2 != response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 // enable_if Arg1 != request
497
498 template <typename Func, unsigned... Indices>
zhanghch058d1b46d2021-04-01 11:18:24 +0800499 std::function<void(const Request&,
500 const std::shared_ptr<bmcweb::AsyncResp>&,
501 const RoutingParams&)>
Ed Tanous988403c2020-08-24 11:29:49 -0700502 wrap(Func f, std::integer_sequence<unsigned, Indices...>)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 {
Ed Tanous988403c2020-08-24 11:29:49 -0700504 using function_t = crow::utility::function_traits<Func>;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505
506 if (!black_magic::isParameterTagCompatible(
Ed Tanous988403c2020-08-24 11:29:49 -0700507 black_magic::getParameterTag(rule.c_str()),
508 black_magic::computeParameterTagFromArgsList<
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 typename function_t::template arg<Indices>...>::value))
510 {
511 throw std::runtime_error("routeDynamic: Handler type is mismatched "
512 "with URL parameters: " +
513 rule);
514 }
515 auto ret = detail::routing_handler_call_helper::Wrapped<
516 Func, typename function_t::template arg<Indices>...>();
517 ret.template set<typename function_t::template arg<Indices>...>(
518 std::move(f));
519 return ret;
520 }
521
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500522 template <typename Func>
523 void operator()(std::string name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 {
525 nameStr = std::move(name);
526 (*this).template operator()<Func>(std::forward(f));
527 }
528
529 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800530 std::function<void(const Request&,
531 const std::shared_ptr<bmcweb::AsyncResp>&,
532 const RoutingParams&)>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700534};
535
536template <typename... Args>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500537class TaggedRule :
538 public BaseRule,
539 public RuleParameterTraits<TaggedRule<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540{
541 public:
542 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700543
Ed Tanousf23b7292020-10-15 09:41:17 -0700544 TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500545 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700546
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 void validate() override
548 {
549 if (!handler)
550 {
551 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
552 "no handler for url " + rule);
553 }
554 }
555
556 template <typename Func>
557 typename std::enable_if<
558 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
559 void>::type
560 operator()(Func&& f)
561 {
562 static_assert(
563 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
564 black_magic::CallHelper<
565 Func, black_magic::S<crow::Request, Args...>>::value,
566 "Handler type is mismatched with URL parameters");
567 static_assert(
568 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
569 "Handler function cannot have void return type; valid return "
570 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500571 "string, int, crow::response, nlohmann::json");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800573 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800574 const Request&,
575 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
576 Args... args) { asyncResp->res.result(f(args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 }
578
579 template <typename Func>
580 typename std::enable_if<
581 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700582 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700583 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 void>::type
585 operator()(Func&& f)
586 {
587 static_assert(
588 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
589 black_magic::CallHelper<
590 Func, black_magic::S<crow::Request, Args...>>::value,
591 "Handler type is mismatched with URL parameters");
592 static_assert(
593 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
594 std::declval<Args>()...))>::value,
595 "Handler function cannot have void return type; valid return "
596 "types: "
Gunnar Mills6be0e402020-07-08 13:21:51 -0500597 "string, int, crow::response,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700598
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800599 handler = [f = std::forward<Func>(f)](
zhanghch058d1b46d2021-04-01 11:18:24 +0800600 const crow::Request& req,
601 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
602 Args... args) { asyncResp->res.result(f(req, args...)); };
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700604
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 template <typename Func>
606 typename std::enable_if<
607 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
608 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700609 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 void>::type
611 operator()(Func&& f)
612 {
613 static_assert(
614 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
615 black_magic::CallHelper<
616 Func, black_magic::S<crow::Request, Args...>>::value ||
617 black_magic::CallHelper<
zhanghch058d1b46d2021-04-01 11:18:24 +0800618 Func, black_magic::S<crow::Request,
619 std::shared_ptr<bmcweb::AsyncResp>&,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 Args...>>::value,
621 "Handler type is mismatched with URL parameters");
622 static_assert(
zhanghch058d1b46d2021-04-01 11:18:24 +0800623 std::is_same<
624 void,
625 decltype(f(std::declval<crow::Request>(),
626 std::declval<std::shared_ptr<bmcweb::AsyncResp>&>(),
627 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300628 "Handler function with response argument should have void "
629 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700631
Ed Tanousf94c4ec2022-01-06 12:44:41 -0800632 handler = std::forward<Func>(f);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700634
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500635 template <typename Func>
Ed Tanousf23b7292020-10-15 09:41:17 -0700636 void operator()(const std::string_view name, Func&& f)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700638 nameStr = name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 (*this).template operator()<Func>(std::forward(f));
640 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700641
zhanghch058d1b46d2021-04-01 11:18:24 +0800642 void handle(const Request& req,
643 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 const RoutingParams& params) override
645 {
646 detail::routing_handler_call_helper::Call<
647 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
648 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
649 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
zhanghch058d1b46d2021-04-01 11:18:24 +0800650 handler, params, req, asyncResp});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700651 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700652
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800654 std::function<void(const crow::Request&,
655 const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
656 handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700657};
658
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700659const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700660
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661class Trie
662{
663 public:
664 struct Node
665 {
666 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700667 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
668 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700670
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 bool isSimpleNode() const
672 {
673 return !ruleIndex && std::all_of(std::begin(paramChildrens),
674 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700675 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700676 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700677 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700678
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 Trie() : nodes(1)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500680 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681
682 private:
683 void optimizeNode(Node* node)
684 {
Ed Tanous271584a2019-07-09 16:24:22 -0700685 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 {
687 if (!x)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700688 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 continue;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700690 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 Node* child = &nodes[x];
692 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700693 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 if (node->children.empty())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700695 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 return;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700697 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300699 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 {
701 Node* child = &nodes[kv.second];
702 if (!child->isSimpleNode())
703 {
704 mergeWithChild = false;
705 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700706 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 }
708 if (mergeWithChild)
709 {
710 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300711 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 {
713 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300714 for (const std::pair<std::string, unsigned>& childKv :
715 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 {
717 merged[kv.first + childKv.first] = childKv.second;
718 }
719 }
720 node->children = std::move(merged);
721 optimizeNode(node);
722 }
723 else
724 {
Tanousf00032d2018-11-05 01:18:10 -0300725 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 {
727 Node* child = &nodes[kv.second];
728 optimizeNode(child);
729 }
730 }
731 }
732
733 void optimize()
734 {
735 optimizeNode(head());
736 }
737
738 public:
739 void validate()
740 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 optimize();
742 }
743
Ed Tanous81ce6092020-12-17 16:54:55 +0000744 void findRouteIndexes(const std::string& reqUrl,
745 std::vector<unsigned>& routeIndexes,
Tanousf00032d2018-11-05 01:18:10 -0300746 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
748 if (node == nullptr)
749 {
750 node = head();
751 }
Tanousf00032d2018-11-05 01:18:10 -0300752 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 {
754 const std::string& fragment = kv.first;
755 const Node* child = &nodes[kv.second];
Ed Tanous81ce6092020-12-17 16:54:55 +0000756 if (pos >= reqUrl.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 {
758 if (child->ruleIndex != 0 && fragment != "/")
759 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000760 routeIndexes.push_back(child->ruleIndex);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000762 findRouteIndexes(reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700763 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 }
765 else
766 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000767 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 {
Ed Tanous271584a2019-07-09 16:24:22 -0700769 findRouteIndexes(
Ed Tanous81ce6092020-12-17 16:54:55 +0000770 reqUrl, routeIndexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700771 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 }
773 }
774 }
775 }
776
777 std::pair<unsigned, RoutingParams>
Ed Tanous81ce6092020-12-17 16:54:55 +0000778 find(const std::string_view reqUrl, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700779 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700780 {
781 RoutingParams empty;
782 if (params == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700783 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 params = &empty;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700785 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786
787 unsigned found{};
788 RoutingParams matchParams;
789
790 if (node == nullptr)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700791 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 node = head();
Ed Tanous3174e4d2020-10-07 11:41:22 -0700793 }
Ed Tanous81ce6092020-12-17 16:54:55 +0000794 if (pos == reqUrl.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -0700795 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 return {node->ruleIndex, *params};
Ed Tanous3174e4d2020-10-07 11:41:22 -0700797 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798
799 auto updateFound =
800 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
801 if (ret.first && (!found || found > ret.first))
802 {
803 found = ret.first;
804 matchParams = std::move(ret.second);
805 }
806 };
807
Ed Tanous271584a2019-07-09 16:24:22 -0700808 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000810 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
812 {
Ed Tanous543f4402022-01-06 13:12:53 -0800813 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700814 errno = 0;
815 long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000816 std::strtoll(reqUrl.data() + pos, &eptr, 10);
817 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818 {
819 params->intParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000820 std::pair<unsigned, RoutingParams> ret =
821 find(reqUrl,
822 &nodes[node->paramChildrens[static_cast<size_t>(
823 ParamType::INT)]],
824 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 updateFound(ret);
826 params->intParams.pop_back();
827 }
828 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700829 }
830
Ed Tanous271584a2019-07-09 16:24:22 -0700831 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000833 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 if ((c >= '0' && c <= '9') || c == '+')
835 {
Ed Tanous543f4402022-01-06 13:12:53 -0800836 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 errno = 0;
838 unsigned long long int value =
Ed Tanous81ce6092020-12-17 16:54:55 +0000839 std::strtoull(reqUrl.data() + pos, &eptr, 10);
840 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
842 params->uintParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000843 std::pair<unsigned, RoutingParams> ret =
844 find(reqUrl,
845 &nodes[node->paramChildrens[static_cast<size_t>(
846 ParamType::UINT)]],
847 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 updateFound(ret);
849 params->uintParams.pop_back();
850 }
851 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700852 }
853
Ed Tanous271584a2019-07-09 16:24:22 -0700854 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000856 char c = reqUrl[pos];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
858 {
Ed Tanous543f4402022-01-06 13:12:53 -0800859 char* eptr = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700860 errno = 0;
Ed Tanous81ce6092020-12-17 16:54:55 +0000861 double value = std::strtod(reqUrl.data() + pos, &eptr);
862 if (errno != ERANGE && eptr != reqUrl.data() + pos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 {
864 params->doubleParams.push_back(value);
Ed Tanous81ce6092020-12-17 16:54:55 +0000865 std::pair<unsigned, RoutingParams> ret =
866 find(reqUrl,
867 &nodes[node->paramChildrens[static_cast<size_t>(
868 ParamType::DOUBLE)]],
869 static_cast<size_t>(eptr - reqUrl.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 updateFound(ret);
871 params->doubleParams.pop_back();
872 }
873 }
874 }
875
Ed Tanous271584a2019-07-09 16:24:22 -0700876 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000878 size_t epos = pos;
Ed Tanous81ce6092020-12-17 16:54:55 +0000879 for (; epos < reqUrl.size(); epos++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000881 if (reqUrl[epos] == '/')
Ed Tanous3174e4d2020-10-07 11:41:22 -0700882 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 break;
Ed Tanous3174e4d2020-10-07 11:41:22 -0700884 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 }
886
887 if (epos != pos)
888 {
889 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000890 reqUrl.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300891 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000892 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700893 &nodes[node->paramChildrens[static_cast<size_t>(
894 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000895 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896 updateFound(ret);
897 params->stringParams.pop_back();
898 }
899 }
900
Ed Tanous271584a2019-07-09 16:24:22 -0700901 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000903 size_t epos = reqUrl.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904
905 if (epos != pos)
906 {
907 params->stringParams.emplace_back(
Ed Tanous81ce6092020-12-17 16:54:55 +0000908 reqUrl.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700909 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000910 find(reqUrl,
Ed Tanous271584a2019-07-09 16:24:22 -0700911 &nodes[node->paramChildrens[static_cast<size_t>(
912 ParamType::PATH)]],
913 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 updateFound(ret);
915 params->stringParams.pop_back();
916 }
917 }
918
Tanousf00032d2018-11-05 01:18:10 -0300919 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 {
921 const std::string& fragment = kv.first;
922 const Node* child = &nodes[kv.second];
923
Ed Tanous81ce6092020-12-17 16:54:55 +0000924 if (reqUrl.compare(pos, fragment.size(), fragment) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 {
Tanousf00032d2018-11-05 01:18:10 -0300926 std::pair<unsigned, RoutingParams> ret =
Ed Tanous81ce6092020-12-17 16:54:55 +0000927 find(reqUrl, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 updateFound(ret);
929 }
930 }
931
932 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700933 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934
935 void add(const std::string& url, unsigned ruleIndex)
936 {
Ed Tanous271584a2019-07-09 16:24:22 -0700937 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938
939 for (unsigned i = 0; i < url.size(); i++)
940 {
941 char c = url[i];
942 if (c == '<')
943 {
Tanousf00032d2018-11-05 01:18:10 -0300944 const static std::array<std::pair<ParamType, std::string>, 7>
945 paramTraits = {{
946 {ParamType::INT, "<int>"},
947 {ParamType::UINT, "<uint>"},
948 {ParamType::DOUBLE, "<float>"},
949 {ParamType::DOUBLE, "<double>"},
950 {ParamType::STRING, "<str>"},
951 {ParamType::STRING, "<string>"},
952 {ParamType::PATH, "<path>"},
953 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954
Tanousf00032d2018-11-05 01:18:10 -0300955 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700956 {
Tanousf00032d2018-11-05 01:18:10 -0300957 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700958 {
Ed Tanous271584a2019-07-09 16:24:22 -0700959 size_t index = static_cast<size_t>(x.first);
960 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961 {
Tanousf00032d2018-11-05 01:18:10 -0300962 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700963 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700964 }
Ed Tanous271584a2019-07-09 16:24:22 -0700965 idx = nodes[idx].paramChildrens[index];
966 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 break;
968 }
969 }
970
971 i--;
972 }
973 else
974 {
975 std::string piece(&c, 1);
976 if (!nodes[idx].children.count(piece))
977 {
Tanousf00032d2018-11-05 01:18:10 -0300978 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 nodes[idx].children.emplace(piece, newNodeIdx);
980 }
981 idx = nodes[idx].children[piece];
982 }
983 }
984 if (nodes[idx].ruleIndex)
Ed Tanous3174e4d2020-10-07 11:41:22 -0700985 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986 throw std::runtime_error("handler already exists for " + url);
Ed Tanous3174e4d2020-10-07 11:41:22 -0700987 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700988 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700989 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700990
Ed Tanous1abe55e2018-09-05 08:30:59 -0700991 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700992 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 {
Ed Tanous271584a2019-07-09 16:24:22 -0700994 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700995 {
996 if (n->paramChildrens[i])
997 {
998 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700999 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
1000 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 {
1002 case ParamType::INT:
1003 BMCWEB_LOG_DEBUG << "<int>";
1004 break;
1005 case ParamType::UINT:
1006 BMCWEB_LOG_DEBUG << "<uint>";
1007 break;
1008 case ParamType::DOUBLE:
1009 BMCWEB_LOG_DEBUG << "<float>";
1010 break;
1011 case ParamType::STRING:
1012 BMCWEB_LOG_DEBUG << "<str>";
1013 break;
1014 case ParamType::PATH:
1015 BMCWEB_LOG_DEBUG << "<path>";
1016 break;
Ed Tanous23a21a12020-07-25 04:45:05 +00001017 case ParamType::MAX:
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 BMCWEB_LOG_DEBUG << "<ERROR>";
1019 break;
1020 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001021
Ed Tanous1abe55e2018-09-05 08:30:59 -07001022 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
1023 }
1024 }
Tanousf00032d2018-11-05 01:18:10 -03001025 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 {
1027 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -07001028 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 << kv.first;
1030 debugNodePrint(&nodes[kv.second], level + 1);
1031 }
1032 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001033
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 public:
1035 void debugPrint()
1036 {
Ed Tanous271584a2019-07-09 16:24:22 -07001037 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001038 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001039
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 private:
1041 const Node* head() const
1042 {
1043 return &nodes.front();
1044 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001045
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046 Node* head()
1047 {
1048 return &nodes.front();
1049 }
1050
1051 unsigned newNode()
1052 {
1053 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001054 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055 }
1056
1057 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001058};
1059
Ed Tanous1abe55e2018-09-05 08:30:59 -07001060class Router
1061{
1062 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001063 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001064
Ed Tanous1abe55e2018-09-05 08:30:59 -07001065 DynamicRule& newRuleDynamic(const std::string& rule)
1066 {
1067 std::unique_ptr<DynamicRule> ruleObject =
1068 std::make_unique<DynamicRule>(rule);
1069 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001070 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001071
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001073 }
1074
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 template <uint64_t N>
1076 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1077 newRuleTagged(const std::string& rule)
1078 {
1079 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1080 TaggedRule>;
1081 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1082 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001083 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084
1085 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001086 }
1087
Tanousf00032d2018-11-05 01:18:10 -03001088 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001089 {
Tanousf00032d2018-11-05 01:18:10 -03001090 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 {
Tanousf00032d2018-11-05 01:18:10 -03001092 return;
1093 }
Ed Tanous888880a2020-08-24 13:48:50 -07001094 for (size_t method = 0, methodBit = 1; method < maxHttpVerbCount;
Ed Tanous2c70f802020-09-28 14:29:23 -07001095 method++, methodBit <<= 1)
Tanousf00032d2018-11-05 01:18:10 -03001096 {
Ed Tanous2c70f802020-09-28 14:29:23 -07001097 if (ruleObject->methodsBitfield & methodBit)
Tanousf00032d2018-11-05 01:18:10 -03001098 {
1099 perMethods[method].rules.emplace_back(ruleObject);
1100 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001101 rule, static_cast<unsigned>(
1102 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001103 // directory case:
1104 // request to `/about' url matches `/about/' rule
1105 if (rule.size() > 2 && rule.back() == '/')
1106 {
1107 perMethods[method].trie.add(
1108 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001109 static_cast<unsigned>(perMethods[method].rules.size() -
1110 1));
Tanousf00032d2018-11-05 01:18:10 -03001111 }
1112 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001113 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001114 }
1115
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116 void validate()
1117 {
Tanousf00032d2018-11-05 01:18:10 -03001118 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001119 {
1120 if (rule)
1121 {
Tanousf00032d2018-11-05 01:18:10 -03001122 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123 if (upgraded)
Ed Tanous3174e4d2020-10-07 11:41:22 -07001124 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001125 rule = std::move(upgraded);
Ed Tanous3174e4d2020-10-07 11:41:22 -07001126 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001127 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001128 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 }
1130 }
Tanousf00032d2018-11-05 01:18:10 -03001131 for (PerMethod& perMethod : perMethods)
1132 {
1133 perMethod.trie.validate();
1134 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001135 }
1136
Ed Tanous1abe55e2018-09-05 08:30:59 -07001137 template <typename Adaptor>
1138 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1139 {
Ed Tanous271584a2019-07-09 16:24:22 -07001140 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001141 {
1142 res.result(boost::beast::http::status::not_found);
1143 res.end();
Tanousf00032d2018-11-05 01:18:10 -03001144 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001145 }
Tanousf00032d2018-11-05 01:18:10 -03001146
Ed Tanous271584a2019-07-09 16:24:22 -07001147 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001148 Trie& trie = perMethod.trie;
1149 std::vector<BaseRule*>& rules = perMethod.rules;
1150
1151 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001152 unsigned ruleIndex = found.first;
1153 if (!ruleIndex)
1154 {
1155 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001156 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001157 res.end();
1158 return;
1159 }
1160
1161 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001162 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001164 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165
1166 if (ruleIndex == ruleSpecialRedirectSlash)
1167 {
1168 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1169 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001170 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001171
1172 // TODO absolute url building
1173 if (req.getHeaderValue("Host").empty())
1174 {
1175 res.addHeader("Location", std::string(req.url) + "/");
1176 }
1177 else
1178 {
1179 res.addHeader(
1180 "Location",
1181 req.isSecure
1182 ? "https://"
1183 : "http://" + std::string(req.getHeaderValue("Host")) +
1184 std::string(req.url) + "/");
1185 }
1186 res.end();
1187 return;
1188 }
1189
Ed Tanous271584a2019-07-09 16:24:22 -07001190 if ((rules[ruleIndex]->getMethods() &
1191 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001192 {
1193 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1194 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001195 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001197 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198 res.end();
1199 return;
1200 }
1201
1202 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001203 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001204 << rules[ruleIndex]->getMethods();
1205
1206 // any uncaught exceptions become 500s
1207 try
1208 {
Ed Tanousf94c4ec2022-01-06 12:44:41 -08001209 rules[ruleIndex]->handleUpgrade(req, res,
1210 std::forward<Adaptor>(adaptor));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001211 }
Patrick Williamsc5967042021-10-06 12:39:54 -05001212 catch (const std::exception& e)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001213 {
1214 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001215 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001216 res.end();
1217 return;
1218 }
1219 catch (...)
1220 {
1221 BMCWEB_LOG_ERROR
1222 << "An uncaught exception occurred. The type was unknown "
1223 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001224 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001225 res.end();
1226 return;
1227 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001228 }
1229
zhanghch058d1b46d2021-04-01 11:18:24 +08001230 void handle(Request& req,
1231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001232 {
Ed Tanous271584a2019-07-09 16:24:22 -07001233 if (static_cast<size_t>(req.method()) >= perMethods.size())
Ed Tanous888880a2020-08-24 13:48:50 -07001234 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001235 asyncResp->res.result(boost::beast::http::status::not_found);
Tanousf00032d2018-11-05 01:18:10 -03001236 return;
Ed Tanous888880a2020-08-24 13:48:50 -07001237 }
Ed Tanous271584a2019-07-09 16:24:22 -07001238 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001239 Trie& trie = perMethod.trie;
1240 std::vector<BaseRule*>& rules = perMethod.rules;
1241
1242 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001243
Ed Tanous1abe55e2018-09-05 08:30:59 -07001244 unsigned ruleIndex = found.first;
1245
1246 if (!ruleIndex)
1247 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001248 // Check to see if this url exists at any verb
1249 for (const PerMethod& p : perMethods)
1250 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001251 const std::pair<unsigned, RoutingParams>& found2 =
Ed Tanous2634dcd2019-03-26 09:28:06 -07001252 p.trie.find(req.url);
Ed Tanous23a21a12020-07-25 04:45:05 +00001253 if (found2.first > 0)
Ed Tanous2634dcd2019-03-26 09:28:06 -07001254 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001255 asyncResp->res.result(
1256 boost::beast::http::status::method_not_allowed);
Ed Tanous2634dcd2019-03-26 09:28:06 -07001257 return;
1258 }
1259 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
zhanghch058d1b46d2021-04-01 11:18:24 +08001261 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 return;
1263 }
1264
1265 if (ruleIndex >= rules.size())
Ed Tanous3174e4d2020-10-07 11:41:22 -07001266 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001267 throw std::runtime_error("Trie internal structure corrupted!");
Ed Tanous3174e4d2020-10-07 11:41:22 -07001268 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001269
1270 if (ruleIndex == ruleSpecialRedirectSlash)
1271 {
1272 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1273 << req.url;
zhanghch058d1b46d2021-04-01 11:18:24 +08001274 asyncResp->res.result(
1275 boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276
1277 // TODO absolute url building
1278 if (req.getHeaderValue("Host").empty())
1279 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001280 asyncResp->res.addHeader("Location",
1281 std::string(req.url) + "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001282 }
1283 else
1284 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001285 asyncResp->res.addHeader(
1286 "Location", (req.isSecure ? "https://" : "http://") +
1287 std::string(req.getHeaderValue("Host")) +
1288 std::string(req.url) + "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 return;
1291 }
1292
Ed Tanous271584a2019-07-09 16:24:22 -07001293 if ((rules[ruleIndex]->getMethods() &
1294 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001295 {
1296 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1297 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001298 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001299 << rules[ruleIndex]->getMethods();
zhanghch058d1b46d2021-04-01 11:18:24 +08001300 asyncResp->res.result(
1301 boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001302 return;
1303 }
1304
1305 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001306 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001307 << rules[ruleIndex]->getMethods();
1308
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001309 if (req.session == nullptr)
James Feist7166bf02019-12-10 16:52:14 +00001310 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001311 rules[ruleIndex]->handle(req, asyncResp, found.second);
James Feist7166bf02019-12-10 16:52:14 +00001312 return;
1313 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001314
1315 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -08001316 [&req, asyncResp, &rules, ruleIndex,
1317 found](const boost::system::error_code ec,
1318 const std::map<std::string, dbus::utility::DbusVariantType>&
1319 userInfo) {
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001320 if (ec)
1321 {
1322 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
zhanghch058d1b46d2021-04-01 11:18:24 +08001323 asyncResp->res.result(
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001324 boost::beast::http::status::internal_server_error);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001325 return;
1326 }
1327
1328 const std::string* userRolePtr = nullptr;
1329 auto userInfoIter = userInfo.find("UserPrivilege");
1330 if (userInfoIter != userInfo.end())
1331 {
1332 userRolePtr =
1333 std::get_if<std::string>(&userInfoIter->second);
1334 }
1335
1336 std::string userRole{};
1337 if (userRolePtr != nullptr)
1338 {
1339 userRole = *userRolePtr;
1340 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1341 << " userRole = " << *userRolePtr;
1342 }
1343
Ed Tanous5dc924d2021-12-20 09:17:28 -08001344 const bool* remoteUserPtr = nullptr;
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001345 auto remoteUserIter = userInfo.find("RemoteUser");
1346 if (remoteUserIter != userInfo.end())
1347 {
1348 remoteUserPtr = std::get_if<bool>(&remoteUserIter->second);
1349 }
1350 if (remoteUserPtr == nullptr)
1351 {
1352 BMCWEB_LOG_ERROR
1353 << "RemoteUser property missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001354 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001355 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001356 return;
1357 }
1358 bool remoteUser = *remoteUserPtr;
1359
1360 bool passwordExpired = false; // default for remote user
1361 if (!remoteUser)
1362 {
Ed Tanous5dc924d2021-12-20 09:17:28 -08001363 const bool* passwordExpiredPtr = nullptr;
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001364 auto passwordExpiredIter =
1365 userInfo.find("UserPasswordExpired");
1366 if (passwordExpiredIter != userInfo.end())
1367 {
1368 passwordExpiredPtr =
1369 std::get_if<bool>(&passwordExpiredIter->second);
1370 }
1371 if (passwordExpiredPtr != nullptr)
1372 {
1373 passwordExpired = *passwordExpiredPtr;
1374 }
1375 else
1376 {
1377 BMCWEB_LOG_ERROR
1378 << "UserPasswordExpired property is expected for"
1379 " local user but is missing or wrong type";
zhanghch058d1b46d2021-04-01 11:18:24 +08001380 asyncResp->res.result(
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001381 boost::beast::http::status::internal_server_error);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001382 return;
1383 }
1384 }
1385
Ed Tanous23a21a12020-07-25 04:45:05 +00001386 // Get the userprivileges from the role
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001387 redfish::Privileges userPrivileges =
1388 redfish::getUserPrivileges(userRole);
1389
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001390 // Set isConfigureSelfOnly based on D-Bus results. This
1391 // ignores the results from both pamAuthenticateUser and the
1392 // value from any previous use of this session.
1393 req.session->isConfigureSelfOnly = passwordExpired;
1394
Ed Tanous23a21a12020-07-25 04:45:05 +00001395 // Modifyprivileges if isConfigureSelfOnly.
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001396 if (req.session->isConfigureSelfOnly)
1397 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001398 // Remove allprivileges except ConfigureSelf
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001399 userPrivileges = userPrivileges.intersection(
1400 redfish::Privileges{"ConfigureSelf"});
1401 BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
1402 }
1403
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001404 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1405 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001406 asyncResp->res.result(
1407 boost::beast::http::status::forbidden);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001408 if (req.session->isConfigureSelfOnly)
1409 {
1410 redfish::messages::passwordChangeRequired(
zhanghch058d1b46d2021-04-01 11:18:24 +08001411 asyncResp->res,
1412 "/redfish/v1/AccountService/Accounts/" +
1413 req.session->username);
Joseph Reynolds3bf4e632020-02-06 14:44:32 -06001414 }
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001415 return;
1416 }
1417
1418 req.userRole = userRole;
zhanghch058d1b46d2021-04-01 11:18:24 +08001419 rules[ruleIndex]->handle(req, asyncResp, found.second);
RAJESWARAN THILLAIGOVINDAN61dbeef2019-12-13 04:26:54 -06001420 },
1421 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1422 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1423 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001424 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001425
Ed Tanous1abe55e2018-09-05 08:30:59 -07001426 void debugPrint()
1427 {
Ed Tanous271584a2019-07-09 16:24:22 -07001428 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001429 {
Ed Tanous23a21a12020-07-25 04:45:05 +00001430 BMCWEB_LOG_DEBUG << boost::beast::http::to_string(
1431 static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001432 perMethods[i].trie.debugPrint();
1433 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001434 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001435
Ed Tanous1abe55e2018-09-05 08:30:59 -07001436 std::vector<const std::string*> getRoutes(const std::string& parent)
1437 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001438 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001439
1440 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001441 {
Tanousf00032d2018-11-05 01:18:10 -03001442 std::vector<unsigned> x;
1443 pm.trie.findRouteIndexes(parent, x);
1444 for (unsigned index : x)
1445 {
1446 ret.push_back(&pm.rules[index]->rule);
1447 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001448 }
1449 return ret;
1450 }
1451
1452 private:
Tanousf00032d2018-11-05 01:18:10 -03001453 struct PerMethod
1454 {
1455 std::vector<BaseRule*> rules;
1456 Trie trie;
1457 // rule index 0, 1 has special meaning; preallocate it to avoid
1458 // duplication.
1459 PerMethod() : rules(2)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001460 {}
Tanousf00032d2018-11-05 01:18:10 -03001461 };
Ed Tanous888880a2020-08-24 13:48:50 -07001462
1463 const static size_t maxHttpVerbCount =
Ed Tanouscc090442020-10-07 08:20:50 -07001464 static_cast<size_t>(boost::beast::http::verb::unlink);
Ed Tanous888880a2020-08-24 13:48:50 -07001465
Tanousf00032d2018-11-05 01:18:10 -03001466 std::array<PerMethod, maxHttpVerbCount> perMethods;
1467 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001468};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469} // namespace crow