mirror of
https://github.com/labstack/echo.git
synced 2025-07-03 00:56:59 +02:00
Poc router stack backtracking (#1791)
Router: PoC stack based backtracking Co-authored-by: stffabi <stffabi@users.noreply.github.com>
This commit is contained in:
176
router_test.go
176
router_test.go
@ -730,26 +730,58 @@ func TestRouterMatchAny(t *testing.T) {
|
||||
r := e.router
|
||||
|
||||
// Routes
|
||||
r.Add(http.MethodGet, "/", func(Context) error {
|
||||
return nil
|
||||
})
|
||||
r.Add(http.MethodGet, "/*", func(Context) error {
|
||||
return nil
|
||||
})
|
||||
r.Add(http.MethodGet, "/users/*", func(Context) error {
|
||||
return nil
|
||||
})
|
||||
r.Add(http.MethodGet, "/", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/*", handlerHelper("case", 2))
|
||||
r.Add(http.MethodGet, "/users/*", handlerHelper("case", 3))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
r.Find(http.MethodGet, "/", c)
|
||||
assert.Equal(t, "", c.Param("*"))
|
||||
c.handler(c)
|
||||
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/", c.Get("path"))
|
||||
|
||||
r.Find(http.MethodGet, "/download", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 2, c.Get("case"))
|
||||
assert.Equal(t, "/*", c.Get("path"))
|
||||
assert.Equal(t, "download", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/users/joe", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 3, c.Get("case"))
|
||||
assert.Equal(t, "/users/*", c.Get("path"))
|
||||
assert.Equal(t, "joe", c.Param("*"))
|
||||
}
|
||||
|
||||
// NOTE: this is to document current implementation. Last added route with `*` asterisk is always the match and no
|
||||
// backtracking or more precise matching is done to find more suitable match.
|
||||
//
|
||||
// Current behaviour might not be correct or expected.
|
||||
// But this is where we are without well defined requirements/rules how (multiple) asterisks work in route
|
||||
func TestRouterAnyMatchesLastAddedAnyRoute(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/users/*", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/users/*/action*", handlerHelper("case", 2))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
|
||||
r.Find(http.MethodGet, "/users/xxx/action/sea", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, "/users/*/action*", c.Get("path"))
|
||||
assert.Equal(t, "xxx/action/sea", c.Param("*"))
|
||||
|
||||
// if we add another route then it is the last added and so it is matched
|
||||
r.Add(http.MethodGet, "/users/*/action/search", handlerHelper("case", 3))
|
||||
|
||||
r.Find(http.MethodGet, "/users/xxx/action/sea", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, "/users/*/action/search", c.Get("path"))
|
||||
assert.Equal(t, "xxx/action/sea", c.Param("*"))
|
||||
}
|
||||
|
||||
// Issue #1739
|
||||
func TestRouterMatchAnyPrefixIssue(t *testing.T) {
|
||||
e := New()
|
||||
@ -791,6 +823,130 @@ func TestRouterMatchAnyPrefixIssue(t *testing.T) {
|
||||
assert.Equal(t, "users_prefix/", c.Param("*"))
|
||||
}
|
||||
|
||||
func TestRouteMultiLevelBacktracking(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/a/:b/c", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/a/c/d", handlerHelper("case", 2))
|
||||
r.Add(http.MethodGet, "/:e/c/f", handlerHelper("case", 3))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
r.Find(http.MethodGet, "/a/c/f", c)
|
||||
|
||||
c.handler(c)
|
||||
assert.Equal(t, 3, c.Get("case"))
|
||||
assert.Equal(t, "/:e/c/f", c.Get("path"))
|
||||
}
|
||||
|
||||
// Issue #
|
||||
func TestRouterBacktrackingFromParam(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/*", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/users/:name/", handlerHelper("case", 2))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
|
||||
r.Find(http.MethodGet, "/users/firstname/no-match", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/*", c.Get("path"))
|
||||
assert.Equal(t, "users/firstname/no-match", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/users/firstname/", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 2, c.Get("case"))
|
||||
assert.Equal(t, "/users/:name/", c.Get("path"))
|
||||
assert.Equal(t, "firstname", c.Param("name"))
|
||||
}
|
||||
|
||||
func TestRouterBacktrackingFromParamAny(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/*", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/:name/lastname", handlerHelper("case", 2))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
|
||||
r.Find(http.MethodGet, "/firstname/test", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/*", c.Get("path"))
|
||||
assert.Equal(t, "firstname/test", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/firstname", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/*", c.Get("path"))
|
||||
assert.Equal(t, "firstname", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/firstname/lastname", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 2, c.Get("case"))
|
||||
assert.Equal(t, "/:name/lastname", c.Get("path"))
|
||||
assert.Equal(t, "firstname", c.Param("name"))
|
||||
}
|
||||
|
||||
func TestRouterBacktrackingFromParamAny2(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/*", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/:name", handlerHelper("case", 2))
|
||||
r.Add(http.MethodGet, "/:name/lastname", handlerHelper("case", 3))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
|
||||
r.Find(http.MethodGet, "/firstname/test", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/*", c.Get("path"))
|
||||
assert.Equal(t, "firstname/test", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/firstname", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 2, c.Get("case"))
|
||||
assert.Equal(t, "/:name", c.Get("path"))
|
||||
assert.Equal(t, "firstname", c.Param("name"))
|
||||
|
||||
r.Find(http.MethodGet, "/firstname/lastname", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 3, c.Get("case"))
|
||||
assert.Equal(t, "/:name/lastname", c.Get("path"))
|
||||
assert.Equal(t, "firstname", c.Param("name"))
|
||||
}
|
||||
|
||||
func TestRouterAnyCommonPath(t *testing.T) {
|
||||
e := New()
|
||||
r := e.router
|
||||
|
||||
r.Add(http.MethodGet, "/ab*", handlerHelper("case", 1))
|
||||
r.Add(http.MethodGet, "/abcd", handlerHelper("case", 2))
|
||||
r.Add(http.MethodGet, "/abcd*", handlerHelper("case", 3))
|
||||
|
||||
c := e.NewContext(nil, nil).(*context)
|
||||
|
||||
r.Find(http.MethodGet, "/abee", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 1, c.Get("case"))
|
||||
assert.Equal(t, "/ab*", c.Get("path"))
|
||||
assert.Equal(t, "ee", c.Param("*"))
|
||||
|
||||
r.Find(http.MethodGet, "/abcd", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, "/abcd", c.Get("path"))
|
||||
assert.Equal(t, 2, c.Get("case"))
|
||||
|
||||
r.Find(http.MethodGet, "/abcde", c)
|
||||
c.handler(c)
|
||||
assert.Equal(t, 3, c.Get("case"))
|
||||
assert.Equal(t, "/abcd*", c.Get("path"))
|
||||
assert.Equal(t, "e", c.Param("*"))
|
||||
}
|
||||
|
||||
// TestRouterMatchAnySlash shall verify finding the best route
|
||||
// for any routes with trailing slash requests
|
||||
func TestRouterMatchAnySlash(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user