Skip to content

Commit 9ac7b32

Browse files
authored
feature: supported to use multiple paths in one rule. (#12)
1 parent 308004d commit 9ac7b32

File tree

3 files changed

+163
-80
lines changed

3 files changed

+163
-80
lines changed

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,33 @@ Synopsis
3737
path = "/aa",
3838
metadata = "metadata /aa",
3939
host = "foo.com",
40-
method = {"GET", "POST"},
40+
method = {"GET", "POST"}, -- multiple method
4141
remote_addr = "127.0.0.1",
4242
},
4343
{
4444
path = "/bb*",
4545
metadata = "metadata /bb",
46-
host = {"*.bar.com", "gloo.com"},
46+
host = {"*.bar.com", "gloo.com"}, -- multiple host
4747
method = {"GET", "POST", "PUT"},
4848
remote_addr = "fe80:fe80::/64",
49-
vars = {
49+
vars = { -- multiple var
5050
{"arg_name", "==", "json"},
5151
{"arg_weight", ">", "10"},
5252
},
53-
filter_fun = function(vars)
53+
filter_fun = function(vars) -- callback fun
5454
return vars["arg_name"] == "json"
5555
end,
5656
},
5757
{
5858
path = "/cc",
5959
metadata = "metadata /cc",
6060
remote_addr = {"127.0.0.1","192.168.0.0/16",
61-
"::1", "fe80::/32"}
61+
"::1", "fe80::/32"} -- multiple remote_addr
6262
},
63+
{
64+
path = {"/dd", "/dd/ee", "/dd/ff/*"}, -- multiple path
65+
metadata = "metadata /dd and /dd/ee"
66+
}
6367
})
6468

6569
-- try to match
@@ -79,19 +83,19 @@ Methods
7983
new
8084
---
8185

82-
`syntax: rx, err = radix:new(routes)`
86+
`syntax: rx, err = radix.new(routes)`
8387

8488
The routes is a array table, like `{ {...}, {...}, {...} }`, Each element in the array is a route, which is a hash table.
8589

8690
The attributes of each element may contain these:
8791

8892
|name |option |description|
8993
|-------- |--------|-----------|
90-
|path |required|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.|
94+
|path |required|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. We can set multiple `path` by an array table, eg: `{"/", "/aa", "/bb"}`.|
9195
|metadata |option |Will return this field if using `rx:match` to match route.|
9296
|handler |option |Will call this function using `rx:dispatch` to match route.|
93-
|host |option |Client request host, not only supports normal domain name, but also supports wildcard name, both `foo.com` and `*.foo.com` are valid.|
94-
|remote_addr|option |Client remote address like `192.168.1.100`, and we can use CIDR format, eg `192.168.1.0/24`. BTW, In addition to supporting the IPv6 format, multiple IP addresses are allowed, this field will be an array table at this case, the elements of array shoud be a string IPv4 or IPv6 address.|
97+
|host |option |Client request host, not only supports normal domain name, but also supports wildcard name, both `foo.com` and `*.foo.com` are valid. We can set multiple `host` by an array table, eg: `{"foo.com", "bar.com"}`.|
98+
|remote_addr|option |Client remote address like `192.168.1.100`, and we can use CIDR format, eg `192.168.1.0/24`. BTW, In addition to supporting the IPv6 format, multiple IP addresses are allowed, this field will be an array table at this case, the elements of array shoud be a string IPv4 or IPv6 address. We can set multiple `remote_addr` by an array table, eg: `{"127.0.0.1", "192.0.0.0/8"}`.|
9599
|method |option |It's an array table, we can put one or more method names together. Here is the valid method list: "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT" and "TRACE".|
96100
|vars |option |It is an array of one or more {var, operator, val} elements. For example: {{var, operator, val}, {var, operator, val}, ...}. `{"arg_key", "==", "val"}` means the value of argument `key` expect to `val`.|
97101
|filter_fun |option |User defined filter function, We can use it to achieve matching logic for special scenes. `radixtree` will only pass one parameter which named `vars` when matching route.|

lib/resty/radixtree.lua

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -181,98 +181,108 @@ end
181181

182182
do
183183
local route_opts = {}
184-
function _M.new(routes)
185-
if not routes then
186-
return nil, "missing argument route"
184+
185+
local function pre_insert_route(self, path, route)
186+
if type(path) ~= "string" then
187+
error("invalid argument path", 2)
187188
end
188189

189-
local route_n = #routes
190+
if type(route.metadata) == "nil" and type(route.handler) == "nil" then
191+
error("missing argument metadata or handler", 2)
192+
end
190193

191-
local self = setmt__gc({
192-
tree = radix.radix_tree_new(),
193-
match_data_index = 0,
194-
match_data = new_tab(#routes, 0),
195-
hash_path = new_tab(0, #routes),
196-
}, mt)
194+
if route.vars then
195+
if type(route.vars) ~= "table" then
196+
error("invalid argument vars", 2)
197+
end
198+
end
197199

198-
-- register routes
199-
for i = 1, route_n do
200-
local route = routes[i]
200+
local method = route.method
201+
local bit_methods
202+
if type(method) ~= "table" then
203+
bit_methods = method and METHODS[method] or 0
201204

202-
if type(route.path) ~= "string" then
203-
error("invalid argument path", 2)
205+
else
206+
bit_methods = 0
207+
for _, m in ipairs(method) do
208+
bit_methods = bit.bor(bit_methods, METHODS[m])
204209
end
210+
end
205211

206-
if type(route.metadata) == "nil" and type(route.handler) == "nil" then
207-
error("missing argument metadata or handler", 2)
208-
end
212+
clear_tab(route_opts)
209213

210-
if route.vars then
211-
if type(route.vars) ~= "table" then
212-
error("invalid argument vars", 2)
214+
local host = route.host
215+
if type(host) == "table" and #host > 0 then
216+
route_opts.hosts = {}
217+
for _, h in ipairs(host) do
218+
local host_is_wildcard = false
219+
if h and h:sub(1, 1) == '*' then
220+
host_is_wildcard = true
221+
h = h:sub(2):reverse()
213222
end
214-
end
215223

216-
local method = route.method
217-
local bit_methods
218-
if type(method) ~= "table" then
219-
bit_methods = method and METHODS[method] or 0
224+
insert_tab(route_opts.hosts, host_is_wildcard)
225+
insert_tab(route_opts.hosts, h)
226+
end
220227

221-
else
222-
bit_methods = 0
223-
for _, m in ipairs(method) do
224-
bit_methods = bit.bor(bit_methods, METHODS[m])
225-
end
228+
elseif type(host) == "string" then
229+
local host_is_wildcard = false
230+
if host and host:sub(1, 1) == '*' then
231+
host_is_wildcard = true
232+
host = host:sub(2):reverse()
226233
end
227234

228-
clear_tab(route_opts)
235+
route_opts.hosts = {host_is_wildcard, host}
236+
end
229237

230-
local host = route.host
231-
if type(host) == "table" and #host > 0 then
232-
route_opts.hosts = {}
233-
for _, h in ipairs(host) do
234-
local host_is_wildcard = false
235-
if h and h:sub(1, 1) == '*' then
236-
host_is_wildcard = true
237-
h = h:sub(2):reverse()
238-
end
238+
if path:sub(#path) == "*" then
239+
path = path:sub(1, #path - 1)
240+
route_opts.path_op = "<="
241+
else
242+
route_opts.path_op = "="
243+
end
244+
route_opts.path = path
245+
246+
route_opts.metadata = route.metadata
247+
route_opts.handler = route.handler
248+
route_opts.method = bit_methods
249+
route_opts.vars = route.vars
250+
route_opts.filter_fun = route.filter_fun
251+
252+
local err
253+
route_opts.matcher_ins, err = parse_remote_addr(route.remote_addr)
254+
if err then
255+
error("invalid IP address: " .. err, 2)
256+
end
239257

240-
insert_tab(route_opts.hosts, host_is_wildcard)
241-
insert_tab(route_opts.hosts, h)
242-
end
258+
insert_route(self, route_opts)
259+
end
243260

244-
elseif type(host) == "string" then
245-
local host_is_wildcard = false
246-
if host and host:sub(1, 1) == '*' then
247-
host_is_wildcard = true
248-
host = host:sub(2):reverse()
249-
end
261+
function _M.new(routes)
262+
if not routes then
263+
return nil, "missing argument route"
264+
end
250265

251-
route_opts.hosts = {host_is_wildcard, host}
252-
end
266+
local route_n = #routes
253267

254-
local path = route.path
255-
if path:sub(#path) == "*" then
256-
path = path:sub(1, #path - 1)
257-
route_opts.path_op = "<="
258-
else
259-
route_opts.path_op = "="
260-
end
261-
route_opts.path = path
268+
local self = setmt__gc({
269+
tree = radix.radix_tree_new(),
270+
match_data_index = 0,
271+
match_data = new_tab(#routes, 0),
272+
hash_path = new_tab(0, #routes),
273+
}, mt)
262274

263-
route_opts.metadata = route.metadata
264-
route_opts.handler = route.handler
265-
route_opts.method = bit_methods
266-
route_opts.vars = route.vars
267-
route_opts.filter_fun = route.filter_fun
275+
-- register routes
276+
for i = 1, route_n do
277+
local route = routes[i]
278+
if type(route.path) == "string" then
279+
pre_insert_route(self, route.path, route)
268280

269-
local err
270-
route_opts.matcher_ins, err = parse_remote_addr(route.remote_addr)
271-
if err then
272-
error("invalid IP address: " .. err, 2)
281+
else
282+
for _, path in ipairs(route.path) do
283+
pre_insert_route(self, path, route)
284+
end
273285
end
274-
275-
insert_route(self, route_opts)
276286
end
277287

278288
return self

t/path.t

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# vim:set ft= ts=4 sw=4 et fdm=marker:
2+
3+
use t::RX 'no_plan';
4+
5+
repeat_each(1);
6+
run_tests();
7+
8+
__DATA__
9+
10+
=== TEST 1: single path
11+
--- config
12+
location /t {
13+
content_by_lua_block {
14+
local radix = require("resty.radixtree")
15+
local rx = radix.new({
16+
{
17+
path = {"/"},
18+
metadata = "metadata /",
19+
},
20+
})
21+
ngx.say(rx:match("/xx"))
22+
ngx.say(rx:match("/"))
23+
}
24+
}
25+
--- request
26+
GET /t
27+
--- no_error_log
28+
[error]
29+
--- response_body
30+
nil
31+
metadata /
32+
33+
34+
35+
=== TEST 2: multiple path
36+
--- config
37+
location /t {
38+
content_by_lua_block {
39+
local radix = require("resty.radixtree")
40+
local rx = radix.new({
41+
{
42+
path = {"/", "/aa", "/bb"},
43+
metadata = "metadata multipe path 1",
44+
},
45+
{
46+
path = {"/cc"},
47+
metadata = "metadata multipe path 2",
48+
},
49+
})
50+
51+
ngx.say(rx:match("/"))
52+
ngx.say(rx:match("/aa"))
53+
ngx.say(rx:match("/bb"))
54+
ngx.say(rx:match("/cc"))
55+
ngx.say(rx:match("/dd"))
56+
ngx.say(rx:match("/ee"))
57+
}
58+
}
59+
--- request
60+
GET /t
61+
--- no_error_log
62+
[error]
63+
--- response_body
64+
metadata multipe path 1
65+
metadata multipe path 1
66+
metadata multipe path 1
67+
metadata multipe path 2
68+
nil
69+
nil

0 commit comments

Comments
 (0)