WIP
diff --git a/methodindex.go b/methodindex.go new file mode 100644 index 0000000..a69d9db --- /dev/null +++ b/methodindex.go
@@ -0,0 +1,125 @@ +// Copyright 2019 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +package httprouter + +import ( + "net/http" +) + +const ( + idxGET = iota + idxHEAD + idxPOST + idxPUT + idxPATCH + idxDELETE + idxCONNECT + idxOPTIONS + idxTRACE + idxEnd +) + +type customMethod struct { + method string + root *node +} + +type methodIndex struct { + roots [idxEnd]*node + custom []customMethod +} + +func (m *methodIndex) init(method string) (root *node) { + var idx int + switch method { + case http.MethodGet: + idx = idxGET + case http.MethodPatch: + idx = idxPATCH + case http.MethodPost: + idx = idxPOST + case http.MethodPut: + idx = idxPUT + case http.MethodDelete: + idx = idxDELETE + case http.MethodHead: + idx = idxHEAD + case http.MethodOptions: + idx = idxOPTIONS + case http.MethodConnect: + idx = idxCONNECT + case http.MethodTrace: + idx = idxTRACE + default: // custom methods + for _, c := range m.custom { + if c.method == method { + return c.root + } + } + + root = new(node) + m.custom = append(m.custom, customMethod{method, root}) + return root + } + + root = m.roots[idx] + if root == nil { + root = new(node) + m.roots[idx] = root + } + return root +} + +func (m *methodIndex) get(method string) *node { + switch method[0] { + case 'G': + if method == http.MethodGet { + return m.roots[idxGET] + } + case 'P': + switch len(method) { + case 5: + if method == http.MethodPatch { + return m.roots[idxPATCH] + } + case 4: + if method == http.MethodPost { + return m.roots[idxPOST] + } + case 3: + if method == http.MethodPut { + return m.roots[idxPUT] + } + } + case 'D': + if method == http.MethodDelete { + return m.roots[idxDELETE] + } + case 'H': + if method == http.MethodHead { + return m.roots[idxHEAD] + } + case 'O': + if method == http.MethodOptions { + return m.roots[idxOPTIONS] + } + case 'C': + if method == http.MethodConnect { + return m.roots[idxCONNECT] + } + case 'T': + if method == http.MethodTrace { + return m.roots[idxTRACE] + } + } + + // custom methods + for _, c := range m.custom { + if c.method == method { + return c.root + } + } + return nil +}
diff --git a/router.go b/router.go index a4e8d4e..afd9e89 100644 --- a/router.go +++ b/router.go
@@ -124,7 +124,7 @@ // Router is a http.Handler which can be used to dispatch requests to different // handler functions via configurable routes type Router struct { - trees map[string]*node + trees methodIndex paramsPool sync.Pool maxParams uint16 @@ -252,16 +252,7 @@ panic("path must begin with '/' in path '" + path + "'") } - if r.trees == nil { - r.trees = make(map[string]*node) - } - - root := r.trees[method] - if root == nil { - root = new(node) - r.trees[method] = root - } - + root := r.trees.init(method) root.addRoute(path, handle) // Update maxParams @@ -335,7 +326,7 @@ // values. Otherwise the third return value indicates whether a redirection to // the same path with an extra / without the trailing slash should be performed. func (r *Router) Lookup(method, path string) (Handle, Params, bool) { - if root := r.trees[method]; root != nil { + if root := r.trees.get(method); root != nil { handle, ps, tsr := root.getValue(path, r.getParams) if handle == nil { r.putParams(ps) @@ -347,37 +338,37 @@ } func (r *Router) allowed(path, reqMethod string) (allow string) { - if path == "*" { // server-wide - for method := range r.trees { - if method == http.MethodOptions { - continue - } + // if path == "*" { // server-wide + // for i, tree := range r.trees { + // if i == idxOPTIONS || tree == nil { + // continue + // } - // add request method to list of allowed methods - if len(allow) == 0 { - allow = method - } else { - allow += ", " + method - } - } - } else { // specific path - for method := range r.trees { - // Skip the requested method - we already tried this one - if method == reqMethod || method == http.MethodOptions { - continue - } + // // add request method to list of allowed methods + // if len(allow) == 0 { + // allow = method + // } else { + // allow += ", " + method + // } + // } + // } else { // specific path + // for method := range r.trees { + // // Skip the requested method - we already tried this one + // if method == reqMethod || method == http.MethodOptions { + // continue + // } - handle, _, _ := r.trees[method].getValue(path, nil) - if handle != nil { - // add request method to list of allowed methods - if len(allow) == 0 { - allow = method - } else { - allow += ", " + method - } - } - } - } + // handle, _, _ := r.trees[method].getValue(path) + // if handle != nil { + // // add request method to list of allowed methods + // if len(allow) == 0 { + // allow = method + // } else { + // allow += ", " + method + // } + // } + // } + // } if len(allow) > 0 { allow += ", OPTIONS" } @@ -392,7 +383,7 @@ path := req.URL.Path - if root := r.trees[req.Method]; root != nil { + if root := r.trees.get(req.Method); root != nil { if handle, psp, tsr := root.getValue(path, r.getParams); handle != nil { var ps Params if psp != nil {