blob: 092928632f770690428ee185d440b801329c901f [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Tanousf00032d2018-11-05 01:18:10 -03003#include "privileges.hpp"
Ratan Gupta6f359562019-04-03 10:39:08 +05304#include "sessions.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07005
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +02006#include <async_resp.hpp>
Tanousf00032d2018-11-05 01:18:10 -03007#include <boost/container/flat_map.hpp>
8#include <boost/container/small_vector.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07009#include <boost/lexical_cast.hpp>
Ed Tanouse0d918b2018-03-27 17:41:04 -070010#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070011#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070012#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070013#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070014#include <memory>
15#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070016#include <utility>
17#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070018
Ed Tanousc94ad492019-10-10 15:39:33 -070019#include "common.h"
20#include "http_request.h"
21#include "http_response.h"
22#include "logging.h"
23#include "utility.h"
24#include "websocket.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace crow
27{
Tanousf00032d2018-11-05 01:18:10 -030028
29constexpr int maxHttpVerbCount =
30 static_cast<int>(boost::beast::http::verb::unlink);
31
Ed Tanous1abe55e2018-09-05 08:30:59 -070032class BaseRule
33{
34 public:
Tanousf00032d2018-11-05 01:18:10 -030035 BaseRule(std::string thisRule) : rule(std::move(thisRule))
Ed Tanous1abe55e2018-09-05 08:30:59 -070036 {
37 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070038
Ed Tanous0c0084a2019-10-24 15:57:51 -070039 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070040
Ed Tanous1abe55e2018-09-05 08:30:59 -070041 virtual void validate() = 0;
42 std::unique_ptr<BaseRule> upgrade()
43 {
44 if (ruleToUpgrade)
45 return std::move(ruleToUpgrade);
46 return {};
47 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070048
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 virtual void handle(const Request&, Response&, const RoutingParams&) = 0;
Ed Tanousceac6f72018-12-02 11:58:47 -080050 virtual void handleUpgrade(const Request&, Response& res,
51 boost::asio::ip::tcp::socket&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070053 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 res.end();
55 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070056#ifdef BMCWEB_ENABLE_SSL
Ed Tanousceac6f72018-12-02 11:58:47 -080057 virtual void
58 handleUpgrade(const Request&, Response& res,
59 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070061 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 res.end();
63 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070064#endif
65
Ed Tanous271584a2019-07-09 16:24:22 -070066 size_t getMethods()
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 {
68 return methodsBitfield;
69 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070070
Tanousf00032d2018-11-05 01:18:10 -030071 bool checkPrivileges(const redfish::Privileges& userPrivileges)
72 {
73 // If there are no privileges assigned, assume no privileges
74 // required
75 if (privilegesSet.empty())
76 {
77 return true;
78 }
79
80 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
81 {
82 if (userPrivileges.isSupersetOf(requiredPrivileges))
83 {
84 return true;
85 }
86 }
87 return false;
88 }
89
Ed Tanous271584a2019-07-09 16:24:22 -070090 size_t methodsBitfield{
91 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070092
Tanousf00032d2018-11-05 01:18:10 -030093 std::vector<redfish::Privileges> privilegesSet;
94
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 std::string rule;
96 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -070097
Ed Tanous1abe55e2018-09-05 08:30:59 -070098 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -070099
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 friend class Router;
101 template <typename T> friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700102};
103
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104namespace detail
105{
106namespace routing_handler_call_helper
107{
108template <typename T, int Pos> struct CallPair
109{
110 using type = T;
111 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700112};
113
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114template <typename H1> struct CallParams
115{
116 H1& handler;
117 const RoutingParams& params;
118 const Request& req;
119 Response& res;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700120};
121
122template <typename F, int NInt, int NUint, int NDouble, int NString,
123 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124struct Call
125{
126};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700127
128template <typename F, int NInt, int NUint, int NDouble, int NString,
129 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700130struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 black_magic::S<Args2...>>
132{
133 void operator()(F cparams)
134 {
135 using pushed = typename black_magic::S<Args2...>::template push_back<
136 CallPair<int64_t, NInt>>;
137 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
138 pushed>()(cparams);
139 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700140};
141
142template <typename F, int NInt, int NUint, int NDouble, int NString,
143 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700144struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
146{
147 void operator()(F cparams)
148 {
149 using pushed = typename black_magic::S<Args2...>::template push_back<
150 CallPair<uint64_t, NUint>>;
151 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
152 pushed>()(cparams);
153 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700154};
155
156template <typename F, int NInt, int NUint, int NDouble, int NString,
157 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700158struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 black_magic::S<Args2...>>
160{
161 void operator()(F cparams)
162 {
163 using pushed = typename black_magic::S<Args2...>::template push_back<
164 CallPair<double, NDouble>>;
165 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
166 pushed>()(cparams);
167 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700168};
169
170template <typename F, int NInt, int NUint, int NDouble, int NString,
171 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700172struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
174{
175 void operator()(F cparams)
176 {
177 using pushed = typename black_magic::S<Args2...>::template push_back<
178 CallPair<std::string, NString>>;
179 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
180 pushed>()(cparams);
181 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700182};
183
184template <typename F, int NInt, int NUint, int NDouble, int NString,
185 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700186struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 black_magic::S<Args1...>>
188{
189 void operator()(F cparams)
190 {
191 cparams.handler(
192 cparams.req, cparams.res,
193 cparams.params.template get<typename Args1::type>(Args1::pos)...);
194 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700195};
196
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197template <typename Func, typename... ArgsWrapped> struct Wrapped
198{
199 template <typename... Args>
200 void set(
201 Func f,
202 typename std::enable_if<
203 !std::is_same<
204 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
205 const Request&>::value,
206 int>::type = 0)
207 {
Tanousf00032d2018-11-05 01:18:10 -0300208 handler = [f = std::move(f)](const Request&, Response& res,
209 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700210 res.result(f(args...));
Tanousf00032d2018-11-05 01:18:10 -0300211 res.end();
212 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700213 }
214
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 template <typename Req, typename... Args> struct ReqHandlerWrapper
216 {
217 ReqHandlerWrapper(Func f) : f(std::move(f))
218 {
219 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700220
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 void operator()(const Request& req, Response& res, Args... args)
222 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700223 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 res.end();
225 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700226
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 Func f;
228 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 template <typename... Args>
231 void set(
232 Func f,
233 typename std::enable_if<
234 std::is_same<
235 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
236 const Request&>::value &&
237 !std::is_same<typename std::tuple_element<
238 1, std::tuple<Args..., void, void>>::type,
239 Response&>::value,
240 int>::type = 0)
241 {
242 handler = ReqHandlerWrapper<Args...>(std::move(f));
243 /*handler = (
244 [f = std::move(f)]
245 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700246 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 res.end();
248 });*/
249 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700250
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 template <typename... Args>
252 void set(
253 Func f,
254 typename std::enable_if<
255 std::is_same<
256 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
257 const Request&>::value &&
258 std::is_same<typename std::tuple_element<
259 1, std::tuple<Args..., void, void>>::type,
260 Response&>::value,
261 int>::type = 0)
262 {
263 handler = std::move(f);
264 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700265
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 template <typename... Args> struct HandlerTypeHelper
267 {
268 using type =
269 std::function<void(const crow::Request&, crow::Response&, Args...)>;
270 using args_type =
271 black_magic::S<typename black_magic::promote_t<Args>...>;
272 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 template <typename... Args>
275 struct HandlerTypeHelper<const Request&, Args...>
276 {
277 using type =
278 std::function<void(const crow::Request&, crow::Response&, Args...)>;
279 using args_type =
280 black_magic::S<typename black_magic::promote_t<Args>...>;
281 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700282
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 template <typename... Args>
284 struct HandlerTypeHelper<const Request&, Response&, Args...>
285 {
286 using type =
287 std::function<void(const crow::Request&, crow::Response&, Args...)>;
288 using args_type =
289 black_magic::S<typename black_magic::promote_t<Args>...>;
290 };
291
292 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
293
294 void operator()(const Request& req, Response& res,
295 const RoutingParams& params)
296 {
297 detail::routing_handler_call_helper::Call<
298 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
299 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
300 black_magic::S<>>()(
301 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
302 handler, params, req, res});
303 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700304};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305} // namespace routing_handler_call_helper
306} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700307
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308class WebSocketRule : public BaseRule
309{
310 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700311
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 public:
313 WebSocketRule(std::string rule) : BaseRule(std::move(rule))
314 {
315 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700316
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 void validate() override
318 {
319 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700320
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 void handle(const Request&, Response& res, const RoutingParams&) override
322 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700323 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 res.end();
325 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700326
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200327 void handleUpgrade(const Request& req, Response& res,
Ed Tanousceac6f72018-12-02 11:58:47 -0800328 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530330 std::shared_ptr<
331 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
332 myConnection = std::make_shared<
333 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200334 req, res, std::move(adaptor), openHandler, messageHandler,
Ratan Gupta02453b12019-10-22 14:43:36 +0530335 closeHandler, errorHandler);
336 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 }
338#ifdef BMCWEB_ENABLE_SSL
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200339 void handleUpgrade(const Request& req, Response& res,
Ed Tanousceac6f72018-12-02 11:58:47 -0800340 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
341 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800343 std::shared_ptr<crow::websocket::ConnectionImpl<
344 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
345 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
346 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200347 req, res, std::move(adaptor), openHandler, messageHandler,
Ed Tanousceac6f72018-12-02 11:58:47 -0800348 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 myConnection->start();
350 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700351#endif
352
Ed Tanous1abe55e2018-09-05 08:30:59 -0700353 template <typename Func> self_t& onopen(Func f)
354 {
355 openHandler = f;
356 return *this;
357 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700358
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 template <typename Func> self_t& onmessage(Func f)
360 {
361 messageHandler = f;
362 return *this;
363 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700364
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 template <typename Func> self_t& onclose(Func f)
366 {
367 closeHandler = f;
368 return *this;
369 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700370
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 template <typename Func> self_t& onerror(Func f)
372 {
373 errorHandler = f;
374 return *this;
375 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700376
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 protected:
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +0200378 std::function<void(crow::websocket::Connection&,
379 std::shared_ptr<bmcweb::AsyncResp>)>
380 openHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700381 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
382 messageHandler;
383 std::function<void(crow::websocket::Connection&, const std::string&)>
384 closeHandler;
385 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700386};
387
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388template <typename T> struct RuleParameterTraits
389{
390 using self_t = T;
391 WebSocketRule& websocket()
392 {
Ed Tanous271584a2019-07-09 16:24:22 -0700393 self_t* self = static_cast<self_t*>(this);
394 WebSocketRule* p = new WebSocketRule(self->rule);
395 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700396 return *p;
397 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700398
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 self_t& name(std::string name) noexcept
400 {
Ed Tanous271584a2019-07-09 16:24:22 -0700401 self_t* self = static_cast<self_t*>(this);
402 self->nameStr = std::move(name);
403 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700405
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 self_t& methods(boost::beast::http::verb method)
407 {
Ed Tanous271584a2019-07-09 16:24:22 -0700408 self_t* self = static_cast<self_t*>(this);
409 self->methodsBitfield = 1U << static_cast<size_t>(method);
410 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700412
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 template <typename... MethodArgs>
414 self_t& methods(boost::beast::http::verb method, MethodArgs... args_method)
415 {
Ed Tanous271584a2019-07-09 16:24:22 -0700416 self_t* self = static_cast<self_t*>(this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 methods(args_method...);
Ed Tanous271584a2019-07-09 16:24:22 -0700418 self->methodsBitfield |= 1U << static_cast<size_t>(method);
419 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 }
Tanousf00032d2018-11-05 01:18:10 -0300421
422 template <typename... MethodArgs>
423 self_t& requires(std::initializer_list<const char*> l)
424 {
Ed Tanous271584a2019-07-09 16:24:22 -0700425 self_t* self = static_cast<self_t*>(this);
426 self->privilegesSet.emplace_back(l);
427 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300428 }
429
430 template <typename... MethodArgs>
431 self_t& requires(const std::vector<redfish::Privileges>& p)
432 {
Ed Tanous271584a2019-07-09 16:24:22 -0700433 self_t* self = static_cast<self_t*>(this);
Tanousf00032d2018-11-05 01:18:10 -0300434 for (const redfish::Privileges& privilege : p)
435 {
Ed Tanous271584a2019-07-09 16:24:22 -0700436 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300437 }
Ed Tanous271584a2019-07-09 16:24:22 -0700438 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300439 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700440};
441
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
443{
444 public:
445 DynamicRule(std::string rule) : BaseRule(std::move(rule))
446 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700447 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700448
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 void validate() override
450 {
451 if (!erasedHandler)
452 {
453 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
454 "no handler for url " + rule);
455 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700456 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700457
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 void handle(const Request& req, Response& res,
459 const RoutingParams& params) override
460 {
461 erasedHandler(req, res, params);
462 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700463
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 template <typename Func> void operator()(Func f)
465 {
466 using function_t = utility::function_traits<Func>;
467
468 erasedHandler =
469 wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
470 }
471
472 // enable_if Arg1 == request && Arg2 == Response
473 // enable_if Arg1 == request && Arg2 != resposne
474 // enable_if Arg1 != request
475
476 template <typename Func, unsigned... Indices>
477
478 std::function<void(const Request&, Response&, const RoutingParams&)>
479 wrap(Func f, black_magic::Seq<Indices...>)
480 {
481 using function_t = utility::function_traits<Func>;
482
483 if (!black_magic::isParameterTagCompatible(
484 black_magic::getParameterTagRuntime(rule.c_str()),
485 black_magic::compute_parameter_tag_from_args_list<
486 typename function_t::template arg<Indices>...>::value))
487 {
488 throw std::runtime_error("routeDynamic: Handler type is mismatched "
489 "with URL parameters: " +
490 rule);
491 }
492 auto ret = detail::routing_handler_call_helper::Wrapped<
493 Func, typename function_t::template arg<Indices>...>();
494 ret.template set<typename function_t::template arg<Indices>...>(
495 std::move(f));
496 return ret;
497 }
498
499 template <typename Func> void operator()(std::string name, Func&& f)
500 {
501 nameStr = std::move(name);
502 (*this).template operator()<Func>(std::forward(f));
503 }
504
505 private:
506 std::function<void(const Request&, Response&, const RoutingParams&)>
507 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700508};
509
510template <typename... Args>
511class TaggedRule : public BaseRule,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700512 public RuleParameterTraits<TaggedRule<Args...>>
513{
514 public:
515 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700516
Ed Tanous271584a2019-07-09 16:24:22 -0700517 TaggedRule(std::string ruleIn) : BaseRule(std::move(ruleIn))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700519 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700520
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 void validate() override
522 {
523 if (!handler)
524 {
525 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
526 "no handler for url " + rule);
527 }
528 }
529
530 template <typename Func>
531 typename std::enable_if<
532 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
533 void>::type
534 operator()(Func&& f)
535 {
536 static_assert(
537 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
538 black_magic::CallHelper<
539 Func, black_magic::S<crow::Request, Args...>>::value,
540 "Handler type is mismatched with URL parameters");
541 static_assert(
542 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
543 "Handler function cannot have void return type; valid return "
544 "types: "
545 "string, int, crow::resposne, nlohmann::json");
546
547 handler = [f = std::move(f)](const Request&, Response& res,
548 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700549 res.result(f(args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 res.end();
551 };
552 }
553
554 template <typename Func>
555 typename std::enable_if<
556 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700557 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700558 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 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<crow::Request>(),
569 std::declval<Args>()...))>::value,
570 "Handler function cannot have void return type; valid return "
571 "types: "
572 "string, int, crow::resposne,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700573
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 handler = [f = std::move(f)](const crow::Request& req,
575 crow::Response& res, Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700576 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 res.end();
578 };
579 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700580
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 template <typename Func>
582 typename std::enable_if<
583 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
584 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700585 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 void>::type
587 operator()(Func&& f)
588 {
589 static_assert(
590 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
591 black_magic::CallHelper<
592 Func, black_magic::S<crow::Request, Args...>>::value ||
593 black_magic::CallHelper<
594 Func, black_magic::S<crow::Request, crow::Response&,
595 Args...>>::value,
596 "Handler type is mismatched with URL parameters");
597 static_assert(
598 std::is_same<void, decltype(f(std::declval<crow::Request>(),
599 std::declval<crow::Response&>(),
600 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300601 "Handler function with response argument should have void "
602 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700604
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 handler = std::move(f);
606 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700607
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 template <typename Func> void operator()(std::string name, Func&& f)
609 {
610 nameStr = std::move(name);
611 (*this).template operator()<Func>(std::forward(f));
612 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700613
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 void handle(const Request& req, Response& res,
615 const RoutingParams& params) override
616 {
617 detail::routing_handler_call_helper::Call<
618 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
619 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
620 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
621 handler, params, req, res});
622 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700623
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 private:
625 std::function<void(const crow::Request&, crow::Response&, Args...)> handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700626};
627
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700628const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700629
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630class Trie
631{
632 public:
633 struct Node
634 {
635 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700636 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
637 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700639
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 bool isSimpleNode() const
641 {
642 return !ruleIndex && std::all_of(std::begin(paramChildrens),
643 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700644 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700645 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700646 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700647
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 Trie() : nodes(1)
649 {
650 }
651
652 private:
653 void optimizeNode(Node* node)
654 {
Ed Tanous271584a2019-07-09 16:24:22 -0700655 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
657 if (!x)
658 continue;
659 Node* child = &nodes[x];
660 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700661 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 if (node->children.empty())
663 return;
664 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300665 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 {
667 Node* child = &nodes[kv.second];
668 if (!child->isSimpleNode())
669 {
670 mergeWithChild = false;
671 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700672 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 }
674 if (mergeWithChild)
675 {
676 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300677 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 {
679 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300680 for (const std::pair<std::string, unsigned>& childKv :
681 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 {
683 merged[kv.first + childKv.first] = childKv.second;
684 }
685 }
686 node->children = std::move(merged);
687 optimizeNode(node);
688 }
689 else
690 {
Tanousf00032d2018-11-05 01:18:10 -0300691 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 {
693 Node* child = &nodes[kv.second];
694 optimizeNode(child);
695 }
696 }
697 }
698
699 void optimize()
700 {
701 optimizeNode(head());
702 }
703
704 public:
705 void validate()
706 {
707 if (!head()->isSimpleNode())
708 throw std::runtime_error(
709 "Internal error: Trie header should be simple!");
710 optimize();
711 }
712
713 void findRouteIndexes(const std::string& req_url,
714 std::vector<unsigned>& route_indexes,
Tanousf00032d2018-11-05 01:18:10 -0300715 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 {
717 if (node == nullptr)
718 {
719 node = head();
720 }
Tanousf00032d2018-11-05 01:18:10 -0300721 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 {
723 const std::string& fragment = kv.first;
724 const Node* child = &nodes[kv.second];
725 if (pos >= req_url.size())
726 {
727 if (child->ruleIndex != 0 && fragment != "/")
728 {
729 route_indexes.push_back(child->ruleIndex);
730 }
731 findRouteIndexes(req_url, route_indexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700732 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 }
734 else
735 {
736 if (req_url.compare(pos, fragment.size(), fragment) == 0)
737 {
Ed Tanous271584a2019-07-09 16:24:22 -0700738 findRouteIndexes(
739 req_url, route_indexes, child,
740 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 }
742 }
743 }
744 }
745
746 std::pair<unsigned, RoutingParams>
Ed Tanous39e77502019-03-04 17:35:53 -0800747 find(const std::string_view req_url, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700748 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 {
750 RoutingParams empty;
751 if (params == nullptr)
752 params = &empty;
753
754 unsigned found{};
755 RoutingParams matchParams;
756
757 if (node == nullptr)
758 node = head();
759 if (pos == req_url.size())
760 return {node->ruleIndex, *params};
761
762 auto updateFound =
763 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
764 if (ret.first && (!found || found > ret.first))
765 {
766 found = ret.first;
767 matchParams = std::move(ret.second);
768 }
769 };
770
Ed Tanous271584a2019-07-09 16:24:22 -0700771 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 {
773 char c = req_url[pos];
774 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
775 {
776 char* eptr;
777 errno = 0;
778 long long int value =
779 std::strtoll(req_url.data() + pos, &eptr, 10);
780 if (errno != ERANGE && eptr != req_url.data() + pos)
781 {
782 params->intParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700783 std::pair<unsigned, RoutingParams> ret = find(
784 req_url,
785 &nodes[node->paramChildrens[static_cast<size_t>(
786 ParamType::INT)]],
787 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 updateFound(ret);
789 params->intParams.pop_back();
790 }
791 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700792 }
793
Ed Tanous271584a2019-07-09 16:24:22 -0700794 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 {
796 char c = req_url[pos];
797 if ((c >= '0' && c <= '9') || c == '+')
798 {
799 char* eptr;
800 errno = 0;
801 unsigned long long int value =
802 std::strtoull(req_url.data() + pos, &eptr, 10);
803 if (errno != ERANGE && eptr != req_url.data() + pos)
804 {
805 params->uintParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700806 std::pair<unsigned, RoutingParams> ret = find(
807 req_url,
808 &nodes[node->paramChildrens[static_cast<size_t>(
809 ParamType::UINT)]],
810 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 updateFound(ret);
812 params->uintParams.pop_back();
813 }
814 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700815 }
816
Ed Tanous271584a2019-07-09 16:24:22 -0700817 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818 {
819 char c = req_url[pos];
820 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
821 {
822 char* eptr;
823 errno = 0;
824 double value = std::strtod(req_url.data() + pos, &eptr);
825 if (errno != ERANGE && eptr != req_url.data() + pos)
826 {
827 params->doubleParams.push_back(value);
Tanousf00032d2018-11-05 01:18:10 -0300828 std::pair<unsigned, RoutingParams> ret = find(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700830 &nodes[node->paramChildrens[static_cast<size_t>(
831 ParamType::DOUBLE)]],
832 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 updateFound(ret);
834 params->doubleParams.pop_back();
835 }
836 }
837 }
838
Ed Tanous271584a2019-07-09 16:24:22 -0700839 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700840 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000841 size_t epos = pos;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 for (; epos < req_url.size(); epos++)
843 {
844 if (req_url[epos] == '/')
845 break;
846 }
847
848 if (epos != pos)
849 {
850 params->stringParams.emplace_back(
851 req_url.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300852 std::pair<unsigned, RoutingParams> ret =
Ed Tanousb01bf292019-03-25 19:25:26 +0000853 find(req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700854 &nodes[node->paramChildrens[static_cast<size_t>(
855 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000856 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 updateFound(ret);
858 params->stringParams.pop_back();
859 }
860 }
861
Ed Tanous271584a2019-07-09 16:24:22 -0700862 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000864 size_t epos = req_url.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865
866 if (epos != pos)
867 {
868 params->stringParams.emplace_back(
869 req_url.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700870 std::pair<unsigned, RoutingParams> ret =
871 find(req_url,
872 &nodes[node->paramChildrens[static_cast<size_t>(
873 ParamType::PATH)]],
874 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 updateFound(ret);
876 params->stringParams.pop_back();
877 }
878 }
879
Tanousf00032d2018-11-05 01:18:10 -0300880 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 {
882 const std::string& fragment = kv.first;
883 const Node* child = &nodes[kv.second];
884
885 if (req_url.compare(pos, fragment.size(), fragment) == 0)
886 {
Tanousf00032d2018-11-05 01:18:10 -0300887 std::pair<unsigned, RoutingParams> ret =
888 find(req_url, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700889 updateFound(ret);
890 }
891 }
892
893 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700894 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895
896 void add(const std::string& url, unsigned ruleIndex)
897 {
Ed Tanous271584a2019-07-09 16:24:22 -0700898 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899
900 for (unsigned i = 0; i < url.size(); i++)
901 {
902 char c = url[i];
903 if (c == '<')
904 {
Tanousf00032d2018-11-05 01:18:10 -0300905 const static std::array<std::pair<ParamType, std::string>, 7>
906 paramTraits = {{
907 {ParamType::INT, "<int>"},
908 {ParamType::UINT, "<uint>"},
909 {ParamType::DOUBLE, "<float>"},
910 {ParamType::DOUBLE, "<double>"},
911 {ParamType::STRING, "<str>"},
912 {ParamType::STRING, "<string>"},
913 {ParamType::PATH, "<path>"},
914 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915
Tanousf00032d2018-11-05 01:18:10 -0300916 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 {
Tanousf00032d2018-11-05 01:18:10 -0300918 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 {
Ed Tanous271584a2019-07-09 16:24:22 -0700920 size_t index = static_cast<size_t>(x.first);
921 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700922 {
Tanousf00032d2018-11-05 01:18:10 -0300923 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700924 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 }
Ed Tanous271584a2019-07-09 16:24:22 -0700926 idx = nodes[idx].paramChildrens[index];
927 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 break;
929 }
930 }
931
932 i--;
933 }
934 else
935 {
936 std::string piece(&c, 1);
937 if (!nodes[idx].children.count(piece))
938 {
Tanousf00032d2018-11-05 01:18:10 -0300939 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940 nodes[idx].children.emplace(piece, newNodeIdx);
941 }
942 idx = nodes[idx].children[piece];
943 }
944 }
945 if (nodes[idx].ruleIndex)
946 throw std::runtime_error("handler already exists for " + url);
947 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700948 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700949
Ed Tanous1abe55e2018-09-05 08:30:59 -0700950 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700951 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 {
Ed Tanous271584a2019-07-09 16:24:22 -0700953 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954 {
955 if (n->paramChildrens[i])
956 {
957 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700958 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
959 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700960 {
961 case ParamType::INT:
962 BMCWEB_LOG_DEBUG << "<int>";
963 break;
964 case ParamType::UINT:
965 BMCWEB_LOG_DEBUG << "<uint>";
966 break;
967 case ParamType::DOUBLE:
968 BMCWEB_LOG_DEBUG << "<float>";
969 break;
970 case ParamType::STRING:
971 BMCWEB_LOG_DEBUG << "<str>";
972 break;
973 case ParamType::PATH:
974 BMCWEB_LOG_DEBUG << "<path>";
975 break;
976 default:
977 BMCWEB_LOG_DEBUG << "<ERROR>";
978 break;
979 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700980
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
982 }
983 }
Tanousf00032d2018-11-05 01:18:10 -0300984 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 {
986 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -0700987 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -0700988 << kv.first;
989 debugNodePrint(&nodes[kv.second], level + 1);
990 }
991 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700992
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 public:
994 void debugPrint()
995 {
Ed Tanous271584a2019-07-09 16:24:22 -0700996 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700998
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 private:
1000 const Node* head() const
1001 {
1002 return &nodes.front();
1003 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001004
Ed Tanous1abe55e2018-09-05 08:30:59 -07001005 Node* head()
1006 {
1007 return &nodes.front();
1008 }
1009
1010 unsigned newNode()
1011 {
1012 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001013 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 }
1015
1016 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001017};
1018
Ed Tanous1abe55e2018-09-05 08:30:59 -07001019class Router
1020{
1021 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001022 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001023
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 DynamicRule& newRuleDynamic(const std::string& rule)
1025 {
1026 std::unique_ptr<DynamicRule> ruleObject =
1027 std::make_unique<DynamicRule>(rule);
1028 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001029 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001030
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001032 }
1033
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 template <uint64_t N>
1035 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1036 newRuleTagged(const std::string& rule)
1037 {
1038 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1039 TaggedRule>;
1040 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1041 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001042 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001043
1044 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001045 }
1046
Tanousf00032d2018-11-05 01:18:10 -03001047 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001048 {
Tanousf00032d2018-11-05 01:18:10 -03001049 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001050 {
Tanousf00032d2018-11-05 01:18:10 -03001051 return;
1052 }
1053 for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
1054 method++, method_bit <<= 1)
1055 {
1056 if (ruleObject->methodsBitfield & method_bit)
1057 {
1058 perMethods[method].rules.emplace_back(ruleObject);
1059 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001060 rule, static_cast<unsigned>(
1061 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001062 // directory case:
1063 // request to `/about' url matches `/about/' rule
1064 if (rule.size() > 2 && rule.back() == '/')
1065 {
1066 perMethods[method].trie.add(
1067 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001068 static_cast<unsigned>(perMethods[method].rules.size() -
1069 1));
Tanousf00032d2018-11-05 01:18:10 -03001070 }
1071 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001073 }
1074
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 void validate()
1076 {
Tanousf00032d2018-11-05 01:18:10 -03001077 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 {
1079 if (rule)
1080 {
Tanousf00032d2018-11-05 01:18:10 -03001081 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001082 if (upgraded)
1083 rule = std::move(upgraded);
1084 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001085 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001086 }
1087 }
Tanousf00032d2018-11-05 01:18:10 -03001088 for (PerMethod& perMethod : perMethods)
1089 {
1090 perMethod.trie.validate();
1091 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001092 }
1093
Ed Tanous1abe55e2018-09-05 08:30:59 -07001094 template <typename Adaptor>
1095 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1096 {
Ed Tanous271584a2019-07-09 16:24:22 -07001097 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001098 return;
1099
Ed Tanous271584a2019-07-09 16:24:22 -07001100 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001101 Trie& trie = perMethod.trie;
1102 std::vector<BaseRule*>& rules = perMethod.rules;
1103
1104 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001105 unsigned ruleIndex = found.first;
1106 if (!ruleIndex)
1107 {
1108 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001109 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001110 res.end();
1111 return;
1112 }
1113
1114 if (ruleIndex >= rules.size())
1115 throw std::runtime_error("Trie internal structure corrupted!");
1116
1117 if (ruleIndex == ruleSpecialRedirectSlash)
1118 {
1119 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1120 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001121 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001122
1123 // TODO absolute url building
1124 if (req.getHeaderValue("Host").empty())
1125 {
1126 res.addHeader("Location", std::string(req.url) + "/");
1127 }
1128 else
1129 {
1130 res.addHeader(
1131 "Location",
1132 req.isSecure
1133 ? "https://"
1134 : "http://" + std::string(req.getHeaderValue("Host")) +
1135 std::string(req.url) + "/");
1136 }
1137 res.end();
1138 return;
1139 }
1140
Ed Tanous271584a2019-07-09 16:24:22 -07001141 if ((rules[ruleIndex]->getMethods() &
1142 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001143 {
1144 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1145 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001146 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001147 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001148 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001149 res.end();
1150 return;
1151 }
1152
1153 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001154 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001155 << rules[ruleIndex]->getMethods();
1156
1157 // any uncaught exceptions become 500s
1158 try
1159 {
1160 rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor));
1161 }
1162 catch (std::exception& e)
1163 {
1164 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001165 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001166 res.end();
1167 return;
1168 }
1169 catch (...)
1170 {
1171 BMCWEB_LOG_ERROR
1172 << "An uncaught exception occurred. The type was unknown "
1173 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001174 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001175 res.end();
1176 return;
1177 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001178 }
1179
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 void handle(const Request& req, Response& res)
1181 {
Ed Tanous271584a2019-07-09 16:24:22 -07001182 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001183 return;
Ed Tanous271584a2019-07-09 16:24:22 -07001184 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001185 Trie& trie = perMethod.trie;
1186 std::vector<BaseRule*>& rules = perMethod.rules;
1187
1188 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001189
Ed Tanous1abe55e2018-09-05 08:30:59 -07001190 unsigned ruleIndex = found.first;
1191
1192 if (!ruleIndex)
1193 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001194 // Check to see if this url exists at any verb
1195 for (const PerMethod& p : perMethods)
1196 {
1197 const std::pair<unsigned, RoutingParams>& found =
1198 p.trie.find(req.url);
1199 if (found.first > 0)
1200 {
1201 res.result(boost::beast::http::status::method_not_allowed);
1202 res.end();
1203 return;
1204 }
1205 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001206 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
1207 res.result(boost::beast::http::status::not_found);
1208 res.end();
1209 return;
1210 }
1211
1212 if (ruleIndex >= rules.size())
1213 throw std::runtime_error("Trie internal structure corrupted!");
1214
1215 if (ruleIndex == ruleSpecialRedirectSlash)
1216 {
1217 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1218 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001219 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001220
1221 // TODO absolute url building
1222 if (req.getHeaderValue("Host").empty())
1223 {
1224 res.addHeader("Location", std::string(req.url) + "/");
1225 }
1226 else
1227 {
1228 res.addHeader("Location",
1229 (req.isSecure ? "https://" : "http://") +
1230 std::string(req.getHeaderValue("Host")) +
1231 std::string(req.url) + "/");
1232 }
1233 res.end();
1234 return;
1235 }
1236
Ed Tanous271584a2019-07-09 16:24:22 -07001237 if ((rules[ruleIndex]->getMethods() &
1238 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001239 {
1240 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1241 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001242 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001243 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001244 res.result(boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001245 res.end();
1246 return;
1247 }
1248
1249 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001250 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001251 << rules[ruleIndex]->getMethods();
1252
RAJESWARAN THILLAIGOVINDAN5e931ae2019-07-15 07:51:33 -05001253 if (req.session == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001254 {
1255 rules[ruleIndex]->handle(req, res, found.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 return;
1257 }
RAJESWARAN THILLAIGOVINDAN5e931ae2019-07-15 07:51:33 -05001258
1259 crow::connections::systemBus->async_method_call(
1260 [&req, &res, &rules, ruleIndex, found](
1261 const boost::system::error_code ec,
1262 std::map<std::string, std::variant<bool, std::string,
1263 std::vector<std::string>>>
1264 userInfo) {
1265 if (ec)
1266 {
1267 BMCWEB_LOG_ERROR << "GetUserInfo failed...";
1268 res.result(
1269 boost::beast::http::status::internal_server_error);
1270 res.end();
1271 return;
1272 }
1273
1274 const std::string* userRolePtr = nullptr;
1275 auto userInfoIter = userInfo.find("UserPrivilege");
1276 if (userInfoIter != userInfo.end())
1277 {
1278 userRolePtr =
1279 std::get_if<std::string>(&userInfoIter->second);
1280 }
1281
1282 std::string userRole{};
1283 if (userRolePtr != nullptr)
1284 {
1285 userRole = *userRolePtr;
1286 BMCWEB_LOG_DEBUG << "userName = " << req.session->username
1287 << " userRole = " << *userRolePtr;
1288 }
1289
1290 // Get the user privileges from the role
1291 redfish::Privileges userPrivileges =
1292 redfish::getUserPrivileges(userRole);
1293
1294 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1295 {
1296 res.result(boost::beast::http::status::forbidden);
1297 res.end();
1298 return;
1299 }
1300
1301 rules[ruleIndex]->handle(req, res, found.second);
1302 },
1303 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1304 "xyz.openbmc_project.User.Manager", "GetUserInfo",
1305 req.session->username);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001306 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001307
Ed Tanous1abe55e2018-09-05 08:30:59 -07001308 void debugPrint()
1309 {
Ed Tanous271584a2019-07-09 16:24:22 -07001310 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001311 {
Ed Tanous271584a2019-07-09 16:24:22 -07001312 BMCWEB_LOG_DEBUG
1313 << methodName(static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001314 perMethods[i].trie.debugPrint();
1315 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001316 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001317
Ed Tanous1abe55e2018-09-05 08:30:59 -07001318 std::vector<const std::string*> getRoutes(const std::string& parent)
1319 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001320 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001321
1322 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001323 {
Tanousf00032d2018-11-05 01:18:10 -03001324 std::vector<unsigned> x;
1325 pm.trie.findRouteIndexes(parent, x);
1326 for (unsigned index : x)
1327 {
1328 ret.push_back(&pm.rules[index]->rule);
1329 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001330 }
1331 return ret;
1332 }
1333
1334 private:
Tanousf00032d2018-11-05 01:18:10 -03001335 struct PerMethod
1336 {
1337 std::vector<BaseRule*> rules;
1338 Trie trie;
1339 // rule index 0, 1 has special meaning; preallocate it to avoid
1340 // duplication.
1341 PerMethod() : rules(2)
1342 {
1343 }
1344 };
1345 std::array<PerMethod, maxHttpVerbCount> perMethods;
1346 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001347};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348} // namespace crow