mirror of
https://github.com/labstack/echo.git
synced 2024-11-24 08:22:21 +02:00
Improving routing performance and benchmark suite
Before this commit, all the node types were added to the same list of children nodes. Taking in consideration that only one Param and Any type of node could exist per node, two new node struct field were added to hold the references to those kind of nodes. This avoid the need to iterate through all the Static type nodes just to find one Param or Any type node. Those iterations could be performed multiple times in the same iteration of Router#Find. Removing the route comments of the Router benchmark tests. Updating the Router benchmarks tests to find the routes defined to each particular benchmark. Before, all the benchmarks tried to find only the GitHub API. Adding new router benchmarks to measure when the Router try to find routes that are not registered.
This commit is contained in:
parent
ad3be08de1
commit
3a6100bebe
117
router.go
117
router.go
@ -14,14 +14,16 @@ type (
|
|||||||
echo *Echo
|
echo *Echo
|
||||||
}
|
}
|
||||||
node struct {
|
node struct {
|
||||||
kind kind
|
kind kind
|
||||||
label byte
|
label byte
|
||||||
prefix string
|
prefix string
|
||||||
parent *node
|
parent *node
|
||||||
children children
|
staticChildrens children
|
||||||
ppath string
|
ppath string
|
||||||
pnames []string
|
pnames []string
|
||||||
methodHandler *methodHandler
|
methodHandler *methodHandler
|
||||||
|
paramChildren *node
|
||||||
|
anyChildren *node
|
||||||
}
|
}
|
||||||
kind uint8
|
kind uint8
|
||||||
children []*node
|
children []*node
|
||||||
@ -44,6 +46,9 @@ const (
|
|||||||
skind kind = iota
|
skind kind = iota
|
||||||
pkind
|
pkind
|
||||||
akind
|
akind
|
||||||
|
|
||||||
|
paramLabel = byte(':')
|
||||||
|
anyLabel = byte('*')
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRouter returns a new Router instance.
|
// NewRouter returns a new Router instance.
|
||||||
@ -134,23 +139,32 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
}
|
}
|
||||||
} else if l < pl {
|
} else if l < pl {
|
||||||
// Split node
|
// Split node
|
||||||
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
|
n := newNode(cn.kind, cn.prefix[l:], cn, cn.staticChildrens, cn.methodHandler, cn.ppath, cn.pnames, cn.paramChildren, cn.anyChildren)
|
||||||
|
|
||||||
// Update parent path for all children to new node
|
// Update parent path for all children to new node
|
||||||
for _, child := range cn.children {
|
for _, child := range cn.staticChildrens {
|
||||||
child.parent = n
|
child.parent = n
|
||||||
}
|
}
|
||||||
|
if cn.paramChildren != nil {
|
||||||
|
cn.paramChildren.parent = n
|
||||||
|
}
|
||||||
|
if cn.anyChildren != nil {
|
||||||
|
cn.anyChildren.parent = n
|
||||||
|
}
|
||||||
|
|
||||||
// Reset parent node
|
// Reset parent node
|
||||||
cn.kind = skind
|
cn.kind = skind
|
||||||
cn.label = cn.prefix[0]
|
cn.label = cn.prefix[0]
|
||||||
cn.prefix = cn.prefix[:l]
|
cn.prefix = cn.prefix[:l]
|
||||||
cn.children = nil
|
cn.staticChildrens = nil
|
||||||
cn.methodHandler = new(methodHandler)
|
cn.methodHandler = new(methodHandler)
|
||||||
cn.ppath = ""
|
cn.ppath = ""
|
||||||
cn.pnames = nil
|
cn.pnames = nil
|
||||||
|
cn.paramChildren = nil
|
||||||
|
cn.anyChildren = nil
|
||||||
|
|
||||||
cn.addChild(n)
|
// Only Static children could reach here
|
||||||
|
cn.addStaticChild(n)
|
||||||
|
|
||||||
if l == sl {
|
if l == sl {
|
||||||
// At parent node
|
// At parent node
|
||||||
@ -160,9 +174,10 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
cn.pnames = pnames
|
cn.pnames = pnames
|
||||||
} else {
|
} else {
|
||||||
// Create child node
|
// Create child node
|
||||||
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
|
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames, nil, nil)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
// Only Static children could reach here
|
||||||
|
cn.addStaticChild(n)
|
||||||
}
|
}
|
||||||
} else if l < sl {
|
} else if l < sl {
|
||||||
search = search[l:]
|
search = search[l:]
|
||||||
@ -173,9 +188,16 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Create child node
|
// Create child node
|
||||||
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
|
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames, nil, nil)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
switch t {
|
||||||
|
case skind:
|
||||||
|
cn.addStaticChild(n)
|
||||||
|
case pkind:
|
||||||
|
cn.paramChildren = n
|
||||||
|
case akind:
|
||||||
|
cn.anyChildren = n
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Node already exists
|
// Node already exists
|
||||||
if h != nil {
|
if h != nil {
|
||||||
@ -190,34 +212,27 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
|
func newNode(t kind, pre string, p *node, sc children, mh *methodHandler, ppath string, pnames []string, paramChildren, anyChildren *node) *node {
|
||||||
return &node{
|
return &node{
|
||||||
kind: t,
|
kind: t,
|
||||||
label: pre[0],
|
label: pre[0],
|
||||||
prefix: pre,
|
prefix: pre,
|
||||||
parent: p,
|
parent: p,
|
||||||
children: c,
|
staticChildrens: sc,
|
||||||
ppath: ppath,
|
ppath: ppath,
|
||||||
pnames: pnames,
|
pnames: pnames,
|
||||||
methodHandler: mh,
|
methodHandler: mh,
|
||||||
|
paramChildren: paramChildren,
|
||||||
|
anyChildren: anyChildren,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) addChild(c *node) {
|
func (n *node) addStaticChild(c *node) {
|
||||||
n.children = append(n.children, c)
|
n.staticChildrens = append(n.staticChildrens, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) findChild(l byte, t kind) *node {
|
func (n *node) findStaticChild(l byte) *node {
|
||||||
for _, c := range n.children {
|
for _, c := range n.staticChildrens {
|
||||||
if c.label == l && c.kind == t {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) findChildWithLabel(l byte) *node {
|
|
||||||
for _, c := range n.children {
|
|
||||||
if c.label == l {
|
if c.label == l {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@ -225,12 +240,18 @@ func (n *node) findChildWithLabel(l byte) *node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) findChildByKind(t kind) *node {
|
func (n *node) findChildWithLabel(l byte) *node {
|
||||||
for _, c := range n.children {
|
for _, c := range n.staticChildrens {
|
||||||
if c.kind == t {
|
if c.label == l {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if l == paramLabel {
|
||||||
|
return n.paramChildren
|
||||||
|
}
|
||||||
|
if l == anyLabel {
|
||||||
|
return n.anyChildren
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +377,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
// Attempt to go back up the tree on no matching prefix or no remaining search
|
// Attempt to go back up the tree on no matching prefix or no remaining search
|
||||||
if l != pl || search == "" {
|
if l != pl || search == "" {
|
||||||
// Handle special case of trailing slash route with existing any route (see #1526)
|
// Handle special case of trailing slash route with existing any route (see #1526)
|
||||||
if path[len(path)-1] == '/' && cn.findChildByKind(akind) != nil {
|
if path[len(path)-1] == '/' && cn.anyChildren != nil {
|
||||||
goto Any
|
goto Any
|
||||||
}
|
}
|
||||||
if nn == nil { // Issue #1348
|
if nn == nil { // Issue #1348
|
||||||
@ -372,7 +393,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Static node
|
// Static node
|
||||||
if child = cn.findChild(search[0], skind); child != nil {
|
if child = cn.findStaticChild(search[0]); child != nil {
|
||||||
// Save next
|
// Save next
|
||||||
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
||||||
nk = pkind
|
nk = pkind
|
||||||
@ -385,7 +406,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
|
|
||||||
Param:
|
Param:
|
||||||
// Param node
|
// Param node
|
||||||
if child = cn.findChildByKind(pkind); child != nil {
|
if child = cn.paramChildren; child != nil {
|
||||||
// Issue #378
|
// Issue #378
|
||||||
if len(pvalues) == n {
|
if len(pvalues) == n {
|
||||||
continue
|
continue
|
||||||
@ -410,7 +431,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
|
|
||||||
Any:
|
Any:
|
||||||
// Any node
|
// Any node
|
||||||
if cn = cn.findChildByKind(akind); cn != nil {
|
if cn = cn.anyChildren; cn != nil {
|
||||||
// If any node is found, use remaining path for pvalues
|
// If any node is found, use remaining path for pvalues
|
||||||
pvalues[len(cn.pnames)-1] = search
|
pvalues[len(cn.pnames)-1] = search
|
||||||
break
|
break
|
||||||
@ -424,7 +445,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
search = ns
|
search = ns
|
||||||
np := nn.parent
|
np := nn.parent
|
||||||
// Consider param route one level up only
|
// Consider param route one level up only
|
||||||
if cn = nn.findChildByKind(pkind); cn != nil {
|
if cn = nn.paramChildren; cn != nil {
|
||||||
pos := strings.IndexByte(ns, '/')
|
pos := strings.IndexByte(ns, '/')
|
||||||
if pos == -1 {
|
if pos == -1 {
|
||||||
// If no slash is remaining in search string set param value
|
// If no slash is remaining in search string set param value
|
||||||
@ -441,7 +462,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
// No param route found, try to resolve nearest any route
|
// No param route found, try to resolve nearest any route
|
||||||
for {
|
for {
|
||||||
np = nn.parent
|
np = nn.parent
|
||||||
if cn = nn.findChildByKind(akind); cn != nil {
|
if cn = nn.anyChildren; cn != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if np == nil {
|
if np == nil {
|
||||||
@ -472,7 +493,7 @@ func (r *Router) Find(method, path string, c Context) {
|
|||||||
|
|
||||||
// Dig further for any, might have an empty value for *, e.g.
|
// Dig further for any, might have an empty value for *, e.g.
|
||||||
// serving a directory. Issue #207.
|
// serving a directory. Issue #207.
|
||||||
if cn = cn.findChildByKind(akind); cn == nil {
|
if cn = cn.anyChildren; cn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h := cn.findHandler(method); h != nil {
|
if h := cn.findHandler(method); h != nil {
|
||||||
|
245
router_test.go
245
router_test.go
@ -175,8 +175,10 @@ var (
|
|||||||
{"GET", "/authorizations", ""},
|
{"GET", "/authorizations", ""},
|
||||||
{"GET", "/authorizations/:id", ""},
|
{"GET", "/authorizations/:id", ""},
|
||||||
{"POST", "/authorizations", ""},
|
{"POST", "/authorizations", ""},
|
||||||
//{"PUT", "/authorizations/clients/:client_id", ""},
|
|
||||||
//{"PATCH", "/authorizations/:id", ""},
|
{"PUT", "/authorizations/clients/:client_id", ""},
|
||||||
|
{"PATCH", "/authorizations/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/authorizations/:id", ""},
|
{"DELETE", "/authorizations/:id", ""},
|
||||||
{"GET", "/applications/:client_id/tokens/:access_token", ""},
|
{"GET", "/applications/:client_id/tokens/:access_token", ""},
|
||||||
{"DELETE", "/applications/:client_id/tokens", ""},
|
{"DELETE", "/applications/:client_id/tokens", ""},
|
||||||
@ -198,7 +200,9 @@ var (
|
|||||||
{"PUT", "/notifications", ""},
|
{"PUT", "/notifications", ""},
|
||||||
{"PUT", "/repos/:owner/:repo/notifications", ""},
|
{"PUT", "/repos/:owner/:repo/notifications", ""},
|
||||||
{"GET", "/notifications/threads/:id", ""},
|
{"GET", "/notifications/threads/:id", ""},
|
||||||
//{"PATCH", "/notifications/threads/:id", ""},
|
|
||||||
|
{"PATCH", "/notifications/threads/:id", ""},
|
||||||
|
|
||||||
{"GET", "/notifications/threads/:id/subscription", ""},
|
{"GET", "/notifications/threads/:id/subscription", ""},
|
||||||
{"PUT", "/notifications/threads/:id/subscription", ""},
|
{"PUT", "/notifications/threads/:id/subscription", ""},
|
||||||
{"DELETE", "/notifications/threads/:id/subscription", ""},
|
{"DELETE", "/notifications/threads/:id/subscription", ""},
|
||||||
@ -221,11 +225,15 @@ var (
|
|||||||
// Gists
|
// Gists
|
||||||
{"GET", "/users/:user/gists", ""},
|
{"GET", "/users/:user/gists", ""},
|
||||||
{"GET", "/gists", ""},
|
{"GET", "/gists", ""},
|
||||||
//{"GET", "/gists/public", ""},
|
|
||||||
//{"GET", "/gists/starred", ""},
|
{"GET", "/gists/public", ""},
|
||||||
|
{"GET", "/gists/starred", ""},
|
||||||
|
|
||||||
{"GET", "/gists/:id", ""},
|
{"GET", "/gists/:id", ""},
|
||||||
{"POST", "/gists", ""},
|
{"POST", "/gists", ""},
|
||||||
//{"PATCH", "/gists/:id", ""},
|
|
||||||
|
{"PATCH", "/gists/:id", ""},
|
||||||
|
|
||||||
{"PUT", "/gists/:id/star", ""},
|
{"PUT", "/gists/:id/star", ""},
|
||||||
{"DELETE", "/gists/:id/star", ""},
|
{"DELETE", "/gists/:id/star", ""},
|
||||||
{"GET", "/gists/:id/star", ""},
|
{"GET", "/gists/:id/star", ""},
|
||||||
@ -237,11 +245,15 @@ var (
|
|||||||
{"POST", "/repos/:owner/:repo/git/blobs", ""},
|
{"POST", "/repos/:owner/:repo/git/blobs", ""},
|
||||||
{"GET", "/repos/:owner/:repo/git/commits/:sha", ""},
|
{"GET", "/repos/:owner/:repo/git/commits/:sha", ""},
|
||||||
{"POST", "/repos/:owner/:repo/git/commits", ""},
|
{"POST", "/repos/:owner/:repo/git/commits", ""},
|
||||||
//{"GET", "/repos/:owner/:repo/git/refs/*ref", ""},
|
|
||||||
|
{"GET", "/repos/:owner/:repo/git/refs/*ref", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/git/refs", ""},
|
{"GET", "/repos/:owner/:repo/git/refs", ""},
|
||||||
{"POST", "/repos/:owner/:repo/git/refs", ""},
|
{"POST", "/repos/:owner/:repo/git/refs", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/git/refs/*ref", ""},
|
|
||||||
//{"DELETE", "/repos/:owner/:repo/git/refs/*ref", ""},
|
{"PATCH", "/repos/:owner/:repo/git/refs/*ref", ""},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/git/refs/*ref", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/git/tags/:sha", ""},
|
{"GET", "/repos/:owner/:repo/git/tags/:sha", ""},
|
||||||
{"POST", "/repos/:owner/:repo/git/tags", ""},
|
{"POST", "/repos/:owner/:repo/git/tags", ""},
|
||||||
{"GET", "/repos/:owner/:repo/git/trees/:sha", ""},
|
{"GET", "/repos/:owner/:repo/git/trees/:sha", ""},
|
||||||
@ -254,22 +266,32 @@ var (
|
|||||||
{"GET", "/repos/:owner/:repo/issues", ""},
|
{"GET", "/repos/:owner/:repo/issues", ""},
|
||||||
{"GET", "/repos/:owner/:repo/issues/:number", ""},
|
{"GET", "/repos/:owner/:repo/issues/:number", ""},
|
||||||
{"POST", "/repos/:owner/:repo/issues", ""},
|
{"POST", "/repos/:owner/:repo/issues", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/issues/:number", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/issues/:number", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/assignees", ""},
|
{"GET", "/repos/:owner/:repo/assignees", ""},
|
||||||
{"GET", "/repos/:owner/:repo/assignees/:assignee", ""},
|
{"GET", "/repos/:owner/:repo/assignees/:assignee", ""},
|
||||||
{"GET", "/repos/:owner/:repo/issues/:number/comments", ""},
|
{"GET", "/repos/:owner/:repo/issues/:number/comments", ""},
|
||||||
//{"GET", "/repos/:owner/:repo/issues/comments", ""},
|
|
||||||
//{"GET", "/repos/:owner/:repo/issues/comments/:id", ""},
|
{"GET", "/repos/:owner/:repo/issues/comments", ""},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/comments/:id", ""},
|
||||||
|
|
||||||
{"POST", "/repos/:owner/:repo/issues/:number/comments", ""},
|
{"POST", "/repos/:owner/:repo/issues/:number/comments", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/issues/comments/:id", ""},
|
|
||||||
//{"DELETE", "/repos/:owner/:repo/issues/comments/:id", ""},
|
{"PATCH", "/repos/:owner/:repo/issues/comments/:id", ""},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/issues/comments/:id", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/issues/:number/events", ""},
|
{"GET", "/repos/:owner/:repo/issues/:number/events", ""},
|
||||||
//{"GET", "/repos/:owner/:repo/issues/events", ""},
|
|
||||||
//{"GET", "/repos/:owner/:repo/issues/events/:id", ""},
|
{"GET", "/repos/:owner/:repo/issues/events", ""},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/events/:id", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/labels", ""},
|
{"GET", "/repos/:owner/:repo/labels", ""},
|
||||||
{"GET", "/repos/:owner/:repo/labels/:name", ""},
|
{"GET", "/repos/:owner/:repo/labels/:name", ""},
|
||||||
{"POST", "/repos/:owner/:repo/labels", ""},
|
{"POST", "/repos/:owner/:repo/labels", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/labels/:name", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/labels/:name", ""},
|
||||||
|
|
||||||
{"DELETE", "/repos/:owner/:repo/labels/:name", ""},
|
{"DELETE", "/repos/:owner/:repo/labels/:name", ""},
|
||||||
{"GET", "/repos/:owner/:repo/issues/:number/labels", ""},
|
{"GET", "/repos/:owner/:repo/issues/:number/labels", ""},
|
||||||
{"POST", "/repos/:owner/:repo/issues/:number/labels", ""},
|
{"POST", "/repos/:owner/:repo/issues/:number/labels", ""},
|
||||||
@ -280,7 +302,9 @@ var (
|
|||||||
{"GET", "/repos/:owner/:repo/milestones", ""},
|
{"GET", "/repos/:owner/:repo/milestones", ""},
|
||||||
{"GET", "/repos/:owner/:repo/milestones/:number", ""},
|
{"GET", "/repos/:owner/:repo/milestones/:number", ""},
|
||||||
{"POST", "/repos/:owner/:repo/milestones", ""},
|
{"POST", "/repos/:owner/:repo/milestones", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/milestones/:number", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/milestones/:number", ""},
|
||||||
|
|
||||||
{"DELETE", "/repos/:owner/:repo/milestones/:number", ""},
|
{"DELETE", "/repos/:owner/:repo/milestones/:number", ""},
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
@ -296,7 +320,9 @@ var (
|
|||||||
{"GET", "/users/:user/orgs", ""},
|
{"GET", "/users/:user/orgs", ""},
|
||||||
{"GET", "/user/orgs", ""},
|
{"GET", "/user/orgs", ""},
|
||||||
{"GET", "/orgs/:org", ""},
|
{"GET", "/orgs/:org", ""},
|
||||||
//{"PATCH", "/orgs/:org", ""},
|
|
||||||
|
{"PATCH", "/orgs/:org", ""},
|
||||||
|
|
||||||
{"GET", "/orgs/:org/members", ""},
|
{"GET", "/orgs/:org/members", ""},
|
||||||
{"GET", "/orgs/:org/members/:user", ""},
|
{"GET", "/orgs/:org/members/:user", ""},
|
||||||
{"DELETE", "/orgs/:org/members/:user", ""},
|
{"DELETE", "/orgs/:org/members/:user", ""},
|
||||||
@ -307,7 +333,9 @@ var (
|
|||||||
{"GET", "/orgs/:org/teams", ""},
|
{"GET", "/orgs/:org/teams", ""},
|
||||||
{"GET", "/teams/:id", ""},
|
{"GET", "/teams/:id", ""},
|
||||||
{"POST", "/orgs/:org/teams", ""},
|
{"POST", "/orgs/:org/teams", ""},
|
||||||
//{"PATCH", "/teams/:id", ""},
|
|
||||||
|
{"PATCH", "/teams/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/teams/:id", ""},
|
{"DELETE", "/teams/:id", ""},
|
||||||
{"GET", "/teams/:id/members", ""},
|
{"GET", "/teams/:id/members", ""},
|
||||||
{"GET", "/teams/:id/members/:user", ""},
|
{"GET", "/teams/:id/members/:user", ""},
|
||||||
@ -323,17 +351,22 @@ var (
|
|||||||
{"GET", "/repos/:owner/:repo/pulls", ""},
|
{"GET", "/repos/:owner/:repo/pulls", ""},
|
||||||
{"GET", "/repos/:owner/:repo/pulls/:number", ""},
|
{"GET", "/repos/:owner/:repo/pulls/:number", ""},
|
||||||
{"POST", "/repos/:owner/:repo/pulls", ""},
|
{"POST", "/repos/:owner/:repo/pulls", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/pulls/:number", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/pulls/:number", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/pulls/:number/commits", ""},
|
{"GET", "/repos/:owner/:repo/pulls/:number/commits", ""},
|
||||||
{"GET", "/repos/:owner/:repo/pulls/:number/files", ""},
|
{"GET", "/repos/:owner/:repo/pulls/:number/files", ""},
|
||||||
{"GET", "/repos/:owner/:repo/pulls/:number/merge", ""},
|
{"GET", "/repos/:owner/:repo/pulls/:number/merge", ""},
|
||||||
{"PUT", "/repos/:owner/:repo/pulls/:number/merge", ""},
|
{"PUT", "/repos/:owner/:repo/pulls/:number/merge", ""},
|
||||||
{"GET", "/repos/:owner/:repo/pulls/:number/comments", ""},
|
{"GET", "/repos/:owner/:repo/pulls/:number/comments", ""},
|
||||||
//{"GET", "/repos/:owner/:repo/pulls/comments", ""},
|
|
||||||
//{"GET", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
{"GET", "/repos/:owner/:repo/pulls/comments", ""},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
||||||
|
|
||||||
{"PUT", "/repos/:owner/:repo/pulls/:number/comments", ""},
|
{"PUT", "/repos/:owner/:repo/pulls/:number/comments", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
|
||||||
//{"DELETE", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
{"PATCH", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/pulls/comments/:number", ""},
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
{"GET", "/user/repos", ""},
|
{"GET", "/user/repos", ""},
|
||||||
@ -343,7 +376,9 @@ var (
|
|||||||
{"POST", "/user/repos", ""},
|
{"POST", "/user/repos", ""},
|
||||||
{"POST", "/orgs/:org/repos", ""},
|
{"POST", "/orgs/:org/repos", ""},
|
||||||
{"GET", "/repos/:owner/:repo", ""},
|
{"GET", "/repos/:owner/:repo", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/contributors", ""},
|
{"GET", "/repos/:owner/:repo/contributors", ""},
|
||||||
{"GET", "/repos/:owner/:repo/languages", ""},
|
{"GET", "/repos/:owner/:repo/languages", ""},
|
||||||
{"GET", "/repos/:owner/:repo/teams", ""},
|
{"GET", "/repos/:owner/:repo/teams", ""},
|
||||||
@ -359,19 +394,26 @@ var (
|
|||||||
{"GET", "/repos/:owner/:repo/commits/:sha/comments", ""},
|
{"GET", "/repos/:owner/:repo/commits/:sha/comments", ""},
|
||||||
{"POST", "/repos/:owner/:repo/commits/:sha/comments", ""},
|
{"POST", "/repos/:owner/:repo/commits/:sha/comments", ""},
|
||||||
{"GET", "/repos/:owner/:repo/comments/:id", ""},
|
{"GET", "/repos/:owner/:repo/comments/:id", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/comments/:id", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/comments/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/repos/:owner/:repo/comments/:id", ""},
|
{"DELETE", "/repos/:owner/:repo/comments/:id", ""},
|
||||||
{"GET", "/repos/:owner/:repo/commits", ""},
|
{"GET", "/repos/:owner/:repo/commits", ""},
|
||||||
{"GET", "/repos/:owner/:repo/commits/:sha", ""},
|
{"GET", "/repos/:owner/:repo/commits/:sha", ""},
|
||||||
{"GET", "/repos/:owner/:repo/readme", ""},
|
{"GET", "/repos/:owner/:repo/readme", ""},
|
||||||
|
|
||||||
//{"GET", "/repos/:owner/:repo/contents/*path", ""},
|
//{"GET", "/repos/:owner/:repo/contents/*path", ""},
|
||||||
//{"PUT", "/repos/:owner/:repo/contents/*path", ""},
|
//{"PUT", "/repos/:owner/:repo/contents/*path", ""},
|
||||||
//{"DELETE", "/repos/:owner/:repo/contents/*path", ""},
|
//{"DELETE", "/repos/:owner/:repo/contents/*path", ""},
|
||||||
//{"GET", "/repos/:owner/:repo/:archive_format/:ref", ""},
|
|
||||||
|
{"GET", "/repos/:owner/:repo/:archive_format/:ref", ""},
|
||||||
|
|
||||||
{"GET", "/repos/:owner/:repo/keys", ""},
|
{"GET", "/repos/:owner/:repo/keys", ""},
|
||||||
{"GET", "/repos/:owner/:repo/keys/:id", ""},
|
{"GET", "/repos/:owner/:repo/keys/:id", ""},
|
||||||
{"POST", "/repos/:owner/:repo/keys", ""},
|
{"POST", "/repos/:owner/:repo/keys", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/keys/:id", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/keys/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/repos/:owner/:repo/keys/:id", ""},
|
{"DELETE", "/repos/:owner/:repo/keys/:id", ""},
|
||||||
{"GET", "/repos/:owner/:repo/downloads", ""},
|
{"GET", "/repos/:owner/:repo/downloads", ""},
|
||||||
{"GET", "/repos/:owner/:repo/downloads/:id", ""},
|
{"GET", "/repos/:owner/:repo/downloads/:id", ""},
|
||||||
@ -381,14 +423,18 @@ var (
|
|||||||
{"GET", "/repos/:owner/:repo/hooks", ""},
|
{"GET", "/repos/:owner/:repo/hooks", ""},
|
||||||
{"GET", "/repos/:owner/:repo/hooks/:id", ""},
|
{"GET", "/repos/:owner/:repo/hooks/:id", ""},
|
||||||
{"POST", "/repos/:owner/:repo/hooks", ""},
|
{"POST", "/repos/:owner/:repo/hooks", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/hooks/:id", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/hooks/:id", ""},
|
||||||
|
|
||||||
{"POST", "/repos/:owner/:repo/hooks/:id/tests", ""},
|
{"POST", "/repos/:owner/:repo/hooks/:id/tests", ""},
|
||||||
{"DELETE", "/repos/:owner/:repo/hooks/:id", ""},
|
{"DELETE", "/repos/:owner/:repo/hooks/:id", ""},
|
||||||
{"POST", "/repos/:owner/:repo/merges", ""},
|
{"POST", "/repos/:owner/:repo/merges", ""},
|
||||||
{"GET", "/repos/:owner/:repo/releases", ""},
|
{"GET", "/repos/:owner/:repo/releases", ""},
|
||||||
{"GET", "/repos/:owner/:repo/releases/:id", ""},
|
{"GET", "/repos/:owner/:repo/releases/:id", ""},
|
||||||
{"POST", "/repos/:owner/:repo/releases", ""},
|
{"POST", "/repos/:owner/:repo/releases", ""},
|
||||||
//{"PATCH", "/repos/:owner/:repo/releases/:id", ""},
|
|
||||||
|
{"PATCH", "/repos/:owner/:repo/releases/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/repos/:owner/:repo/releases/:id", ""},
|
{"DELETE", "/repos/:owner/:repo/releases/:id", ""},
|
||||||
{"GET", "/repos/:owner/:repo/releases/:id/assets", ""},
|
{"GET", "/repos/:owner/:repo/releases/:id/assets", ""},
|
||||||
{"GET", "/repos/:owner/:repo/stats/contributors", ""},
|
{"GET", "/repos/:owner/:repo/stats/contributors", ""},
|
||||||
@ -412,7 +458,9 @@ var (
|
|||||||
// Users
|
// Users
|
||||||
{"GET", "/users/:user", ""},
|
{"GET", "/users/:user", ""},
|
||||||
{"GET", "/user", ""},
|
{"GET", "/user", ""},
|
||||||
//{"PATCH", "/user", ""},
|
|
||||||
|
{"PATCH", "/user", ""},
|
||||||
|
|
||||||
{"GET", "/users", ""},
|
{"GET", "/users", ""},
|
||||||
{"GET", "/user/emails", ""},
|
{"GET", "/user/emails", ""},
|
||||||
{"POST", "/user/emails", ""},
|
{"POST", "/user/emails", ""},
|
||||||
@ -429,7 +477,9 @@ var (
|
|||||||
{"GET", "/user/keys", ""},
|
{"GET", "/user/keys", ""},
|
||||||
{"GET", "/user/keys/:id", ""},
|
{"GET", "/user/keys/:id", ""},
|
||||||
{"POST", "/user/keys", ""},
|
{"POST", "/user/keys", ""},
|
||||||
//{"PATCH", "/user/keys/:id", ""},
|
|
||||||
|
{"PATCH", "/user/keys/:id", ""},
|
||||||
|
|
||||||
{"DELETE", "/user/keys/:id", ""},
|
{"DELETE", "/user/keys/:id", ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,6 +550,88 @@ var (
|
|||||||
{"DELETE", "/moments/:id", ""},
|
{"DELETE", "/moments/:id", ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paramAndAnyAPI = []*Route{
|
||||||
|
{"GET", "/root/:first/foo/*", ""},
|
||||||
|
{"GET", "/root/:first/:second/*", ""},
|
||||||
|
{"GET", "/root/:first/bar/:second/*", ""},
|
||||||
|
{"GET", "/root/:first/qux/:second/:third/:fourth", ""},
|
||||||
|
{"GET", "/root/:first/qux/:second/:third/:fourth/*", ""},
|
||||||
|
{"GET", "/root/*", ""},
|
||||||
|
|
||||||
|
{"POST", "/root/:first/foo/*", ""},
|
||||||
|
{"POST", "/root/:first/:second/*", ""},
|
||||||
|
{"POST", "/root/:first/bar/:second/*", ""},
|
||||||
|
{"POST", "/root/:first/qux/:second/:third/:fourth", ""},
|
||||||
|
{"POST", "/root/:first/qux/:second/:third/:fourth/*", ""},
|
||||||
|
{"POST", "/root/*", ""},
|
||||||
|
|
||||||
|
{"PUT", "/root/:first/foo/*", ""},
|
||||||
|
{"PUT", "/root/:first/:second/*", ""},
|
||||||
|
{"PUT", "/root/:first/bar/:second/*", ""},
|
||||||
|
{"PUT", "/root/:first/qux/:second/:third/:fourth", ""},
|
||||||
|
{"PUT", "/root/:first/qux/:second/:third/:fourth/*", ""},
|
||||||
|
{"PUT", "/root/*", ""},
|
||||||
|
|
||||||
|
{"DELETE", "/root/:first/foo/*", ""},
|
||||||
|
{"DELETE", "/root/:first/:second/*", ""},
|
||||||
|
{"DELETE", "/root/:first/bar/:second/*", ""},
|
||||||
|
{"DELETE", "/root/:first/qux/:second/:third/:fourth", ""},
|
||||||
|
{"DELETE", "/root/:first/qux/:second/:third/:fourth/*", ""},
|
||||||
|
{"DELETE", "/root/*", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
paramAndAnyAPIToFind = []*Route{
|
||||||
|
{"GET", "/root/one/foo/after/the/asterisk", ""},
|
||||||
|
{"GET", "/root/one/foo/path/after/the/asterisk", ""},
|
||||||
|
{"GET", "/root/one/two/path/after/the/asterisk", ""},
|
||||||
|
{"GET", "/root/one/bar/two/after/the/asterisk", ""},
|
||||||
|
{"GET", "/root/one/qux/two/three/four", ""},
|
||||||
|
{"GET", "/root/one/qux/two/three/four/after/the/asterisk", ""},
|
||||||
|
|
||||||
|
{"POST", "/root/one/foo/after/the/asterisk", ""},
|
||||||
|
{"POST", "/root/one/foo/path/after/the/asterisk", ""},
|
||||||
|
{"POST", "/root/one/two/path/after/the/asterisk", ""},
|
||||||
|
{"POST", "/root/one/bar/two/after/the/asterisk", ""},
|
||||||
|
{"POST", "/root/one/qux/two/three/four", ""},
|
||||||
|
{"POST", "/root/one/qux/two/three/four/after/the/asterisk", ""},
|
||||||
|
|
||||||
|
{"PUT", "/root/one/foo/after/the/asterisk", ""},
|
||||||
|
{"PUT", "/root/one/foo/path/after/the/asterisk", ""},
|
||||||
|
{"PUT", "/root/one/two/path/after/the/asterisk", ""},
|
||||||
|
{"PUT", "/root/one/bar/two/after/the/asterisk", ""},
|
||||||
|
{"PUT", "/root/one/qux/two/three/four", ""},
|
||||||
|
{"PUT", "/root/one/qux/two/three/four/after/the/asterisk", ""},
|
||||||
|
|
||||||
|
{"DELETE", "/root/one/foo/after/the/asterisk", ""},
|
||||||
|
{"DELETE", "/root/one/foo/path/after/the/asterisk", ""},
|
||||||
|
{"DELETE", "/root/one/two/path/after/the/asterisk", ""},
|
||||||
|
{"DELETE", "/root/one/bar/two/after/the/asterisk", ""},
|
||||||
|
{"DELETE", "/root/one/qux/two/three/four", ""},
|
||||||
|
{"DELETE", "/root/one/qux/two/three/four/after/the/asterisk", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
missesAPI = []*Route{
|
||||||
|
{"GET", "/missOne", ""},
|
||||||
|
{"GET", "/miss/two", ""},
|
||||||
|
{"GET", "/miss/three/levels", ""},
|
||||||
|
{"GET", "/miss/four/levels/nooo", ""},
|
||||||
|
|
||||||
|
{"POST", "/missOne", ""},
|
||||||
|
{"POST", "/miss/two", ""},
|
||||||
|
{"POST", "/miss/three/levels", ""},
|
||||||
|
{"POST", "/miss/four/levels/nooo", ""},
|
||||||
|
|
||||||
|
{"PUT", "/missOne", ""},
|
||||||
|
{"PUT", "/miss/two", ""},
|
||||||
|
{"PUT", "/miss/three/levels", ""},
|
||||||
|
{"PUT", "/miss/four/levels/nooo", ""},
|
||||||
|
|
||||||
|
{"DELETE", "/missOne", ""},
|
||||||
|
{"DELETE", "/miss/two", ""},
|
||||||
|
{"DELETE", "/miss/three/levels", ""},
|
||||||
|
{"DELETE", "/miss/four/levels/nooo", ""},
|
||||||
|
}
|
||||||
|
|
||||||
// handlerHelper created a function that will set a context key for assertion
|
// handlerHelper created a function that will set a context key for assertion
|
||||||
handlerHelper = func(key string, value int) func(c Context) error {
|
handlerHelper = func(key string, value int) func(c Context) error {
|
||||||
return func(c Context) error {
|
return func(c Context) error {
|
||||||
@ -1298,7 +1430,7 @@ func TestRouterParam1466(t *testing.T) {
|
|||||||
assert.Equal(t, 0, c.response.Status)
|
assert.Equal(t, 0, c.response.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkRouterRoutes(b *testing.B, routes []*Route) {
|
func benchmarkRouterRoutes(b *testing.B, routes []*Route, routesToFind []*Route) {
|
||||||
e := New()
|
e := New()
|
||||||
r := e.router
|
r := e.router
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@ -1310,9 +1442,12 @@ func benchmarkRouterRoutes(b *testing.B, routes []*Route) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routes adding are performed just once, so it doesn't make sense to see that in the benchmark
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
// Find routes
|
// Find routes
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, route := range gitHubAPI {
|
for _, route := range routesToFind {
|
||||||
c := e.pool.Get().(*context)
|
c := e.pool.Get().(*context)
|
||||||
r.Find(route.Method, route.Path, c)
|
r.Find(route.Method, route.Path, c)
|
||||||
e.pool.Put(c)
|
e.pool.Put(c)
|
||||||
@ -1321,28 +1456,56 @@ func benchmarkRouterRoutes(b *testing.B, routes []*Route) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRouterStaticRoutes(b *testing.B) {
|
func BenchmarkRouterStaticRoutes(b *testing.B) {
|
||||||
benchmarkRouterRoutes(b, staticRoutes)
|
benchmarkRouterRoutes(b, staticRoutes, staticRoutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRouterStaticRoutesMisses(b *testing.B) {
|
||||||
|
benchmarkRouterRoutes(b, staticRoutes, missesAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRouterGitHubAPI(b *testing.B) {
|
func BenchmarkRouterGitHubAPI(b *testing.B) {
|
||||||
benchmarkRouterRoutes(b, gitHubAPI)
|
benchmarkRouterRoutes(b, gitHubAPI, gitHubAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRouterGitHubAPIMisses(b *testing.B) {
|
||||||
|
benchmarkRouterRoutes(b, gitHubAPI, missesAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRouterParseAPI(b *testing.B) {
|
func BenchmarkRouterParseAPI(b *testing.B) {
|
||||||
benchmarkRouterRoutes(b, parseAPI)
|
benchmarkRouterRoutes(b, parseAPI, parseAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRouterParseAPIMisses(b *testing.B) {
|
||||||
|
benchmarkRouterRoutes(b, parseAPI, missesAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRouterGooglePlusAPI(b *testing.B) {
|
func BenchmarkRouterGooglePlusAPI(b *testing.B) {
|
||||||
benchmarkRouterRoutes(b, googlePlusAPI)
|
benchmarkRouterRoutes(b, googlePlusAPI, googlePlusAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRouterGooglePlusAPIMisses(b *testing.B) {
|
||||||
|
benchmarkRouterRoutes(b, googlePlusAPI, missesAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRouterParamsAndAnyAPI(b *testing.B) {
|
||||||
|
benchmarkRouterRoutes(b, paramAndAnyAPI, paramAndAnyAPIToFind)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) printTree(pfx string, tail bool) {
|
func (n *node) printTree(pfx string, tail bool) {
|
||||||
p := prefix(tail, pfx, "└── ", "├── ")
|
p := prefix(tail, pfx, "└── ", "├── ")
|
||||||
fmt.Printf("%s%s, %p: type=%d, parent=%p, handler=%v, pnames=%v\n", p, n.prefix, n, n.kind, n.parent, n.methodHandler, n.pnames)
|
fmt.Printf("%s%s, %p: type=%d, parent=%p, handler=%v, pnames=%v\n", p, n.prefix, n, n.kind, n.parent, n.methodHandler, n.pnames)
|
||||||
|
|
||||||
children := n.children
|
|
||||||
l := len(children)
|
|
||||||
p = prefix(tail, pfx, " ", "│ ")
|
p = prefix(tail, pfx, " ", "│ ")
|
||||||
|
|
||||||
|
children := n.staticChildrens
|
||||||
|
l := len(children)
|
||||||
|
|
||||||
|
if n.paramChildren != nil {
|
||||||
|
n.paramChildren.printTree(p, n.anyChildren == nil && l == 0)
|
||||||
|
}
|
||||||
|
if n.anyChildren != nil {
|
||||||
|
n.anyChildren.printTree(p, l == 0)
|
||||||
|
}
|
||||||
for i := 0; i < l-1; i++ {
|
for i := 0; i < l-1; i++ {
|
||||||
children[i].printTree(p, false)
|
children[i].printTree(p, false)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user