Skip to content

Commit 4d27bbc

Browse files
authored
Merge pull request #177 from xushiwei/q
radix.Node: Route
2 parents b5cfc93 + 8ebf3a4 commit 4d27bbc

File tree

3 files changed

+36
-24
lines changed

3 files changed

+36
-24
lines changed

radix/tree.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ type context interface {
8383
UnderlyingSetPathParam(name, val string)
8484
}
8585

86+
type nilContext struct{}
87+
88+
func (nilContext) UnderlyingSetPathParam(name, val string) {}
89+
8690
// Node is a node in the radix tree.
8791
type Node[H any] struct {
8892
path string
@@ -334,13 +338,22 @@ func (n *Node[H]) insertChild(path, fullPath string, handle H) {
334338
n.h, n.ok = handle, true
335339
}
336340

341+
// Route returns the handle registered with the given path (key).
342+
//
343+
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
344+
// made if a handle exists with an extra (without the) trailing slash for the
345+
// given path.
346+
func (n *Node[H]) Route(path string) (handle H, ok, tsr bool) {
347+
return Route(n, path, nilContext{})
348+
}
349+
337350
// Route returns the handle registered with the given path (key). The values of
338351
// wildcard values are set on the context via UnderlyingSetPathParam.
339352
//
340353
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
341354
// made if a handle exists with an extra (without the) trailing slash for the
342355
// given path.
343-
func Route[T context, H any](n *Node[H], path string, ctx T) (handle H, tsr bool) {
356+
func Route[T context, H any](n *Node[H], path string, ctx T) (handle H, ok, tsr bool) {
344357
walk: // Outer loop for walking the tree
345358
for {
346359
prefix := n.path
@@ -395,7 +408,7 @@ walk: // Outer loop for walking the tree
395408
return
396409
}
397410

398-
if handle = n.h; n.ok {
411+
if handle, ok = n.h, n.ok; ok {
399412
return
400413
} else if len(n.children) == 1 {
401414
// No handle found. Check if a handle for this path + a
@@ -411,7 +424,7 @@ walk: // Outer loop for walking the tree
411424
ctx.UnderlyingSetPathParam(n.path[2:], path)
412425
}
413426

414-
handle = n.h
427+
handle, ok = n.h, n.ok
415428
return
416429

417430
default:
@@ -421,7 +434,7 @@ walk: // Outer loop for walking the tree
421434
} else if path == prefix {
422435
// We should have reached the node containing the handle.
423436
// Check if this node has a handle registered.
424-
if handle = n.h; n.ok {
437+
if handle, ok = n.h, n.ok; ok {
425438
return
426439
}
427440

radix/tree_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestAddRouteGetValueSingleStatic(t *testing.T) {
4545
root.AddRoute("/users", func(ctx *testContext) { called = true })
4646

4747
ctx := newTestCtx()
48-
h, tsr := Route(&root, "/users", ctx)
48+
h, _, tsr := Route(&root, "/users", ctx)
4949
if h == nil {
5050
t.Fatal("expected handle for /users")
5151
}
@@ -71,7 +71,7 @@ func TestAddRouteGetValueMultipleStatic(t *testing.T) {
7171
for _, r := range routes {
7272
results[r] = false
7373
ctx := newTestCtx()
74-
h, tsr := Route(&root, r, ctx)
74+
h, _, tsr := Route(&root, r, ctx)
7575
if h == nil {
7676
t.Fatalf("expected handle for %q", r)
7777
}
@@ -89,7 +89,7 @@ func TestGetValueNotFound(t *testing.T) {
8989
var root Node[func(*testContext)]
9090
root.AddRoute("/users", func(ctx *testContext) {})
9191

92-
h, _ := Route(&root, "/nonexistent", newTestCtx())
92+
h, _, _ := Route(&root, "/nonexistent", newTestCtx())
9393
if h != nil {
9494
t.Fatal("expected no handle for /nonexistent")
9595
}
@@ -99,12 +99,12 @@ func TestGetValueRootOnly(t *testing.T) {
9999
var root Node[func(*testContext)]
100100
root.AddRoute("/", func(ctx *testContext) {})
101101

102-
h, _ := Route(&root, "/", newTestCtx())
102+
h, _, _ := Route(&root, "/", newTestCtx())
103103
if h == nil {
104104
t.Fatal("expected handle for /")
105105
}
106106

107-
h2, _ := Route(&root, "/other", newTestCtx())
107+
h2, _, _ := Route(&root, "/other", newTestCtx())
108108
if h2 != nil {
109109
t.Fatal("expected no handle for /other")
110110
}
@@ -118,7 +118,7 @@ func TestGetValueSingleParam(t *testing.T) {
118118
root.AddRoute("/users/:id", func(ctx *testContext) {})
119119

120120
ctx := newTestCtx()
121-
h, tsr := Route(&root, "/users/42", ctx)
121+
h, _, tsr := Route(&root, "/users/42", ctx)
122122
if h == nil {
123123
t.Fatal("expected handle for /users/42")
124124
}
@@ -135,7 +135,7 @@ func TestGetValueMultipleParams(t *testing.T) {
135135
root.AddRoute("/users/:id/posts/:postId", func(ctx *testContext) {})
136136

137137
ctx := newTestCtx()
138-
h, _ := Route(&root, "/users/123/posts/456", ctx)
138+
h, _, _ := Route(&root, "/users/123/posts/456", ctx)
139139
if h == nil {
140140
t.Fatal("expected handle")
141141
}
@@ -152,7 +152,7 @@ func TestGetValueParamWithTrailingSlash(t *testing.T) {
152152
root.AddRoute("/users/:id/", func(ctx *testContext) {})
153153

154154
ctx := newTestCtx()
155-
h, _ := Route(&root, "/users/42/", ctx)
155+
h, _, _ := Route(&root, "/users/42/", ctx)
156156
if h == nil {
157157
t.Fatal("expected handle")
158158
}
@@ -166,7 +166,7 @@ func TestGetValueParamNilCtx(t *testing.T) {
166166
root.AddRoute("/users/:id", func(ctx *testContext) {})
167167

168168
// Pass nil context (zero value for *testContext) - should not panic
169-
h, _ := Route[*testContext](&root, "/users/42", nil)
169+
h, _, _ := root.Route("/users/42")
170170
if h == nil {
171171
t.Fatal("expected handle even with nil ctx")
172172
}
@@ -177,7 +177,7 @@ func TestGetValueParamNotFound(t *testing.T) {
177177
root.AddRoute("/users/:id", func(ctx *testContext) {})
178178

179179
// /users alone has no handler
180-
h, _ := Route(&root, "/users", newTestCtx())
180+
h, _, _ := Route(&root, "/users", newTestCtx())
181181
if h != nil {
182182
t.Fatal("expected no handle for /users without param")
183183
}
@@ -191,7 +191,7 @@ func TestGetValueCatchAll(t *testing.T) {
191191
root.AddRoute("/files/*filepath", func(ctx *testContext) {})
192192

193193
ctx := newTestCtx()
194-
h, _ := Route(&root, "/files/path/to/file.txt", ctx)
194+
h, _, _ := Route(&root, "/files/path/to/file.txt", ctx)
195195
if h == nil {
196196
t.Fatal("expected handle")
197197
}
@@ -204,7 +204,7 @@ func TestGetValueCatchAllNilCtx(t *testing.T) {
204204
var root Node[func(*testContext)]
205205
root.AddRoute("/files/*filepath", func(ctx *testContext) {})
206206

207-
h, _ := Route[*testContext](&root, "/files/a/b/c", nil)
207+
h, _, _ := root.Route("/files/a/b/c")
208208
if h == nil {
209209
t.Fatal("expected handle with nil ctx")
210210
}
@@ -217,7 +217,7 @@ func TestGetValueTSRMissingTrailingSlash(t *testing.T) {
217217
var root Node[func(*testContext)]
218218
root.AddRoute("/users/", func(ctx *testContext) {})
219219

220-
h, tsr := Route(&root, "/users", newTestCtx())
220+
h, _, tsr := Route(&root, "/users", newTestCtx())
221221
if h != nil {
222222
t.Fatal("expected no handle for /users (without trailing slash)")
223223
}
@@ -230,7 +230,7 @@ func TestGetValueTSRExtraTrailingSlash(t *testing.T) {
230230
var root Node[func(*testContext)]
231231
root.AddRoute("/users", func(ctx *testContext) {})
232232

233-
h, tsr := Route(&root, "/users/", newTestCtx())
233+
h, _, tsr := Route(&root, "/users/", newTestCtx())
234234
if h != nil {
235235
t.Fatal("expected no handle for /users/ (with extra trailing slash)")
236236
}
@@ -241,7 +241,7 @@ func TestGetValueTSRWithParam(t *testing.T) {
241241
var root Node[func(*testContext)]
242242
root.AddRoute("/users/:id/", func(ctx *testContext) {})
243243

244-
h, tsr := Route(&root, "/users/42", newTestCtx())
244+
h, _, tsr := Route(&root, "/users/42", newTestCtx())
245245
if h != nil {
246246
t.Fatal("expected no handle")
247247
}
@@ -276,7 +276,7 @@ func TestMixedRoutes(t *testing.T) {
276276

277277
for _, tt := range tests {
278278
ctx := newTestCtx()
279-
h, _ := Route(&root, tt.path, ctx)
279+
h, _, _ := Route(&root, tt.path, ctx)
280280
if (h != nil) != tt.wantMatch {
281281
t.Fatalf("Route(%q): got match=%v, want match=%v", tt.path, h != nil, tt.wantMatch)
282282
}
@@ -300,7 +300,7 @@ func TestRoutePriorityOrdering(t *testing.T) {
300300
root.AddRoute("/b/c", func(ctx *testContext) {})
301301

302302
for _, path := range []string{"/a", "/b", "/a/b", "/b/c"} {
303-
h, _ := Route(&root, path, newTestCtx())
303+
h, _, _ := Route(&root, path, newTestCtx())
304304
if h == nil {
305305
t.Fatalf("expected handle for %q after priority reordering", path)
306306
}

router.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,7 @@ func (p *router) allowed(path, reqMethod string) (allow string) {
190190
continue
191191
}
192192

193-
handle, _ := radix.Route[*Context](p.trees[method], path, nil)
194-
if handle != nil {
193+
if _, ok, _ := radix.Route[*Context](p.trees[method], path, nil); ok {
195194
// Route request method to list of allowed methods
196195
allowed = append(allowed, method)
197196
}
@@ -227,7 +226,7 @@ func (p *router) serveHTTP(w http.ResponseWriter, req *http.Request, e *Engine)
227226
root := p.trees[req.Method]
228227
if root != nil {
229228
ctx := e.NewContext(w, req)
230-
if handle, tsr := radix.Route(root, path, ctx); handle != nil {
229+
if handle, ok, tsr := radix.Route(root, path, ctx); ok {
231230
handle(ctx)
232231
return
233232
} else if req.Method != http.MethodConnect && path != "/" {

0 commit comments

Comments
 (0)