Skip to content

Commit f4b5a90

Browse files
authored
Fix #1493 router loop for param routes (#1501)
* Add test to reproduce router loop for #1493 * Simplify and correct router param tests * Fix #1493 to avoid router loop for param nodes
1 parent 504f39a commit f4b5a90

File tree

2 files changed

+61
-33
lines changed

2 files changed

+61
-33
lines changed

router.go

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,8 @@ func (r *Router) Find(method, path string, c Context) {
376376
continue
377377
}
378378

379-
// Param node
380379
Param:
380+
// Param node
381381
if child = cn.findChildByKind(pkind); child != nil {
382382
// Issue #378
383383
if len(pvalues) == n {
@@ -401,47 +401,58 @@ func (r *Router) Find(method, path string, c Context) {
401401
continue
402402
}
403403

404-
// Any node
405404
Any:
406-
if cn = cn.findChildByKind(akind); cn == nil {
407-
if nn != nil {
408-
// No next node to go down in routing (issue #954)
409-
// Find nearest "any" route going up the routing tree
410-
search = ns
411-
np := nn.parent
412-
// Consider param route one level up only
413-
// if no slash is remaining in search string
414-
if cn = nn.findChildByKind(pkind); cn != nil && strings.IndexByte(ns, '/') == -1 {
405+
// Any node
406+
if cn = cn.findChildByKind(akind); cn != nil {
407+
// If any node is found, use remaining path for pvalues
408+
pvalues[len(cn.pnames)-1] = search
409+
break
410+
}
411+
412+
// No node found, continue at stored next node
413+
// or find nearest "any" route
414+
if nn != nil {
415+
// No next node to go down in routing (issue #954)
416+
// Find nearest "any" route going up the routing tree
417+
search = ns
418+
np := nn.parent
419+
// Consider param route one level up only
420+
if cn = nn.findChildByKind(pkind); cn != nil {
421+
pos := strings.IndexByte(ns, '/')
422+
if pos == -1 {
423+
// If no slash is remaining in search string set param value
415424
pvalues[len(cn.pnames)-1] = search
416425
break
417-
} else if cn != nil && strings.IndexByte(ns, '/') != 1 {
418-
// If slash is present, it means that this is a parameterized route.
419-
cn = cn.parent
426+
} else if pos > 0 {
427+
// Otherwise continue route processing with restored next node
428+
cn = nn
429+
nn = nil
430+
ns = ""
420431
goto Param
421432
}
422-
for {
423-
np = nn.parent
424-
if cn = nn.findChildByKind(akind); cn != nil {
425-
break
426-
}
427-
if np == nil {
428-
break // no further parent nodes in tree, abort
429-
}
430-
var str strings.Builder
431-
str.WriteString(nn.prefix)
432-
str.WriteString(search)
433-
search = str.String()
434-
nn = np
435-
}
436-
if cn != nil { // use the found "any" route and update path
437-
pvalues[len(cn.pnames)-1] = search
433+
}
434+
// No param route found, try to resolve nearest any route
435+
for {
436+
np = nn.parent
437+
if cn = nn.findChildByKind(akind); cn != nil {
438438
break
439439
}
440+
if np == nil {
441+
break // no further parent nodes in tree, abort
442+
}
443+
var str strings.Builder
444+
str.WriteString(nn.prefix)
445+
str.WriteString(search)
446+
search = str.String()
447+
nn = np
448+
}
449+
if cn != nil { // use the found "any" route and update path
450+
pvalues[len(cn.pnames)-1] = search
451+
break
440452
}
441-
return // Not found
442453
}
443-
pvalues[len(cn.pnames)-1] = search
444-
break
454+
return // Not found
455+
445456
}
446457

447458
ctx.handler = cn.findHandler(method)

router_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,12 +1031,15 @@ func TestRouterParamBacktraceNotFound(t *testing.T) {
10311031
r.Find(http.MethodGet, "/a", c)
10321032
assert.Equal(t, "a", c.Param("param1"))
10331033

1034+
c = e.NewContext(nil, nil).(*context)
10341035
r.Find(http.MethodGet, "/a/foo", c)
10351036
assert.Equal(t, "a", c.Param("param1"))
10361037

1038+
c = e.NewContext(nil, nil).(*context)
10371039
r.Find(http.MethodGet, "/a/bar", c)
10381040
assert.Equal(t, "a", c.Param("param1"))
10391041

1042+
c = e.NewContext(nil, nil).(*context)
10401043
r.Find(http.MethodGet, "/a/bar/b", c)
10411044
assert.Equal(t, "a", c.Param("param1"))
10421045
assert.Equal(t, "b", c.Param("param2"))
@@ -1157,31 +1160,45 @@ func TestRouterParam1466(t *testing.T) {
11571160
r.Find(http.MethodGet, "/users/ajitem", c)
11581161
assert.Equal(t, "ajitem", c.Param("username"))
11591162

1163+
c = e.NewContext(nil, nil).(*context)
11601164
r.Find(http.MethodGet, "/users/sharewithme", c)
11611165
assert.Equal(t, "sharewithme", c.Param("username"))
11621166

1167+
c = e.NewContext(nil, nil).(*context)
11631168
r.Find(http.MethodGet, "/users/signup", c)
11641169
assert.Equal(t, "", c.Param("username"))
11651170
// Additional assertions for #1479
1171+
c = e.NewContext(nil, nil).(*context)
11661172
r.Find(http.MethodGet, "/users/sharewithme/likes/projects/ids", c)
11671173
assert.Equal(t, "sharewithme", c.Param("username"))
11681174

1175+
c = e.NewContext(nil, nil).(*context)
11691176
r.Find(http.MethodGet, "/users/ajitem/likes/projects/ids", c)
11701177
assert.Equal(t, "ajitem", c.Param("username"))
11711178

1179+
c = e.NewContext(nil, nil).(*context)
11721180
r.Find(http.MethodGet, "/users/sharewithme/profile", c)
11731181
assert.Equal(t, "sharewithme", c.Param("username"))
11741182

1183+
c = e.NewContext(nil, nil).(*context)
11751184
r.Find(http.MethodGet, "/users/ajitem/profile", c)
11761185
assert.Equal(t, "ajitem", c.Param("username"))
11771186

1187+
c = e.NewContext(nil, nil).(*context)
11781188
r.Find(http.MethodGet, "/users/sharewithme/uploads/self", c)
11791189
assert.Equal(t, "sharewithme", c.Param("username"))
11801190
assert.Equal(t, "self", c.Param("type"))
11811191

1192+
c = e.NewContext(nil, nil).(*context)
11821193
r.Find(http.MethodGet, "/users/ajitem/uploads/self", c)
11831194
assert.Equal(t, "ajitem", c.Param("username"))
11841195
assert.Equal(t, "self", c.Param("type"))
1196+
1197+
// Issue #1493 - check for routing loop
1198+
c = e.NewContext(nil, nil).(*context)
1199+
r.Find(http.MethodGet, "/users/tree/free", c)
1200+
assert.Equal(t, "", c.Param("id"))
1201+
assert.Equal(t, 0, c.response.Status)
11851202
}
11861203

11871204
func benchmarkRouterRoutes(b *testing.B, routes []*Route) {

0 commit comments

Comments
 (0)