blob: 758f8dd4d753d332d2cccb73a50f8709c20b90b4 [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 Tanous1abe55e2018-09-05 08:30:59 -070038 virtual ~BaseRule()
39 {
40 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070041
Ed Tanous1abe55e2018-09-05 08:30:59 -070042 virtual void validate() = 0;
43 std::unique_ptr<BaseRule> upgrade()
44 {
45 if (ruleToUpgrade)
46 return std::move(ruleToUpgrade);
47 return {};
48 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070049
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 virtual void handle(const Request&, Response&, const RoutingParams&) = 0;
Ed Tanousceac6f72018-12-02 11:58:47 -080051 virtual void handleUpgrade(const Request&, Response& res,
52 boost::asio::ip::tcp::socket&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070054 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 res.end();
56 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070057#ifdef BMCWEB_ENABLE_SSL
Ed Tanousceac6f72018-12-02 11:58:47 -080058 virtual void
59 handleUpgrade(const Request&, Response& res,
60 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&)
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 {
Ed Tanousde5c9f32019-03-26 09:17:55 -070062 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 res.end();
64 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070065#endif
66
Ed Tanous271584a2019-07-09 16:24:22 -070067 size_t getMethods()
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
69 return methodsBitfield;
70 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070071
Tanousf00032d2018-11-05 01:18:10 -030072 bool checkPrivileges(const redfish::Privileges& userPrivileges)
73 {
74 // If there are no privileges assigned, assume no privileges
75 // required
76 if (privilegesSet.empty())
77 {
78 return true;
79 }
80
81 for (const redfish::Privileges& requiredPrivileges : privilegesSet)
82 {
83 if (userPrivileges.isSupersetOf(requiredPrivileges))
84 {
85 return true;
86 }
87 }
88 return false;
89 }
90
Ed Tanous271584a2019-07-09 16:24:22 -070091 size_t methodsBitfield{
92 1 << static_cast<size_t>(boost::beast::http::verb::get)};
Ed Tanous7045c8d2017-04-03 10:04:37 -070093
Tanousf00032d2018-11-05 01:18:10 -030094 std::vector<redfish::Privileges> privilegesSet;
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 std::string rule;
97 std::string nameStr;
Ed Tanous7045c8d2017-04-03 10:04:37 -070098
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 std::unique_ptr<BaseRule> ruleToUpgrade;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700100
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 friend class Router;
102 template <typename T> friend struct RuleParameterTraits;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103};
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105namespace detail
106{
107namespace routing_handler_call_helper
108{
109template <typename T, int Pos> struct CallPair
110{
111 using type = T;
112 static const int pos = Pos;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113};
114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115template <typename H1> struct CallParams
116{
117 H1& handler;
118 const RoutingParams& params;
119 const Request& req;
120 Response& res;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700121};
122
123template <typename F, int NInt, int NUint, int NDouble, int NString,
124 typename S1, typename S2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125struct Call
126{
127};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128
129template <typename F, int NInt, int NUint, int NDouble, int NString,
130 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700131struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 black_magic::S<Args2...>>
133{
134 void operator()(F cparams)
135 {
136 using pushed = typename black_magic::S<Args2...>::template push_back<
137 CallPair<int64_t, NInt>>;
138 Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>,
139 pushed>()(cparams);
140 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700141};
142
143template <typename F, int NInt, int NUint, int NDouble, int NString,
144 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700145struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700146 black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
147{
148 void operator()(F cparams)
149 {
150 using pushed = typename black_magic::S<Args2...>::template push_back<
151 CallPair<uint64_t, NUint>>;
152 Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>,
153 pushed>()(cparams);
154 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700155};
156
157template <typename F, int NInt, int NUint, int NDouble, int NString,
158 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700159struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 black_magic::S<Args2...>>
161{
162 void operator()(F cparams)
163 {
164 using pushed = typename black_magic::S<Args2...>::template push_back<
165 CallPair<double, NDouble>>;
166 Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>,
167 pushed>()(cparams);
168 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700169};
170
171template <typename F, int NInt, int NUint, int NDouble, int NString,
172 typename... Args1, typename... Args2>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700173struct Call<F, NInt, NUint, NDouble, NString,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
175{
176 void operator()(F cparams)
177 {
178 using pushed = typename black_magic::S<Args2...>::template push_back<
179 CallPair<std::string, NString>>;
180 Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>,
181 pushed>()(cparams);
182 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700183};
184
185template <typename F, int NInt, int NUint, int NDouble, int NString,
186 typename... Args1>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700187struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 black_magic::S<Args1...>>
189{
190 void operator()(F cparams)
191 {
192 cparams.handler(
193 cparams.req, cparams.res,
194 cparams.params.template get<typename Args1::type>(Args1::pos)...);
195 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700196};
197
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198template <typename Func, typename... ArgsWrapped> struct Wrapped
199{
200 template <typename... Args>
201 void set(
202 Func f,
203 typename std::enable_if<
204 !std::is_same<
205 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
206 const Request&>::value,
207 int>::type = 0)
208 {
Tanousf00032d2018-11-05 01:18:10 -0300209 handler = [f = std::move(f)](const Request&, Response& res,
210 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700211 res.result(f(args...));
Tanousf00032d2018-11-05 01:18:10 -0300212 res.end();
213 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700214 }
215
Ed Tanous1abe55e2018-09-05 08:30:59 -0700216 template <typename Req, typename... Args> struct ReqHandlerWrapper
217 {
218 ReqHandlerWrapper(Func f) : f(std::move(f))
219 {
220 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700221
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 void operator()(const Request& req, Response& res, Args... args)
223 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700224 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 res.end();
226 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700227
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 Func f;
229 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700230
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 template <typename... Args>
232 void set(
233 Func f,
234 typename std::enable_if<
235 std::is_same<
236 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
237 const Request&>::value &&
238 !std::is_same<typename std::tuple_element<
239 1, std::tuple<Args..., void, void>>::type,
240 Response&>::value,
241 int>::type = 0)
242 {
243 handler = ReqHandlerWrapper<Args...>(std::move(f));
244 /*handler = (
245 [f = std::move(f)]
246 (const Request& req, Response& res, Args... args){
Ed Tanousde5c9f32019-03-26 09:17:55 -0700247 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 res.end();
249 });*/
250 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700251
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 template <typename... Args>
253 void set(
254 Func f,
255 typename std::enable_if<
256 std::is_same<
257 typename std::tuple_element<0, std::tuple<Args..., void>>::type,
258 const Request&>::value &&
259 std::is_same<typename std::tuple_element<
260 1, std::tuple<Args..., void, void>>::type,
261 Response&>::value,
262 int>::type = 0)
263 {
264 handler = std::move(f);
265 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700266
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 template <typename... Args> struct HandlerTypeHelper
268 {
269 using type =
270 std::function<void(const crow::Request&, crow::Response&, Args...)>;
271 using args_type =
272 black_magic::S<typename black_magic::promote_t<Args>...>;
273 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700274
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 template <typename... Args>
276 struct HandlerTypeHelper<const Request&, Args...>
277 {
278 using type =
279 std::function<void(const crow::Request&, crow::Response&, Args...)>;
280 using args_type =
281 black_magic::S<typename black_magic::promote_t<Args>...>;
282 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 template <typename... Args>
285 struct HandlerTypeHelper<const Request&, Response&, Args...>
286 {
287 using type =
288 std::function<void(const crow::Request&, crow::Response&, Args...)>;
289 using args_type =
290 black_magic::S<typename black_magic::promote_t<Args>...>;
291 };
292
293 typename HandlerTypeHelper<ArgsWrapped...>::type handler;
294
295 void operator()(const Request& req, Response& res,
296 const RoutingParams& params)
297 {
298 detail::routing_handler_call_helper::Call<
299 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
300 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type,
301 black_magic::S<>>()(
302 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
303 handler, params, req, res});
304 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700305};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306} // namespace routing_handler_call_helper
307} // namespace detail
Ed Tanous7045c8d2017-04-03 10:04:37 -0700308
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309class WebSocketRule : public BaseRule
310{
311 using self_t = WebSocketRule;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 public:
314 WebSocketRule(std::string rule) : BaseRule(std::move(rule))
315 {
316 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700317
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 void validate() override
319 {
320 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700321
Ed Tanous1abe55e2018-09-05 08:30:59 -0700322 void handle(const Request&, Response& res, const RoutingParams&) override
323 {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700324 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 res.end();
326 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700327
Ed Tanous1abe55e2018-09-05 08:30:59 -0700328 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800329 boost::asio::ip::tcp::socket&& adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 {
Ratan Gupta02453b12019-10-22 14:43:36 +0530331 std::shared_ptr<
332 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
333 myConnection = std::make_shared<
334 crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
335 req, std::move(adaptor), openHandler, messageHandler,
336 closeHandler, errorHandler);
337 myConnection->start();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 }
339#ifdef BMCWEB_ENABLE_SSL
340 void handleUpgrade(const Request& req, Response&,
Ed Tanousceac6f72018-12-02 11:58:47 -0800341 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
342 adaptor) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700343 {
Ed Tanousceac6f72018-12-02 11:58:47 -0800344 std::shared_ptr<crow::websocket::ConnectionImpl<
345 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
346 myConnection = std::make_shared<crow::websocket::ConnectionImpl<
347 boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
348 req, std::move(adaptor), openHandler, messageHandler,
349 closeHandler, errorHandler);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 myConnection->start();
351 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700352#endif
353
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 template <typename Func> self_t& onopen(Func f)
355 {
356 openHandler = f;
357 return *this;
358 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700359
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 template <typename Func> self_t& onmessage(Func f)
361 {
362 messageHandler = f;
363 return *this;
364 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700365
Ed Tanous1abe55e2018-09-05 08:30:59 -0700366 template <typename Func> self_t& onclose(Func f)
367 {
368 closeHandler = f;
369 return *this;
370 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700371
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 template <typename Func> self_t& onerror(Func f)
373 {
374 errorHandler = f;
375 return *this;
376 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700377
Ed Tanous1abe55e2018-09-05 08:30:59 -0700378 protected:
379 std::function<void(crow::websocket::Connection&)> openHandler;
380 std::function<void(crow::websocket::Connection&, const std::string&, bool)>
381 messageHandler;
382 std::function<void(crow::websocket::Connection&, const std::string&)>
383 closeHandler;
384 std::function<void(crow::websocket::Connection&)> errorHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700385};
386
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387template <typename T> struct RuleParameterTraits
388{
389 using self_t = T;
390 WebSocketRule& websocket()
391 {
Ed Tanous271584a2019-07-09 16:24:22 -0700392 self_t* self = static_cast<self_t*>(this);
393 WebSocketRule* p = new WebSocketRule(self->rule);
394 self->ruleToUpgrade.reset(p);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700395 return *p;
396 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700397
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398 self_t& name(std::string name) noexcept
399 {
Ed Tanous271584a2019-07-09 16:24:22 -0700400 self_t* self = static_cast<self_t*>(this);
401 self->nameStr = std::move(name);
402 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700404
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 self_t& methods(boost::beast::http::verb method)
406 {
Ed Tanous271584a2019-07-09 16:24:22 -0700407 self_t* self = static_cast<self_t*>(this);
408 self->methodsBitfield = 1U << static_cast<size_t>(method);
409 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700411
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 template <typename... MethodArgs>
413 self_t& methods(boost::beast::http::verb method, MethodArgs... args_method)
414 {
Ed Tanous271584a2019-07-09 16:24:22 -0700415 self_t* self = static_cast<self_t*>(this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 methods(args_method...);
Ed Tanous271584a2019-07-09 16:24:22 -0700417 self->methodsBitfield |= 1U << static_cast<size_t>(method);
418 return *self;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 }
Tanousf00032d2018-11-05 01:18:10 -0300420
421 template <typename... MethodArgs>
422 self_t& requires(std::initializer_list<const char*> l)
423 {
Ed Tanous271584a2019-07-09 16:24:22 -0700424 self_t* self = static_cast<self_t*>(this);
425 self->privilegesSet.emplace_back(l);
426 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300427 }
428
429 template <typename... MethodArgs>
430 self_t& requires(const std::vector<redfish::Privileges>& p)
431 {
Ed Tanous271584a2019-07-09 16:24:22 -0700432 self_t* self = static_cast<self_t*>(this);
Tanousf00032d2018-11-05 01:18:10 -0300433 for (const redfish::Privileges& privilege : p)
434 {
Ed Tanous271584a2019-07-09 16:24:22 -0700435 self->privilegesSet.emplace_back(privilege);
Tanousf00032d2018-11-05 01:18:10 -0300436 }
Ed Tanous271584a2019-07-09 16:24:22 -0700437 return *self;
Tanousf00032d2018-11-05 01:18:10 -0300438 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700439};
440
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
442{
443 public:
444 DynamicRule(std::string rule) : BaseRule(std::move(rule))
445 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700446 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 void validate() override
449 {
450 if (!erasedHandler)
451 {
452 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
453 "no handler for url " + rule);
454 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700455 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700456
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 void handle(const Request& req, Response& res,
458 const RoutingParams& params) override
459 {
460 erasedHandler(req, res, params);
461 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700462
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 template <typename Func> void operator()(Func f)
464 {
465 using function_t = utility::function_traits<Func>;
466
467 erasedHandler =
468 wrap(std::move(f), black_magic::gen_seq<function_t::arity>());
469 }
470
471 // enable_if Arg1 == request && Arg2 == Response
472 // enable_if Arg1 == request && Arg2 != resposne
473 // enable_if Arg1 != request
474
475 template <typename Func, unsigned... Indices>
476
477 std::function<void(const Request&, Response&, const RoutingParams&)>
478 wrap(Func f, black_magic::Seq<Indices...>)
479 {
480 using function_t = utility::function_traits<Func>;
481
482 if (!black_magic::isParameterTagCompatible(
483 black_magic::getParameterTagRuntime(rule.c_str()),
484 black_magic::compute_parameter_tag_from_args_list<
485 typename function_t::template arg<Indices>...>::value))
486 {
487 throw std::runtime_error("routeDynamic: Handler type is mismatched "
488 "with URL parameters: " +
489 rule);
490 }
491 auto ret = detail::routing_handler_call_helper::Wrapped<
492 Func, typename function_t::template arg<Indices>...>();
493 ret.template set<typename function_t::template arg<Indices>...>(
494 std::move(f));
495 return ret;
496 }
497
498 template <typename Func> void operator()(std::string name, Func&& f)
499 {
500 nameStr = std::move(name);
501 (*this).template operator()<Func>(std::forward(f));
502 }
503
504 private:
505 std::function<void(const Request&, Response&, const RoutingParams&)>
506 erasedHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700507};
508
509template <typename... Args>
510class TaggedRule : public BaseRule,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 public RuleParameterTraits<TaggedRule<Args...>>
512{
513 public:
514 using self_t = TaggedRule<Args...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700515
Ed Tanous271584a2019-07-09 16:24:22 -0700516 TaggedRule(std::string ruleIn) : BaseRule(std::move(ruleIn))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700518 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700519
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520 void validate() override
521 {
522 if (!handler)
523 {
524 throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") +
525 "no handler for url " + rule);
526 }
527 }
528
529 template <typename Func>
530 typename std::enable_if<
531 black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
532 void>::type
533 operator()(Func&& f)
534 {
535 static_assert(
536 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
537 black_magic::CallHelper<
538 Func, black_magic::S<crow::Request, Args...>>::value,
539 "Handler type is mismatched with URL parameters");
540 static_assert(
541 !std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
542 "Handler function cannot have void return type; valid return "
543 "types: "
544 "string, int, crow::resposne, nlohmann::json");
545
546 handler = [f = std::move(f)](const Request&, Response& res,
547 Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700548 res.result(f(args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 res.end();
550 };
551 }
552
553 template <typename Func>
554 typename std::enable_if<
555 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
Ed Tanous7045c8d2017-04-03 10:04:37 -0700556 black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700557 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 void>::type
559 operator()(Func&& f)
560 {
561 static_assert(
562 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
563 black_magic::CallHelper<
564 Func, black_magic::S<crow::Request, Args...>>::value,
565 "Handler type is mismatched with URL parameters");
566 static_assert(
567 !std::is_same<void, decltype(f(std::declval<crow::Request>(),
568 std::declval<Args>()...))>::value,
569 "Handler function cannot have void return type; valid return "
570 "types: "
571 "string, int, crow::resposne,nlohmann::json");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700572
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 handler = [f = std::move(f)](const crow::Request& req,
574 crow::Response& res, Args... args) {
Ed Tanousde5c9f32019-03-26 09:17:55 -0700575 res.result(f(req, args...));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 res.end();
577 };
578 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700579
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 template <typename Func>
581 typename std::enable_if<
582 !black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
583 !black_magic::CallHelper<
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700584 Func, black_magic::S<crow::Request, Args...>>::value,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 void>::type
586 operator()(Func&& f)
587 {
588 static_assert(
589 black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
590 black_magic::CallHelper<
591 Func, black_magic::S<crow::Request, Args...>>::value ||
592 black_magic::CallHelper<
593 Func, black_magic::S<crow::Request, crow::Response&,
594 Args...>>::value,
595 "Handler type is mismatched with URL parameters");
596 static_assert(
597 std::is_same<void, decltype(f(std::declval<crow::Request>(),
598 std::declval<crow::Response&>(),
599 std::declval<Args>()...))>::value,
Tanousf00032d2018-11-05 01:18:10 -0300600 "Handler function with response argument should have void "
601 "return "
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602 "type");
Ed Tanous7045c8d2017-04-03 10:04:37 -0700603
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 handler = std::move(f);
605 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700606
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 template <typename Func> void operator()(std::string name, Func&& f)
608 {
609 nameStr = std::move(name);
610 (*this).template operator()<Func>(std::forward(f));
611 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700612
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 void handle(const Request& req, Response& res,
614 const RoutingParams& params) override
615 {
616 detail::routing_handler_call_helper::Call<
617 detail::routing_handler_call_helper::CallParams<decltype(handler)>,
618 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(
619 detail::routing_handler_call_helper::CallParams<decltype(handler)>{
620 handler, params, req, res});
621 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700622
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 private:
624 std::function<void(const crow::Request&, crow::Response&, Args...)> handler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700625};
626
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700627const int ruleSpecialRedirectSlash = 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700628
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629class Trie
630{
631 public:
632 struct Node
633 {
634 unsigned ruleIndex{};
Ed Tanous271584a2019-07-09 16:24:22 -0700635 std::array<size_t, static_cast<size_t>(ParamType::MAX)>
636 paramChildrens{};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 boost::container::flat_map<std::string, unsigned> children;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700638
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 bool isSimpleNode() const
640 {
641 return !ruleIndex && std::all_of(std::begin(paramChildrens),
642 std::end(paramChildrens),
Ed Tanous271584a2019-07-09 16:24:22 -0700643 [](size_t x) { return !x; });
Ed Tanous7045c8d2017-04-03 10:04:37 -0700644 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700645 };
Ed Tanous7045c8d2017-04-03 10:04:37 -0700646
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 Trie() : nodes(1)
648 {
649 }
650
651 private:
652 void optimizeNode(Node* node)
653 {
Ed Tanous271584a2019-07-09 16:24:22 -0700654 for (size_t x : node->paramChildrens)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
656 if (!x)
657 continue;
658 Node* child = &nodes[x];
659 optimizeNode(child);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700660 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 if (node->children.empty())
662 return;
663 bool mergeWithChild = true;
Tanousf00032d2018-11-05 01:18:10 -0300664 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
666 Node* child = &nodes[kv.second];
667 if (!child->isSimpleNode())
668 {
669 mergeWithChild = false;
670 break;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700671 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 }
673 if (mergeWithChild)
674 {
675 decltype(node->children) merged;
Tanousf00032d2018-11-05 01:18:10 -0300676 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 {
678 Node* child = &nodes[kv.second];
Tanousf00032d2018-11-05 01:18:10 -0300679 for (const std::pair<std::string, unsigned>& childKv :
680 child->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
682 merged[kv.first + childKv.first] = childKv.second;
683 }
684 }
685 node->children = std::move(merged);
686 optimizeNode(node);
687 }
688 else
689 {
Tanousf00032d2018-11-05 01:18:10 -0300690 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 {
692 Node* child = &nodes[kv.second];
693 optimizeNode(child);
694 }
695 }
696 }
697
698 void optimize()
699 {
700 optimizeNode(head());
701 }
702
703 public:
704 void validate()
705 {
706 if (!head()->isSimpleNode())
707 throw std::runtime_error(
708 "Internal error: Trie header should be simple!");
709 optimize();
710 }
711
712 void findRouteIndexes(const std::string& req_url,
713 std::vector<unsigned>& route_indexes,
Tanousf00032d2018-11-05 01:18:10 -0300714 const Node* node = nullptr, unsigned pos = 0) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 {
716 if (node == nullptr)
717 {
718 node = head();
719 }
Tanousf00032d2018-11-05 01:18:10 -0300720 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
722 const std::string& fragment = kv.first;
723 const Node* child = &nodes[kv.second];
724 if (pos >= req_url.size())
725 {
726 if (child->ruleIndex != 0 && fragment != "/")
727 {
728 route_indexes.push_back(child->ruleIndex);
729 }
730 findRouteIndexes(req_url, route_indexes, child,
Ed Tanous271584a2019-07-09 16:24:22 -0700731 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 }
733 else
734 {
735 if (req_url.compare(pos, fragment.size(), fragment) == 0)
736 {
Ed Tanous271584a2019-07-09 16:24:22 -0700737 findRouteIndexes(
738 req_url, route_indexes, child,
739 static_cast<unsigned>(pos + fragment.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 }
741 }
742 }
743 }
744
745 std::pair<unsigned, RoutingParams>
Ed Tanous39e77502019-03-04 17:35:53 -0800746 find(const std::string_view req_url, const Node* node = nullptr,
Ed Tanous271584a2019-07-09 16:24:22 -0700747 size_t pos = 0, RoutingParams* params = nullptr) const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 {
749 RoutingParams empty;
750 if (params == nullptr)
751 params = &empty;
752
753 unsigned found{};
754 RoutingParams matchParams;
755
756 if (node == nullptr)
757 node = head();
758 if (pos == req_url.size())
759 return {node->ruleIndex, *params};
760
761 auto updateFound =
762 [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) {
763 if (ret.first && (!found || found > ret.first))
764 {
765 found = ret.first;
766 matchParams = std::move(ret.second);
767 }
768 };
769
Ed Tanous271584a2019-07-09 16:24:22 -0700770 if (node->paramChildrens[static_cast<size_t>(ParamType::INT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 {
772 char c = req_url[pos];
773 if ((c >= '0' && c <= '9') || c == '+' || c == '-')
774 {
775 char* eptr;
776 errno = 0;
777 long long int value =
778 std::strtoll(req_url.data() + pos, &eptr, 10);
779 if (errno != ERANGE && eptr != req_url.data() + pos)
780 {
781 params->intParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700782 std::pair<unsigned, RoutingParams> ret = find(
783 req_url,
784 &nodes[node->paramChildrens[static_cast<size_t>(
785 ParamType::INT)]],
786 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 updateFound(ret);
788 params->intParams.pop_back();
789 }
790 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700791 }
792
Ed Tanous271584a2019-07-09 16:24:22 -0700793 if (node->paramChildrens[static_cast<size_t>(ParamType::UINT)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 {
795 char c = req_url[pos];
796 if ((c >= '0' && c <= '9') || c == '+')
797 {
798 char* eptr;
799 errno = 0;
800 unsigned long long int value =
801 std::strtoull(req_url.data() + pos, &eptr, 10);
802 if (errno != ERANGE && eptr != req_url.data() + pos)
803 {
804 params->uintParams.push_back(value);
Ed Tanous271584a2019-07-09 16:24:22 -0700805 std::pair<unsigned, RoutingParams> ret = find(
806 req_url,
807 &nodes[node->paramChildrens[static_cast<size_t>(
808 ParamType::UINT)]],
809 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810 updateFound(ret);
811 params->uintParams.pop_back();
812 }
813 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700814 }
815
Ed Tanous271584a2019-07-09 16:24:22 -0700816 if (node->paramChildrens[static_cast<size_t>(ParamType::DOUBLE)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 {
818 char c = req_url[pos];
819 if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
820 {
821 char* eptr;
822 errno = 0;
823 double value = std::strtod(req_url.data() + pos, &eptr);
824 if (errno != ERANGE && eptr != req_url.data() + pos)
825 {
826 params->doubleParams.push_back(value);
Tanousf00032d2018-11-05 01:18:10 -0300827 std::pair<unsigned, RoutingParams> ret = find(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700829 &nodes[node->paramChildrens[static_cast<size_t>(
830 ParamType::DOUBLE)]],
831 static_cast<size_t>(eptr - req_url.data()), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 updateFound(ret);
833 params->doubleParams.pop_back();
834 }
835 }
836 }
837
Ed Tanous271584a2019-07-09 16:24:22 -0700838 if (node->paramChildrens[static_cast<size_t>(ParamType::STRING)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000840 size_t epos = pos;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 for (; epos < req_url.size(); epos++)
842 {
843 if (req_url[epos] == '/')
844 break;
845 }
846
847 if (epos != pos)
848 {
849 params->stringParams.emplace_back(
850 req_url.substr(pos, epos - pos));
Tanousf00032d2018-11-05 01:18:10 -0300851 std::pair<unsigned, RoutingParams> ret =
Ed Tanousb01bf292019-03-25 19:25:26 +0000852 find(req_url,
Ed Tanous271584a2019-07-09 16:24:22 -0700853 &nodes[node->paramChildrens[static_cast<size_t>(
854 ParamType::STRING)]],
Ed Tanousb01bf292019-03-25 19:25:26 +0000855 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 updateFound(ret);
857 params->stringParams.pop_back();
858 }
859 }
860
Ed Tanous271584a2019-07-09 16:24:22 -0700861 if (node->paramChildrens[static_cast<size_t>(ParamType::PATH)])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 {
Ed Tanousb01bf292019-03-25 19:25:26 +0000863 size_t epos = req_url.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700864
865 if (epos != pos)
866 {
867 params->stringParams.emplace_back(
868 req_url.substr(pos, epos - pos));
Ed Tanous271584a2019-07-09 16:24:22 -0700869 std::pair<unsigned, RoutingParams> ret =
870 find(req_url,
871 &nodes[node->paramChildrens[static_cast<size_t>(
872 ParamType::PATH)]],
873 epos, params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 updateFound(ret);
875 params->stringParams.pop_back();
876 }
877 }
878
Tanousf00032d2018-11-05 01:18:10 -0300879 for (const std::pair<std::string, unsigned>& kv : node->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 {
881 const std::string& fragment = kv.first;
882 const Node* child = &nodes[kv.second];
883
884 if (req_url.compare(pos, fragment.size(), fragment) == 0)
885 {
Tanousf00032d2018-11-05 01:18:10 -0300886 std::pair<unsigned, RoutingParams> ret =
887 find(req_url, child, pos + fragment.size(), params);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888 updateFound(ret);
889 }
890 }
891
892 return {found, matchParams};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700893 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894
895 void add(const std::string& url, unsigned ruleIndex)
896 {
Ed Tanous271584a2019-07-09 16:24:22 -0700897 size_t idx = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898
899 for (unsigned i = 0; i < url.size(); i++)
900 {
901 char c = url[i];
902 if (c == '<')
903 {
Tanousf00032d2018-11-05 01:18:10 -0300904 const static std::array<std::pair<ParamType, std::string>, 7>
905 paramTraits = {{
906 {ParamType::INT, "<int>"},
907 {ParamType::UINT, "<uint>"},
908 {ParamType::DOUBLE, "<float>"},
909 {ParamType::DOUBLE, "<double>"},
910 {ParamType::STRING, "<str>"},
911 {ParamType::STRING, "<string>"},
912 {ParamType::PATH, "<path>"},
913 }};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914
Tanousf00032d2018-11-05 01:18:10 -0300915 for (const std::pair<ParamType, std::string>& x : paramTraits)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700916 {
Tanousf00032d2018-11-05 01:18:10 -0300917 if (url.compare(i, x.second.size(), x.second) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700918 {
Ed Tanous271584a2019-07-09 16:24:22 -0700919 size_t index = static_cast<size_t>(x.first);
920 if (!nodes[idx].paramChildrens[index])
Ed Tanous1abe55e2018-09-05 08:30:59 -0700921 {
Tanousf00032d2018-11-05 01:18:10 -0300922 unsigned newNodeIdx = newNode();
Ed Tanous271584a2019-07-09 16:24:22 -0700923 nodes[idx].paramChildrens[index] = newNodeIdx;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 }
Ed Tanous271584a2019-07-09 16:24:22 -0700925 idx = nodes[idx].paramChildrens[index];
926 i += static_cast<unsigned>(x.second.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927 break;
928 }
929 }
930
931 i--;
932 }
933 else
934 {
935 std::string piece(&c, 1);
936 if (!nodes[idx].children.count(piece))
937 {
Tanousf00032d2018-11-05 01:18:10 -0300938 unsigned newNodeIdx = newNode();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 nodes[idx].children.emplace(piece, newNodeIdx);
940 }
941 idx = nodes[idx].children[piece];
942 }
943 }
944 if (nodes[idx].ruleIndex)
945 throw std::runtime_error("handler already exists for " + url);
946 nodes[idx].ruleIndex = ruleIndex;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700947 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700948
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 private:
Ed Tanous271584a2019-07-09 16:24:22 -0700950 void debugNodePrint(Node* n, size_t level)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 {
Ed Tanous271584a2019-07-09 16:24:22 -0700952 for (size_t i = 0; i < static_cast<size_t>(ParamType::MAX); i++)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700953 {
954 if (n->paramChildrens[i])
955 {
956 BMCWEB_LOG_DEBUG << std::string(
Ed Tanous271584a2019-07-09 16:24:22 -0700957 2U * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/;
958 switch (static_cast<ParamType>(i))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700959 {
960 case ParamType::INT:
961 BMCWEB_LOG_DEBUG << "<int>";
962 break;
963 case ParamType::UINT:
964 BMCWEB_LOG_DEBUG << "<uint>";
965 break;
966 case ParamType::DOUBLE:
967 BMCWEB_LOG_DEBUG << "<float>";
968 break;
969 case ParamType::STRING:
970 BMCWEB_LOG_DEBUG << "<str>";
971 break;
972 case ParamType::PATH:
973 BMCWEB_LOG_DEBUG << "<path>";
974 break;
975 default:
976 BMCWEB_LOG_DEBUG << "<ERROR>";
977 break;
978 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700979
Ed Tanous1abe55e2018-09-05 08:30:59 -0700980 debugNodePrint(&nodes[n->paramChildrens[i]], level + 1);
981 }
982 }
Tanousf00032d2018-11-05 01:18:10 -0300983 for (const std::pair<std::string, unsigned>& kv : n->children)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700984 {
985 BMCWEB_LOG_DEBUG
Ed Tanous271584a2019-07-09 16:24:22 -0700986 << std::string(2U * level, ' ') /*<< "(" << kv.second << ") "*/
Ed Tanous1abe55e2018-09-05 08:30:59 -0700987 << kv.first;
988 debugNodePrint(&nodes[kv.second], level + 1);
989 }
990 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700991
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 public:
993 void debugPrint()
994 {
Ed Tanous271584a2019-07-09 16:24:22 -0700995 debugNodePrint(head(), 0U);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700997
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 private:
999 const Node* head() const
1000 {
1001 return &nodes.front();
1002 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001003
Ed Tanous1abe55e2018-09-05 08:30:59 -07001004 Node* head()
1005 {
1006 return &nodes.front();
1007 }
1008
1009 unsigned newNode()
1010 {
1011 nodes.resize(nodes.size() + 1);
Ed Tanous271584a2019-07-09 16:24:22 -07001012 return static_cast<unsigned>(nodes.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001013 }
1014
1015 std::vector<Node> nodes;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001016};
1017
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018class Router
1019{
1020 public:
Tanousf00032d2018-11-05 01:18:10 -03001021 Router()
Ed Tanous1abe55e2018-09-05 08:30:59 -07001022 {
Ed Tanous7045c8d2017-04-03 10:04:37 -07001023 }
1024
Ed Tanous1abe55e2018-09-05 08:30:59 -07001025 DynamicRule& newRuleDynamic(const std::string& rule)
1026 {
1027 std::unique_ptr<DynamicRule> ruleObject =
1028 std::make_unique<DynamicRule>(rule);
1029 DynamicRule* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001030 allRules.emplace_back(std::move(ruleObject));
Ed Tanous7045c8d2017-04-03 10:04:37 -07001031
Ed Tanous1abe55e2018-09-05 08:30:59 -07001032 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001033 }
1034
Ed Tanous1abe55e2018-09-05 08:30:59 -07001035 template <uint64_t N>
1036 typename black_magic::Arguments<N>::type::template rebind<TaggedRule>&
1037 newRuleTagged(const std::string& rule)
1038 {
1039 using RuleT = typename black_magic::Arguments<N>::type::template rebind<
1040 TaggedRule>;
1041 std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule);
1042 RuleT* ptr = ruleObject.get();
Tanousf00032d2018-11-05 01:18:10 -03001043 allRules.emplace_back(std::move(ruleObject));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001044
1045 return *ptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001046 }
1047
Tanousf00032d2018-11-05 01:18:10 -03001048 void internalAddRuleObject(const std::string& rule, BaseRule* ruleObject)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001049 {
Tanousf00032d2018-11-05 01:18:10 -03001050 if (ruleObject == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001051 {
Tanousf00032d2018-11-05 01:18:10 -03001052 return;
1053 }
1054 for (uint32_t method = 0, method_bit = 1; method < maxHttpVerbCount;
1055 method++, method_bit <<= 1)
1056 {
1057 if (ruleObject->methodsBitfield & method_bit)
1058 {
1059 perMethods[method].rules.emplace_back(ruleObject);
1060 perMethods[method].trie.add(
Ed Tanous271584a2019-07-09 16:24:22 -07001061 rule, static_cast<unsigned>(
1062 perMethods[method].rules.size() - 1U));
Tanousf00032d2018-11-05 01:18:10 -03001063 // directory case:
1064 // request to `/about' url matches `/about/' rule
1065 if (rule.size() > 2 && rule.back() == '/')
1066 {
1067 perMethods[method].trie.add(
1068 rule.substr(0, rule.size() - 1),
Ed Tanous271584a2019-07-09 16:24:22 -07001069 static_cast<unsigned>(perMethods[method].rules.size() -
1070 1));
Tanousf00032d2018-11-05 01:18:10 -03001071 }
1072 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001073 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001074 }
1075
Ed Tanous1abe55e2018-09-05 08:30:59 -07001076 void validate()
1077 {
Tanousf00032d2018-11-05 01:18:10 -03001078 for (std::unique_ptr<BaseRule>& rule : allRules)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001079 {
1080 if (rule)
1081 {
Tanousf00032d2018-11-05 01:18:10 -03001082 std::unique_ptr<BaseRule> upgraded = rule->upgrade();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001083 if (upgraded)
1084 rule = std::move(upgraded);
1085 rule->validate();
Tanousf00032d2018-11-05 01:18:10 -03001086 internalAddRuleObject(rule->rule, rule.get());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 }
1088 }
Tanousf00032d2018-11-05 01:18:10 -03001089 for (PerMethod& perMethod : perMethods)
1090 {
1091 perMethod.trie.validate();
1092 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001093 }
1094
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 template <typename Adaptor>
1096 void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
1097 {
Ed Tanous271584a2019-07-09 16:24:22 -07001098 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001099 return;
1100
Ed Tanous271584a2019-07-09 16:24:22 -07001101 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001102 Trie& trie = perMethod.trie;
1103 std::vector<BaseRule*>& rules = perMethod.rules;
1104
1105 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001106 unsigned ruleIndex = found.first;
1107 if (!ruleIndex)
1108 {
1109 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001110 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001111 res.end();
1112 return;
1113 }
1114
1115 if (ruleIndex >= rules.size())
1116 throw std::runtime_error("Trie internal structure corrupted!");
1117
1118 if (ruleIndex == ruleSpecialRedirectSlash)
1119 {
1120 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1121 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001122 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123
1124 // TODO absolute url building
1125 if (req.getHeaderValue("Host").empty())
1126 {
1127 res.addHeader("Location", std::string(req.url) + "/");
1128 }
1129 else
1130 {
1131 res.addHeader(
1132 "Location",
1133 req.isSecure
1134 ? "https://"
1135 : "http://" + std::string(req.getHeaderValue("Host")) +
1136 std::string(req.url) + "/");
1137 }
1138 res.end();
1139 return;
1140 }
1141
Ed Tanous271584a2019-07-09 16:24:22 -07001142 if ((rules[ruleIndex]->getMethods() &
1143 (1U << static_cast<size_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001144 {
1145 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1146 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001147 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001149 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 res.end();
1151 return;
1152 }
1153
1154 BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule
Ed Tanous271584a2019-07-09 16:24:22 -07001155 << "' " << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 << rules[ruleIndex]->getMethods();
1157
1158 // any uncaught exceptions become 500s
1159 try
1160 {
1161 rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor));
1162 }
1163 catch (std::exception& e)
1164 {
1165 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001166 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001167 res.end();
1168 return;
1169 }
1170 catch (...)
1171 {
1172 BMCWEB_LOG_ERROR
1173 << "An uncaught exception occurred. The type was unknown "
1174 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001175 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001176 res.end();
1177 return;
1178 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001179 }
1180
Ed Tanous1abe55e2018-09-05 08:30:59 -07001181 void handle(const Request& req, Response& res)
1182 {
Ed Tanous271584a2019-07-09 16:24:22 -07001183 if (static_cast<size_t>(req.method()) >= perMethods.size())
Tanousf00032d2018-11-05 01:18:10 -03001184 return;
Ed Tanous271584a2019-07-09 16:24:22 -07001185 PerMethod& perMethod = perMethods[static_cast<size_t>(req.method())];
Tanousf00032d2018-11-05 01:18:10 -03001186 Trie& trie = perMethod.trie;
1187 std::vector<BaseRule*>& rules = perMethod.rules;
1188
1189 const std::pair<unsigned, RoutingParams>& found = trie.find(req.url);
Ed Tanous7045c8d2017-04-03 10:04:37 -07001190
Ed Tanous1abe55e2018-09-05 08:30:59 -07001191 unsigned ruleIndex = found.first;
1192
1193 if (!ruleIndex)
1194 {
Ed Tanous2634dcd2019-03-26 09:28:06 -07001195 // Check to see if this url exists at any verb
1196 for (const PerMethod& p : perMethods)
1197 {
1198 const std::pair<unsigned, RoutingParams>& found =
1199 p.trie.find(req.url);
1200 if (found.first > 0)
1201 {
1202 res.result(boost::beast::http::status::method_not_allowed);
1203 res.end();
1204 return;
1205 }
1206 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001207 BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
1208 res.result(boost::beast::http::status::not_found);
1209 res.end();
1210 return;
1211 }
1212
1213 if (ruleIndex >= rules.size())
1214 throw std::runtime_error("Trie internal structure corrupted!");
1215
1216 if (ruleIndex == ruleSpecialRedirectSlash)
1217 {
1218 BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
1219 << req.url;
Ed Tanousde5c9f32019-03-26 09:17:55 -07001220 res.result(boost::beast::http::status::moved_permanently);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001221
1222 // TODO absolute url building
1223 if (req.getHeaderValue("Host").empty())
1224 {
1225 res.addHeader("Location", std::string(req.url) + "/");
1226 }
1227 else
1228 {
1229 res.addHeader("Location",
1230 (req.isSecure ? "https://" : "http://") +
1231 std::string(req.getHeaderValue("Host")) +
1232 std::string(req.url) + "/");
1233 }
1234 res.end();
1235 return;
1236 }
1237
Ed Tanous271584a2019-07-09 16:24:22 -07001238 if ((rules[ruleIndex]->getMethods() &
1239 (1U << static_cast<uint32_t>(req.method()))) == 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001240 {
1241 BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url
1242 << " with " << req.methodString() << "("
Ed Tanous271584a2019-07-09 16:24:22 -07001243 << static_cast<uint32_t>(req.method()) << ") / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001244 << rules[ruleIndex]->getMethods();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001245 res.result(boost::beast::http::status::method_not_allowed);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001246 res.end();
1247 return;
1248 }
1249
1250 BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' "
Ed Tanous271584a2019-07-09 16:24:22 -07001251 << static_cast<uint32_t>(req.method()) << " / "
Ed Tanous1abe55e2018-09-05 08:30:59 -07001252 << rules[ruleIndex]->getMethods();
1253
Ratan Gupta6f359562019-04-03 10:39:08 +05301254 redfish::Privileges userPrivileges;
1255 if (req.session != nullptr)
1256 {
1257 // Get the user role from the session.
Ed Tanousca0c93b2019-09-19 11:53:50 -07001258 const std::string& userRole =
1259 persistent_data::UserRoleMap::getInstance().getUserRole(
1260 req.session->username);
Ratan Gupta6f359562019-04-03 10:39:08 +05301261
1262 BMCWEB_LOG_DEBUG << "USER ROLE=" << userRole;
1263
1264 // Get the user privileges from the role
1265 userPrivileges = redfish::getUserPrivileges(userRole);
1266 }
Tanousf00032d2018-11-05 01:18:10 -03001267
1268 if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
1269 {
Gunnar Mills961c9d92019-06-18 07:39:22 -05001270 res.result(boost::beast::http::status::forbidden);
Tanousf00032d2018-11-05 01:18:10 -03001271 res.end();
1272 return;
1273 }
1274
Ed Tanous1abe55e2018-09-05 08:30:59 -07001275 // any uncaught exceptions become 500s
1276 try
1277 {
1278 rules[ruleIndex]->handle(req, res, found.second);
1279 }
1280 catch (std::exception& e)
1281 {
1282 BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
Ed Tanousde5c9f32019-03-26 09:17:55 -07001283 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284 res.end();
1285 return;
1286 }
1287 catch (...)
1288 {
1289 BMCWEB_LOG_ERROR
1290 << "An uncaught exception occurred. The type was unknown "
1291 "so no information was available.";
Ed Tanousde5c9f32019-03-26 09:17:55 -07001292 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001293 res.end();
1294 return;
1295 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001296 }
Ed Tanous7045c8d2017-04-03 10:04:37 -07001297
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 void debugPrint()
1299 {
Ed Tanous271584a2019-07-09 16:24:22 -07001300 for (size_t i = 0; i < perMethods.size(); i++)
Tanousf00032d2018-11-05 01:18:10 -03001301 {
Ed Tanous271584a2019-07-09 16:24:22 -07001302 BMCWEB_LOG_DEBUG
1303 << methodName(static_cast<boost::beast::http::verb>(i));
Tanousf00032d2018-11-05 01:18:10 -03001304 perMethods[i].trie.debugPrint();
1305 }
Ed Tanous3dac7492017-08-02 13:46:20 -07001306 }
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07001307
Ed Tanous1abe55e2018-09-05 08:30:59 -07001308 std::vector<const std::string*> getRoutes(const std::string& parent)
1309 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001310 std::vector<const std::string*> ret;
Tanousf00032d2018-11-05 01:18:10 -03001311
1312 for (const PerMethod& pm : perMethods)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001313 {
Tanousf00032d2018-11-05 01:18:10 -03001314 std::vector<unsigned> x;
1315 pm.trie.findRouteIndexes(parent, x);
1316 for (unsigned index : x)
1317 {
1318 ret.push_back(&pm.rules[index]->rule);
1319 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001320 }
1321 return ret;
1322 }
1323
1324 private:
Tanousf00032d2018-11-05 01:18:10 -03001325 struct PerMethod
1326 {
1327 std::vector<BaseRule*> rules;
1328 Trie trie;
1329 // rule index 0, 1 has special meaning; preallocate it to avoid
1330 // duplication.
1331 PerMethod() : rules(2)
1332 {
1333 }
1334 };
1335 std::array<PerMethod, maxHttpVerbCount> perMethods;
1336 std::vector<std::unique_ptr<BaseRule>> allRules;
Ed Tanous7045c8d2017-04-03 10:04:37 -07001337};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001338} // namespace crow