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:
parent
5ad859d28f
commit
682d637d93
29
handler.go
29
handler.go
@ -185,6 +185,8 @@ func (pset pathConfigSet) Swap(i, j int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pset pathConfigSet) find(path string) (pc *pathConfig, subpath string) {
|
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 {
|
i := sort.Search(len(pset), func(i int) bool {
|
||||||
return pset[i].path >= path
|
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+"/") {
|
if i > 0 && strings.HasPrefix(path, pset[i-1].path+"/") {
|
||||||
return &pset[i-1], path[len(pset[i-1].path)+1:]
|
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
|
||||||
}
|
}
|
||||||
|
@ -207,6 +207,46 @@ func TestPathConfigSetFind(t *testing.T) {
|
|||||||
want: "/portmidi",
|
want: "/portmidi",
|
||||||
subpath: "foo",
|
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 {
|
emptyToNil := func(s string) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user