Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router/atc): support field http.path.segments.len #12398

Merged
merged 6 commits into from
Jan 24, 2024
Merged
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
7 changes: 4 additions & 3 deletions kong/db/schema/entities/routes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ do

for _, f in ipairs(fields) do
if f:find(HTTP_PATH_SEGMENTS_PREFIX, 1, true) then
local m = re_match(f:sub(#HTTP_PATH_SEGMENTS_PREFIX + 1),
HTTP_PATH_SEGMENTS_SUFFIX_REG, "jo")
local suffix = f:sub(#HTTP_PATH_SEGMENTS_PREFIX + 1)
local m = re_match(suffix, HTTP_PATH_SEGMENTS_SUFFIX_REG, "jo")

if not m or (m[2] and tonumber(m[1]) >= tonumber(m[3])) then
if (suffix ~= "len") and
(not m or (m[2] and tonumber(m[1]) >= tonumber(m[3]))) then
chobits marked this conversation as resolved.
Show resolved Hide resolved
return nil, "Router Expression failed validation: " ..
"illformed http.path.segments.* field"
end
Expand Down
32 changes: 25 additions & 7 deletions kong/router/fields.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ local HTTP_FIELDS = {
},

["Int"] = {"net.src.port", "net.dst.port",
"http.path.segments.len",
},

["IpAddr"] = {"net.src.ip", "net.dst.ip",
Expand Down Expand Up @@ -209,10 +210,32 @@ if is_http then

local HTTP_SEGMENTS_PREFIX = "http.path.segments."
local HTTP_SEGMENTS_PREFIX_LEN = #HTTP_SEGMENTS_PREFIX
local HTTP_SEGMENTS_REG_CTX = { pos = 2, } -- skip first '/'
local HTTP_SEGMENTS_OFFSET = 1


chronolaw marked this conversation as resolved.
Show resolved Hide resolved
local get_http_segments
do
local HTTP_SEGMENTS_REG_CTX = { pos = 2, } -- skip first '/'

get_http_segments = function(params)
if not params.segments then
HTTP_SEGMENTS_REG_CTX.pos = 2 -- reset ctx, skip first '/'
params.segments = re_split(params.uri, "/", "jo", HTTP_SEGMENTS_REG_CTX)
end

return params.segments
end
end


FIELDS_FUNCS["http.path.segments.len"] =
function(params)
chobits marked this conversation as resolved.
Show resolved Hide resolved
local segments = get_http_segments(params)

return #segments
end


-- func => get_headers or get_uri_args
-- name => "headers" or "queries"
-- max_config_option => "lua_max_req_headers" or "lua_max_uri_args"
Expand Down Expand Up @@ -281,12 +304,7 @@ if is_http then
local range = field:sub(HTTP_SEGMENTS_PREFIX_LEN + 1)

f = function(params)
if not params.segments then
HTTP_SEGMENTS_REG_CTX.pos = 2 -- reset ctx, skip first '/'
params.segments = re_split(params.uri, "/", "jo", HTTP_SEGMENTS_REG_CTX)
end

local segments = params.segments
local segments = get_http_segments(params)

local value = segments[range]

Expand Down
24 changes: 20 additions & 4 deletions spec/01-unit/01-db/01-schema/06-routes_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1563,16 +1563,28 @@ describe("routes schema (flavor = expressions)", function()
end)

it("http route supports http.path.segments.* fields", function()
local route = {
local r = {
id = a_valid_uuid,
name = "my_route",
protocols = { "grpcs" },
expression = [[http.path.segments.0 == "foo" && http.path.segments.1 ^= "bar" && http.path.segments.20_30 ~ r#"x/y"#]],
priority = 100,
service = { id = another_uuid },
}
route = Routes:process_auto_fields(route, "insert")
assert.truthy(Routes:validate(route))

local expressions = {
[[http.path.segments.0 == "foo"]],
[[http.path.segments.1 ^= "bar"]],
[[http.path.segments.20_30 ~ r#"x/y"#]],
[[http.path.segments.len == 10]],
}

for _, exp in ipairs(expressions) do
r.expression = exp

local route = Routes:process_auto_fields(r, "insert")
assert.truthy(Routes:validate(route))
end

end)

it("fails if http route has invalid http.path.segments.* fields", function()
Expand All @@ -1585,6 +1597,10 @@ describe("routes schema (flavor = expressions)", function()
}

local wrong_expressions = {
[[http.path.segments.len0 == 10]],
[[http.path.segments.len_a == 10]],
[[http.path.segments.len == "10"]],

[[http.path.segments. == "foo"]],
[[http.path.segments.abc == "foo"]],
[[http.path.segments.a_c == "foo"]],
Expand Down
42 changes: 42 additions & 0 deletions spec/01-unit/08-router_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5443,6 +5443,32 @@ do
assert.falsy(match_t)
end)

it("select() should match http.segments.* with len", function()
local use_case = {
{
service = service,
route = {
id = "e8fb37f1-102d-461e-9c51-6608a6bb8101",
expression = [[http.path.segments.0 == "foo" && http.path.segments.len == 1]],
priority = 100,
},
},
}

local router = assert(new_router(use_case))

local match_t = router:select("GET", "/foo")
assert.truthy(match_t)
assert.same(use_case[1].route, match_t.route)

local match_t = router:select("GET", "/foo/")
assert.truthy(match_t)
assert.same(use_case[1].route, match_t.route)

local match_t = router:select("GET", "/foo/xxx")
assert.falsy(match_t)
end)

it("select() should match range http.segments.*", function()
local use_case = {
{
Expand All @@ -5461,6 +5487,14 @@ do
priority = 100,
},
},
{
service = service,
route = {
id = "e8fb37f1-102d-461e-9c51-6608a6bb8103",
expression = [[http.path.segments.1_2 == r#"xxx/yyy"# && http.path.segments.len == 3]],
priority = 100,
},
},
}

local router = assert(new_router(use_case))
Expand All @@ -5476,6 +5510,14 @@ do
local match_t = router:select("GET", "/foo/xxx/yyy/zzz/bar")
assert.truthy(match_t)
assert.same(use_case[2].route, match_t.route)

local match_t = router:select("GET", "/foo/xxx/yyy")
assert.truthy(match_t)
assert.same(use_case[3].route, match_t.route)

local match_t = router:select("GET", "/foo/xxx/yyy/")
assert.truthy(match_t)
assert.same(use_case[3].route, match_t.route)
end)

it("select() accepts but does not match wrong http.segments.*", function()
Expand Down
Loading