Skip to content

Commit 99b9284

Browse files
authored
feature: added new uri option. (#19)
1 parent bea4dc3 commit 99b9284

File tree

3 files changed

+189
-12
lines changed

3 files changed

+189
-12
lines changed

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ Table of Contents
2121
* [DEV ENV](#dev-env)
2222
* [Benchmark](#benchmark)
2323

24-
Status
25-
======
26-
27-
**This repository is an experimental.**
28-
2924
Synopsis
3025
========
3126

@@ -79,6 +74,7 @@ The attributes of each element may contain these:
7974
|:-------- |:--------|:-----------|:-----|
8075
|paths |required|A list of client request uri. The default is a full match, but if the end of the path is `*`, it means that this is a prefix path. For example `/foo*`, it'll match `/foo/bar` or `/foo/glo/grey` etc.|{"/", "/aa", "/bb"}|
8176
|hosts |option |A list of client request host, not only supports normal domain name, but also supports wildcard name.|{"foo.com", "*.bar.com"}|
77+
|uris |option |A list of client request uris, not only supports static uri, but also supports prefix uri.|{"/foo", "/bar/*"}|
8278
|remote_addrs|option |A list of client remote address(IPv4 and IPv6), and we can use CIDR format, eg `192.168.1.0/24`.|{"127.0.0.1", "192.0.0.0/8", "::1", "fe80::/32"}|
8379
|methods |option |A list of method name. Here is full valid method list: "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT" and "TRACE".|{"GET", "POST"}|
8480
|vars |option |A list of `{var, operator, val}`. For example: {{var, operator, val}, {var, operator, val}, ...}, `{"arg_name", "==", "json"}` means the value of argument `name` expect to `json`.|{{"arg_name", "==", "json"}, {"arg_age", ">", 18}}|
@@ -93,14 +89,15 @@ match
9389

9490
`syntax: metadata = rx:match(path, opts)`
9591

96-
* `path`: client request uri.
92+
* `path`: client request path.
9793
* `opts`: a Lua tale (optional).
9894
* `method`: optional, method name of client request.
99-
* `host`: optional, client request host, not only supports normal domain name, but also supports wildcard name, both `foo.com` and `*.foo.com` are valid.
95+
* `host`: optional, client request host.
10096
* `remote_addr`: optional, client remote address like `192.168.1.100`.
97+
* `uri`: optional, client request uri.
10198
* `vars`: optional, a Lua table to fetch variable, default value is `ngx.var` to fetch Ningx builtin variable.
10299

103-
Matchs the route by `method`, `path` and `host`, and return `metadata` if successful.
100+
Matchs the route by `method`, `path` and `host` etc, and return `metadata` if successful.
104101

105102
```lua
106103
local metadata = rx:match(ngx.var.uri, {...})
@@ -113,14 +110,15 @@ dispatch
113110

114111
`syntax: ok = rx:dispatch(path, opts, ...)`
115112

116-
* `path`: client request uri.
113+
* `path`: client request path.
117114
* `opts`: a Lua tale (optional).
118115
* `method`: optional, method name of client request.
119-
* `host`: optional, client request host, not only supports normal domain name, but also supports wildcard name, both `foo.com` and `*.foo.com` are valid.
116+
* `host`: optional, client request host.
120117
* `remote_addr`: optional, client remote address like `192.168.1.100`.
118+
* `uri`: optional, client request uri.
121119
* `vars`: optional, a Lua table to fetch variable, default value is `ngx.var` to fetch Ningx builtin variable.
122120

123-
Dispatchs the route by `method`, `path` and `host`, and call `handler` function if successful.
121+
Matchs the route by `method`, `path` and `host` etc, and call `handler` function if successful.
124122

125123
```lua
126124
local ok = rx:dispatch(ngx.var.uri, {...})

lib/resty/radixtree.lua

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,30 @@ local function pre_insert_route(self, path, route)
235235
route_opts.hosts = {host_is_wildcard, hosts}
236236
end
237237

238+
local uris = route.uris
239+
if type(uris) == "table" and #uris > 0 then
240+
route_opts.uris = {}
241+
for _, uri in ipairs(uris) do
242+
local is_wildcard = false
243+
if uri and uri:sub(#uri, -1) == '*' then
244+
is_wildcard = true
245+
uri = uri:sub(1, -2)
246+
end
247+
248+
insert_tab(route_opts.uris, is_wildcard)
249+
insert_tab(route_opts.uris, uri)
250+
end
251+
252+
elseif type(uris) == "string" then
253+
local is_wildcard = false
254+
if uris and uris:sub(#uris, -1) == '*' then
255+
is_wildcard = true
256+
uris = uris:sub(1, -2)
257+
end
258+
259+
route_opts.uris = {is_wildcard, uris}
260+
end
261+
238262
if path:sub(#path) == "*" then
239263
path = path:sub(1, #path - 1)
240264
route_opts.path_op = "<="
@@ -321,6 +345,23 @@ local function match_host(route_host_is_wildcard, route_host, request_host)
321345
return true
322346
end
323347

348+
local function match_uri(route_uri_is_wildcard, route_uri, request_uri)
349+
if type(request_uri) ~= "string" or #route_uri > #request_uri then
350+
return false
351+
end
352+
353+
if not route_uri_is_wildcard then
354+
return route_uri == request_uri
355+
end
356+
357+
local i = request_uri:find(route_uri, 1, true)
358+
if i ~= 1 then
359+
return false
360+
end
361+
362+
return true
363+
end
364+
324365
local compare_funcs = {
325366
["=="] = function (l_v, r_v)
326367
if type(r_v) == "number" then
@@ -379,7 +420,7 @@ local function match_route_opts(route, opts)
379420
if route.hosts then
380421
local matched = false
381422
local hosts = route.hosts
382-
for i = 1, #route.hosts, 2 do
423+
for i = 1, #hosts, 2 do
383424
if match_host(hosts[i], hosts[i + 1], opts.host) then
384425
matched = true
385426
break
@@ -392,6 +433,22 @@ local function match_route_opts(route, opts)
392433
end
393434
end
394435

436+
if route.uris then
437+
local matched = false
438+
local uris = route.uris
439+
for i = 1, #uris, 2 do
440+
if match_uri(uris[i], uris[i + 1], opts.uri) then
441+
matched = true
442+
break
443+
end
444+
end
445+
446+
log_info("uris match: ", matched)
447+
if not matched then
448+
return false
449+
end
450+
end
451+
395452
if route.vars then
396453
local vars = opts.vars or ngx_var
397454
if type(vars) ~= "table" then

t/uri.t

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use t::RX 'no_plan';
2+
3+
repeat_each(1);
4+
run_tests();
5+
6+
__DATA__
7+
8+
=== TEST 1: sanity
9+
--- config
10+
location /t {
11+
content_by_lua_block {
12+
local radix = require("resty.radixtree")
13+
local rx = radix.new({
14+
{
15+
paths = {"/aa*"},
16+
metadata = "metadata /aa",
17+
uris = {"/foo"},
18+
}
19+
})
20+
21+
ngx.say(rx:match("/aa/bb", {uri = "/foo"}))
22+
ngx.say(rx:match("/aa/bb", {uri = "/foo/bar"}))
23+
}
24+
}
25+
--- request
26+
GET /t
27+
--- no_error_log
28+
[error]
29+
--- response_body
30+
metadata /aa
31+
nil
32+
33+
34+
35+
=== TEST 2: wildcard
36+
--- config
37+
location /t {
38+
content_by_lua_block {
39+
local radix = require("resty.radixtree")
40+
local rx = radix.new({
41+
{
42+
paths = {"/aa*"},
43+
uris = {"/aa*"},
44+
metadata = "metadata /aa",
45+
}
46+
})
47+
48+
ngx.say(rx:match("/aa/bb", {uri = "/a"}))
49+
ngx.say(rx:match("/aa/bb", {uri = "/aa"}))
50+
ngx.say(rx:match("/aa/bb", {uri = "/aa/b"}))
51+
ngx.say(rx:match("/aa/bb", {uri = "/aa/c/d"}))
52+
}
53+
}
54+
--- request
55+
GET /t
56+
--- no_error_log
57+
[error]
58+
--- response_body
59+
nil
60+
metadata /aa
61+
metadata /aa
62+
metadata /aa
63+
64+
65+
66+
=== TEST 3: mutiple uris
67+
--- config
68+
location /t {
69+
content_by_lua_block {
70+
local radix = require("resty.radixtree")
71+
local rx = radix.new({
72+
{
73+
paths = {"/aa*"},
74+
metadata = "metadata /aa",
75+
uris = {"/foo", "/bar"},
76+
}
77+
})
78+
79+
ngx.say(rx:match("/aa/bb", {uri = "/foo"}))
80+
ngx.say(rx:match("/aa/bb", {uri = "/bar"}))
81+
ngx.say(rx:match("/aa/bb", {uri = "/ggg"}))
82+
}
83+
}
84+
--- request
85+
GET /t
86+
--- no_error_log
87+
[error]
88+
--- response_body
89+
metadata /aa
90+
metadata /aa
91+
nil
92+
93+
94+
95+
=== TEST 4: mutiple uri (include prefix)
96+
--- config
97+
location /t {
98+
content_by_lua_block {
99+
local radix = require("resty.radixtree")
100+
local rx = radix.new({
101+
{
102+
paths = {"/aa*"},
103+
metadata = "metadata /aa",
104+
uris = {"/foo", "/bar/*"}
105+
}
106+
})
107+
108+
ngx.say(rx:match("/aa/bb", {uri = "/foo"}))
109+
ngx.say(rx:match("/aa/bb", {uri = "/bar"}))
110+
ngx.say(rx:match("/aa/bb", {uri = "/bar/com"}))
111+
ngx.say(rx:match("/aa/bb", {uri = "/ggg/glo"}))
112+
}
113+
}
114+
--- request
115+
GET /t
116+
--- no_error_log
117+
[error]
118+
--- response_body
119+
metadata /aa
120+
nil
121+
metadata /aa
122+
nil

0 commit comments

Comments
 (0)