Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
177 lines (151 sloc) 3.63 KB
// +build !goji_router_simple
package goji
import (
"net/http"
"sort"
"strings"
"goji.io/internal"
)
type router struct {
routes []route
methods map[string]*trieNode
wildcard trieNode
}
type route struct {
Pattern
http.Handler
}
type child struct {
prefix string
node *trieNode
}
type trieNode struct {
routes []int
children []child
}
func (rt *router) add(p Pattern, h http.Handler) {
i := len(rt.routes)
rt.routes = append(rt.routes, route{p, h})
var prefix string
if pp, ok := p.(pathPrefix); ok {
prefix = pp.PathPrefix()
}
var methods map[string]struct{}
if hm, ok := p.(httpMethods); ok {
methods = hm.HTTPMethods()
}
if methods == nil {
rt.wildcard.add(prefix, i)
for _, sub := range rt.methods {
sub.add(prefix, i)
}
} else {
if rt.methods == nil {
rt.methods = make(map[string]*trieNode)
}
for method := range methods {
if _, ok := rt.methods[method]; !ok {
rt.methods[method] = rt.wildcard.clone()
}
rt.methods[method].add(prefix, i)
}
}
}
func (rt *router) route(r *http.Request) *http.Request {
tn := &rt.wildcard
if tn2, ok := rt.methods[r.Method]; ok {
tn = tn2
}
ctx := r.Context()
path := ctx.Value(internal.Path).(string)
for path != "" {
i := sort.Search(len(tn.children), func(i int) bool {
return path[0] <= tn.children[i].prefix[0]
})
if i == len(tn.children) || !strings.HasPrefix(path, tn.children[i].prefix) {
break
}
path = path[len(tn.children[i].prefix):]
tn = tn.children[i].node
}
for _, i := range tn.routes {
if r2 := rt.routes[i].Match(r); r2 != nil {
return r2.WithContext(&match{
Context: r2.Context(),
p: rt.routes[i].Pattern,
h: rt.routes[i].Handler,
})
}
}
return r.WithContext(&match{Context: ctx})
}
// We can be a teensy bit more efficient here: we're maintaining a sorted list,
// so we know exactly where to insert the new element. But since that involves
// more bookkeeping and makes the code messier, let's cross that bridge when we
// come to it.
type byPrefix []child
func (b byPrefix) Len() int {
return len(b)
}
func (b byPrefix) Less(i, j int) bool {
return b[i].prefix < b[j].prefix
}
func (b byPrefix) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func longestPrefix(a, b string) string {
mlen := len(a)
if len(b) < mlen {
mlen = len(b)
}
for i := 0; i < mlen; i++ {
if a[i] != b[i] {
return a[:i]
}
}
return a[:mlen]
}
func (tn *trieNode) add(prefix string, idx int) {
if len(prefix) == 0 {
tn.routes = append(tn.routes, idx)
for i := range tn.children {
tn.children[i].node.add(prefix, idx)
}
return
}
ch := prefix[0]
i := sort.Search(len(tn.children), func(i int) bool {
return ch <= tn.children[i].prefix[0]
})
if i == len(tn.children) || ch != tn.children[i].prefix[0] {
routes := append([]int(nil), tn.routes...)
tn.children = append(tn.children, child{
prefix: prefix,
node: &trieNode{routes: append(routes, idx)},
})
} else {
lp := longestPrefix(prefix, tn.children[i].prefix)
if tn.children[i].prefix == lp {
tn.children[i].node.add(prefix[len(lp):], idx)
return
}
split := new(trieNode)
split.children = []child{
{tn.children[i].prefix[len(lp):], tn.children[i].node},
}
split.routes = append([]int(nil), tn.routes...)
split.add(prefix[len(lp):], idx)
tn.children[i].prefix = lp
tn.children[i].node = split
}
sort.Sort(byPrefix(tn.children))
}
func (tn *trieNode) clone() *trieNode {
clone := new(trieNode)
clone.routes = append(clone.routes, tn.routes...)
clone.children = append(clone.children, tn.children...)
for i := range clone.children {
clone.children[i].node = tn.children[i].node.clone()
}
return clone
}