handler: use longest prefix, shortest subpath matches to query if no route is found (#19)

Fixes #18

If any pathConfig is the shortest prefix of a query,
find and return it.
This commit is contained in:
Emmanuel T Odeke 2018-03-15 13:24:24 -07:00 committed by JBD
parent 5ad859d28f
commit 682d637d93
2 changed files with 68 additions and 1 deletions

View File

@ -185,6 +185,8 @@ func (pset pathConfigSet) Swap(i, j int) {
}
func (pset pathConfigSet) find(path string) (pc *pathConfig, subpath string) {
// Fast path with binary search to retrieve exact matches
// e.g. given pset ["/", "/abc", "/xyz"], path "/def" won't match.
i := sort.Search(len(pset), func(i int) bool {
return pset[i].path >= path
})
@ -194,5 +196,30 @@ func (pset pathConfigSet) find(path string) (pc *pathConfig, subpath string) {
if i > 0 && strings.HasPrefix(path, pset[i-1].path+"/") {
return &pset[i-1], path[len(pset[i-1].path)+1:]
}
return nil, ""
// Slow path, now looking for the longest prefix/shortest subpath i.e.
// e.g. given pset ["/", "/abc/", "/abc/def/", "/xyz"/]
// * query "/abc/foo" returns "/abc/" with a subpath of "foo"
// * query "/x" returns "/" with a subpath of "x"
lenShortestSubpath := len(path)
var bestMatchConfig *pathConfig
// After binary search with the >= lexicographic comparison,
// nothing greater than i will be a prefix of path.
max := i
for i := 0; i < max; i++ {
ps := pset[i]
if len(ps.path) >= len(path) {
// We previously didn't find the path by search, so any
// route with equal or greater length is NOT a match.
continue
}
sSubpath := strings.TrimPrefix(path, ps.path)
if len(sSubpath) < lenShortestSubpath {
subpath = sSubpath
lenShortestSubpath = len(sSubpath)
bestMatchConfig = &pset[i]
}
}
return bestMatchConfig, subpath
}

View File

@ -207,6 +207,46 @@ func TestPathConfigSetFind(t *testing.T) {
want: "/portmidi",
subpath: "foo",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/x",
want: "/",
subpath: "x",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/",
want: "/",
subpath: "",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/example",
want: "/",
subpath: "example",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/example/foo",
want: "/",
subpath: "example/foo",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/y",
want: "/y",
},
{
paths: []string{"/example/helloworld", "/", "/y", "/foo"},
query: "/x/y/",
want: "/",
subpath: "x/y/",
},
{
paths: []string{"/example/helloworld", "/y", "/foo"},
query: "/x",
want: "",
},
}
emptyToNil := func(s string) string {
if s == "" {