mirror of
https://github.com/restic/rest-server.git
synced 2025-12-07 09:36:13 -08:00
177 lines
3.6 KiB
Go
177 lines
3.6 KiB
Go
// +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
|
|
}
|