Skip to content

Commit

Permalink
router: route specificity by symbols known
Browse files Browse the repository at this point in the history
Now we prioritize route (a) over (b) for some
request that matches both:

1. if it has more "constant" symbols (that is
symbols hardcoded in the path and not matched
in regexp group generated from : or * stashes),
or, if this number is equal,
2. if (a) has more specific filter on HTTP method
  • Loading branch information
ASverdlov authored and Konstantin Nazarov committed Oct 4, 2019
1 parent ed8107c commit da4cb2f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
18 changes: 14 additions & 4 deletions http/router/matching.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,19 @@ local function matches(r, filter)
return false
end

-- how many symbols were not known (were hidden behind : and * patterns)
local symbols_didnt_know = 0
for _, matched_part in ipairs(regex_groups_matched) do
symbols_didnt_know = symbols_didnt_know + #matched_part
end

return true, {
route = r,
stash = regex_groups_matched,

-- the more symbols were known in advance by route,
-- the more priority we give the route
specificity = -symbols_didnt_know,
}
end

Expand All @@ -91,10 +101,10 @@ local function better_than(newmatch, oldmatch)
return true
end

-- current match (route) is prioritized iff:
-- 1. it has less matched words, or
-- 2. if current match (route) has more specific method filter
if #oldmatch.stash > #newmatch.stash then
-- newmatch route is prioritized over oldmatch iff:
-- 1. its' path is more specific (see matches() function), or
-- 2. if current route has more specific method filter
if newmatch.specificity > oldmatch.specificity then
return true
end
return newmatch.route.method ~= oldmatch.route.method and
Expand Down
31 changes: 29 additions & 2 deletions test/http.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ test:test("server url_for", function(test)
end)

test:test("server requests", function(test)
test:plan(42)
test:plan(43)
local httpd, router = cfgserv()
httpd:start()

Expand Down Expand Up @@ -501,7 +501,6 @@ test:test("server requests", function(test)
test:is(parsed_body.read_cached, 'hello mister', 'non-json req:read_cached()')
end)


if is_builtin_test() then
test:test('post body', function(test)
test:plan(2)
Expand Down Expand Up @@ -569,6 +568,34 @@ test:test("server requests", function(test)
test:ok(true, 'pong received - ignored on NGINX')
end

test:test('prioritization of more specific routes', function(test)
test:plan(4)

router:route({method = 'GET', path = '*stashname'}, function(_)
return {
status = 200,
body = 'GET *',
}
end)
local r = http_client.get('http://127.0.0.1:12345/a/b/c')
test:is(r.status, 200, '/a/b/c request returns 200')
test:is(r.body, 'GET *', 'GET * matches')

router:route({method = 'ANY', path = '/a/:foo/:bar'}, function(_)
return {
status = 200,
body = 'ANY /a/:foo/:bar',
}
end)
local r = http_client.get('http://127.0.0.1:12345/a/b/c')
test:is(r.status, 200, '/a/b/c request returns 200')
test:is(
r.body,
'ANY /a/:foo/:bar',
'# of stashes matched doesnt matter - only # of known symbols by the route matters'
)
end)

httpd:stop()
end)

Expand Down

0 comments on commit da4cb2f

Please sign in to comment.