Skip to content

Commit

Permalink
Add path redirection, rename handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
Mis1eader-dev committed Sep 27, 2023
1 parent 5702c1c commit 6ddc82c
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 59 deletions.
15 changes: 13 additions & 2 deletions lib/inc/drogon/plugins/HostRedirector.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <json/value.h>
#include <memory>
#include <unordered_map>
#include <unordered_set>

namespace drogon::plugin
{
Expand All @@ -38,9 +39,12 @@ namespace drogon::plugin
"ww.example.com"
],
"images.example.com": [
"image.example.com"
"image.example.com",
"www.example.com/images",
"www.example.com/image"
],
"www.example.com/maps": [
"www.example.com/map",
"map.example.com",
"maps.example.com"
]
Expand All @@ -56,6 +60,13 @@ class DROGON_EXPORT HostRedirector
: public drogon::Plugin<HostRedirector>,
public std::enable_shared_from_this<HostRedirector>
{
private:
struct RedirectRule
{
std::string redirectToHost, redirectToPath;
std::unordered_set<std::string> paths;
};

public:
HostRedirector()
{
Expand All @@ -69,6 +80,6 @@ class DROGON_EXPORT HostRedirector
std::string &,
bool &);

std::unordered_map<std::string, std::string> rules_;
std::unordered_map<std::string, RedirectRule> rules_;
};
} // namespace drogon::plugin
93 changes: 59 additions & 34 deletions lib/inc/drogon/plugins/Redirector.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,48 @@ namespace drogon
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.
* @brief The PreRedirectorHandler 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 PreRedirectorHandler =
std::function<bool(const drogon::HttpRequestPtr &,
std::string &, //"http://" or "https://"
std::string &, // host
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.
* @brief The PathRedirectorHandler 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 PathRedirectorHandlers in the
* order of registration. If one or more handlers return true, the request will
* be redirected to the new path.
*/
using PathRedirectorHandler =
std::function<bool(const drogon::HttpRequestPtr &)>;

/**
* @brief The PostRedirectorHandler 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 host or 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 PathRewriteHandler = std::function<bool(const drogon::HttpRequestPtr &)>;
using PostRedirectorHandler =
std::function<bool(const drogon::HttpRequestPtr &,
std::string &, // host
bool &)>; // path changed or not

/**
* @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.
* @brief The PathForwarderHandler 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 PathForwarderHandlers 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 &)>;
using PathForwarderHandler =
std::function<void(const drogon::HttpRequestPtr &)>;

/**
* @brief This plugin is used to redirect requests to proper URLs. It is a
Expand Down Expand Up @@ -79,41 +93,52 @@ class DROGON_EXPORT Redirector : public drogon::Plugin<Redirector>,
void initAndStart(const Json::Value &config) override;
void shutdown() override;

void registerRedirectHandler(RedirectorHandler &&handler)
void registerPreRedirectorHandler(PreRedirectorHandler &&handler)
{
preRedirectorHandlers_.emplace_back(std::move(handler));
}

void registerPreRedirectorHandler(const PreRedirectorHandler &handler)
{
preRedirectorHandlers_.emplace_back(handler);
}

void registerPathRedirectorHandler(PathRedirectorHandler &&handler)
{
handlers_.emplace_back(std::move(handler));
pathRedirectorHandlers_.emplace_back(std::move(handler));
}

void registerRedirectHandler(const RedirectorHandler &handler)
void registerPathRedirectorHandler(const PathRedirectorHandler &handler)
{
handlers_.emplace_back(handler);
pathRedirectorHandlers_.emplace_back(handler);
}

void registerPathRewriteHandler(PathRewriteHandler &&handler)
void registerPostRedirectorHandler(PostRedirectorHandler &&handler)
{
pathRewriteHandlers_.emplace_back(std::move(handler));
postRedirectorHandlers_.emplace_back(std::move(handler));
}

void registerPathRewriteHandler(const PathRewriteHandler &handler)
void registerPostRedirectorHandler(const PostRedirectorHandler &handler)
{
pathRewriteHandlers_.emplace_back(handler);
postRedirectorHandlers_.emplace_back(handler);
}

void registerForwardHandler(ForwardHandler &&handler)
void registerPathForwarderHandler(PathForwarderHandler &&handler)
{
forwardHandlers_.emplace_back(std::move(handler));
pathForwarderHandlers_.emplace_back(std::move(handler));
}

void registerForwardHandler(const ForwardHandler &handler)
void registerPathForwarderHandler(const PathForwarderHandler &handler)
{
forwardHandlers_.emplace_back(handler);
pathForwarderHandlers_.emplace_back(handler);
}

private:
std::vector<RedirectorHandler> handlers_;
std::vector<PathRewriteHandler> pathRewriteHandlers_;
std::vector<ForwardHandler> forwardHandlers_;
std::vector<PreRedirectorHandler> preRedirectorHandlers_;
std::vector<PathRedirectorHandler> pathRedirectorHandlers_;
std::vector<PostRedirectorHandler> postRedirectorHandlers_;
std::vector<PathForwarderHandler> pathForwarderHandlers_;
};

} // namespace plugin
} // namespace drogon
} // namespace drogon
76 changes: 59 additions & 17 deletions lib/src/HostRedirector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,27 @@ bool HostRedirector::redirectingAdvice(const HttpRequestPtr& req,
bool& pathChanged)
{
const string& reqHost = host.empty() ? req->getHeader("host") : host;
auto find = rules_.find(reqHost);
if (find != rules_.end())
host = find->second;

// TODO: some may need to change the path as well
auto findRule = rules_.find(reqHost);
if (findRule != rules_.end())
{
auto& rule = findRule->second;
auto& paths = rule.paths;
auto& path = req->path();
auto findPath = paths.find(req->path());
if (findPath != paths.end())
{
auto& redirectToHost = rule.redirectToHost;
if (redirectToHost != reqHost)
host = redirectToHost;

auto& redirectToPath = rule.redirectToPath;
if (redirectToPath != path)
{
req->setPath(redirectToPath);
pathChanged = true;
}
}
}
return true;
}

Expand All @@ -39,12 +54,39 @@ void HostRedirector::initAndStart(const Json::Value& config)
continue;

const string redirectToStr = redirectTo.asString();
string redirectToHost, redirectToPath;
auto pathIndex = redirectToStr.find_first_of('/');
if (pathIndex != string::npos)
{
redirectToHost = redirectToStr.substr(0, pathIndex);
redirectToPath = redirectToStr.substr(pathIndex);
}
else
redirectToPath = "/";

for (const auto& redirectFrom : rules[redirectToStr])
{
if (!redirectFrom.isString())
continue;

rules_[redirectFrom.asString()] = redirectToStr;
const string redirectFromStr = redirectFrom.asString();
string redirectFromHost, redirectFromPath;
pathIndex = redirectFromStr.find_first_of('/');
if (pathIndex != string::npos)
{
redirectFromHost = redirectFromStr.substr(0, pathIndex);
redirectFromPath = redirectFromStr.substr(pathIndex);
}
else
redirectFromPath = "/";

auto& rule =
rules_[redirectFromHost.empty() ? redirectFromStr
: redirectFromHost];
rule.redirectToHost =
redirectToHost.empty() ? redirectToStr : redirectToHost;
rule.redirectToPath = redirectToPath;
rule.paths.insert(redirectFromPath);
}
}
}
Expand All @@ -56,17 +98,17 @@ void HostRedirector::initAndStart(const Json::Value& config)
LOG_ERROR << "Redirector plugin is not found!";
return;
}
redirector->registerRedirectHandler([weakPtr](const HttpRequestPtr& req,
string&,
string& host,
bool& pathChanged) -> bool {
auto thisPtr = weakPtr.lock();
if (!thisPtr)
{
return false;
}
return thisPtr->redirectingAdvice(req, host, pathChanged);
});
redirector->registerPostRedirectorHandler(
[weakPtr](const HttpRequestPtr& req,
string& host,
bool& pathChanged) -> bool {
auto thisPtr = weakPtr.lock();
if (!thisPtr)
{
return false;
}
return thisPtr->redirectingAdvice(req, host, pathChanged);
});
}

void HostRedirector::shutdown()
Expand Down
13 changes: 10 additions & 3 deletions lib/src/Redirector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ void Redirector::initAndStart(const Json::Value &config)
}
std::string protocol, host;
bool pathChanged{false};
for (auto &handler : thisPtr->handlers_)
for (auto &handler : thisPtr->preRedirectorHandlers_)
{
if (!handler(req, protocol, host, pathChanged))
{
return HttpResponse::newNotFoundResponse();
}
}
for (auto &handler : thisPtr->pathRewriteHandlers_)
for (auto &handler : thisPtr->pathRedirectorHandlers_)
{
pathChanged |= handler(req);
}
for (auto &handler : thisPtr->postRedirectorHandlers_)
{
if (!handler(req, host, pathChanged))
{
return HttpResponse::newNotFoundResponse();
}
}
if (!protocol.empty() || !host.empty() || pathChanged)
{
std::string url;
Expand Down Expand Up @@ -73,7 +80,7 @@ void Redirector::initAndStart(const Json::Value &config)
}
return HttpResponse::newRedirectionResponse(url);
}
for (auto &handler : thisPtr->forwardHandlers_)
for (auto &handler : thisPtr->pathForwarderHandlers_)
{
handler(req);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/SecureSSLRedirector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void SecureSSLRedirector::initAndStart(const Json::Value &config)
LOG_ERROR << "Redirector plugin is not found!";
return;
}
redirector->registerRedirectHandler(
redirector->registerPreRedirectorHandler(
[weakPtr](const drogon::HttpRequestPtr &req,
std::string &protocol,
std::string &host,
Expand Down
4 changes: 2 additions & 2 deletions lib/src/SlashRemover.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ void SlashRemover::initAndStart(const Json::Value& config)
};
if (redirect_)
{
redirector->registerPathRewriteHandler(std::move(func));
redirector->registerPathRedirectorHandler(std::move(func));
}
else
{
redirector->registerForwardHandler(std::move(func));
redirector->registerPathForwarderHandler(std::move(func));
}
}

Expand Down

0 comments on commit 6ddc82c

Please sign in to comment.