-
Notifications
You must be signed in to change notification settings - Fork 47
/
tree.go
116 lines (94 loc) · 2.48 KB
/
tree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package radix
import (
"errors"
"strings"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
// New returns an empty routes storage
func New() *Tree {
return &Tree{
root: &node{
nType: root,
},
}
}
// Add adds a node with the given handle to the path.
//
// WARNING: Not concurrency-safe!
func (t *Tree) Add(path string, handler fasthttp.RequestHandler) {
if !strings.HasPrefix(path, "/") {
panicf("path must begin with '/' in path '%s'", path)
} else if handler == nil {
panic("nil handler")
}
fullPath := path
i := longestCommonPrefix(path, t.root.path)
if i > 0 {
if len(t.root.path) > i {
t.root.split(i)
}
path = path[i:]
}
n, err := t.root.add(path, fullPath, handler)
if err != nil {
var radixErr radixError
if errors.As(err, &radixErr) && t.Mutable && !n.tsr {
switch radixErr.msg {
case errSetHandler:
n.handler = handler
return
case errSetWildcardHandler:
n.wildcard.handler = handler
return
}
}
panic(err)
}
if len(t.root.path) == 0 {
t.root = t.root.children[0]
t.root.nType = root
}
// Reorder the nodes
t.root.sort()
}
// Get returns the handle registered with the given path (key). The values of
// param/wildcard are saved as ctx.UserValue.
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
// made if a handle exists with an extra (without the) trailing slash for the
// given path.
func (t *Tree) Get(path string, ctx *fasthttp.RequestCtx) (fasthttp.RequestHandler, bool) {
if len(path) > len(t.root.path) {
if path[:len(t.root.path)] != t.root.path {
return nil, false
}
path = path[len(t.root.path):]
return t.root.getFromChild(path, ctx)
} else if path == t.root.path {
switch {
case t.root.tsr:
return nil, true
case t.root.handler != nil:
return t.root.handler, false
case t.root.wildcard != nil:
if ctx != nil {
ctx.SetUserValue(t.root.wildcard.paramKey, "")
}
return t.root.wildcard.handler, false
}
}
return nil, false
}
// FindCaseInsensitivePath makes a case-insensitive lookup of the given path
// and tries to find a handler.
// It can optionally also fix trailing slashes.
// It returns the case-corrected path and a bool indicating whether the lookup
// was successful.
func (t *Tree) FindCaseInsensitivePath(path string, fixTrailingSlash bool, buf *bytebufferpool.ByteBuffer) bool {
found, tsr := t.root.find(path, buf)
if !found || (tsr && !fixTrailingSlash) {
buf.Reset()
return false
}
return true
}