blob: 200cfa09fb61683503ae79cd5bab9acb1e08b232 [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
Tanousf00032d2018-11-05 01:18:10 -03006#include <boost/container/flat_map.hpp>
7#include <boost/container/small_vector.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07008#include <boost/lexical_cast.hpp>
Ed Tanouse0d918b2018-03-27 17:41:04 -07009#include <cerrno>
Ed Tanous7045c8d2017-04-03 10:04:37 -070010#include <cstdint>
Ed Tanouse0d918b2018-03-27 17:41:04 -070011#include <cstdlib>
Ed Tanous3dac7492017-08-02 13:46:20 -070012#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070013#include <memory>
14#include <tuple>
Ed Tanous7045c8d2017-04-03 10:04:37 -070015#include <utility>
16#include <vector>
Ed Tanous9140a672017-04-24 17:01:32 -070017
Ed Tanousc94ad492019-10-10 15:39:33 -070018#include "common.h"
19#include "http_request.h"
20#include "http_response.h"
21#include "logging.h"
22#include "utility.h"
23#include "websocket.h"
Ed Tanous7045c8d2017-04-03 10:04:37 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025namespace crow
26{
Tanousf00032d2018-11-05 01:18:10 -030027
28constexpr int maxHttpVerbCount =
29 static_cast<int>(boost::beast::http::verb::unlink);
30
Ed Tanous1abe55e2018-09-05 08:30:59 -070031class BaseRule
32{
33 public:
Tanousf00032d2018-11-05 01:18:10 -030034 BaseRule(std::string thisRule) : rule(std::move(thisRule))
Ed Tanous1abe55e2018-09-05 08:30:59 -070035 {
36 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070037
Ed Tanous0c0084a2019-10-24 15:57:51 -070038 virtual ~BaseRule() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanous1abe55e2018-09-05 08:30:59 -070040 virtual void validate() = 0;
41 std::unique_ptr<BaseRule> upgrade()
42 {
43 if (ruleToUpgrade)
44 return std::move(ruleToUpgrade);
45 return {};
46 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070047
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 virtual void handle(const Request&, Response&, const RoutingParams&) = 0;
Ed Tanousceac6f72018-12-02 11:58:47 -080049 virtual void handleUpgrade(const Request&, Response& res,
50 boost::asio::ip::tcp::socket&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070052 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 res.end();
54 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070055#ifdef BMCWEB_ENABLE_SSL
Ed Tanousceac6f72018-12-02 11:58:47 -080056 virtual void
57 handleUpgrade(const Request&, Response& res,
58 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070060 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 res.end();
62 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070063#endif
64
Ed Tanous271584a2019-07-09 16:24:22 -070065 size_t getMethods()
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 {
67 return methodsBitfield;
68 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070069
Tanousf00032d2018-11-05 01:18:10 -030070 bool checkPrivileges(const redfish::Privileges& userPrivileges)
71 {
72 // If there are no privileges assigned, assume no privileges
73 // required
74 if (privilegesSet.empty())
75 {
76 return true;
77 }
78
79 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
80 {
81 if (userPrivileges.isSupersetOf(requiredPrivileges))
82 {
83 return true;
84 }
85 }
86 return false;
87 }
88
Ed Tanous271584a2019-07-09 16:24:22 -070089 size_t methodsBitfield{
90 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070091
Tanousf00032d2018-11-05 01:18:10 -030092 std::vector<redfish::Privileges> privilegesSet;
93
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 std::string rule;
95 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -070096
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -070098
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 friend class Router;
100 template <typename T> friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700101};
102
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103namespace detail
104{
105namespace routing_handler_call_helper
106{
107template <typename T, int Pos> struct CallPair
108{
109 using type = T;
110 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700111};
112
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113template <typename H1> struct CallParams
114{
115 H1& handler;
116 const RoutingParams& params;
117 const Request& req;
118 Response& res;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700119};
120
121template <typename F, int NInt, int NUint, int NDouble, int NString,
122 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123struct Call
124{
125};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126
127template <typename F, int NInt, int NUint, int NDouble, int NString,
128 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700129struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 black_magic::S<Args2...>>
131{
132 void operator()(F cparams)
133 {
134 using pushed = typename black_magic::S<Args2...>::template push_back<
135 CallPair<int64_t, NInt>>;
136 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
137 pushed>()(cparams);
138 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700139};
140
141template <typename F, int NInt, int NUint, int NDouble, int NString,
142 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700143struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
145{
146 void operator()(F cparams)
147 {
148 using pushed = typename black_magic::S<Args2...>::template push_back<
149 CallPair<uint64_t, NUint>>;
150 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
151 pushed>()(cparams);
152 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700153};
154
155template <typename F, int NInt, int NUint, int NDouble, int NString,
156 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700157struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 black_magic::S<Args2...>>
159{
160 void operator()(F cparams)
161 {
162 using pushed = typename black_magic::S<Args2...>::template push_back<
163 CallPair<double, NDouble>>;
164 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
165 pushed>()(cparams);
166 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700167};
168
169template <typename F, int NInt, int NUint, int NDouble, int NString,
170 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700171struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
173{
174 void operator()(F cparams)
175 {
176 using pushed = typename black_magic::S<Args2...>::template push_back<
177 CallPair<std::string, NString>>;
178 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
179 pushed>()(cparams);
180 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700181};
182
183template <typename F, int NInt, int NUint, int NDouble, int NString,
184 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700185struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700186 black_magic::S<Args1...>>
187{
188 void operator()(F cparams)
189 {
190 cparams.handler(
191 cparams.req, cparams.res,
192 cparams.params.template get<typename Args1::type>(Args1::pos)...);
193 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700194};
195
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196template <typename Func, typename... ArgsWrapped> struct Wrapped
197{
198 template <typename... Args>
199 void set(
200 Func f,
201 typename std::enable_if<
202 !std::is_same<
203 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
204 const Request&>::value,
205 int>::type = 0)
206 {
Tanousf00032d2018-11-05 01:18:10 -0300207 handler = [f = std::move(f)](const Request&, Response& res,
208 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700209 res.result(f(args...));
Tanousf00032d2018-11-05 01:18:10 -0300210 res.end();
211 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700212 }
213
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214 template <typename Req, typename... Args> struct ReqHandlerWrapper
215 {
216 ReqHandlerWrapper(Func f) : f(std::move(f))
217 {
218 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700219
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220 void operator()(const Request& req, Response& res, Args... args)
221 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700222 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 res.end();
224 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700225
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 Func f;
227 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700228
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 template <typename... Args>
230 void set(
231 Func f,
232 typename std::enable_if<
233 std::is_same<
234 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
235 const Request&>::value &&
236 !std::is_same<typename std::tuple_element<
237 1, std::tuple<Args..., void, void>>::type,
238 Response&>::value,
239 int>::type = 0)
240 {
241 handler = ReqHandlerWrapper<Args...>(std::move(f));
242 /*handler = (
243 [f = std::move(f)]
244 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700245 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 res.end();
247 });*/
248 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700249
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 template <typename... Args>
251 void set(
252 Func f,
253 typename std::enable_if<
254 std::is_same<
255 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
256 const Request&>::value &&
257 std::is_same<typename std::tuple_element<
258 1, std::tuple<Args..., void, void>>::type,
259 Response&>::value,
260 int>::type = 0)
261 {
262 handler = std::move(f);
263 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700264
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 template <typename... Args> struct HandlerTypeHelper
266 {
267 using type =
268 std::function<void(const crow::Request&, crow::Response&, Args...)>;
269 using args_type =
270 black_magic::S<typename black_magic::promote_t<Args>...>;
271 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700272
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 template <typename... Args>
274 struct HandlerTypeHelper<const Request&, Args...>
275 {
276 using type =
277 std::function<void(const crow::Request&, crow::Response&, Args...)>;
278 using args_type =
279 black_magic::S<typename black_magic::promote_t<Args>...>;
280 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700281
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 template <typename... Args>
283 struct HandlerTypeHelper<const Request&, Response&, Args...>
284 {
285 using type =
286 std::function<void(const crow::Request&, crow::Response&, Args...)>;
287 using args_type =
288 black_magic::S<typename black_magic::promote_t<Args>...>;
289 };
290
291 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
292
293 void operator()(const Request& req, Response& res,
294 const RoutingParams& params)
295 {
296 detail::routing_handler_call_helper::Call<
297 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
298 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
299 black_magic::S<>>()(
300 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
301 handler, params, req, res});
302 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700303};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304} // namespace routing_handler_call_helper
305} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700306
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307class WebSocketRule : public BaseRule
308{
309 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700310
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 public:
312 WebSocketRule(std::string rule) : BaseRule(std::move(rule))
313 {
314 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700315
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 void validate() override
317 {
318 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700319
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320 void handle(const Request&, Response& res, const RoutingParams&) override
321 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700322 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323 res.end();
324 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700325
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800327 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530329 std::shared_ptr<
330 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
331 myConnection = std::make_shared<
332 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
333 req, std::move(adaptor), openHandler, messageHandler,
334 closeHandler, errorHandler);
335 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336 }
337#ifdef BMCWEB_ENABLE_SSL
338 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800339 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
340 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800342 std::shared_ptr<crow::websocket::ConnectionImpl<
343 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
344 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
345 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
346 req, std::move(adaptor), openHandler, messageHandler,
347 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 myConnection->start();
349 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700350#endif
351
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 template <typename Func> self_t& onopen(Func f)
353 {
354 openHandler = f;
355 return *this;
356 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700357
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 template <typename Func> self_t& onmessage(Func f)
359 {
360 messageHandler = f;
361 return *this;
362 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700363
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 template <typename Func> self_t& onclose(Func f)
365 {
366 closeHandler = f;
367 return *this;
368 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700369
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 template <typename Func> self_t& onerror(Func f)
371 {
372 errorHandler = f;
373 return *this;
374 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700375
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376 protected:
377 std::function<void(crow::websocket::Connection&)> openHandler;
378 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
379 messageHandler;
380 std::function<void(crow::websocket::Connection&, const std::string&)>
381 closeHandler;
382 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700383};
384
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385template <typename T> struct RuleParameterTraits
386{
387 using self_t = T;
388 WebSocketRule& websocket()
389 {
Ed Tanous271584a2019-07-09 16:24:22 -0700390 self_t* self = static_cast<self_t*>(this);
391 WebSocketRule* p = new WebSocketRule(self->rule);
392 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 return *p;
394 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700395
Ed Tanous1abe55e2018-09-05 08:30:59 -0700396 self_t& name(std::string name) noexcept
397 {
Ed Tanous271584a2019-07-09 16:24:22 -0700398 self_t* self = static_cast<self_t*>(this);
399 self->nameStr = std::move(name);
400 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700401 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700402
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 self_t& methods(boost::beast::http::verb method)
404 {
Ed Tanous271584a2019-07-09 16:24:22 -0700405 self_t* self = static_cast<self_t*>(this);
406 self->methodsBitfield = 1U << static_cast<size_t>(method);
407 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700409
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 template <typename... MethodArgs>
411 self_t& methods(boost::beast::http::verb method, MethodArgs... args_method)
412 {
Ed Tanous271584a2019-07-09 16:24:22 -0700413 self_t* self = static_cast<self_t*>(this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 methods(args_method...);
Ed Tanous271584a2019-07-09 16:24:22 -0700415 self->methodsBitfield |= 1U << static_cast<size_t>(method);
416 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 }
Tanousf00032d2018-11-05 01:18:10 -0300418
419 template <typename... MethodArgs>
420 self_t& requires(std::initializer_list<const char*> l)
421 {
Ed Tanous271584a2019-07-09 16:24:22 -0700422 self_t* self = static_cast<self_t*>(this);
423 self->privilegesSet.emplace_back(l);
424 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300425 }
426
427 template <typename... MethodArgs>
428 self_t& requires(const std::vector<redfish::Privileges>& p)
429 {
Ed Tanous271584a2019-07-09 16:24:22 -0700430 self_t* self = static_cast<self_t*>(this);
Tanousf00032d2018-11-05 01:18:10 -0300431 for (const redfish::Privileges& privilege : p)
432 {
Ed Tanous271584a2019-07-09 16:24:22 -0700433 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300434 }
Ed Tanous271584a2019-07-09 16:24:22 -0700435 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300436 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700437};
438
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
440{
441 public:
442 DynamicRule(std::string rule) : BaseRule(std::move(rule))
443 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700444 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700445
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 void validate() override
447 {
448 if (!erasedHandler)
449 {
450 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
451 "no handler for url " + rule);
452 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700453 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700454
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 void handle(const Request& req, Response& res,
456 const RoutingParams& params) override
457 {
458 erasedHandler(req, res, params);
459 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700460
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461 template <typename Func> void operator()(Func f)
462 {
463 using function_t = utility::function_traits<Func>;
464
465 erasedHandler =
466 wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
467 }
468
469 // enable_if Arg1 == request && Arg2 == Response
470 // enable_if Arg1 == request && Arg2 != resposne
471 // enable_if Arg1 != request
472
473 template <typename Func, unsigned... Indices>
474
475 std::function<void(const Request&, Response&, const RoutingParams&)>
476 wrap(Func f, black_magic::Seq<Indices...>)
477 {
478 using function_t = utility::function_traits<Func>;
479
480 if (!black_magic::isParameterTagCompatible(
481 black_magic::getParameterTagRuntime(rule.c_str()),
482 black_magic::compute_parameter_tag_from_args_list<
483 typename function_t::template arg<Indices>...>::value))
484 {
485 throw std::runtime_error("routeDynamic: Handler type is mismatched "
486 "with URL parameters: " +
487 rule);
488 }
489 auto ret = detail::routing_handler_call_helper::Wrapped<
490 Func, typename function_t::template arg<Indices>...>();
491 ret.template set<typename function_t::template arg<Indices>...>(
492 std::move(f));
493 return ret;
494 }
495
496 template <typename Func> void operator()(std::string name, Func&& f)
497 {
498 nameStr = std::move(name);
499 (*this).template operator()<Func>(std::forward(f));
500 }
501
502 private:
503 std::function<void(const Request&, Response&, const RoutingParams&)>
504 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700505};
506
507template <typename... Args>
508class TaggedRule : public BaseRule,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 public RuleParameterTraits<TaggedRule<Args...>>
510{
511 public:
512 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700513
Ed Tanous271584a2019-07-09 16:24:22 -0700514 TaggedRule(std::string ruleIn) : BaseRule(std::move(ruleIn))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700516 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 void validate() override
519 {
520 if (!handler)
521 {
522 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
523 "no handler for url " + rule);
524 }
525 }
526
527 template <typename Func>
528 typename std::enable_if<
529 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
530 void>::type
531 operator()(Func&& f)
532 {
533 static_assert(
534 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
535 black_magic::CallHelper<
536 Func, black_magic::S<crow::Request, Args...>>::value,
537 "Handler type is mismatched with URL parameters");
538 static_assert(
539 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
540 "Handler function cannot have void return type; valid return "
541 "types: "
542 "string, int, crow::resposne, nlohmann::json");
543
544 handler = [f = std::move(f)](const Request&, Response& res,
545 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700546 res.result(f(args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 res.end();
548 };
549 }
550
551 template <typename Func>
552 typename std::enable_if<
553 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700554 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700555 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 void>::type
557 operator()(Func&& f)
558 {
559 static_assert(
560 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
561 black_magic::CallHelper<
562 Func, black_magic::S<crow::Request, Args...>>::value,
563 "Handler type is mismatched with URL parameters");
564 static_assert(
565 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
566 std::declval<Args>()...))>::value,
567 "Handler function cannot have void return type; valid return "
568 "types: "
569 "string, int, crow::resposne,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700570
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 handler = [f = std::move(f)](const crow::Request& req,
572 crow::Response& res, Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700573 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 res.end();
575 };
576 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700577
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 template <typename Func>
579 typename std::enable_if<
580 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
581 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700582 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 void>::type
584 operator()(Func&& f)
585 {
586 static_assert(
587 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
588 black_magic::CallHelper<
589 Func, black_magic::S<crow::Request, Args...>>::value ||
590 black_magic::CallHelper<
591 Func, black_magic::S<crow::Request, crow::Response&,
592 Args...>>::value,
593 "Handler type is mismatched with URL parameters");
594 static_assert(
595 std::is_same<void, decltype(f(std::declval<crow::Request>(),
596 std::declval<crow::Response&>(),
597 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300598 "Handler function with response argument should have void "
599 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700601
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602 handler = std::move(f);
603 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700604
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 template <typename Func> void operator()(std::string name, Func&& f)
606 {
607 nameStr = std::move(name);
608 (*this).template operator()<Func>(std::forward(f));
609 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700610
Ed Tanous1abe55e2018-09-05 08:30:59 -0700611 void handle(const Request& req, Response& res,
612 const RoutingParams& params) override
613 {
614 detail::routing_handler_call_helper::Call<
615 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
616 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
617 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
618 handler, params, req, res});
619 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700620
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 private:
622 std::function<void(const crow::Request&, crow::Response&, Args...)> handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700623};
624
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700625const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700626
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627class Trie
628{
629 public:
630 struct Node
631 {
632 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700633 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
634 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700636
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 bool isSimpleNode() const
638 {
639 return !ruleIndex && std::all_of(std::begin(paramChildrens),
640 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700641 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700642 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700643 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700644
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 Trie() : nodes(1)
646 {
647 }
648
649 private:
650 void optimizeNode(Node* node)
651 {
Ed Tanous271584a2019-07-09 16:24:22 -0700652 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 {
654 if (!x)
655 continue;
656 Node* child = &nodes[x];
657 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700658 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 if (node->children.empty())
660 return;
661 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300662 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 {
664 Node* child = &nodes[kv.second];
665 if (!child->isSimpleNode())
666 {
667 mergeWithChild = false;
668 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700669 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700670 }
671 if (mergeWithChild)
672 {
673 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300674 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
676 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300677 for (const std::pair<std::string, unsigned>& childKv :
678 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 {
680 merged[kv.first + childKv.first] = childKv.second;
681 }
682 }
683 node->children = std::move(merged);
684 optimizeNode(node);
685 }
686 else
687 {
Tanousf00032d2018-11-05 01:18:10 -0300688 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 {
690 Node* child = &nodes[kv.second];
691 optimizeNode(child);
692 }
693 }
694 }
695
696 void optimize()
697 {
698 optimizeNode(head());
699 }
700
701 public:
702 void validate()
703 {
704 if (!head()->isSimpleNode())
705 throw std::runtime_error(
706 "Internal error: Trie header should be simple!");
707 optimize();
708 }
709
710 void findRouteIndexes(const std::string& req_url,
711 std::vector<unsigned>& route_indexes,
Tanousf00032d2018-11-05 01:18:10 -0300712 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 {
714 if (node == nullptr)
715 {
716 node = head();
717 }
Tanousf00032d2018-11-05 01:18:10 -0300718 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 {
720 const std::string& fragment = kv.first;
721 const Node* child = &nodes[kv.second];
722 if (pos >= req_url.size())
723 {
724 if (child->ruleIndex != 0 && fragment != "/")
725 {
726 route_indexes.push_back(child->ruleIndex);
727 }
728 findRouteIndexes(req_url, route_indexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700729 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 }
731 else
732 {
733 if (req_url.compare(pos, fragment.size(), fragment) == 0)
734 {
Ed Tanous271584a2019-07-09 16:24:22 -0700735 findRouteIndexes(
736 req_url, route_indexes, child,
737 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 }
739 }
740 }
741 }
742
743 std::pair<unsigned, RoutingParams>
Ed Tanous39e77502019-03-04 17:35:53 -0800744 find(const std::string_view req_url, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700745 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 {
747 RoutingParams empty;
748 if (params == nullptr)
749 params = &empty;
750
751 unsigned found{};
752 RoutingParams matchParams;
753
754 if (node == nullptr)
755 node = head();
756 if (pos == req_url.size())
757 return {node->ruleIndex, *params};
758
759 auto updateFound =
760 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
761 if (ret.first && (!found || found > ret.first))
762 {
763 found = ret.first;
764 matchParams = std::move(ret.second);
765 }
766 };
767
Ed Tanous271584a2019-07-09 16:24:22 -0700768 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 {
770 char c = req_url[pos];
771 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
772 {
773 char* eptr;
774 errno = 0;
775 long long int value =
776 std::strtoll(req_url.data() + pos, &eptr, 10);
777 if (errno != ERANGE && eptr != req_url.data() + pos)
778 {
779 params->intParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700780 std::pair<unsigned, RoutingParams> ret = find(
781 req_url,
782 &nodes[node->paramChildrens[static_cast<size_t>(
783 ParamType::INT)]],
784 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 updateFound(ret);
786 params->intParams.pop_back();
787 }
788 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700789 }
790
Ed Tanous271584a2019-07-09 16:24:22 -0700791 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 {
793 char c = req_url[pos];
794 if ((c >= '0' && c <= '9') || c == '+')
795 {
796 char* eptr;
797 errno = 0;
798 unsigned long long int value =
799 std::strtoull(req_url.data() + pos, &eptr, 10);
800 if (errno != ERANGE && eptr != req_url.data() + pos)
801 {
802 params->uintParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700803 std::pair<unsigned, RoutingParams> ret = find(
804 req_url,
805 &nodes[node->paramChildrens[static_cast<size_t>(
806 ParamType::UINT)]],
807 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 updateFound(ret);
809 params->uintParams.pop_back();
810 }
811 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700812 }
813
Ed Tanous271584a2019-07-09 16:24:22 -0700814 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 {
816 char c = req_url[pos];
817 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
818 {
819 char* eptr;
820 errno = 0;
821 double value = std::strtod(req_url.data() + pos, &eptr);
822 if (errno != ERANGE && eptr != req_url.data() + pos)
823 {
824 params->doubleParams.push_back(value);
Tanousf00032d2018-11-05 01:18:10 -0300825 std::pair<unsigned, RoutingParams> ret = find(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700827 &nodes[node->paramChildrens[static_cast<size_t>(
828 ParamType::DOUBLE)]],
829 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 updateFound(ret);
831 params->doubleParams.pop_back();
832 }
833 }
834 }
835
Ed Tanous271584a2019-07-09 16:24:22 -0700836 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000838 size_t epos = pos;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 for (; epos < req_url.size(); epos++)
840 {
841 if (req_url[epos] == '/')
842 break;
843 }
844
845 if (epos != pos)
846 {
847 params->stringParams.emplace_back(
848 req_url.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300849 std::pair<unsigned, RoutingParams> ret =
Ed Tanousb01bf292019-03-25 19:25:26 +0000850 find(req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700851 &nodes[node->paramChildrens[static_cast<size_t>(
852 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000853 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 updateFound(ret);
855 params->stringParams.pop_back();
856 }
857 }
858
Ed Tanous271584a2019-07-09 16:24:22 -0700859 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700860 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000861 size_t epos = req_url.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862
863 if (epos != pos)
864 {
865 params->stringParams.emplace_back(
866 req_url.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700867 std::pair<unsigned, RoutingParams> ret =
868 find(req_url,
869 &nodes[node->paramChildrens[static_cast<size_t>(
870 ParamType::PATH)]],
871 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 updateFound(ret);
873 params->stringParams.pop_back();
874 }
875 }
876
Tanousf00032d2018-11-05 01:18:10 -0300877 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 {
879 const std::string& fragment = kv.first;
880 const Node* child = &nodes[kv.second];
881
882 if (req_url.compare(pos, fragment.size(), fragment) == 0)
883 {
Tanousf00032d2018-11-05 01:18:10 -0300884 std::pair<unsigned, RoutingParams> ret =
885 find(req_url, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 updateFound(ret);
887 }
888 }
889
890 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700891 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892
893 void add(const std::string& url, unsigned ruleIndex)
894 {
Ed Tanous271584a2019-07-09 16:24:22 -0700895 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896
897 for (unsigned i = 0; i < url.size(); i++)
898 {
899 char c = url[i];
900 if (c == '<')
901 {
Tanousf00032d2018-11-05 01:18:10 -0300902 const static std::array<std::pair<ParamType, std::string>, 7>
903 paramTraits = {{
904 {ParamType::INT, "<int>"},
905 {ParamType::UINT, "<uint>"},
906 {ParamType::DOUBLE, "<float>"},
907 {ParamType::DOUBLE, "<double>"},
908 {ParamType::STRING, "<str>"},
909 {ParamType::STRING, "<string>"},
910 {ParamType::PATH, "<path>"},
911 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700912
Tanousf00032d2018-11-05 01:18:10 -0300913 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Tanousf00032d2018-11-05 01:18:10 -0300915 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700916 {
Ed Tanous271584a2019-07-09 16:24:22 -0700917 size_t index = static_cast<size_t>(x.first);
918 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 {
Tanousf00032d2018-11-05 01:18:10 -0300920 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700921 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700922 }
Ed Tanous271584a2019-07-09 16:24:22 -0700923 idx = nodes[idx].paramChildrens[index];
924 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 break;
926 }
927 }
928
929 i--;
930 }
931 else
932 {
933 std::string piece(&c, 1);
934 if (!nodes[idx].children.count(piece))
935 {
Tanousf00032d2018-11-05 01:18:10 -0300936 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700937 nodes[idx].children.emplace(piece, newNodeIdx);
938 }
939 idx = nodes[idx].children[piece];
940 }
941 }
942 if (nodes[idx].ruleIndex)
943 throw std::runtime_error("handler already exists for " + url);
944 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700945 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700946
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700948 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 {
Ed Tanous271584a2019-07-09 16:24:22 -0700950 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 {
952 if (n->paramChildrens[i])
953 {
954 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700955 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
956 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 {
958 case ParamType::INT:
959 BMCWEB_LOG_DEBUG << "<int>";
960 break;
961 case ParamType::UINT:
962 BMCWEB_LOG_DEBUG << "<uint>";
963 break;
964 case ParamType::DOUBLE:
965 BMCWEB_LOG_DEBUG << "<float>";
966 break;
967 case ParamType::STRING:
968 BMCWEB_LOG_DEBUG << "<str>";
969 break;
970 case ParamType::PATH:
971 BMCWEB_LOG_DEBUG << "<path>";
972 break;
973 default:
974 BMCWEB_LOG_DEBUG << "<ERROR>";
975 break;
976 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700977
Ed Tanous1abe55e2018-09-05 08:30:59 -0700978 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
979 }
980 }
Tanousf00032d2018-11-05 01:18:10 -0300981 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700982 {
983 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -0700984 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 << kv.first;
986 debugNodePrint(&nodes[kv.second], level + 1);
987 }
988 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700989
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 public:
991 void debugPrint()
992 {
Ed Tanous271584a2019-07-09 16:24:22 -0700993 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700995
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 private:
997 const Node* head() const
998 {
999 return &nodes.front();
1000 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001001
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002 Node* head()
1003 {
1004 return &nodes.front();
1005 }
1006
1007 unsigned newNode()
1008 {
1009 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001010 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001011 }
1012
1013 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001014};
1015
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016class Router
1017{
1018 public:
Ed Tanous0c0084a2019-10-24 15:57:51 -07001019 Router() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001020
Ed Tanous1abe55e2018-09-05 08:30:59 -07001021 DynamicRule& newRuleDynamic(const std::string& rule)
1022 {
1023 std::unique_ptr<DynamicRule> ruleObject =
1024 std::make_unique<DynamicRule>(rule);
1025 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001026 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001027
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001029 }
1030
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 template <uint64_t N>
1032 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1033 newRuleTagged(const std::string& rule)
1034 {
1035 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1036 TaggedRule>;
1037 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1038 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001039 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040
1041 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001042 }
1043
Tanousf00032d2018-11-05 01:18:10 -03001044 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001045 {
Tanousf00032d2018-11-05 01:18:10 -03001046 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001047 {
Tanousf00032d2018-11-05 01:18:10 -03001048 return;
1049 }
1050 for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
1051 method++, method_bit <<= 1)
1052 {
1053 if (ruleObject->methodsBitfield & method_bit)
1054 {
1055 perMethods[method].rules.emplace_back(ruleObject);
1056 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001057 rule, static_cast<unsigned>(
1058 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001059 // directory case:
1060 // request to `/about' url matches `/about/' rule
1061 if (rule.size() > 2 && rule.back() == '/')
1062 {
1063 perMethods[method].trie.add(
1064 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001065 static_cast<unsigned>(perMethods[method].rules.size() -
1066 1));
Tanousf00032d2018-11-05 01:18:10 -03001067 }
1068 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001069 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001070 }
1071
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 void validate()
1073 {
Tanousf00032d2018-11-05 01:18:10 -03001074 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001075 {
1076 if (rule)
1077 {
Tanousf00032d2018-11-05 01:18:10 -03001078 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001079 if (upgraded)
1080 rule = std::move(upgraded);
1081 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001082 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 }
1084 }
Tanousf00032d2018-11-05 01:18:10 -03001085 for (PerMethod& perMethod : perMethods)
1086 {
1087 perMethod.trie.validate();
1088 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001089 }
1090
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 template <typename Adaptor>
1092 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1093 {
Ed Tanous271584a2019-07-09 16:24:22 -07001094 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001095 return;
1096
Ed Tanous271584a2019-07-09 16:24:22 -07001097 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001098 Trie& trie = perMethod.trie;
1099 std::vector<BaseRule*>& rules = perMethod.rules;
1100
1101 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 unsigned ruleIndex = found.first;
1103 if (!ruleIndex)
1104 {
1105 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001106 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001107 res.end();
1108 return;
1109 }
1110
1111 if (ruleIndex >= rules.size())
1112 throw std::runtime_error("Trie internal structure corrupted!");
1113
1114 if (ruleIndex == ruleSpecialRedirectSlash)
1115 {
1116 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1117 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001118 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001119
1120 // TODO absolute url building
1121 if (req.getHeaderValue("Host").empty())
1122 {
1123 res.addHeader("Location", std::string(req.url) + "/");
1124 }
1125 else
1126 {
1127 res.addHeader(
1128 "Location",
1129 req.isSecure
1130 ? "https://"
1131 : "http://" + std::string(req.getHeaderValue("Host")) +
1132 std::string(req.url) + "/");
1133 }
1134 res.end();
1135 return;
1136 }
1137
Ed Tanous271584a2019-07-09 16:24:22 -07001138 if ((rules[ruleIndex]->getMethods() &
1139 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001140 {
1141 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1142 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001143 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001144 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001145 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001146 res.end();
1147 return;
1148 }
1149
1150 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001151 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001152 << rules[ruleIndex]->getMethods();
1153
1154 // any uncaught exceptions become 500s
1155 try
1156 {
1157 rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor));
1158 }
1159 catch (std::exception& e)
1160 {
1161 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001162 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163 res.end();
1164 return;
1165 }
1166 catch (...)
1167 {
1168 BMCWEB_LOG_ERROR
1169 << "An uncaught exception occurred. The type was unknown "
1170 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001171 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001172 res.end();
1173 return;
1174 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001175 }
1176
Ed Tanous1abe55e2018-09-05 08:30:59 -07001177 void handle(const Request& req, Response& res)
1178 {
Ed Tanous271584a2019-07-09 16:24:22 -07001179 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001180 return;
Ed Tanous271584a2019-07-09 16:24:22 -07001181 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001182 Trie& trie = perMethod.trie;
1183 std::vector<BaseRule*>& rules = perMethod.rules;
1184
1185 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001186
Ed Tanous1abe55e2018-09-05 08:30:59 -07001187 unsigned ruleIndex = found.first;
1188
1189 if (!ruleIndex)
1190 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001191 // Check to see if this url exists at any verb
1192 for (const PerMethod& p : perMethods)
1193 {
1194 const std::pair<unsigned, RoutingParams>& found =
1195 p.trie.find(req.url);
1196 if (found.first > 0)
1197 {
1198 res.result(boost::beast::http::status::method_not_allowed);
1199 res.end();
1200 return;
1201 }
1202 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001203 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
1204 res.result(boost::beast::http::status::not_found);
1205 res.end();
1206 return;
1207 }
1208
1209 if (ruleIndex >= rules.size())
1210 throw std::runtime_error("Trie internal structure corrupted!");
1211
1212 if (ruleIndex == ruleSpecialRedirectSlash)
1213 {
1214 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1215 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001216 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001217
1218 // TODO absolute url building
1219 if (req.getHeaderValue("Host").empty())
1220 {
1221 res.addHeader("Location", std::string(req.url) + "/");
1222 }
1223 else
1224 {
1225 res.addHeader("Location",
1226 (req.isSecure ? "https://" : "http://") +
1227 std::string(req.getHeaderValue("Host")) +
1228 std::string(req.url) + "/");
1229 }
1230 res.end();
1231 return;
1232 }
1233
Ed Tanous271584a2019-07-09 16:24:22 -07001234 if ((rules[ruleIndex]->getMethods() &
1235 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001236 {
1237 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1238 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001239 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001240 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001241 res.result(boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001242 res.end();
1243 return;
1244 }
1245
1246 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001247 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001248 << rules[ruleIndex]->getMethods();
1249
Ratan Gupta6f359562019-04-03 10:39:08 +05301250 redfish::Privileges userPrivileges;
1251 if (req.session != nullptr)
1252 {
1253 // Get the user role from the session.
Ed Tanousca0c93b2019-09-19 11:53:50 -07001254 const std::string& userRole =
1255 persistent_data::UserRoleMap::getInstance().getUserRole(
1256 req.session->username);
Ratan Gupta6f359562019-04-03 10:39:08 +05301257
1258 BMCWEB_LOG_DEBUG << "USER ROLE=" << userRole;
1259
1260 // Get the user privileges from the role
1261 userPrivileges = redfish::getUserPrivileges(userRole);
1262 }
Tanousf00032d2018-11-05 01:18:10 -03001263
1264 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1265 {
Gunnar Mills961c9d92019-06-18 07:39:22 -05001266 res.result(boost::beast::http::status::forbidden);
Tanousf00032d2018-11-05 01:18:10 -03001267 res.end();
1268 return;
1269 }
1270
Ed Tanous1abe55e2018-09-05 08:30:59 -07001271 // any uncaught exceptions become 500s
1272 try
1273 {
1274 rules[ruleIndex]->handle(req, res, found.second);
1275 }
1276 catch (std::exception& e)
1277 {
1278 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001279 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001280 res.end();
1281 return;
1282 }
1283 catch (...)
1284 {
1285 BMCWEB_LOG_ERROR
1286 << "An uncaught exception occurred. The type was unknown "
1287 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001288 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289 res.end();
1290 return;
1291 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001292 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001293
Ed Tanous1abe55e2018-09-05 08:30:59 -07001294 void debugPrint()
1295 {
Ed Tanous271584a2019-07-09 16:24:22 -07001296 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001297 {
Ed Tanous271584a2019-07-09 16:24:22 -07001298 BMCWEB_LOG_DEBUG
1299 << methodName(static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001300 perMethods[i].trie.debugPrint();
1301 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001302 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001303
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304 std::vector<const std::string*> getRoutes(const std::string& parent)
1305 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001306 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001307
1308 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001309 {
Tanousf00032d2018-11-05 01:18:10 -03001310 std::vector<unsigned> x;
1311 pm.trie.findRouteIndexes(parent, x);
1312 for (unsigned index : x)
1313 {
1314 ret.push_back(&pm.rules[index]->rule);
1315 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001316 }
1317 return ret;
1318 }
1319
1320 private:
Tanousf00032d2018-11-05 01:18:10 -03001321 struct PerMethod
1322 {
1323 std::vector<BaseRule*> rules;
1324 Trie trie;
1325 // rule index 0, 1 has special meaning; preallocate it to avoid
1326 // duplication.
1327 PerMethod() : rules(2)
1328 {
1329 }
1330 };
1331 std::array<PerMethod, maxHttpVerbCount> perMethods;
1332 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001333};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001334} // namespace crow