Skip to content

Commit

Permalink
feat(router): add router.validate method for validating a ATC expre…
Browse files Browse the repository at this point in the history
…ssion against a schema without creating a router

KAG-1524

---------
Co-authored-by: Chrono <[email protected]>
Co-authored-by: Wangchong Zhou <[email protected]>
Co-authored-by: Datong Sun <[email protected]>
  • Loading branch information
pluveto authored May 11, 2023
1 parent 0300f68 commit 3e7cdb5
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 1 deletion.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ build: target/release/libatc_router.$(SHLIB_EXT) target/release/libatc_router.a
target/release/libatc_router.%: src/*.rs
cargo build --release

install: build
install-lualib:
$(INSTALL) -d $(DESTDIR)$(LUA_LIB_DIR)/resty/router/
$(INSTALL) -m 664 lib/resty/router/*.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/router/

install: build install-lualib
$(INSTALL) -m 775 target/release/libatc_router.$(SHLIB_EXT) $(DESTDIR)$(LUA_LIB_DIR)/libatc_router.$(SHLIB_EXT)

clean:
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Table of Contents
* [remove\_matcher](#remove_matcher)
* [execute](#execute)
* [get\_fields](#get_fields)
* [validate](#validate)
* [resty.router.context](#restyroutercontext)
* [new](#new)
* [add\_value](#add_value)
Expand Down Expand Up @@ -144,6 +145,21 @@ actually used by the user supplied matchers.

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

### validate

**syntax:** *ok, err = router.validate(schema, expr)*

**context:** *any*

Validates an expression against a given schema.

Returns `true` when the expression is valid.

If the expression is invalid,
`nil` and a string describing the reason will be returned.

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

## resty.router.context

### new
Expand Down
29 changes: 29 additions & 0 deletions lib/resty/router/router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,33 @@ function _M:get_fields()
end


do
local ROUTERS = setmetatable({}, { __mode = "k" })
local DEFAULT_UUID = "00000000-0000-0000-0000-000000000000"
local DEFAULT_PRIORITY = 0

-- validate an expression against a schema
-- @param expr the expression to validate
-- @param schema the schema to validate against
-- @return true if the expression is valid, (nil, error) otherwise
function _M.validate(schema, expr)
local r = ROUTERS[schema]

if not r then
r = _M.new(schema, 1)
ROUTERS[schema] = r
end

local ok, err = r:add_matcher(DEFAULT_PRIORITY, DEFAULT_UUID, expr)
if not ok then
return nil, err
end

assert(r:remove_matcher(DEFAULT_UUID))

return true
end
end


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

use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);

repeat_each(2);

plan tests => repeat_each() * blocks() * 5;

my $pwd = cwd();

our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_package_cpath "$pwd/target/debug/?.so;;";
};

no_long_string();
no_diff();

run_tests();

__DATA__

=== TEST 1: test valid schema + expr
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")
local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = "http.headers.foo == \"bar\""
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
true
nil
--- no_error_log
[error]
[warn]
[crit]


=== TEST 2: test type inconsistency (schema is String, expr is Int)
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")
local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = "http.headers.foo == 123"
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body_like
nil
Type mismatch between the LHS and RHS values of predicate
--- no_error_log
[error]
[warn]
[crit]


=== TEST 3: test invalid schema + invalid expr
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")
local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = "== 123"
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
nil
--> 1:1
|
1 | == 123
| ^---
|
= expected term

--- no_error_log
[error]
[warn]
[crit]


=== TEST 4: test valid schema + invalid expr
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")
local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = "== \"bar\""
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
nil
--> 1:1
|
1 | == "bar"
| ^---
|
= expected term

--- no_error_log
[error]
[warn]
[crit]


=== TEST 5: valid regex
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")

local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = [[http.headers.foo ~ "/\\\\/*user$"]]
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
true
nil
--- no_error_log
[error]
[warn]
[crit]


=== TEST 6: invalid regex
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")

local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = [[http.headers.foo ~ "([."]]
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
nil
--> 1:1
|
1 | http.headers.foo ~ "([."
| ^----------------------^
|
= regex parse error:
([.
^
error: unclosed character class

--- no_error_log
[error]
[warn]
[crit]


=== TEST 7: Rust regex 1.8.x will not think the regex is invalid
--- http_config eval: $::HttpConfig
--- config
location = /t {
content_by_lua_block {
local schema = require("resty.router.schema")
local router = require("resty.router.router")

local s = schema.new()
s:add_field("http.headers.foo", "String")
local expr = [[http.headers.foo ~ "/\\/*user$"]]
local r, err = router.validate(s, expr)
ngx.say(r)
ngx.say(err)
}
}
--- request
GET /t
--- response_body
true
nil
--- no_error_log
[error]
[warn]
[crit]

0 comments on commit 3e7cdb5

Please sign in to comment.