Skip to content

Commit

Permalink
wip: all test green
Browse files Browse the repository at this point in the history
  • Loading branch information
vm-001 committed Dec 18, 2023
1 parent 147cfb0 commit 95d4bc0
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 158 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ test: build
luajit spec/utils_spec.lua

test2: build
TEST_NGINX_LOG_LEVEL=info \
prove -I../test-nginx/lib -I. -r -s t/
TEST_NGINX_LOG_LEVEL=info prove -I../test-nginx/lib -I. -r -s t/

bench: build
resty -I=./lib -I=./deps/share/lua/5.1 benchmark/match-parameter.lua
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# lua-resty-router-tree


兼容性:

Lua>=5.2, LuaJIT, OpenResty,

(`::continue::` 在 5.1 不支持)


```lua
local routes = {}
local router = router.new(routes)
Expand Down Expand Up @@ -34,4 +41,4 @@ go httprouter
TODO 处理路由冲突

- /user/:name/info
- /user/:id/info
- /user/:id/info
1 change: 1 addition & 0 deletions lua-resty-router-tree-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ build = {
type = "builtin",
modules = {
["router-tree"] = "src/resty/router.lua",
["router-tree.route"] = "src/resty/route.lua",
["router-tree.parser"] = "src/resty/parser/parser.lua",
["router-tree.parser.style.default"] = "src/resty/parser/style/default.lua",
["router-tree.parser.style.ant"] = "src/resty/parser/style/ant.lua",
Expand Down
150 changes: 150 additions & 0 deletions src/resty/route.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
local utils = require "router-tree.utils"
local Parser = require "router-tree.parser"
local bit = utils.is_luajit and require "bit"

local ipairs = ipairs
local is_luajit = utils.is_luajit
local starts_with = utils.starts_with

local EMPTY = {}

local METHODS = {}
for i, name in ipairs({"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"}) do
METHODS[name] = bit.lshift(1, i - 1)
-- ngx.log(ngx.WARN, "name: ", name, " val: ", METHODS[name])
end



local Route = {}
local mt = { __index = Route }

local function extract_params(path)
local params = {}
local parser = Parser.parse(path, "default")
local token = parser:next()
while (token) do
local c = string.sub(token, 1, 1)
if c == ":" then
table.insert(params, string.sub(token, 2))
elseif c == "*" then
if #token > 1 then
table.insert(params, string.sub(token, 2))
else
table.insert(params, ":ext")
end
end
token = parser:next()
end
return params
end

function Route.new(route)

local self = {
priority = route.priority,
handler = route.handler or route.metadata,
params = {},
}

for _, path in ipairs(route.paths) do
self.params[path] = extract_params(path)
end

if is_luajit then
local methods_bit = 0
for _, method in ipairs(route.methods or EMPTY) do
methods_bit = bit.bor(methods_bit, METHODS[method])
end
self.method = methods_bit
else
local methods = {}
for _, method in ipairs(route.methods or EMPTY) do
methods[method] = true
end
self.method = methods
end

-- hosts
local hosts = route.hosts
if type(hosts) == "table" and #hosts > 0 then
self.hosts = {}
for _, h in ipairs(hosts) do
local is_wildcard = false
if h and h:sub(1, 1) == '*' then
is_wildcard = true
h = h:sub(2)
end

h = string.lower(h)
table.insert(self.hosts, is_wildcard)
table.insert(self.hosts, h)
end

elseif type(hosts) == "string" then
local is_wildcard = false
local host = string.lower(hosts)
if host:sub(1, 1) == '*' then
is_wildcard = true
host = host:sub(2)
end

self.hosts = { is_wildcard, host}
end

return setmetatable(self, mt)
end

local function match_host(route_host_is_wildcard, route_host, request_host)
if not route_host_is_wildcard then
return route_host == request_host
end

return starts_with(request_host, route_host)
end


function Route:is_match(ctx)
local route = self

if route.method ~= 0 then
local method = ctx.method
-- 或者直接使用 map 就好
if not method or METHODS[method] == nil or bit.band(route.method, METHODS[method]) == 0 then
return false
end
end

-- log_info("route.hosts: ", type(route.hosts))
if route.hosts then
local matched = false

local hosts = route.hosts
local host = ctx.host
if host then
local len = #hosts
for i = 1, len, 2 do
if match_host(hosts[i], hosts[i + 1], host) then
if ctx and ctx.matched then
if hosts[i] then
ctx.matched._host = "*" .. hosts[i + 1]
else
ctx.matched._host = ctx.host
end
end
matched = true
break
end
end
end

if not matched then
return false
end
end

return true
end


return Route
120 changes: 6 additions & 114 deletions src/resty/router.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
local Trie = require "router-tree.trie"
local Route = require "router-tree.route"
local utils = require "router-tree.utils"
local Parser = require "router-tree.parser"
local bit = require("bit")

local ipairs = ipairs
local has_wildcard = utils.has_wildcard
local clear_table = utils.clear_table
local start_with = utils.start_with


local EMPTY = {}

local Router = {}
local mt = { __index = Router }

local METHODS = {}
for i, name in ipairs({"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"}) do
METHODS[name] = bit.lshift(1, i - 1)
-- ngx.log(ngx.WARN, "name: ", name, " val: ", METHODS[name])
end

local function sort_route(route_a, route_b)
return (route_a.priority or 0) > (route_b.priority or 0)
Expand All @@ -40,25 +32,6 @@ local function add_route(self, path, route)
end
end

local function extract_params(path)
local params = {}
local parser = Parser.parse(path, "default")
local token = parser:next()
while (token) do
local c = string.sub(token, 1, 1)
if c == ":" then
table.insert(params, string.sub(token, 2))
elseif c == "*" then
if #token > 1 then
table.insert(params, string.sub(token, 2))
else
table.insert(params, ":ext")
end
end
token = parser:next()
end
return params
end

function Router.new(routes, options)
options = options or EMPTY
Expand All @@ -75,104 +48,23 @@ function Router.new(routes, options)
}

for _, route in ipairs(routes) do
local bit_methods = 0
for _, m in ipairs(route.methods or EMPTY) do
bit_methods = bit.bor(bit_methods, METHODS[m])
end

local route_t = {
priority = route.priority,
handler = route.handler or route.metadata,
method = bit_methods,
params = {}
}

-- hosts
local hosts = route.hosts
if type(hosts) == "table" and #hosts > 0 then
route_t.hosts = {}
for _, h in ipairs(hosts) do
local is_wildcard = false
if h and h:sub(1, 1) == '*' then
is_wildcard = true
h = h:sub(2)
end

h = string.lower(h)
table.insert(route_t.hosts, is_wildcard)
table.insert(route_t.hosts, h)
end

elseif type(hosts) == "string" then
local is_wildcard = false
local host = string.lower(hosts)
if host:sub(1, 1) == '*' then
is_wildcard = true
host = host:sub(2)
end

route_t.hosts = {is_wildcard, host}
local route_t, err = Route.new(route)
if err then
return nil, err
end

for _, path in ipairs(route.paths) do
route_t.params[path] = extract_params(path)
add_route(self, path, route_t)
end
end

return setmetatable(self, mt)
end

local function match_host(route_host_is_wildcard, route_host, request_host)
if not route_host_is_wildcard then
return route_host == request_host
end

return start_with(request_host, route_host)
end

local function is_match(route, ctx)
if route.method ~= 0 then
local method = ctx.method
if not method or METHODS[method] == nil or bit.band(route.method, METHODS[method]) == 0 then
return false
end
end

-- log_info("route.hosts: ", type(route.hosts))
if route.hosts then
local matched = false

local hosts = route.hosts
local host = ctx.host
if host then
local len = #hosts
for i = 1, len, 2 do
if match_host(hosts[i], hosts[i + 1], host) then
if ctx and ctx.matched then
if hosts[i] then
ctx.matched._host = "*" .. hosts[i + 1]
else
ctx.matched._host = ctx.host
end
end
matched = true
break
end
end
end

if not matched then
return false
end
end

return true
end

local function find_route(routes, ctx)
for _, route in ipairs(routes) do
if is_match(route, ctx) then
if route:is_match(ctx) then
return route
end
end
Expand Down Expand Up @@ -238,7 +130,7 @@ function Router:match(path, ctx)
end
end

if route and is_match(route, ctx) then
if route and route:is_match(ctx) then
if ctx.matched then
local _path = ctx.matched._path
local params = route.params[_path]
Expand Down
15 changes: 8 additions & 7 deletions src/resty/sample.lua
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,19 @@ local function test_example()
end

local function test5()
local mobdebug = require "mobdebug"
local a, b, c = mobdebug.start("localhost", 28172)
print(a, b, c)
local radix = require("router-tree")
local rx = radix.new({
{
paths = {"/*"},
metadata = "OK",
paths = {"/aa"},
metadata = "metadata 1",
},
{
paths = {"/aa"},
metadata = "metadata 2",
},
})
local opts = {method = "GET", matched = {}}
ngx.say(rx:match("/ip\ni\ni", opts))

ngx.say(rx:match("/aa"))
end

--test1()
Expand Down
Loading

0 comments on commit 95d4bc0

Please sign in to comment.