Skip to content

Commit

Permalink
feat: refactor with lua-resty-expr (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
spacewander committed Dec 3, 2020
1 parent 99f4049 commit d89b485
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 118 deletions.
17 changes: 1 addition & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
- [Full path match](#full-path-match)
- [Prefix match](#prefix-match)
- [Parameters in path](#parameters-in-path)
- [Operator List](#operator-list)
- [match](#match)
- [dispatch](#dispatch)
- [Install](#install)
Expand Down Expand Up @@ -99,7 +98,7 @@ The attributes of each element may contain these:
|hosts |option |A list of client request host, not only supports normal domain name, but also supports wildcard name.|{"foo.com", "*.bar.com"}|
|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"}|
|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"}|
|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`. Here is the full [Operator List](#operator-list).|{{"arg_name", "==", "json"}, {"arg_age", ">", 18}}|
|vars |option |A DSL to evaluate with the given `opts.vars` or `ngx.var`. See https://github.com/api7/lua-resty-expr#new |{{"arg_name", "==", "json"}, {"arg_age", ">", 18}}|
|filter_fun |option |User defined filter function, We can use it to achieve matching logic for special scenes. `radixtree` will pass `vars` and other arguments when matching route.|function(vars) return vars["arg_name"] == "json" end|
|priority |option |Routing priority, default is 0.|priority = 100|
|metadata |option |Will return this field if using `rx:match` to match route.||
Expand Down Expand Up @@ -162,20 +161,6 @@ local rx = radix.new({
})
```

#### Operator List

|operator|description|example|
|--------|-----------|-------|
|== |equal |{"arg_name", "==", "json"}|
|~= |not equal |{"arg_name", "~=", "json"}|
|> |greater than|{"arg_age", ">", 24}|
|< |less than |{"arg_age", "<", 24}|
|~~ |Regular match|{"arg_name", "~~", "[a-z]+"}|
|in |find in array|{"arg_name", "in", {"1","2"}}|
|has |left value array has value in the right |{"graphql_root_fields", "has", "repo"}|

[Back to TOC](#table-of-contents)

### match

`syntax: metadata = rx:match(path, opts)`
Expand Down
125 changes: 27 additions & 98 deletions lib/resty/radixtree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ local ipmatcher = require("resty.ipmatcher")
local base = require("resty.core.base")
local clone_tab = require("table.clone")
local lrucache = require("resty.lrucache")
local expr = require("resty.expr.v1")
local bit = require("bit")
local ngx = ngx
local table = table
Expand Down Expand Up @@ -163,6 +164,7 @@ end

local ngx_log = ngx.log
local ngx_INFO = ngx.INFO
local ngx_ERR = ngx.ERR
local function log_info(...)
if cur_level and ngx_INFO > cur_level then
return
Expand All @@ -171,6 +173,13 @@ local function log_info(...)
return ngx_log(ngx_INFO, ...)
end

local function log_err(...)
if cur_level and ngx_ERR > cur_level then
return
end

return ngx_log(ngx_ERR, ...)
end

local mt = { __index = _M, __gc = gc_free }

Expand Down Expand Up @@ -256,12 +265,6 @@ function pre_insert_route(self, path, route)
error("missing argument metadata or handler", 2)
end

if route.vars then
if type(route.vars) ~= "table" then
error("invalid argument vars", 2)
end
end

local method = route.methods
local bit_methods
if type(method) ~= "table" then
Expand All @@ -276,6 +279,18 @@ function pre_insert_route(self, path, route)

clear_tab(route_opts)

if route.vars then
if type(route.vars) ~= "table" then
error("invalid argument vars", 2)
end

local route_expr, err = expr.new(route.vars)
if not route_expr then
error("failed to handle expression: " .. err, 2)
end
route_opts.vars = route_expr
end

local hosts = route.hosts
if type(hosts) == "table" and #hosts > 0 then
route_opts.hosts = {}
Expand Down Expand Up @@ -330,7 +345,6 @@ function pre_insert_route(self, path, route)
route_opts.metadata = route.metadata
route_opts.handler = route.handler
route_opts.method = bit_methods
route_opts.vars = route.vars
route_opts.filter_fun = route.filter_fun
route_opts.priority = route.priority or 0

Expand Down Expand Up @@ -485,86 +499,6 @@ local function compare_param(req_path, route, opts)
return true
end

local function in_array(l_v, r_v)
if type(r_v) == "table" then
for _,v in ipairs(r_v) do
if v == l_v then
return true
end
end
end
return false
end

local function has_element(l_v, r_v)
if type(l_v) == "table" then
for _, v in ipairs(l_v) do
if v == r_v then
return true
end
end

return false
end

return false
end

local compare_funcs = {
["=="] = function (l_v, r_v)
if type(r_v) == "number" then
l_v = tonumber(l_v)
if not l_v then
return false
end
end
return l_v == r_v
end,
["~="] = function (l_v, r_v)
return l_v ~= r_v
end,
[">"] = function (l_v, r_v)
l_v = tonumber(l_v)
r_v = tonumber(r_v)
if not l_v or not r_v then
return false
end
return l_v > r_v
end,
["<"] = function (l_v, r_v)
l_v = tonumber(l_v)
r_v = tonumber(r_v)
if not l_v or not r_v then
return false
end
return l_v < r_v
end,
["~~"] = function (l_v, r_v)
local from = re_find(l_v, r_v, "jo")
if from then
return true
end
return false
end,
["IN"] = in_array,
["in"] = in_array,
["has"] = has_element,
}


local function compare_val(l_v, op, r_v, opts)
if r_v == ngx_null then
r_v = nil
end

local com_fun = compare_funcs[op or "=="]
if not com_fun then
return false
end
return com_fun(l_v, r_v, opts)
end


local function match_route_opts(route, opts, args)
local method = opts.method
local opts_matched_exists = (opts.matched ~= nil)
Expand Down Expand Up @@ -621,18 +555,13 @@ local function match_route_opts(route, opts, args)
end

if route.vars then
local vars = opts.vars or ngx_var
if type(vars) ~= "table" then
return false
end

for _, route_var in ipairs(route.vars) do
local l_v, op, r_v = route_var[1], route_var[2], route_var[3]
l_v = vars[l_v]

if not compare_val(l_v, op, r_v, opts) then
return false
local ok, err = route.vars:eval(opts.vars, opts)
if not ok then
if ok == nil then
log_err("failed to eval expression: ", err)
end

return false
end
end

Expand Down
1 change: 1 addition & 0 deletions rockspec/lua-resty-radixtree-master-0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ description = {

dependencies = {
"lua-resty-ipmatcher",
"lua-resty-expr = 1.0.0",
}

build = {
Expand Down
8 changes: 4 additions & 4 deletions t/vars.t
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ nil
location /t {
content_by_lua_block {
local radix = require("resty.radixtree")
local rx = radix.new({
local ok, err = pcall(radix.new, {
{
paths = "/aa",
metadata = "metadata /aa",
Expand All @@ -329,15 +329,15 @@ nil
}
})
ngx.say(rx:match("/aa", {vars = ngx.var}))
ngx.say(ok, " ", err)
}
}
--- request
GET /t?k=9
--- no_error_log
[error]
--- response_body
nil
--- response_body_like eval
qr/failed to handle expression: invalid operator 'invalid'/
Expand Down

0 comments on commit d89b485

Please sign in to comment.