From 77b83441698ffcc400d02b9849f15d7a47e0b8bc Mon Sep 17 00:00:00 2001 From: Victor Castell Date: Mon, 2 Dec 2019 13:59:56 +0100 Subject: [PATCH 1/5] Add project to README (#2165) Add Dkron as user of Gin in the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 012152a0..f8bc4239 100644 --- a/README.md +++ b/README.md @@ -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. * [picfit](https://github.com/thoas/picfit): An image resizing server written in Go. * [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes. +* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system. From 7c21e04f628f1cabdf4029fd5c78a8f2b152faae Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 4 Dec 2019 07:56:01 +0800 Subject: [PATCH 2/5] fix maxParams bug (#2166) --- tree.go | 4 ++++ tree_test.go | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/tree.go b/tree.go index 1187f3f6..b46ec828 100644 --- a/tree.go +++ b/tree.go @@ -357,6 +357,10 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle maxParams: 1, fullPath: fullPath, } + // update maxParams of the parent node + if n.maxParams < 1 { + n.maxParams = 1 + } n.children = []*node{child} n.indices = string(path[i]) n = child diff --git a/tree_test.go b/tree_test.go index e6e28865..0fe2fe00 100644 --- a/tree_test.go +++ b/tree_test.go @@ -368,6 +368,13 @@ func TestTreeCatchAllConflictRoot(t *testing.T) { 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) { const panicMsg = "only one wildcard per path segment is allowed" From c6544855d7244db15858cbc0bb200da5efa45b83 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 8 Dec 2019 18:35:08 +0800 Subject: [PATCH 3/5] tree: sync httprouter update (#2171) --- tree.go | 61 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tree.go b/tree.go index b46ec828..ffd99896 100644 --- a/tree.go +++ b/tree.go @@ -107,16 +107,15 @@ type node struct { // increments priority of the given child and reorders if necessary. func (n *node) incrementChildPrio(pos int) int { - n.children[pos].priority++ - prio := n.children[pos].priority + cs := n.children + cs[pos].priority++ + prio := cs[pos].priority - // adjust position (move to front) + // Adjust position (move to front) newPos := pos - for newPos > 0 && n.children[newPos-1].priority < prio { - // swap node positions - n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1] - - newPos-- + for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { + // Swap node positions + cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] } // build new index char string @@ -231,7 +230,7 @@ walk: } // 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] { parentFullPathIndex += len(n.path) i = n.incrementChildPrio(i) @@ -404,17 +403,20 @@ func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) value.params = po walk: // Outer loop for walking the tree for { - if len(path) > len(n.path) { - if path[:len(n.path)] == n.path { - path = path[len(n.path):] + prefix := n.path + if len(path) > len(prefix) { + if path[:len(prefix)] == prefix { + path = path[len(prefix):] // If this node does not have a wildcard (param or catchAll) // child, we can just look up the next child node and continue // to walk down the tree if !n.wildChild { c := path[0] - for i := 0; i < len(n.indices); i++ { - if c == n.indices[i] { + indices := n.indices + for i, max := 0, len(indices); i < max; i++ { + if c == indices[i] { n = n.children[i] + prefix = n.path continue walk } } @@ -458,6 +460,7 @@ walk: // Outer loop for walking the tree if len(n.children) > 0 { path = path[end:] n = n.children[0] + prefix = n.path continue walk } @@ -504,7 +507,7 @@ walk: // Outer loop for walking the tree panic("invalid node type") } } - } else if path == n.path { + } else if path == prefix { // We should have reached the node containing the handle. // Check if this node has a handle registered. if value.handlers = n.handlers; value.handlers != nil { @@ -519,8 +522,9 @@ walk: // Outer loop for walking the tree // No handle found. Check if a handle for this path + a // trailing slash exists for trailing slash recommendation - for i := 0; i < len(n.indices); i++ { - if n.indices[i] == '/' { + indices := n.indices + for i, max := 0, len(indices); i < max; i++ { + if indices[i] == '/' { n = n.children[i] value.tsr = (len(n.path) == 1 && n.handlers != nil) || (n.nType == catchAll && n.children[0].handlers != nil) @@ -534,8 +538,8 @@ walk: // Outer loop for walking the tree // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = (path == "/") || - (len(n.path) == len(path)+1 && n.path[len(path)] == '/' && - path == n.path[:len(n.path)-1] && n.handlers != nil) + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) return } } @@ -601,25 +605,25 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa n = n.children[0] switch n.nType { case param: - // find param end (either '/' or path end) - k := 0 - for k < len(path) && path[k] != '/' { - k++ + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ } // add param value to case insensitive path - ciPath = append(ciPath, path[:k]...) + ciPath = append(ciPath, path[:end]...) // we need to go deeper! - if k < len(path) { + if end < len(path) { if len(n.children) > 0 { - path = path[k:] + path = path[end:] n = n.children[0] continue } // ... but we can't - if fixTrailingSlash && len(path) == k+1 { + if fixTrailingSlash && len(path) == end+1 { return ciPath, true } return @@ -627,7 +631,8 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa if n.handlers != nil { 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 // trailing slash exists n = n.children[0] From 6e16da8683136c68164b9011fc5678f46ad78d27 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sun, 8 Dec 2019 19:34:05 +0800 Subject: [PATCH 4/5] tree: sync httprouter update (#2172) --- tree.go | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/tree.go b/tree.go index ffd99896..3a8a4355 100644 --- a/tree.go +++ b/tree.go @@ -253,13 +253,13 @@ walk: } n.insertChild(numParams, path, fullPath, handlers) 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 } } @@ -267,31 +267,31 @@ walk: func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { var offset int // already handled bytes of the path - // find prefix until first wildcard (beginning with ':' or '*') + // Find prefix until first wildcard (beginning with ':' or '*') for i, max := 0, len(path); numParams > 0; i++ { c := path[i] if c != ':' && c != '*' { continue } - // find wildcard end (either '/' or path end) + // Find wildcard end (either '/' or path end) and check the name for invalid characters end := i + 1 - for end < max && path[end] != '/' { - switch path[end] { - // the wildcard name must not contain ':' and '*' - case ':', '*': - panic("only one wildcard per path segment is allowed, has: '" + - path[i:] + "' in path '" + fullPath + "'") - default: - end++ + invalid := false + for end < max { + c := path[end] + if c == '/' { + break } + if c == ':' || c == '*' { + invalid = true + } + end++ } - // check if this Node existing children which would be - // unreachable if we insert the wildcard here - if len(n.children) > 0 { - panic("wildcard route '" + path[i:end] + - "' conflicts with existing children in path '" + fullPath + "'") + // The wildcard name must not contain ':' and '*' + if invalid { + panic("only one wildcard per path segment is allowed, has: '" + + path[i:end] + "' in path '" + fullPath + "'") } // check if the wildcard has a name @@ -299,6 +299,13 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") } + // Check if this node has existing children which would be + // unreachable if we insert the wildcard here + if len(n.children) > 0 { + panic("wildcard route '" + path[i:end] + + "' conflicts with existing children in path '" + fullPath + "'") + } + if c == ':' { // param // split path at the beginning of the wildcard if i > 0 { From 168fa945168119b7f72d5359f4abaf311e52f1b8 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Mon, 9 Dec 2019 15:04:35 +0800 Subject: [PATCH 5/5] tree: sync httprouter update (#2173) --- tree.go | 164 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 89 insertions(+), 75 deletions(-) diff --git a/tree.go b/tree.go index 3a8a4355..b09d3f67 100644 --- a/tree.go +++ b/tree.go @@ -264,71 +264,80 @@ walk: } } -func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { - var offset int // already handled bytes of the path - - // Find prefix until first wildcard (beginning with ':' or '*') - for i, max := 0, len(path); numParams > 0; i++ { - c := path[i] +// Search for a wildcard segment and check the name for invalid characters. +// Returns -1 as index, if no wildcard war found. +func findWildcard(path string) (wildcard string, i int, valid bool) { + // Find start + for start, c := range []byte(path) { + // A wildcard starts with ':' (param) or '*' (catch-all) if c != ':' && c != '*' { continue } - // Find wildcard end (either '/' or path end) and check the name for invalid characters - end := i + 1 - invalid := false - for end < max { - c := path[end] - if c == '/' { - break + // Find end and check for invalid characters + valid = true + for end, c := range []byte(path[start+1:]) { + switch c { + case '/': + return path[start : start+1+end], start, valid + case ':', '*': + valid = false } - if c == ':' || c == '*' { - invalid = true - } - end++ + } + return path[start:], start, valid + } + return "", -1, false +} + +func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { + for numParams > 0 { + // Find prefix until first wildcard + wildcard, i, valid := findWildcard(path) + if i < 0 { // No wildcard found + break } // The wildcard name must not contain ':' and '*' - if invalid { + if !valid { panic("only one wildcard per path segment is allowed, has: '" + - path[i:end] + "' in path '" + fullPath + "'") + wildcard + "' in path '" + fullPath + "'") } // 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 + "'") } // Check if this node has existing children which would be // unreachable if we insert the wildcard here if len(n.children) > 0 { - panic("wildcard route '" + path[i:end] + + panic("wildcard segment '" + wildcard + "' conflicts with existing children in path '" + fullPath + "'") } - if c == ':' { // param - // split path at the beginning of the wildcard + if wildcard[0] == ':' { // param if i > 0 { - n.path = path[offset:i] - offset = i + // Insert prefix before the current wildcard + n.path = path[:i] + path = path[i:] } + n.wildChild = true child := &node{ nType: param, + path: wildcard, maxParams: numParams, fullPath: fullPath, } n.children = []*node{child} - n.wildChild = true n = child n.priority++ numParams-- // if the path doesn't end with the wildcard, then there // will be another non-wildcard subpath starting with '/' - if end < max { - n.path = path[offset:end] - offset = end + if len(wildcard) < len(path) { + path = path[len(wildcard):] child := &node{ maxParams: numParams, @@ -337,58 +346,63 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle } n.children = []*node{child} n = child + continue } - } else { // catchAll - if end != max || 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[offset: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(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} - + // Otherwise we're done. Insert the handle in the new leaf + n.handlers = handlers 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 - n.path = path[offset:] + // If no wildcard was found, simple insert the path and handle + n.path = path n.handlers = handlers n.fullPath = fullPath }