Skip to content

Commit 16ed3a8

Browse files
committed
refs #1412: Fix multi level match any routes
1 parent b129098 commit 16ed3a8

File tree

2 files changed

+92
-13
lines changed

2 files changed

+92
-13
lines changed

router.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package echo
22

3-
import "net/http"
3+
import (
4+
"net/http"
5+
"strings"
6+
)
47

58
type (
69
// Router is the registry of all registered routes for an `Echo` instance for
@@ -20,8 +23,8 @@ type (
2023
pnames []string
2124
methodHandler *methodHandler
2225
}
23-
kind uint8
24-
children []*node
26+
kind uint8
27+
children []*node
2528
methodHandler struct {
2629
connect HandlerFunc
2730
delete HandlerFunc
@@ -336,7 +339,6 @@ func (r *Router) Find(method, path string, c Context) {
336339
}
337340
}
338341

339-
340342
if l == pl {
341343
// Continue search
342344
search = search[l:]
@@ -398,16 +400,28 @@ func (r *Router) Find(method, path string, c Context) {
398400
Any:
399401
if cn = cn.findChildByKind(akind); cn == nil {
400402
if nn != nil {
401-
cn = nn
402-
nn = cn.parent // Next (Issue #954)
403-
if nn != nil {
404-
nk = nn.kind
405-
}
403+
// No next node to go down in routing (issue #954)
404+
// Find nearest "any" route going up the routing tree
406405
search = ns
407-
if nk == pkind {
408-
goto Param
409-
} else if nk == akind {
410-
goto Any
406+
np := nn.parent
407+
// Consider param route one level up only
408+
// if no slash is remaining in search string
409+
if cn = nn.findChildByKind(pkind); cn != nil && strings.IndexByte(ns, '/') == -1 {
410+
break
411+
}
412+
for {
413+
np = nn.parent
414+
if cn = nn.findChildByKind(akind); cn != nil {
415+
break
416+
}
417+
if np == nil {
418+
break // no further parent nodes in tree, abort
419+
}
420+
nn = np
421+
}
422+
if cn != nil { // use the found "any" route and update path
423+
pvalues[len(cn.pnames)-1] = search
424+
break
411425
}
412426
}
413427
return // Not found

router_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,20 +595,41 @@ func TestRouterMatchAnyMultiLevel(t *testing.T) {
595595
// Routes
596596
r.Add(http.MethodGet, "/api/users/jack", handler)
597597
r.Add(http.MethodGet, "/api/users/jill", handler)
598+
r.Add(http.MethodGet, "/api/users/*", handler)
599+
r.Add(http.MethodGet, "/api/*", handler)
600+
r.Add(http.MethodGet, "/other/*", handler)
598601
r.Add(http.MethodGet, "/*", handler)
599602

600603
c := e.NewContext(nil, nil).(*context)
601604
r.Find(http.MethodGet, "/api/users/jack", c)
602605
c.handler(c)
603606
assert.Equal(t, "/api/users/jack", c.Get("path"))
607+
assert.Equal(t, "", c.Param("*"))
604608

605609
r.Find(http.MethodGet, "/api/users/jill", c)
606610
c.handler(c)
607611
assert.Equal(t, "/api/users/jill", c.Get("path"))
612+
assert.Equal(t, "", c.Param("*"))
608613

609614
r.Find(http.MethodGet, "/api/users/joe", c)
610615
c.handler(c)
616+
assert.Equal(t, "/api/users/*", c.Get("path"))
617+
assert.Equal(t, "joe", c.Param("*"))
618+
619+
r.Find(http.MethodGet, "/api/nousers/joe", c)
620+
c.handler(c)
621+
assert.Equal(t, "/api/*", c.Get("path"))
622+
assert.Equal(t, "nousers/joe", c.Param("*"))
623+
624+
r.Find(http.MethodGet, "/api/none", c)
625+
c.handler(c)
626+
assert.Equal(t, "/api/*", c.Get("path"))
627+
assert.Equal(t, "none", c.Param("*"))
628+
629+
r.Find(http.MethodGet, "/noapi/users/jim", c)
630+
c.handler(c)
611631
assert.Equal(t, "/*", c.Get("path"))
632+
assert.Equal(t, "noapi/users/jim", c.Param("*"))
612633
}
613634

614635
func TestRouterMicroParam(t *testing.T) {
@@ -702,6 +723,15 @@ func TestRouterPriority(t *testing.T) {
702723
c.Set("g", 7)
703724
return nil
704725
})
726+
r.Add(http.MethodGet, "/users/new/*", func(c Context) error {
727+
c.Set("h", 8)
728+
return nil
729+
})
730+
r.Add(http.MethodGet, "/*", func(c Context) error {
731+
c.Set("i", 9)
732+
return nil
733+
})
734+
705735
c := e.NewContext(nil, nil).(*context)
706736

707737
// Route > /users
@@ -734,11 +764,46 @@ func TestRouterPriority(t *testing.T) {
734764
c.handler(c)
735765
assert.Equal(t, 3, c.Get("c"))
736766

767+
// Route > /users/newsee
768+
r.Find(http.MethodGet, "/users/newsee", c)
769+
c.handler(c)
770+
assert.Equal(t, 6, c.Get("f"))
771+
737772
// Route > /users/*
738773
r.Find(http.MethodGet, "/users/joe/books", c)
739774
c.handler(c)
740775
assert.Equal(t, 7, c.Get("g"))
741776
assert.Equal(t, "joe/books", c.Param("*"))
777+
778+
// Route > /users/new/* should be matched
779+
r.Find(http.MethodGet, "/users/new/someone", c)
780+
c.handler(c)
781+
assert.Equal(t, 8, c.Get("h"))
782+
assert.Equal(t, "someone", c.Param("*"))
783+
784+
// Route > /users/* should be matched although /users/dew exists
785+
r.Find(http.MethodGet, "/users/dew/someone", c)
786+
c.handler(c)
787+
assert.Equal(t, 7, c.Get("g"))
788+
assert.Equal(t, "dew/someone", c.Param("*"))
789+
790+
// Route > /users/* should be matched although /users/dew exists
791+
r.Find(http.MethodGet, "/users/notexists/someone/else", c)
792+
c.handler(c)
793+
assert.Equal(t, 7, c.Get("g"))
794+
assert.Equal(t, "notexists/someone/else", c.Param("*"))
795+
796+
// Route > *
797+
r.Find(http.MethodGet, "/nousers", c)
798+
c.handler(c)
799+
assert.Equal(t, 9, c.Get("i"))
800+
assert.Equal(t, "nousers", c.Param("*"))
801+
802+
// Route > *
803+
r.Find(http.MethodGet, "/nousers/new", c)
804+
c.handler(c)
805+
assert.Equal(t, 9, c.Get("i"))
806+
assert.Equal(t, "nousers/new", c.Param("*"))
742807
}
743808

744809
func TestRouterIssue1348(t *testing.T) {

0 commit comments

Comments
 (0)