Add the GlobalFilters plugin (#1555)

This commit is contained in:
An Tao 2023-04-16 11:55:00 +08:00 committed by GitHub
parent d133b21804
commit c7912f246b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 192 additions and 15 deletions

View File

@ -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)

View File

@ -0,0 +1,51 @@
#pragma once
#include <drogon/plugins/Plugin.h>
#include <regex>
#include <vector>
#include <memory>
#include <drogon/HttpFilter.h>
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<GlobalFilters>,
public std::enable_shared_from_this<GlobalFilters>
{
public:
GlobalFilters() = default;
void initAndStart(const Json::Value &config) override;
void shutdown() override;
private:
std::vector<std::shared_ptr<drogon::HttpFilterBase>> filters_;
std::regex exemptPegex_;
bool regexFlag_{false};
};
} // namespace plugin
} // namespace drogon

View File

@ -9,6 +9,7 @@
#include <drogon/drogon_callbacks.h>
#include <drogon/plugins/Plugin.h>
#include <regex>
#include <memory>
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<SecureSSLRedirector>
: public drogon::Plugin<SecureSSLRedirector>,
public std::enable_shared_from_this<SecureSSLRedirector>
{
public:
SecureSSLRedirector()

99
lib/src/GlobalFilters.cc Normal file
View File

@ -0,0 +1,99 @@
#include <drogon/plugins/GlobalFilters.h>
#include <drogon/DrClassMap.h>
#include <drogon/HttpAppFramework.h>
#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<HttpFilterBase>(
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<GlobalFilters> 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::function<void(const HttpResponsePtr &)>>(
std::move(acb));
drogon::filters_function::doFilters(
thisPtr->filters_,
std::static_pointer_cast<HttpRequestImpl>(req),
callbackPtr,
std::move(accb));
});
}
void GlobalFilters::shutdown()
{
filters_.clear();
}

View File

@ -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<SecureSSLRedirector> weakPtr = shared_from_this();
app().registerSyncAdvice([weakPtr](const HttpRequestPtr &req) {
auto thisPtr = weakPtr.lock();
if (!thisPtr)
{
return HttpResponsePtr{};
}
return thisPtr->redirectingAdvice(req);
});
}