Skip to content

Commit 1a3e58b

Browse files
g1eny0ungappleboy
authored andcommitted
Fix conflict between param and exact path (#2706)
* Fix conflict between param and exact path Signed-off-by: Yue Yang <[email protected]> * Add test Signed-off-by: Yue Yang <[email protected]> * Fix prefix conflict in exact paths Signed-off-by: Yue Yang <[email protected]> * Use backtracking Signed-off-by: Yue Yang <[email protected]> * Fix panic Signed-off-by: Yue Yang <[email protected]>
1 parent 51c7d00 commit 1a3e58b

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

tree.go

+29
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ type node struct {
118118
fullPath string
119119
}
120120

121+
type skip struct {
122+
path string
123+
paramNode *node
124+
}
125+
121126
// Increments priority of the given child and reorders if necessary
122127
func (n *node) incrementChildPrio(pos int) int {
123128
cs := n.children
@@ -400,6 +405,8 @@ type nodeValue struct {
400405
// made if a handle exists with an extra (without the) trailing slash for the
401406
// given path.
402407
func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) {
408+
var skipped *skip
409+
403410
walk: // Outer loop for walking the tree
404411
for {
405412
prefix := n.path
@@ -411,6 +418,21 @@ walk: // Outer loop for walking the tree
411418
idxc := path[0]
412419
for i, c := range []byte(n.indices) {
413420
if c == idxc {
421+
if strings.HasPrefix(n.children[len(n.children)-1].path, ":") {
422+
skipped = &skip{
423+
path: prefix + path,
424+
paramNode: &node{
425+
path: n.path,
426+
wildChild: n.wildChild,
427+
nType: n.nType,
428+
priority: n.priority,
429+
children: n.children,
430+
handlers: n.handlers,
431+
fullPath: n.fullPath,
432+
},
433+
}
434+
}
435+
414436
n = n.children[i]
415437
continue walk
416438
}
@@ -542,6 +564,13 @@ walk: // Outer loop for walking the tree
542564
return
543565
}
544566

567+
if path != "/" && skipped != nil && strings.HasSuffix(skipped.path, path) {
568+
path = skipped.path
569+
n = skipped.paramNode
570+
skipped = nil
571+
continue walk
572+
}
573+
545574
// Nothing found. We can recommend to redirect to the same URL with an
546575
// extra trailing slash if a leaf exists for that path
547576
value.tsr = (path == "/") ||

tree_test.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,16 @@ func TestTreeWildcard(t *testing.T) {
135135

136136
routes := [...]string{
137137
"/",
138-
"/cmd/:tool/:sub",
139138
"/cmd/:tool/",
139+
"/cmd/:tool/:sub",
140140
"/cmd/whoami",
141+
"/cmd/whoami/root",
141142
"/cmd/whoami/root/",
142143
"/src/*filepath",
143144
"/search/",
144145
"/search/:query",
146+
"/search/gin-gonic",
147+
"/search/google",
145148
"/user_:name",
146149
"/user_:name/about",
147150
"/files/:dir/*filepath",
@@ -150,6 +153,7 @@ func TestTreeWildcard(t *testing.T) {
150153
"/doc/go1.html",
151154
"/info/:user/public",
152155
"/info/:user/project/:project",
156+
"/info/:user/project/golang",
153157
}
154158
for _, route := range routes {
155159
tree.addRoute(route, fakeHandler(route))
@@ -159,21 +163,29 @@ func TestTreeWildcard(t *testing.T) {
159163
{"/", false, "/", nil},
160164
{"/cmd/test", true, "/cmd/:tool/", Params{Param{"tool", "test"}}},
161165
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
166+
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "test"}, Param{Key: "sub", Value: "3"}}},
167+
{"/cmd/who", true, "/cmd/:tool/", Params{Param{"tool", "who"}}},
168+
{"/cmd/who/", false, "/cmd/:tool/", Params{Param{"tool", "who"}}},
162169
{"/cmd/whoami", false, "/cmd/whoami", nil},
163170
{"/cmd/whoami/", true, "/cmd/whoami", nil},
171+
{"/cmd/whoami/r", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}},
172+
{"/cmd/whoami/r/", true, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}},
173+
{"/cmd/whoami/root", false, "/cmd/whoami/root", nil},
164174
{"/cmd/whoami/root/", false, "/cmd/whoami/root/", nil},
165-
{"/cmd/whoami/root", true, "/cmd/whoami/root/", nil},
166-
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "test"}, Param{Key: "sub", Value: "3"}}},
167175
{"/src/", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/"}}},
168176
{"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}},
169177
{"/search/", false, "/search/", nil},
170178
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}},
171179
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}},
180+
{"/search/gin", false, "/search/:query", Params{Param{"query", "gin"}}},
181+
{"/search/gin-gonic", false, "/search/gin-gonic", nil},
182+
{"/search/google", false, "/search/google", nil},
172183
{"/user_gopher", false, "/user_:name", Params{Param{Key: "name", Value: "gopher"}}},
173184
{"/user_gopher/about", false, "/user_:name/about", Params{Param{Key: "name", Value: "gopher"}}},
174185
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{Key: "dir", Value: "js"}, Param{Key: "filepath", Value: "/inc/framework.js"}}},
175186
{"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}},
176187
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}},
188+
{"/info/gordon/project/golang", false, "/info/:user/project/golang", Params{Param{Key: "user", Value: "gordon"}}},
177189
})
178190

179191
checkPriorities(t, tree)

0 commit comments

Comments
 (0)