-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
router not found #1754
Comments
So for case Route tree is built as: // Issue #1754 - router needs to backtrack multiple levels upwards in tree to find the matching route
// route evaluation order
// +-0,7--------+
// | "/" (root) |
// +------------+
// | |
// | |
// +-1,6-----------+ | | +-8-----------+
// | "a/" (static) +<--+ +--------->+ ":" (param) |
// +---------------+ +-------------+
// | | |
// +-2--------v-----+ +v-3,5--------+ +-9------v--------+
// | "c/d" (static) | | ":" (param) | | "/c/f" (static) |
// +----------------+ +-------------+ +-----------------+
// |
// +-4--v----------+
// | "/c" (static) |
// +---------------+
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"))
} so when searching match for request uri In theory backtracking should be as (upwards 4->5->6->7 and 7->8->9 downwards) to root level and choose adjacent node |
I guess this needs to be investigated, why the backtracking does not reach the root now. @Duslia997 Which version of echo are you using? |
github.com/labstack/echo/v4 v4.1.17 |
after looking Some remarks:
|
@pafuent if you rename variables then definitely do it in separate commit. It is hard to search in history when many different things (cleaning code and fixing bugs) are in same commit. Looking from history how code evolves is very handy way to understand problems. I created branch just for myself to be able to understand/read After 3 hours of read code/debugging, looking related issues - I would say I still do not feel comfortable explaining how execution flow goes and what should be done to fix backtracking. It does not feel right to hack another condition into that flow without understanding broader problem domain - tree traversal itself not hard problem most of the time. |
For the first fixes it took me a day and a lot of debugging to understand where to start fixing ;-) Now with the child for any/param/static being different entities we might have a chance for further improvement. The test-suite is quite extensive already, so changes in the router logic are quite save to do in term of detecting misbehaviour. |
I think I'll create one complex example routing tree for additional testing - that tree should be multiple levels deep, having different variant of nodes with different mix of children (having only static leafs beneath, having static+param, param+any, static+any, multiple levels of static or param nodes etc). actually thinking about it - I think there could be one problem (not defined requirement) with current implementation searching for matching node does not take HTTP method into consideration. It might match node too early that does not actually have that method registered (ala finds static route but it does not have DELETE, but delete exists in param routes) |
Currently the router is not able to distinguish between HTTP methods, which also may cause a wrong routing decisions. The methods (methodHandler in the the route nodes) are there, but are not considered for routing (probably to avoid performance hit during routing). To ensure correct routing the available methods for a node should also be considered and nodes skipped that do not have such an handler. This will avoid shadowing routes where a GET route might shadow other routes, aklthBut we need to think about how we define how the router should work. But I think the tests already provide some complex router setups, which might be a good starting point. Many frameworks allow an |
Another simpler test case is the following: 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"))
} I think the main problem in the I've tried to hack together a PoC code with a very simple stack based on a slice. Currently all test cases pass, in addition also mine and the main test case from this issue pass. As I've mentioned it's pure PoC code and my intention was not to get a PR which would be ready for merge. But maybe we could use it as a starting point for a discussion. Which is why I will publish it as a Draft PR. Thanks to all of you for your hard work on echo. |
#1770 is interesting. I have been tinkering around with version without stack and simplifying code just because I'm pretty much unable to mentally follow current flow (my personal problem) This is good that we could have multiple possible solutions as every approach has their pros and cons but we at least have choice. Backtracking by multiple levels needs information about previous node where backtracking was made. This is because 1 node can have 3 different types of child nodes (static, param, any) and depending where you backtrack your next sibling changes (or parent). For example when you backtrack from static node your next evaluation should be:
I think good complex multilevel backtracking is this example: r.Add(http.MethodGet, "/a/:b/c", handlerHelper("case", 1))
r.Add(http.MethodGet, "/a/c/d", handlerHelper("case", 2))
r.Add(http.MethodGet, "/a/c/df", handlerHelper("case", 3))
r.Add(http.MethodGet, "/:e/c/f", handlerHelper("case", 5))
r.Add(http.MethodGet, "/*", handlerHelper("case", 6))
// Request for "/a/c/f" should match "/:e/c/f"
//
// +-0,7--------+
// | "/" (root) |----------------------------------+
// +------------+ |
// | | |
// | | |
// +-1,6-----------+ | | +-8-----------+ +------v----+
// | "a/" (static) +<--+ +--------->+ ":" (param) | | "*" (any) |
// +---------------+ +-------------+ +-----------+
// | | |
// +-2--------v-----+ +v-3,5--------+ +-9------v--------+
// | "c/d" (static) | | ":" (param) | | "/c/f" (static) |
// +----------------+ +-------------+ +-----------------+
// |
// +-4--v----------+
// | "/c" (static) |
// +---------------+ |
Code:
`
`
When I send /a/c/f, it will return not found.
The text was updated successfully, but these errors were encountered: