mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 22:32:26 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
6a15de79c5
@ -2096,3 +2096,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor
|
|||||||
* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares.
|
* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares.
|
||||||
* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.
|
* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.
|
||||||
* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes.
|
* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes.
|
||||||
|
* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system.
|
||||||
|
240
tree.go
240
tree.go
@ -107,16 +107,15 @@ type node struct {
|
|||||||
|
|
||||||
// increments priority of the given child and reorders if necessary.
|
// increments priority of the given child and reorders if necessary.
|
||||||
func (n *node) incrementChildPrio(pos int) int {
|
func (n *node) incrementChildPrio(pos int) int {
|
||||||
n.children[pos].priority++
|
cs := n.children
|
||||||
prio := n.children[pos].priority
|
cs[pos].priority++
|
||||||
|
prio := cs[pos].priority
|
||||||
|
|
||||||
// adjust position (move to front)
|
// Adjust position (move to front)
|
||||||
newPos := pos
|
newPos := pos
|
||||||
for newPos > 0 && n.children[newPos-1].priority < prio {
|
for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
|
||||||
// swap node positions
|
// Swap node positions
|
||||||
n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1]
|
cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
|
||||||
|
|
||||||
newPos--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// build new index char string
|
// build new index char string
|
||||||
@ -231,7 +230,7 @@ walk:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if a child with the next path byte exists
|
// Check if a child with the next path byte exists
|
||||||
for i := 0; i < len(n.indices); i++ {
|
for i, max := 0, len(n.indices); i < max; i++ {
|
||||||
if c == n.indices[i] {
|
if c == n.indices[i] {
|
||||||
parentFullPathIndex += len(n.path)
|
parentFullPathIndex += len(n.path)
|
||||||
i = n.incrementChildPrio(i)
|
i = n.incrementChildPrio(i)
|
||||||
@ -254,75 +253,91 @@ walk:
|
|||||||
}
|
}
|
||||||
n.insertChild(numParams, path, fullPath, handlers)
|
n.insertChild(numParams, path, fullPath, handlers)
|
||||||
return
|
return
|
||||||
|
|
||||||
} else if i == len(path) { // Make node a (in-path) leaf
|
|
||||||
if n.handlers != nil {
|
|
||||||
panic("handlers are already registered for path '" + fullPath + "'")
|
|
||||||
}
|
|
||||||
n.handlers = handlers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise and handle to current node
|
||||||
|
if n.handlers != nil {
|
||||||
|
panic("handlers are already registered for path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
n.handlers = handlers
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
|
// Search for a wildcard segment and check the name for invalid characters.
|
||||||
var offset int // already handled bytes of the path
|
// Returns -1 as index, if no wildcard war found.
|
||||||
|
func findWildcard(path string) (wildcard string, i int, valid bool) {
|
||||||
// find prefix until first wildcard (beginning with ':' or '*')
|
// Find start
|
||||||
for i, max := 0, len(path); numParams > 0; i++ {
|
for start, c := range []byte(path) {
|
||||||
c := path[i]
|
// A wildcard starts with ':' (param) or '*' (catch-all)
|
||||||
if c != ':' && c != '*' {
|
if c != ':' && c != '*' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// find wildcard end (either '/' or path end)
|
// Find end and check for invalid characters
|
||||||
end := i + 1
|
valid = true
|
||||||
for end < max && path[end] != '/' {
|
for end, c := range []byte(path[start+1:]) {
|
||||||
switch path[end] {
|
switch c {
|
||||||
// the wildcard name must not contain ':' and '*'
|
case '/':
|
||||||
|
return path[start : start+1+end], start, valid
|
||||||
case ':', '*':
|
case ':', '*':
|
||||||
panic("only one wildcard per path segment is allowed, has: '" +
|
valid = false
|
||||||
path[i:] + "' in path '" + fullPath + "'")
|
|
||||||
default:
|
|
||||||
end++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return path[start:], start, valid
|
||||||
|
}
|
||||||
|
return "", -1, false
|
||||||
|
}
|
||||||
|
|
||||||
// check if this Node existing children which would be
|
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
|
||||||
// unreachable if we insert the wildcard here
|
for numParams > 0 {
|
||||||
if len(n.children) > 0 {
|
// Find prefix until first wildcard
|
||||||
panic("wildcard route '" + path[i:end] +
|
wildcard, i, valid := findWildcard(path)
|
||||||
"' conflicts with existing children in path '" + fullPath + "'")
|
if i < 0 { // No wildcard found
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// The wildcard name must not contain ':' and '*'
|
||||||
|
if !valid {
|
||||||
|
panic("only one wildcard per path segment is allowed, has: '" +
|
||||||
|
wildcard + "' in path '" + fullPath + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the wildcard has a name
|
// check if the wildcard has a name
|
||||||
if end-i < 2 {
|
if len(wildcard) < 2 {
|
||||||
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
|
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == ':' { // param
|
// Check if this node has existing children which would be
|
||||||
// split path at the beginning of the wildcard
|
// unreachable if we insert the wildcard here
|
||||||
|
if len(n.children) > 0 {
|
||||||
|
panic("wildcard segment '" + wildcard +
|
||||||
|
"' conflicts with existing children in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if wildcard[0] == ':' { // param
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
n.path = path[offset:i]
|
// Insert prefix before the current wildcard
|
||||||
offset = i
|
n.path = path[:i]
|
||||||
|
path = path[i:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.wildChild = true
|
||||||
child := &node{
|
child := &node{
|
||||||
nType: param,
|
nType: param,
|
||||||
|
path: wildcard,
|
||||||
maxParams: numParams,
|
maxParams: numParams,
|
||||||
fullPath: fullPath,
|
fullPath: fullPath,
|
||||||
}
|
}
|
||||||
n.children = []*node{child}
|
n.children = []*node{child}
|
||||||
n.wildChild = true
|
|
||||||
n = child
|
n = child
|
||||||
n.priority++
|
n.priority++
|
||||||
numParams--
|
numParams--
|
||||||
|
|
||||||
// if the path doesn't end with the wildcard, then there
|
// if the path doesn't end with the wildcard, then there
|
||||||
// will be another non-wildcard subpath starting with '/'
|
// will be another non-wildcard subpath starting with '/'
|
||||||
if end < max {
|
if len(wildcard) < len(path) {
|
||||||
n.path = path[offset:end]
|
path = path[len(wildcard):]
|
||||||
offset = end
|
|
||||||
|
|
||||||
child := &node{
|
child := &node{
|
||||||
maxParams: numParams,
|
maxParams: numParams,
|
||||||
@ -331,54 +346,63 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
|
|||||||
}
|
}
|
||||||
n.children = []*node{child}
|
n.children = []*node{child}
|
||||||
n = child
|
n = child
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // catchAll
|
// Otherwise we're done. Insert the handle in the new leaf
|
||||||
if end != max || numParams > 1 {
|
n.handlers = handlers
|
||||||
panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
|
||||||
panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently fixed width 1 for '/'
|
|
||||||
i--
|
|
||||||
if path[i] != '/' {
|
|
||||||
panic("no / before catch-all in path '" + fullPath + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
n.path = path[offset:i]
|
|
||||||
|
|
||||||
// first node: catchAll node with empty path
|
|
||||||
child := &node{
|
|
||||||
wildChild: true,
|
|
||||||
nType: catchAll,
|
|
||||||
maxParams: 1,
|
|
||||||
fullPath: fullPath,
|
|
||||||
}
|
|
||||||
n.children = []*node{child}
|
|
||||||
n.indices = string(path[i])
|
|
||||||
n = child
|
|
||||||
n.priority++
|
|
||||||
|
|
||||||
// second node: node holding the variable
|
|
||||||
child = &node{
|
|
||||||
path: path[i:],
|
|
||||||
nType: catchAll,
|
|
||||||
maxParams: 1,
|
|
||||||
handlers: handlers,
|
|
||||||
priority: 1,
|
|
||||||
fullPath: fullPath,
|
|
||||||
}
|
|
||||||
n.children = []*node{child}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// catchAll
|
||||||
|
if i+len(wildcard) != len(path) || numParams > 1 {
|
||||||
|
panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
||||||
|
panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// currently fixed width 1 for '/'
|
||||||
|
i--
|
||||||
|
if path[i] != '/' {
|
||||||
|
panic("no / before catch-all in path '" + fullPath + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
n.path = path[:i]
|
||||||
|
|
||||||
|
// First node: catchAll node with empty path
|
||||||
|
child := &node{
|
||||||
|
wildChild: true,
|
||||||
|
nType: catchAll,
|
||||||
|
maxParams: 1,
|
||||||
|
fullPath: fullPath,
|
||||||
|
}
|
||||||
|
// update maxParams of the parent node
|
||||||
|
if n.maxParams < 1 {
|
||||||
|
n.maxParams = 1
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
n.indices = string('/')
|
||||||
|
n = child
|
||||||
|
n.priority++
|
||||||
|
|
||||||
|
// second node: node holding the variable
|
||||||
|
child = &node{
|
||||||
|
path: path[i:],
|
||||||
|
nType: catchAll,
|
||||||
|
maxParams: 1,
|
||||||
|
handlers: handlers,
|
||||||
|
priority: 1,
|
||||||
|
fullPath: fullPath,
|
||||||
|
}
|
||||||
|
n.children = []*node{child}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert remaining path part and handle to the leaf
|
// If no wildcard was found, simple insert the path and handle
|
||||||
n.path = path[offset:]
|
n.path = path
|
||||||
n.handlers = handlers
|
n.handlers = handlers
|
||||||
n.fullPath = fullPath
|
n.fullPath = fullPath
|
||||||
}
|
}
|
||||||
@ -400,17 +424,20 @@ func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue)
|
|||||||
value.params = po
|
value.params = po
|
||||||
walk: // Outer loop for walking the tree
|
walk: // Outer loop for walking the tree
|
||||||
for {
|
for {
|
||||||
if len(path) > len(n.path) {
|
prefix := n.path
|
||||||
if path[:len(n.path)] == n.path {
|
if len(path) > len(prefix) {
|
||||||
path = path[len(n.path):]
|
if path[:len(prefix)] == prefix {
|
||||||
|
path = path[len(prefix):]
|
||||||
// If this node does not have a wildcard (param or catchAll)
|
// If this node does not have a wildcard (param or catchAll)
|
||||||
// child, we can just look up the next child node and continue
|
// child, we can just look up the next child node and continue
|
||||||
// to walk down the tree
|
// to walk down the tree
|
||||||
if !n.wildChild {
|
if !n.wildChild {
|
||||||
c := path[0]
|
c := path[0]
|
||||||
for i := 0; i < len(n.indices); i++ {
|
indices := n.indices
|
||||||
if c == n.indices[i] {
|
for i, max := 0, len(indices); i < max; i++ {
|
||||||
|
if c == indices[i] {
|
||||||
n = n.children[i]
|
n = n.children[i]
|
||||||
|
prefix = n.path
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,6 +481,7 @@ walk: // Outer loop for walking the tree
|
|||||||
if len(n.children) > 0 {
|
if len(n.children) > 0 {
|
||||||
path = path[end:]
|
path = path[end:]
|
||||||
n = n.children[0]
|
n = n.children[0]
|
||||||
|
prefix = n.path
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +528,7 @@ walk: // Outer loop for walking the tree
|
|||||||
panic("invalid node type")
|
panic("invalid node type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if path == n.path {
|
} else if path == prefix {
|
||||||
// We should have reached the node containing the handle.
|
// We should have reached the node containing the handle.
|
||||||
// Check if this node has a handle registered.
|
// Check if this node has a handle registered.
|
||||||
if value.handlers = n.handlers; value.handlers != nil {
|
if value.handlers = n.handlers; value.handlers != nil {
|
||||||
@ -515,8 +543,9 @@ walk: // Outer loop for walking the tree
|
|||||||
|
|
||||||
// No handle found. Check if a handle for this path + a
|
// No handle found. Check if a handle for this path + a
|
||||||
// trailing slash exists for trailing slash recommendation
|
// trailing slash exists for trailing slash recommendation
|
||||||
for i := 0; i < len(n.indices); i++ {
|
indices := n.indices
|
||||||
if n.indices[i] == '/' {
|
for i, max := 0, len(indices); i < max; i++ {
|
||||||
|
if indices[i] == '/' {
|
||||||
n = n.children[i]
|
n = n.children[i]
|
||||||
value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
|
value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
|
||||||
(n.nType == catchAll && n.children[0].handlers != nil)
|
(n.nType == catchAll && n.children[0].handlers != nil)
|
||||||
@ -530,8 +559,8 @@ walk: // Outer loop for walking the tree
|
|||||||
// Nothing found. We can recommend to redirect to the same URL with an
|
// Nothing found. We can recommend to redirect to the same URL with an
|
||||||
// extra trailing slash if a leaf exists for that path
|
// extra trailing slash if a leaf exists for that path
|
||||||
value.tsr = (path == "/") ||
|
value.tsr = (path == "/") ||
|
||||||
(len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
|
(len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
|
||||||
path == n.path[:len(n.path)-1] && n.handlers != nil)
|
path == prefix[:len(prefix)-1] && n.handlers != nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -597,25 +626,25 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa
|
|||||||
n = n.children[0]
|
n = n.children[0]
|
||||||
switch n.nType {
|
switch n.nType {
|
||||||
case param:
|
case param:
|
||||||
// find param end (either '/' or path end)
|
// Find param end (either '/' or path end)
|
||||||
k := 0
|
end := 0
|
||||||
for k < len(path) && path[k] != '/' {
|
for end < len(path) && path[end] != '/' {
|
||||||
k++
|
end++
|
||||||
}
|
}
|
||||||
|
|
||||||
// add param value to case insensitive path
|
// add param value to case insensitive path
|
||||||
ciPath = append(ciPath, path[:k]...)
|
ciPath = append(ciPath, path[:end]...)
|
||||||
|
|
||||||
// we need to go deeper!
|
// we need to go deeper!
|
||||||
if k < len(path) {
|
if end < len(path) {
|
||||||
if len(n.children) > 0 {
|
if len(n.children) > 0 {
|
||||||
path = path[k:]
|
path = path[end:]
|
||||||
n = n.children[0]
|
n = n.children[0]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... but we can't
|
// ... but we can't
|
||||||
if fixTrailingSlash && len(path) == k+1 {
|
if fixTrailingSlash && len(path) == end+1 {
|
||||||
return ciPath, true
|
return ciPath, true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -623,7 +652,8 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa
|
|||||||
|
|
||||||
if n.handlers != nil {
|
if n.handlers != nil {
|
||||||
return ciPath, true
|
return ciPath, true
|
||||||
} else if fixTrailingSlash && len(n.children) == 1 {
|
}
|
||||||
|
if fixTrailingSlash && len(n.children) == 1 {
|
||||||
// No handle found. Check if a handle for this path + a
|
// No handle found. Check if a handle for this path + a
|
||||||
// trailing slash exists
|
// trailing slash exists
|
||||||
n = n.children[0]
|
n = n.children[0]
|
||||||
|
@ -368,6 +368,13 @@ func TestTreeCatchAllConflictRoot(t *testing.T) {
|
|||||||
testRoutes(t, routes)
|
testRoutes(t, routes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeCatchMaxParams(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
var route = "/cmd/*filepath"
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
checkMaxParams(t, tree)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTreeDoubleWildcard(t *testing.T) {
|
func TestTreeDoubleWildcard(t *testing.T) {
|
||||||
const panicMsg = "only one wildcard per path segment is allowed"
|
const panicMsg = "only one wildcard per path segment is allowed"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user