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) {
|
||||
// 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
|
||||
}
|
||||
|
@ -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 == "" {
|
||||
|
Loading…
Reference in New Issue
Block a user