fix tsr with mixed static and wildcard paths (#2924)

This commit is contained in:
Ibraheem Ahmed 2021-10-31 20:21:37 -04:00 committed by GitHub
parent d4e72a17f7
commit cbdd47a7e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 25 deletions

46
tree.go
View File

@ -447,27 +447,26 @@ walk: // Outer loop for walking the tree
continue walk continue walk
} }
} }
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
// the current node needs to roll back to last vaild skippedNode
if path != "/" && !n.wildChild { if !n.wildChild {
for l := len(*skippedNodes); l > 0; { // If the path at the end of the loop is not equal to '/' and the current node has no child nodes
skippedNode := (*skippedNodes)[l-1] // the current node needs to roll back to last vaild skippedNode
*skippedNodes = (*skippedNodes)[:l-1] if path != "/" {
if strings.HasSuffix(skippedNode.path, path) { for l := len(*skippedNodes); l > 0; {
path = skippedNode.path skippedNode := (*skippedNodes)[l-1]
n = skippedNode.node *skippedNodes = (*skippedNodes)[:l-1]
if value.params != nil { if strings.HasSuffix(skippedNode.path, path) {
*value.params = (*value.params)[:skippedNode.paramsCount] path = skippedNode.path
n = skippedNode.node
if value.params != nil {
*value.params = (*value.params)[:skippedNode.paramsCount]
}
globalParamsCount = skippedNode.paramsCount
continue walk
} }
globalParamsCount = skippedNode.paramsCount
continue walk
} }
} }
}
// If there is no wildcard pattern, recommend a redirection
if !n.wildChild {
// Nothing found. // Nothing found.
// We can recommend to redirect to the same URL without a // We can recommend to redirect to the same URL without a
// trailing slash if a leaf exists for that path. // trailing slash if a leaf exists for that path.
@ -614,8 +613,14 @@ walk: // Outer loop for walking the tree
return return
} }
// roll back to last vaild skippedNode // Nothing found. We can recommend to redirect to the same URL with an
if path != "/" { // extra trailing slash if a leaf exists for that path
value.tsr = path == "/" ||
(len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
path == prefix[:len(prefix)-1] && n.handlers != nil)
// roll back to last valid skippedNode
if !value.tsr && path != "/" {
for l := len(*skippedNodes); l > 0; { for l := len(*skippedNodes); l > 0; {
skippedNode := (*skippedNodes)[l-1] skippedNode := (*skippedNodes)[l-1]
*skippedNodes = (*skippedNodes)[:l-1] *skippedNodes = (*skippedNodes)[:l-1]
@ -631,11 +636,6 @@ walk: // Outer loop for walking the tree
} }
} }
// Nothing found. We can recommend to redirect to the same URL with an
// extra trailing slash if a leaf exists for that path
value.tsr = path == "/" ||
(len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
path == prefix[:len(prefix)-1] && n.handlers != nil)
return return
} }
} }

View File

@ -587,7 +587,14 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/doc/go1.html", "/doc/go1.html",
"/no/a", "/no/a",
"/no/b", "/no/b",
"/api/hello/:name", "/api/:page/:name",
"/api/hello/:name/bar/",
"/api/bar/:name",
"/api/baz/foo",
"/api/baz/foo/bar",
"/blog/:p",
"/posts/:b/:c",
"/posts/b/:c/d/",
} }
for _, route := range routes { for _, route := range routes {
recv := catchPanic(func() { recv := catchPanic(func() {
@ -613,7 +620,19 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/admin/config/", "/admin/config/",
"/admin/config/permissions/", "/admin/config/permissions/",
"/doc/", "/doc/",
"/admin/static/",
"/admin/cfg/",
"/admin/cfg/users/",
"/api/hello/x/bar",
"/api/baz/foo/",
"/api/baz/bax/",
"/api/bar/huh/",
"/api/baz/foo/bar/",
"/api/world/abc/",
"/blog/pp/",
"/posts/b/c/d",
} }
for _, route := range tsrRoutes { for _, route := range tsrRoutes {
value := tree.getValue(route, nil, getSkippedNodes(), false) value := tree.getValue(route, nil, getSkippedNodes(), false)
if value.handlers != nil { if value.handlers != nil {
@ -629,7 +648,11 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/no/", "/no/",
"/_", "/_",
"/_/", "/_/",
"/api/world/abc", "/api",
"/api/",
"/api/hello/x/foo",
"/api/baz/foo/bad",
"/foo/p/p",
} }
for _, route := range noTsrRoutes { for _, route := range noTsrRoutes {
value := tree.getValue(route, nil, getSkippedNodes(), false) value := tree.getValue(route, nil, getSkippedNodes(), false)