From c7912f246b27a45fc1efe53ab1479ab527e41b27 Mon Sep 17 00:00:00 2001 From: An Tao Date: Sun, 16 Apr 2023 11:55:00 +0800 Subject: [PATCH] Add the GlobalFilters plugin (#1555) --- CMakeLists.txt | 5 +- lib/inc/drogon/plugins/GlobalFilters.h | 51 ++++++++++ lib/inc/drogon/plugins/SecureSSLRedirector.h | 12 ++- lib/src/GlobalFilters.cc | 99 ++++++++++++++++++++ lib/src/SecureSSLRedirector.cc | 40 +++++--- 5 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 lib/inc/drogon/plugins/GlobalFilters.h create mode 100644 lib/src/GlobalFilters.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index b884f646..16d36fce 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,7 @@ set(DROGON_SOURCES lib/src/PluginsManager.cc lib/src/RangeParser.cc lib/src/SecureSSLRedirector.cc + lib/src/GlobalFilters.cc lib/src/AccessLogger.cc lib/src/RealIpResolver.cc lib/src/SessionManager.cc @@ -729,7 +730,9 @@ set(DROGON_PLUGIN_HEADERS lib/inc/drogon/plugins/SecureSSLRedirector.h lib/inc/drogon/plugins/AccessLogger.h lib/inc/drogon/plugins/RealIpResolver.h - lib/inc/drogon/plugins/Hodor.h) + lib/inc/drogon/plugins/Hodor.h + lib/inc/drogon/plugins/GlobalFilters.h) + install(FILES ${DROGON_PLUGIN_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins) diff --git a/lib/inc/drogon/plugins/GlobalFilters.h b/lib/inc/drogon/plugins/GlobalFilters.h new file mode 100644 index 00000000..4ee99403 --- /dev/null +++ b/lib/inc/drogon/plugins/GlobalFilters.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace plugin +{ +/** + * @brif This plugin is used to add global filters to all HTTP requests. + * The json configuration is as follows: + * + * @code + { + "name": "drogon::plugin::GlobalFilters", + "dependencies": [], + "config": { + // filters: the list of global filter names. + "filters": [ + "FilterName1", "FilterName2",... + ], + // exempt: exempt must be a string or string array, regular + expressions for + // URLs that don't have to be filtered. + "exempt": [ + "^/static/.*\\.css", "^/images/.*",... + ] + } + } + @endcode + * + */ +class GlobalFilters : public drogon::Plugin, + public std::enable_shared_from_this +{ + public: + GlobalFilters() = default; + void initAndStart(const Json::Value &config) override; + void shutdown() override; + + private: + std::vector> filters_; + std::regex exemptPegex_; + bool regexFlag_{false}; +}; +} // namespace plugin +} // namespace drogon \ No newline at end of file diff --git a/lib/inc/drogon/plugins/SecureSSLRedirector.h b/lib/inc/drogon/plugins/SecureSSLRedirector.h index fbc8e996..bd079436 100644 --- a/lib/inc/drogon/plugins/SecureSSLRedirector.h +++ b/lib/inc/drogon/plugins/SecureSSLRedirector.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace drogon { @@ -32,8 +33,12 @@ namespace plugin } @endcode * - * ssl_redirect_exempt: a regular expression (for matching the path of a - * request) list for URLs that don't have to be redirected. + * ssl_redirect_exempt: must be a string or a string array, present a regular + expression + * (for matching the path of a request) or a regular expression list for URLs + that don't + * have to be redirected. + * * secure_ssl_host: If this string is not empty, all SSL redirects * will be directed to this host rather than the originally-requested host. * @@ -42,7 +47,8 @@ namespace plugin * */ class DROGON_EXPORT SecureSSLRedirector - : public drogon::Plugin + : public drogon::Plugin, + public std::enable_shared_from_this { public: SecureSSLRedirector() diff --git a/lib/src/GlobalFilters.cc b/lib/src/GlobalFilters.cc new file mode 100644 index 00000000..363b6bf5 --- /dev/null +++ b/lib/src/GlobalFilters.cc @@ -0,0 +1,99 @@ +#include +#include +#include +#include "FiltersFunction.h" +#include "HttpRequestImpl.h" + +using namespace drogon::plugin; + +void GlobalFilters::initAndStart(const Json::Value &config) +{ + if (config.isMember("filters") && config["filters"].isArray()) + { + auto &filters = config["filters"]; + + for (auto const &filter : filters) + { + if (filter.isString()) + { + auto filterPtr = std::dynamic_pointer_cast( + drogon::DrClassMap::getSingleInstance(filter.asString())); + if (filterPtr) + { + filters_.push_back(filterPtr); + } + else + { + LOG_ERROR << "Filter " << filter.asString() + << " not found!"; + } + } + } + } + if (config.isMember("exempt")) + { + auto exempt = config["exempt"]; + if (exempt.isArray()) + { + std::string regexStr; + for (auto const &ex : exempt) + { + if (ex.isString()) + { + regexStr.append("(").append(exempt.asString()).append(")|"); + } + else + { + LOG_ERROR << "exempt must be a string array!"; + } + } + if (!regexStr.empty()) + { + regexStr.pop_back(); + exemptPegex_ = std::regex(regexStr); + regexFlag_ = true; + } + } + else if (exempt.isString()) + { + exemptPegex_ = std::regex(exempt.asString()); + regexFlag_ = true; + } + else + { + LOG_ERROR << "exempt must be a string or string array!"; + } + } + std::weak_ptr weakPtr = shared_from_this(); + drogon::app().registerPreRoutingAdvice( + [weakPtr](const drogon::HttpRequestPtr &req, + drogon::AdviceCallback &&acb, + drogon::AdviceChainCallback &&accb) { + auto thisPtr = weakPtr.lock(); + if (!thisPtr) + { + accb(); + return; + } + if (thisPtr->regexFlag_) + { + if (std::regex_match(req->path(), thisPtr->exemptPegex_)) + { + accb(); + return; + } + } + auto callbackPtr = + std::make_shared>( + std::move(acb)); + drogon::filters_function::doFilters( + thisPtr->filters_, + std::static_pointer_cast(req), + callbackPtr, + std::move(accb)); + }); +} +void GlobalFilters::shutdown() +{ + filters_.clear(); +} \ No newline at end of file diff --git a/lib/src/SecureSSLRedirector.cc b/lib/src/SecureSSLRedirector.cc index 547dd3f1..da5d0e31 100644 --- a/lib/src/SecureSSLRedirector.cc +++ b/lib/src/SecureSSLRedirector.cc @@ -12,25 +12,43 @@ using namespace drogon::plugin; void SecureSSLRedirector::initAndStart(const Json::Value &config) { - if (config.isMember("ssl_redirect_exempt") && - config["ssl_redirect_exempt"].isArray()) + if (config.isMember("ssl_redirect_exempt")) { - std::string regexString; - for (auto &exempt : config["ssl_redirect_exempt"]) + if (config["ssl_redirect_exempt"].isArray()) { - assert(exempt.isString()); - regexString.append("(").append(exempt.asString()).append(")|"); + std::string regexString; + for (auto &exempt : config["ssl_redirect_exempt"]) + { + assert(exempt.isString()); + regexString.append("(").append(exempt.asString()).append(")|"); + } + if (!regexString.empty()) + { + regexString.resize(regexString.length() - 1); + exemptPegex_ = std::regex(regexString); + regexFlag_ = true; + } } - if (!regexString.empty()) + else if (config["ssl_redirect_exempt"].isString()) { - regexString.resize(regexString.length() - 1); - exemptPegex_ = std::regex(regexString); + exemptPegex_ = std::regex(config["ssl_redirect_exempt"].asString()); regexFlag_ = true; } + else + { + LOG_ERROR + << "ssl_redirect_exempt must be a string or string array!"; + } } secureHost_ = config.get("secure_ssl_host", "").asString(); - app().registerSyncAdvice([this](const HttpRequestPtr &req) { - return this->redirectingAdvice(req); + std::weak_ptr weakPtr = shared_from_this(); + app().registerSyncAdvice([weakPtr](const HttpRequestPtr &req) { + auto thisPtr = weakPtr.lock(); + if (!thisPtr) + { + return HttpResponsePtr{}; + } + return thisPtr->redirectingAdvice(req); }); }