Skip to content
Permalink
Browse files

Routing, feat: support virtual host routing.

  • Loading branch information...
xicilion committed Aug 15, 2019
1 parent 612271c commit 330d1d049ebba464d4ec0442bf9ba9e139dc3274
Showing with 185 additions and 17 deletions.
  1. +2 −0 fibjs/include/Routing.h
  2. +28 −0 fibjs/include/ifs/Routing.h
  3. +86 −16 fibjs/src/mq/Routing.cpp
  4. +14 −1 idl/zh-cn/Routing.idl
  5. +55 −0 test/mq_test.js
@@ -49,6 +49,8 @@ class Routing : public Routing_base {
virtual result_t append(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal);
virtual result_t append(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal);
virtual result_t append(exlib::string method, exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal);
virtual result_t host(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal);
virtual result_t host(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal);
virtual result_t all(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal);
virtual result_t all(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal);
virtual result_t get(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal);
@@ -30,6 +30,8 @@ class Routing_base : public Handler_base {
virtual result_t append(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t append(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t append(exlib::string method, exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t host(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t host(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t all(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t all(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal) = 0;
virtual result_t get(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal) = 0;
@@ -52,6 +54,7 @@ class Routing_base : public Handler_base {
public:
static void s__new(const v8::FunctionCallbackInfo<v8::Value>& args);
static void s_append(const v8::FunctionCallbackInfo<v8::Value>& args);
static void s_host(const v8::FunctionCallbackInfo<v8::Value>& args);
static void s_all(const v8::FunctionCallbackInfo<v8::Value>& args);
static void s_get(const v8::FunctionCallbackInfo<v8::Value>& args);
static void s_post(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -67,6 +70,7 @@ inline ClassInfo& Routing_base::class_info()
{
static ClassData::ClassMethod s_method[] = {
{ "append", s_append, false },
{ "host", s_host, false },
{ "all", s_all, false },
{ "get", s_get, false },
{ "post", s_post, false },
@@ -154,6 +158,30 @@ inline void Routing_base::s_append(const v8::FunctionCallbackInfo<v8::Value>& ar
METHOD_RETURN();
}

inline void Routing_base::s_host(const v8::FunctionCallbackInfo<v8::Value>& args)
{
obj_ptr<Routing_base> vr;

METHOD_NAME("Routing.host");
METHOD_INSTANCE(Routing_base);
METHOD_ENTER();

METHOD_OVER(1, 1);

ARG(v8::Local<v8::Object>, 0);

hr = pInst->host(v0, vr);

METHOD_OVER(2, 2);

ARG(exlib::string, 0);
ARG(obj_ptr<Handler_base>, 1);

hr = pInst->host(v0, v1, vr);

METHOD_RETURN();
}

inline void Routing_base::s_all(const v8::FunctionCallbackInfo<v8::Value>& args)
{
obj_ptr<Routing_base> vr;
@@ -45,6 +45,7 @@ result_t Routing::invoke(object_base* v, obj_ptr<Handler_base>& retVal,

exlib::string value;
exlib::string method;
exlib::string host;

msg->get_value(value);

@@ -54,11 +55,34 @@ result_t Routing::invoke(object_base* v, obj_ptr<Handler_base>& retVal,

for (i = (int32_t)m_array.size() - 1; i >= 0; i--) {
obj_ptr<rule>& r = m_array[i];
exlib::string& test = value;
bool isHost = false;

if (htmsg) {
if (!qstricmp(r->m_method.c_str(), "HOST")) {
if (host.empty()) {
Variant v;

htmsg->firstHeader("host", v);
host = v.string();
if (host.empty())
host = "*";
else {
size_t pos = host.find(':');
if (pos != exlib::string::npos)
host = host.substr(0, pos);
}
}

if (htmsg && r->m_method != "*" && qstricmp(method.c_str(), r->m_method.c_str()))
continue;
test = host;
isHost = true;
} else {
if (r->m_method != "*" && qstricmp(method.c_str(), r->m_method.c_str()))
continue;
}
}

rc = pcre_exec(r->m_re, NULL, value.c_str(), (int32_t)value.length(),
rc = pcre_exec(r->m_re, NULL, test.c_str(), (int32_t)test.length(),
0, 0, ovector, RE_SIZE);
if (rc > 0) {
obj_ptr<NArray> list;
@@ -83,21 +107,23 @@ result_t Routing::invoke(object_base* v, obj_ptr<Handler_base>& retVal,

if (r->m_bSub) {
i = rc - 1;
msg->set_value(value.substr(ovector[i * 2], ovector[i * 2 + 1] - ovector[i * 2]));
if (!isHost)
msg->set_value(test.substr(ovector[i * 2], ovector[i * 2 + 1] - ovector[i * 2]));
} else {
if (levelCount[1] == 1) {
msg->set_value(value.substr(ovector[2], ovector[3] - ovector[2]));
if (!isHost)
msg->set_value(test.substr(ovector[2], ovector[3] - ovector[2]));
if (levelCount[2] > 0)
p = 2;
} else
} else if (!isHost)
msg->set_value("");

if (levelCount[p]) {
Variant vUndefined;
for (i = 0; i < rc; i++)
if (level[i] == p) {
if (ovector[i * 2 + 1] - ovector[i * 2] > 0)
list->append(value.substr(ovector[i * 2],
list->append(test.substr(ovector[i * 2],
ovector[i * 2 + 1] - ovector[i * 2]));
else
list->append(vUndefined);
@@ -232,6 +258,36 @@ exlib::string path2RegExp(exlib::string pattern)
return res;
}

exlib::string host2RegExp(exlib::string pattern)
{
size_t len = pattern.length();

if (len > 0 && pattern[len - 1] == '/')
pattern.resize(len - 1);

_parser p(pattern);
exlib::string res;
exlib::string str;
exlib::string re, re1;
char ch, last_ch;

while (!p.end()) {
ch = p.getChar();
if (ch == '\\') {
res.append(1, '\\');
res.append(1, p.getChar());
} else if (ch == '.') {
res.append("\\.");
} else if (ch == '*') {
res.append("((?:.*))");
} else
res.append(1, ch);
}

res = "^" + res + "(?:/(?=$))?$";
return res;
}

result_t Routing::append(exlib::string method, exlib::string pattern, Handler_base* hdlr,
obj_ptr<Routing_base>& retVal)
{
@@ -242,16 +298,20 @@ result_t Routing::append(exlib::string method, exlib::string pattern, Handler_ba
bool bSub = false;

if (pattern.length() > 0 && pattern[0] != '^') {
obj_ptr<Routing_base> rt = Routing_base::getInstance(hdlr);
if (rt) {
int32_t len = (int32_t)pattern.length();
if (len > 0 && pattern[len - 1] == '/')
pattern.resize(len - 1);
pattern += "(.*)";
bSub = true;
}
if (!qstricmp(method.c_str(), "HOST"))
pattern = host2RegExp(pattern);
else {
obj_ptr<Routing_base> rt = Routing_base::getInstance(hdlr);
if (rt) {
int32_t len = (int32_t)pattern.length();
if (len > 0 && pattern[len - 1] == '/')
pattern.resize(len - 1);
pattern += "(.*)";
bSub = true;
}

pattern = path2RegExp(pattern);
pattern = path2RegExp(pattern);
}
}

re = pcre_compile(pattern.c_str(), opt, &error, &erroffset, NULL);
@@ -312,6 +372,16 @@ result_t Routing::append(exlib::string pattern, Handler_base* hdlr,
return append("*", pattern, hdlr, retVal);
}

result_t Routing::host(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal)
{
return _append("HOST", map, retVal);
}

result_t Routing::host(exlib::string pattern, Handler_base* hdlr, obj_ptr<Routing_base>& retVal)
{
return append("HOST", pattern, hdlr, retVal);
}

result_t Routing::all(v8::Local<v8::Object> map, obj_ptr<Routing_base>& retVal)
{
return _append("*", map, retVal);
@@ -110,13 +110,26 @@ interface Routing : Handler
Routing append(String pattern, Handler hdlr);

/*! @brief 添加一条路由规则
@param method 指定 http 请求方法,"*" 接受所有方法
@param method 指定 http 请求方法,"*" 接受所有方法,"host" 指定虚拟域名
@param pattern 消息匹配格式
@param hdlr 内置消息处理器,处理函数,链式处理数组,路由对象,详见 mq.Handler
@return 返回路由对象本身
*/
Routing append(String method, String pattern, Handler hdlr);

/*! @brief 添加一组 http 域名的路由规则
@param map 路由参数
@return 返回路由对象本身
*/
Routing host(Object map);

/*! @brief 添加一条接受 http 域名的路由规则
@param pattern 消息匹配格式
@param hdlr 内置消息处理器,处理函数,链式处理数组,路由对象,详见 mq.Handler
@return 返回路由对象本身
*/
Routing host(String pattern, Handler hdlr);

/*! @brief 添加一组接受所有 http 方法路由规则
@param map 路由参数
@return 返回路由对象本身
@@ -533,6 +533,31 @@ var p2r_sub_tests = {
}
};

var p2r_host_tests = {
"test.com": {
"test.com": ["test.com"],
"test.com:80": ["test.com:80"],
"www.test.com": null,
"test1.com": null
},
"*.test.com": {
"test.com": null,
"www.test.com": ["www.test.com", "www"],
"test1.com": null
},
"w*.test.com": {
"test.com": null,
"web.test.com": ["web.test.com", "eb"],
"web.test.com:8080": ["web.test.com:8080", "eb"],
"test1.com": null
},
"www.*.test.com": {
"test.com": null,
"www.api.test.com": ["www.api.test.com", "api"],
"test1.com": null
}
};

var n;

function hdlr1(v) {
@@ -957,6 +982,36 @@ describe("mq", () => {

for (k in p2r_sub_tests)
test_one_sub(k);

function test_host_route(p, v) {
var r = null;
var rt = new mq.Routing();
rt.append("host", p, function (v1) {
r = Array.prototype.slice.call(arguments);
r[0] = v;
assert.equal(v1.value, '/test');
});

rt.append("^.*$", () => {});

var m = new http.Request();
m.value = '/test';
m.addHeader('host', v);

mq.invoke(rt, m);
return r;
}

function test_one_host(k) {
it("host: " + k, () => {
var cases = p2r_host_tests[k];
for (u in cases)
assert.deepEqual(test_host_route(k, u), cases[u]);
});
}

for (k in p2r_host_tests)
test_one_host(k);
});

describe("order", () => {

0 comments on commit 330d1d0

Please sign in to comment.
You can’t perform that action at this time.