Compare commits

..

2 Commits

Author SHA1 Message Date
an-tao
b37cf6ff97 Add new type of redirect handlers 2023-09-16 11:13:57 +08:00
an-tao
171f895975 opt 2023-09-15 13:24:32 +08:00
5 changed files with 111 additions and 62 deletions

View File

@ -22,11 +22,35 @@ namespace drogon
{ {
namespace plugin namespace plugin
{ {
/**
* @brief The RedirectorHandler is a function object that can be registered to
* the Redirector plugin. It is used to redirect requests to proper URLs. Users
* can modify the protocol, host and path of the request. If a false value is
* returned, the request will be considered as invalid and a 404 response will
* be sent to the client.
*/
using RedirectorHandler = using RedirectorHandler =
std::function<bool(const drogon::HttpRequestPtr &, std::function<bool(const drogon::HttpRequestPtr &,
std::string &, //"http://" or "https://" std::string &, //"http://" or "https://"
std::string &, // host std::string &, // host
bool &)>; // path changed or not bool &)>; // path changed or not
/**
* @brief The PathRewriteHandler is a function object that can be registered to
* the Redirector plugin. It is used to rewrite the path of the request. The
* Redirector plugin will call all registered PathRewriteHandlers in the order
* of registration. If one or more handlers return true, the request will be
* redirected to the new path.
*/
using PathRewriteHandler = std::function<bool(const drogon::HttpRequestPtr &)>;
/**
* @brief The ForwardHandler is a function object that can be registered to the
* Redirector plugin. It is used to forward the request to next processing steps
* in the framework. The Redirector plugin will call all registered
* ForwardHandlers in the order of registration. Users can use this handler to
* change the request path or any other part of the request.
*/
using ForwardHandler = std::function<void(const drogon::HttpRequestPtr &)>;
/** /**
* @brief This plugin is used to redirect requests to proper URLs. It is a * @brief This plugin is used to redirect requests to proper URLs. It is a
@ -56,18 +80,40 @@ class DROGON_EXPORT Redirector : public drogon::Plugin<Redirector>,
void initAndStart(const Json::Value &config) override; void initAndStart(const Json::Value &config) override;
void shutdown() override; void shutdown() override;
void registerHandler(RedirectorHandler &&handler) void registerRedirectHandler(RedirectorHandler &&handler)
{ {
handlers_.emplace_back(std::move(handler)); handlers_.emplace_back(std::move(handler));
} }
void registerHandler(const RedirectorHandler &handler) void registerRedirectHandler(const RedirectorHandler &handler)
{ {
handlers_.emplace_back(handler); handlers_.emplace_back(handler);
} }
void registerPathRewriteHandler(PathRewriteHandler &&handler)
{
pathRewriteHandlers_.emplace_back(std::move(handler));
}
void registerPathRewriteHandler(const PathRewriteHandler &handler)
{
pathRewriteHandlers_.emplace_back(handler);
}
void registerForwardHandler(ForwardHandler &&handler)
{
forwardHandlers_.emplace_back(std::move(handler));
}
void registerForwardHandler(const ForwardHandler &handler)
{
forwardHandlers_.emplace_back(handler);
}
private: private:
std::vector<RedirectorHandler> handlers_; std::vector<RedirectorHandler> handlers_;
std::vector<PathRewriteHandler> pathRewriteHandlers_;
std::vector<ForwardHandler> forwardHandlers_;
}; };
} // namespace plugin } // namespace plugin

View File

@ -66,12 +66,10 @@ class DROGON_EXPORT SecureSSLRedirector
private: private:
bool redirectingAdvice(const HttpRequestPtr &, bool redirectingAdvice(const HttpRequestPtr &,
std::string &, std::string &,
std::string &, std::string &) const;
bool &) const;
bool redirectToSSL(const HttpRequestPtr &, bool redirectToSSL(const HttpRequestPtr &,
std::string &, std::string &,
std::string &, std::string &) const;
bool &) const;
std::regex exemptPegex_; std::regex exemptPegex_;
bool regexFlag_{false}; bool regexFlag_{false};

View File

@ -37,6 +37,10 @@ void Redirector::initAndStart(const Json::Value &config)
return HttpResponse::newNotFoundResponse(); return HttpResponse::newNotFoundResponse();
} }
} }
for (auto &handler : thisPtr->pathRewriteHandlers_)
{
pathChanged |= handler(req);
}
if (!protocol.empty() || !host.empty() || pathChanged) if (!protocol.empty() || !host.empty() || pathChanged)
{ {
std::string url; std::string url;
@ -48,10 +52,6 @@ void Redirector::initAndStart(const Json::Value &config)
: "http://"; : "http://";
url.append(host); url.append(host);
} }
if (pathChanged)
{
url.append(req->path());
}
} }
else else
{ {
@ -64,11 +64,8 @@ void Redirector::initAndStart(const Json::Value &config)
{ {
url.append(req->getHeader("host")); url.append(req->getHeader("host"));
} }
if (pathChanged)
{
url.append(req->path());
}
} }
url.append(req->path());
auto &query = req->query(); auto &query = req->query();
if (!query.empty()) if (!query.empty())
{ {
@ -76,6 +73,10 @@ void Redirector::initAndStart(const Json::Value &config)
} }
return HttpResponse::newRedirectionResponse(url); return HttpResponse::newRedirectionResponse(url);
} }
for (auto &handler : thisPtr->forwardHandlers_)
{
handler(req);
}
return HttpResponsePtr{}; return HttpResponsePtr{};
}); });
} }

View File

@ -49,17 +49,18 @@ void SecureSSLRedirector::initAndStart(const Json::Value &config)
LOG_ERROR << "Redirector plugin is not found!"; LOG_ERROR << "Redirector plugin is not found!";
return; return;
} }
redirector->registerHandler([weakPtr](const drogon::HttpRequestPtr &req, redirector->registerRedirectHandler(
std::string &protocol, [weakPtr](const drogon::HttpRequestPtr &req,
std::string &host, std::string &protocol,
bool &pathChanged) -> bool { std::string &host,
auto thisPtr = weakPtr.lock(); bool &) -> bool {
if (!thisPtr) auto thisPtr = weakPtr.lock();
{ if (!thisPtr)
return false; {
} return false;
return thisPtr->redirectingAdvice(req, protocol, host, pathChanged); }
}); return thisPtr->redirectingAdvice(req, protocol, host);
});
} }
void SecureSSLRedirector::shutdown() void SecureSSLRedirector::shutdown()
@ -69,8 +70,7 @@ void SecureSSLRedirector::shutdown()
bool SecureSSLRedirector::redirectingAdvice(const HttpRequestPtr &req, bool SecureSSLRedirector::redirectingAdvice(const HttpRequestPtr &req,
std::string &protocol, std::string &protocol,
std::string &host, std::string &host) const
bool &pathChanged) const
{ {
if (req->isOnSecureConnection() || protocol == "https://") if (req->isOnSecureConnection() || protocol == "https://")
{ {
@ -85,21 +85,19 @@ bool SecureSSLRedirector::redirectingAdvice(const HttpRequestPtr &req,
} }
else else
{ {
return redirectToSSL(req, protocol, host, pathChanged); return redirectToSSL(req, protocol, host);
} }
} }
else else
{ {
return redirectToSSL(req, protocol, host, pathChanged); return redirectToSSL(req, protocol, host);
} }
} }
bool SecureSSLRedirector::redirectToSSL(const HttpRequestPtr &req, bool SecureSSLRedirector::redirectToSSL(const HttpRequestPtr &req,
std::string &protocol, std::string &protocol,
std::string &host, std::string &host) const
bool &pathChanged) const
{ {
(void)pathChanged;
if (!secureHost_.empty()) if (!secureHost_.empty())
{ {
host = secureHost_; host = secureHost_;

View File

@ -66,6 +66,31 @@ static inline void removeExcessiveSlashes(string& url)
removeDuplicateSlashes(url); removeDuplicateSlashes(url);
} }
static inline bool handleReq(const drogon::HttpRequestPtr& req, int removeMode)
{
static const std::regex regex(regexes[removeMode - 1]);
if (std::regex_match(req->path(), regex))
{
string newPath = req->path();
switch (removeMode)
{
case trailing:
removeTrailingSlashes(newPath);
break;
case duplicate:
removeDuplicateSlashes(newPath);
break;
case both:
default:
removeExcessiveSlashes(newPath);
break;
}
req->setPath(std::move(newPath));
return true;
}
return false;
}
void SlashRemover::initAndStart(const Json::Value& config) void SlashRemover::initAndStart(const Json::Value& config)
{ {
trailingSlashes_ = config.get("remove_trailing_slashes", true).asBool(); trailingSlashes_ = config.get("remove_trailing_slashes", true).asBool();
@ -81,36 +106,17 @@ void SlashRemover::initAndStart(const Json::Value& config)
LOG_ERROR << "Redirector plugin is not found!"; LOG_ERROR << "Redirector plugin is not found!";
return; return;
} }
redirector->registerHandler( auto func = [removeMode](const HttpRequestPtr& req) -> bool {
[removeMode, redirect = redirect_](const HttpRequestPtr& req, return handleReq(req, removeMode);
std::string& protocol, };
std::string& host, if (redirect_)
bool& pathChanged) -> bool { {
static const std::regex regex(regexes[removeMode - 1]); redirector->registerPathRewriteHandler(std::move(func));
if (std::regex_match(req->path(), regex)) }
{ else
string newPath = req->path(); {
switch (removeMode) redirector->registerForwardHandler(std::move(func));
{ }
case trailing:
removeTrailingSlashes(newPath);
break;
case duplicate:
removeDuplicateSlashes(newPath);
break;
case both:
default:
removeExcessiveSlashes(newPath);
break;
}
req->setPath(std::move(newPath));
if (redirect)
{
pathChanged = true;
}
}
return true;
});
} }
void SlashRemover::shutdown() void SlashRemover::shutdown()