plugin: implement replaceRedfishHandler
This commit implements the Redfish Plugin macro:
REDFISH_HANDLER_REPLACE
This plugin macro gives plugin user ability to replace an existing route
with a platform specific route. The plugin takes URL, Verb, and the new
callback.
Change-Id: I13d20b81635e64e9c74d9131c6e553daca6a77e8
Signed-off-by: Nan Zhou <nanzhou@google.com>
diff --git a/http/app.hpp b/http/app.hpp
index bd30007..019b5e7 100644
--- a/http/app.hpp
+++ b/http/app.hpp
@@ -126,6 +126,13 @@
router.newRuleTagged<Tag>(url).methods(verb).privileges(p)(func);
}
+ template <uint64_t Tag, typename Func>
+ void replaceHandler(std::string_view url, boost::beast::http::verb verb,
+ Func&& func)
+ {
+ router.replaceHandler<Tag>(url, verb, func);
+ }
+
#ifdef BMCWEB_ENABLE_SSL
App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx)
{
diff --git a/http/routing.cpp b/http/routing.cpp
index dce69f6..c04f051 100644
--- a/http/routing.cpp
+++ b/http/routing.cpp
@@ -679,4 +679,15 @@
return ret;
}
+void Router::throwHandlerNotFound(std::string_view url,
+ boost::beast::http::verb boostVerb)
+{
+
+ std::string error = "handler doesn't exist for ";
+ error += url;
+ error += ", method ";
+ error += boostVerbToString(boostVerb);
+ throw std::runtime_error(error);
+}
+
} // namespace crow
\ No newline at end of file
diff --git a/http/routing.hpp b/http/routing.hpp
index c60721c..92bbce8 100644
--- a/http/routing.hpp
+++ b/http/routing.hpp
@@ -694,6 +694,12 @@
(*this).template operator()<Func>(std::forward(f));
}
+ template <typename Func>
+ void replaceHandler(Func&& f)
+ {
+ this->operator()(f);
+ }
+
void handle(const Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const RoutingParams& params) override
@@ -961,6 +967,28 @@
});
}
+ template <uint64_t Tag>
+ size_t findRuleByUrlAndVerb(std::string_view url,
+ boost::beast::http::verb boostVerb)
+ {
+ std::optional<HttpVerb> verb = httpVerbFromBoost(boostVerb);
+ if (!verb)
+ {
+ return allRules.size();
+ }
+
+ for (size_t i = 0; i < allRules.size(); ++i)
+ {
+ if (allRules[i]->rule == url &&
+ (allRules[i]->methodsBitfield &
+ (1 << static_cast<size_t>(*verb))) > 0)
+ {
+ return i;
+ }
+ }
+ return allRules.size();
+ }
+
void handle(Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp);
@@ -968,6 +996,24 @@
std::vector<const std::string*> getRoutes(const std::string& parent);
+ void throwHandlerNotFound(std::string_view url,
+ boost::beast::http::verb boostVerb);
+
+ template <uint64_t Tag, typename Func>
+ void replaceHandler(std::string_view url,
+ boost::beast::http::verb boostVerb, Func&& func)
+ {
+ size_t index = findRuleByUrlAndVerb<Tag>(url, boostVerb);
+ if (index == allRules.size())
+ {
+ throwHandlerNotFound(url, boostVerb);
+ }
+ using RuleT =
+ black_magic::Arguments<Tag>::type::template rebind<TaggedRule>;
+ RuleT* rule = dynamic_cast<RuleT*>(allRules[index].get());
+ rule->replaceHandler(func);
+ }
+
private:
bool isUserPrivileged(Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
diff --git a/http/verb.hpp b/http/verb.hpp
index 4213604..bae3ec7 100644
--- a/http/verb.hpp
+++ b/http/verb.hpp
@@ -76,3 +76,13 @@
// Should never reach here
return "";
}
+
+inline std::string_view boostVerbToString(boost::beast::http::verb bv)
+{
+ std::optional<HttpVerb> verb = httpVerbFromBoost(bv);
+ if (!verb)
+ {
+ return "";
+ }
+ return httpVerbToString(*verb);
+}
\ No newline at end of file
diff --git a/meson.build b/meson.build
index 0245754..911ad93 100644
--- a/meson.build
+++ b/meson.build
@@ -7,7 +7,6 @@
'b_lto=true',
'b_ndebug=if-release',
'buildtype=debugoptimized',
- 'cpp_rtti=false',
'cpp_std=c++20',
'warning_level=3',
'werror=true',
diff --git a/plugins/macros.hpp b/plugins/macros.hpp
index 0d90605..8d1f6c4 100644
--- a/plugins/macros.hpp
+++ b/plugins/macros.hpp
@@ -12,7 +12,12 @@
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define REDFISH_HANDLER_ADD(url, verb, privileges, func) \
plugins::addRedfishHandler<crow::black_magic::getParameterTag(url)>( \
- url, verb, privileges, func);
+ url, verb, privileges, func)
+
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define REDFISH_HANDLER_REPLACE(url, verb, func) \
+ plugins::replaceRedfishHandler<crow::black_magic::getParameterTag(url)>( \
+ url, verb, func)
namespace plugins
{
@@ -23,4 +28,12 @@
{
crow::globalBmcWebApp->addHandler<tag>(url, verb, p, func);
}
+
+template <uint64_t tag, typename RedfishHandler>
+void replaceRedfishHandler(std::string_view url, boost::beast::http::verb verb,
+ RedfishHandler&& func)
+{
+ crow::globalBmcWebApp->replaceHandler<tag>(url, verb, func);
+}
+
} // namespace plugins
\ No newline at end of file
diff --git a/plugins/macros_test.cpp b/plugins/macros_test.cpp
index ca15030..5133d2d 100644
--- a/plugins/macros_test.cpp
+++ b/plugins/macros_test.cpp
@@ -35,7 +35,7 @@
std::unique_ptr<crow::App> app;
};
-TEST_F(PluginMacrosTest, AddHandlerCanBeInvoked)
+TEST_F(PluginMacrosTest, AddedHandlerCanBeInvoked)
{
bool called = false;
auto callback = [&called](const Request&,
@@ -60,8 +60,8 @@
TEST_F(PluginMacrosTest, DuplicateHandlerThrows)
{
- auto callback =
- [](const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) {};
+ auto callback = [](const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {};
REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get,
redfish::privileges::getChassis, std::move(callback));
@@ -71,5 +71,48 @@
EXPECT_ANY_THROW(crow::globalBmcWebApp->validate());
}
+TEST_F(PluginMacrosTest, ReplacedNonExistHandlerThrows)
+{
+ auto callback = [](const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {};
+ EXPECT_ANY_THROW(REDFISH_HANDLER_REPLACE("/hello/",
+ boost::beast::http::verb::get,
+ std::move(callback)););
+}
+
+TEST_F(PluginMacrosTest, ReplacedHandlerCanBeInvoked)
+{
+ bool originalCalled = false;
+ auto originalCallback =
+ [&originalCalled](const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ originalCalled = true;
+ };
+
+ bool replacedCalled = false;
+ auto replacedCallback =
+ [&replacedCalled](const Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ replacedCalled = true;
+ };
+
+ REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get,
+ redfish::privileges::getChassis,
+ std::move(originalCallback));
+ REDFISH_HANDLER_REPLACE("/hello/", boost::beast::http::verb::get,
+ std::move(replacedCallback));
+ crow::globalBmcWebApp->validate();
+
+ constexpr std::string_view url = "/hello/";
+ std::error_code ec;
+ Request req{{boost::beast::http::verb::get, url, 11}, ec};
+
+ auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
+ crow::globalBmcWebApp->handle(req, asyncResp);
+
+ EXPECT_FALSE(originalCalled);
+ EXPECT_TRUE(replacedCalled);
+}
+
} // namespace
} // namespace crow
\ No newline at end of file