Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 85 additions & 21 deletions lib/resty/radixtree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
local ipmatcher = require("resty.ipmatcher")
local base = require("resty.core.base")
local clone_tab = require("table.clone")
local isempty = require("table.isempty")
local lrucache = require("resty.lrucache")
local expr = require("resty.expr.v1")
local bit = require("bit")
Expand Down Expand Up @@ -312,6 +313,11 @@ local function remove_route(self, opts)
return false
end

if #route_arr == 1 then
self.hash_path[path] = nil
return true
end

table.remove(route_arr, idx)

return true
Expand Down Expand Up @@ -413,6 +419,30 @@ local function parse_remote_addr(route_remote_addrs)
end


local function define_path_op(path, route_opts, global_opts)
local pos = not global_opts.no_param_match and str_find(path, ':', 1, true)
if pos then
path = path:sub(1, pos - 1)
route_opts.path_op = "<="
route_opts.path = path
route_opts.param = true

else
pos = str_find(path, '*', 1, true)
if pos then
if pos ~= #path then
route_opts.param = true
end
path = path:sub(1, pos - 1)
route_opts.path_op = "<="
else
route_opts.path_op = "="
end
route_opts.path = path
end
end


local function common_route_data(path, route, route_opts, global_opts)
local method = route.methods
local bit_methods
Expand Down Expand Up @@ -469,34 +499,15 @@ local function common_route_data(path, route, route_opts, global_opts)
route_opts.path_org = path
route_opts.param = false

local pos = not global_opts.no_param_match and str_find(path, ':', 1, true)
if pos then
path = path:sub(1, pos - 1)
route_opts.path_op = "<="
route_opts.path = path
route_opts.param = true

else
pos = str_find(path, '*', 1, true)
if pos then
if pos ~= #path then
route_opts.param = true
end
path = path:sub(1, pos - 1)
route_opts.path_op = "<="
else
route_opts.path_op = "="
end
route_opts.path = path
end

define_path_op(path, route_opts, global_opts)
log_info("path: ", route_opts.path, " operator: ", route_opts.path_op)

route_opts.metadata = route.metadata
route_opts.handler = route.handler
route_opts.method = bit_methods
route_opts.filter_fun = route.filter_fun
route_opts.priority = route.priority or 0
route_opts.sub_router = route.sub_router

local err
local remote_addrs = route.remote_addrs
Expand Down Expand Up @@ -1015,4 +1026,57 @@ function _M.dispatch(self, path, opts, ...)
end


function _M.get_sub_router(self, id, path, global_opts)
local route_opts = {}

define_path_op(path, route_opts, global_opts)
path = route_opts.path

if not self.disable_path_cache_opt and route_opts.path_op == '=' then
local route_arr = self.hash_path[path]
if route_arr == nil or type(route_arr) ~= "table" then
log_info("no route arr exists in hash_path.", path)
return nil
end

for i, route in ipairs(route_arr) do
if route.id == id then
return route_arr[i].sub_router
end
end

log_info("cannot find route in hash_path.", path, id)
return nil
end

local data_idx = radix.radix_tree_find(self.tree, path, #path)
if data_idx == nil then
log_info("cannot find the index in radixtree" .. path)
return nil
end

local idx = tonumber(ffi_cast('intptr_t', data_idx))
local routes = self.match_data[idx]

for i, route in ipairs(routes) do
if route.id == id then
return routes[i].sub_router
end
end

log_info("cannot find route in match_data.", path, id)

return nil
end


function _M.isempty(self)
if not isempty(self.hash_path) then
return false
end

return isempty(self.match_data)
end


return _M
70 changes: 70 additions & 0 deletions t/empty.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:

use t::RX 'no_plan';

repeat_each(1);
run_tests();

__DATA__

=== TEST 1: isempty() on empty router
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local rx = radix.new({})
ngx.say(rx:isempty())
}
}
--- request
GET /t
--- response_body
true
--- no_error_log
[error]



=== TEST 2: isempty on router with simple path
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local rx = radix.new({
{
paths = "/",
metadata = "metadata /",
},
})
ngx.say(rx:isempty())
}
}
--- request
GET /t
--- response_body
false
--- no_error_log
[error]



=== TEST 3: isempty on router with path with '*'
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local rx = radix.new({
{
paths = "/*",
metadata = "metadata /",
},
})
ngx.say(rx:isempty())
}
}
--- request
GET /t
--- response_body
false
--- no_error_log
[error]
112 changes: 112 additions & 0 deletions t/sub_router.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:

use t::RX 'no_plan';

repeat_each(1);
run_tests();

__DATA__

=== TEST 1: get_sub_router on exact path match with sub_router
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local router_opts = {
no_param_match = true
}
local rx = radix.new({
{
id = "1",
paths = string.reverse("example-1.com"),
sub_router = { name = "sub_router 1" },
metadata = "metadata 1",
},
{
id = "2",
paths = string.reverse("example-1.com"),
sub_router = { name = "sub_router 2" },
metadata = "metadata 2",
},
{
id = "3",
paths = string.reverse("example-2.com"),
sub_router = { name = "sub_router 3" },
metadata = "metadata 3",
}
})


-- step 1. get existing subrouter with id = '1'
ngx.say(rx:get_sub_router("1", string.reverse("example-1.com"), router_opts).name)

-- step 2. get existing subrouter with id = '2'
ngx.say(rx:get_sub_router("2", string.reverse("example-1.com"), router_opts).name)

-- step 3. get existing subrouter with id = '3'
ngx.say(rx:get_sub_router("3", string.reverse("example-2.com"), router_opts).name)

-- step 4. get subrouter with not existing 'id', but existing path
ngx.say(rx:get_sub_router("5", string.reverse("example-1.com"), router_opts))

-- step 5. get subrouter with not existing 'path'
ngx.say(rx:get_sub_router("1", string.reverse("example-10.com"), router_opts))
}
}
--- request
GET /t
--- response_body
sub_router 1
sub_router 2
sub_router 3
nil
nil
--- no_error_log
[error]



=== TEST 2: get_sub_router on '*' path match with sub_router
--- config
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local router_opts = {
no_param_match = true
}
local rx = radix.new({
{
id = "1",
paths = string.reverse("*.example.com"),
sub_router = { name = "sub_router 1" },
metadata = "metadata 1",
},
{
id = "2",
paths = string.reverse("test.example.com"),
sub_router = { name = "sub_router 2" },
metadata = "metadata 2",
},
})

-- step 1. get existing subrouter with id = '1'
ngx.say(rx:get_sub_router("1", string.reverse("*.example.com"), router_opts).name)
-- step 2. get existing subrouter with id = '2'
ngx.say(rx:get_sub_router("2", string.reverse("test.example.com"), router_opts).name)
-- step 3. get subrouter with not existing 'id', but existing path
ngx.say(rx:get_sub_router("3", string.reverse("*.example.com"), router_opts))
-- step 4. get subrouter with not existing 'path'
ngx.say(rx:get_sub_router("1", string.reverse("*.example-1.com"), router_opts))
}
}
--- request
GET /t
--- response_body
sub_router 1
sub_router 2
nil
nil
--- no_error_log
[error]