-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Host Redirector plugin #1796
base: master
Are you sure you want to change the base?
Host Redirector plugin #1796
Conversation
We can add a new group to the redirector plugin, and rename the groups to something easy to remember:
This HostRedirector will register itself within the 3rd group, to receive the path after clean up from SlashRemover, which sits within group 2. |
24ba2ac
to
5702c1c
Compare
I noticed the formatter doesn't move const string& host = ...; Will not get formatted to: const string &host = ...; Is it perhaps a bug, or did formatting rules get changed to allow both styles? |
This seems too complex for users. |
Both styles seem to be present in the current code, you can try the configuration of clang-format and choose the second style. |
Yeah, and we don't have another choice. The only way to not introduce another group is to integrate this plugin into Redirector itself. I have added the 3rd group and renamed the handlers for better clarity, should I revert it? We can think of it the same as AOP callbacks, we can say they also are complex in a way without reading the documentation and seeing the picture visualization. |
I also think that it is kind of over-engineered for most use cases and makes the API harder to understand for new users. I’m in favor of keeping things simple. |
The shown diagram redirects in the following fashion:
The code of said draft #include <cstddef>
#include <cstdint>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
using std::cout;
using std::string;
struct RedirectTo
{
string host, path;
};
struct RedirectPath
{
std::unordered_map<string, RedirectPath*> paths;
size_t minPathLen = SIZE_MAX;
size_t minPathDepth = SIZE_MAX;
RedirectTo* wildcard;
RedirectTo* to;
};
static void recurse(const RedirectPath* parent, size_t tabs = 1)
{
if(!parent)
return;
const size_t moreTab = tabs + 1;
for(const auto& [childPathStr, childPathObj] : parent->paths)
{
bool newline = false;
if(childPathObj->to)
{
for(size_t t = tabs; t; --t)
cout << '\t';
cout << childPathStr;
cout << " -> " << childPathObj->to->host << childPathObj->to->path;
newline = true;
}
if(childPathObj->wildcard)
{
if(newline)
cout << '\n';
for(size_t t = tabs; t; --t)
cout << '\t';
cout << childPathStr;
cout << " (*)";
cout << " -> " << childPathObj->wildcard->host << childPathObj->wildcard->path << "/*";
newline = true;
}
if(newline)
cout << '\n';
else
{
for(size_t t = tabs; t; --t)
cout << '\t';
cout << childPathStr << '\n';
}
recurse(childPathObj, moreTab);
}
}
int main()
{
std::unordered_map<string, RedirectPath> rulesFrom;
std::vector<RedirectTo> rulesTo;
rulesTo.push_back({"www.example.com"});
rulesTo.push_back({"files.example.com"});
rulesTo.push_back({"files.example.com", "/imgs"});
rulesTo.push_back({"files.example.com", "/docs"});
rulesTo.push_back({"www.example.com", "/maps"});
{
auto rp0 = rulesFrom["10.10.10.1"].paths["/"] = new RedirectPath;
rp0->wildcard = &rulesTo[0];
rp0->to = &rulesTo[1];
}
{
auto rp0 = rulesFrom["10.10.10.1"].paths["/files"] = new RedirectPath;
rp0->wildcard = &rulesTo[1];
{
{
auto rp1 = rp0->paths["/img"] = new RedirectPath;
rp1->wildcard = &rulesTo[2];
{
auto rp2 = rp1->paths[""] = new RedirectPath;
rp2->to = &rulesTo[2];
}
{
auto rp2 = rp1->paths["s"] = new RedirectPath;
rp2->to = &rulesTo[2];
}
}
{
auto rp1 = rp0->paths["/map"] = new RedirectPath;
{
auto rp2 = rp1->paths[""] = new RedirectPath;
rp2->wildcard = &rulesTo[4];
}
{
auto rp2 = rp1->paths["s"] = new RedirectPath;
rp2->to = &rulesTo[4];
}
}
}
{
auto rp1 = rp0->paths["/docs"] = new RedirectPath;
rp1->wildcard = &rulesTo[3];
}
}
for(const auto& [host, redirects] : rulesFrom)
{
cout << host << ":\n";
recurse(&redirects);
}
} The final implementation should do some checks to determine common points and make the data structure compact, and cache some information for quick execution during runtime after the initial mapping from startup. If anybody has a better idea, feel free to share. Edit: Change of algorithm, the maps can be populated using inequality boundaries and length exhaustion. // 10.10.10.1/user/file
// 10.10.10.1/user/files/*
// 10.10.10.1/user/files/audio
// 10.10.10.1/user/files/video/*
//
// 10.10.10.1/global/*
//
// 10.10.10.1/user/crocodile
rulesFrom = {
"10.10.10.1": {
maxPathLen = 6,
"/user/": {
maxPathLen = 4,
"file": {
to = ...,
maxPathLen = 1,
"s": {
wildcard = ...,
maxPathLen = 6,
"/audio": {
to = ...,
maxPathLen = 0
},
"/video": {
wildcard = ...,
maxPathLen = 0
}
}
},
"croc": {
maxPathLen = 5,
"odile": {
to = ...,
maxPathLen = 0
}
}
},
"/globa": {
maxPathLen = 1,
"l": {
wildcard = ...,
maxPathLen = 0
}
}
}
} |
Through experimenting, I came up with 1. Nearest slash separatorThis splits out nodes based on the closest slash in a specific window. Example10.10.10.1/user/files
10.10.10.1/user/apps
10.10.10.1/usr/lib
10.10.10.1/usr/include
rulesFrom = {
"10.10.10.1": {
maxPathLen = 5,
"/user": {
maxPathLen = 5,
"/file": {
maxPathLen = 1,
"s": {
to = ...,
maxPathLen = 0
}
},
"/apps": {
to = ...,
maxPathLen = 0
}
},
"/usr/": {
maxPathLen = 3,
"lib": {
to = ...,
maxPathLen = 0
},
"inc": {
maxPathLen = 4,
"lude": {
to = ...,
maxPathLen = 0
}
}
}
}
} Pros:
Cons:
2. Common letter separatorSplits out nodes based on intersecting letters in a specific window. Example10.10.10.1/user/files
10.10.10.1/user/apps
10.10.10.1/usr/lib
10.10.10.1/usr/include
rulesFrom = {
"10.10.10.1": {
maxPathLen = 2,
"/us": {
maxPathLen = 2,
"er": {
maxPathLen = 1,
"/": {
maxPathLen = 4,
"file": {
maxPathLen = 1,
"s": {
to = ...,
maxPathLen = 0
}
},
"apps": {
to = ...,
maxPathLen = 0
}
}
},
"r/": {
maxPathLen = 3,
"lib": {
to = ...,
maxPathLen = 0
},
"inc": {
maxPathLen = 4,
"lude": {
to = ...,
maxPathLen = 0
}
}
}
}
}
} Pros:
Cons:
Edit: 3. Local length boundary separatorSplits out nodes on local length boundaries. Example10.10.10.1/user/files
10.10.10.1/user/apps
10.10.10.1/usr/lib
10.10.10.1/usr/include
rulesFrom = {
"10.10.10.1": {
maxPathLen = 8,
"/user/fi": {
maxPathLen = 3,
"les": {
to = ...,
maxPathLen = 0
}
},
"/user/ap": {
maxPathLen = 2,
"ps": {
to = ...,
maxPathLen = 0
}
}
"/usr/lib": {
to = ...,
maxPathLen = 0
},
"/usr/inc": {
maxPathLen = 4,
"lude": {
to = ...,
maxPathLen = 0
}
}
}
} Pros:
Cons:
4. Global length boundary separatorSplits out nodes on length boundaries across all rules. Example10.10.10.1/user/files
10.10.10.1/user/apps
10.10.10.1/usr/lib
10.10.10.1/usr/include
rulesFrom = {
"10.10.10.1": {
maxPathLen = 8,
"/user/fi": {
maxPathLen = 2,
"le": {
maxPathLen = 1,
"s": {
to = ...,
maxPathLen = 0
}
}
},
"/user/ap": {
maxPathLen = 2,
"ps": {
to = ...,
maxPathLen = 0
}
}
"/usr/lib": {
to = ...,
maxPathLen = 0
},
"/usr/inc": {
maxPathLen = 2,
"lu": {
maxPathLen = 1,
"d": {
maxPathLen = 1,
"e": {
to = ...,
maxPathLen = 0
}
}
}
}
}
} Pros:
Cons:
5. Sorted vector and binary searchInserts each strict rule into an unordered map with path length as key, and has an unordered map within each one with the path as key, and its Example10.10.10.1/user/files
10.10.10.1/user/apps
10.10.10.1/usr/lib
10.10.10.1/usr/maps
10.10.10.1/usr/include
10.10.10.1/usr/include/*
10.10.10.1/usr/bin/*
rulesFrom = {
"10.10.10.1": {
8: {
"/usr/lib": ...
},
10: {
"/user/apps": ...,
"/user/maps": ...
},
11: {
"/user/files": ...
},
12: {
"/usr/include": ...
}
]
}
rulesFromWildcard = {
"10.10.10.1": [
{
"/usr/bin", (8)
wildcard = ...
},
{
"/usr/include", (12)
wildcard = ...
}
]
} Pros:
Cons:
Uncertainties:
I'm thinking of method 1, 4, or 5. Will update on which one I decide to implement. Update: Decided on method 3, it seems the most efficient in terms of CPU usage, memory, and runtime speed. |
The implementation of the third method is complete. See the standalone source code and let me know if there is a need for any changes before implementing it within this drogon plugin. |
f2743a3
to
46c5c73
Compare
89f73f0
to
e5c2c40
Compare
TODO: Add support for dynamic addition and removal of rules. We can start discussing where this plugin ends up in the drogon ecosystem. |
Currently the plugin has support for normal redirects and mandated splat, if we go off based on Cloudflare Pages redirects I will add another TODO: Allow splatting to be optional, by having an asterisk added to the I would argue this can replace drogon's controller routers if it uses for loops or regexes to check for matches one by one, however, we have to add support for additional things before doing so, as regex controllers must be kept, and normal controllers can rely on this trie implementation to quickly route requests, and potentially redirect if its configuration becomes part of the drogon's |
40b9104
to
078a395
Compare
078a395
to
f8af65e
Compare
65b3d69
to
886f40c
Compare
#1783
Needs path redirection
I'm aware the pull request includes a small edit to SlashRemover, I believe it doesn't need its own PR for a small change.