Blame vendor/github.com/go-chi/chi/tree.go

Packit Service 509fd4
package chi
Packit Service 509fd4
Packit Service 509fd4
// Radix tree implementation below is a based on the original work by
Packit Service 509fd4
// Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go
Packit Service 509fd4
// (MIT licensed). It's been heavily modified for use as a HTTP routing tree.
Packit Service 509fd4
Packit Service 509fd4
import (
Packit Service 509fd4
	"fmt"
Packit Service 509fd4
	"math"
Packit Service 509fd4
	"net/http"
Packit Service 509fd4
	"regexp"
Packit Service 509fd4
	"sort"
Packit Service 509fd4
	"strconv"
Packit Service 509fd4
	"strings"
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
type methodTyp int
Packit Service 509fd4
Packit Service 509fd4
const (
Packit Service 509fd4
	mSTUB methodTyp = 1 << iota
Packit Service 509fd4
	mCONNECT
Packit Service 509fd4
	mDELETE
Packit Service 509fd4
	mGET
Packit Service 509fd4
	mHEAD
Packit Service 509fd4
	mOPTIONS
Packit Service 509fd4
	mPATCH
Packit Service 509fd4
	mPOST
Packit Service 509fd4
	mPUT
Packit Service 509fd4
	mTRACE
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
var mALL = mCONNECT | mDELETE | mGET | mHEAD |
Packit Service 509fd4
	mOPTIONS | mPATCH | mPOST | mPUT | mTRACE
Packit Service 509fd4
Packit Service 509fd4
var methodMap = map[string]methodTyp{
Packit Service 509fd4
	http.MethodConnect: mCONNECT,
Packit Service 509fd4
	http.MethodDelete:  mDELETE,
Packit Service 509fd4
	http.MethodGet:     mGET,
Packit Service 509fd4
	http.MethodHead:    mHEAD,
Packit Service 509fd4
	http.MethodOptions: mOPTIONS,
Packit Service 509fd4
	http.MethodPatch:   mPATCH,
Packit Service 509fd4
	http.MethodPost:    mPOST,
Packit Service 509fd4
	http.MethodPut:     mPUT,
Packit Service 509fd4
	http.MethodTrace:   mTRACE,
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// RegisterMethod adds support for custom HTTP method handlers, available
Packit Service 509fd4
// via Router#Method and Router#MethodFunc
Packit Service 509fd4
func RegisterMethod(method string) {
Packit Service 509fd4
	if method == "" {
Packit Service 509fd4
		return
Packit Service 509fd4
	}
Packit Service 509fd4
	method = strings.ToUpper(method)
Packit Service 509fd4
	if _, ok := methodMap[method]; ok {
Packit Service 509fd4
		return
Packit Service 509fd4
	}
Packit Service 509fd4
	n := len(methodMap)
Packit Service 509fd4
	if n > strconv.IntSize {
Packit Service 509fd4
		panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize))
Packit Service 509fd4
	}
Packit Service 509fd4
	mt := methodTyp(math.Exp2(float64(n)))
Packit Service 509fd4
	methodMap[method] = mt
Packit Service 509fd4
	mALL |= mt
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
type nodeTyp uint8
Packit Service 509fd4
Packit Service 509fd4
const (
Packit Service 509fd4
	ntStatic   nodeTyp = iota // /home
Packit Service 509fd4
	ntRegexp                  // /{id:[0-9]+}
Packit Service 509fd4
	ntParam                   // /{user}
Packit Service 509fd4
	ntCatchAll                // /api/v1/*
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
type node struct {
Packit Service 509fd4
	// node type: static, regexp, param, catchAll
Packit Service 509fd4
	typ nodeTyp
Packit Service 509fd4
Packit Service 509fd4
	// first byte of the prefix
Packit Service 509fd4
	label byte
Packit Service 509fd4
Packit Service 509fd4
	// first byte of the child prefix
Packit Service 509fd4
	tail byte
Packit Service 509fd4
Packit Service 509fd4
	// prefix is the common prefix we ignore
Packit Service 509fd4
	prefix string
Packit Service 509fd4
Packit Service 509fd4
	// regexp matcher for regexp nodes
Packit Service 509fd4
	rex *regexp.Regexp
Packit Service 509fd4
Packit Service 509fd4
	// HTTP handler endpoints on the leaf node
Packit Service 509fd4
	endpoints endpoints
Packit Service 509fd4
Packit Service 509fd4
	// subroutes on the leaf node
Packit Service 509fd4
	subroutes Routes
Packit Service 509fd4
Packit Service 509fd4
	// child nodes should be stored in-order for iteration,
Packit Service 509fd4
	// in groups of the node type.
Packit Service 509fd4
	children [ntCatchAll + 1]nodes
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// endpoints is a mapping of http method constants to handlers
Packit Service 509fd4
// for a given route.
Packit Service 509fd4
type endpoints map[methodTyp]*endpoint
Packit Service 509fd4
Packit Service 509fd4
type endpoint struct {
Packit Service 509fd4
	// endpoint handler
Packit Service 509fd4
	handler http.Handler
Packit Service 509fd4
Packit Service 509fd4
	// pattern is the routing pattern for handler nodes
Packit Service 509fd4
	pattern string
Packit Service 509fd4
Packit Service 509fd4
	// parameter keys recorded on handler nodes
Packit Service 509fd4
	paramKeys []string
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (s endpoints) Value(method methodTyp) *endpoint {
Packit Service 509fd4
	mh, ok := s[method]
Packit Service 509fd4
	if !ok {
Packit Service 509fd4
		mh = &endpoint{}
Packit Service 509fd4
		s[method] = mh
Packit Service 509fd4
	}
Packit Service 509fd4
	return mh
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node {
Packit Service 509fd4
	var parent *node
Packit Service 509fd4
	search := pattern
Packit Service 509fd4
Packit Service 509fd4
	for {
Packit Service 509fd4
		// Handle key exhaustion
Packit Service 509fd4
		if len(search) == 0 {
Packit Service 509fd4
			// Insert or update the node's leaf handler
Packit Service 509fd4
			n.setEndpoint(method, handler, pattern)
Packit Service 509fd4
			return n
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// We're going to be searching for a wild node next,
Packit Service 509fd4
		// in this case, we need to get the tail
Packit Service 509fd4
		var label = search[0]
Packit Service 509fd4
		var segTail byte
Packit Service 509fd4
		var segEndIdx int
Packit Service 509fd4
		var segTyp nodeTyp
Packit Service 509fd4
		var segRexpat string
Packit Service 509fd4
		if label == '{' || label == '*' {
Packit Service 509fd4
			segTyp, _, segRexpat, segTail, _, segEndIdx = patNextSegment(search)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var prefix string
Packit Service 509fd4
		if segTyp == ntRegexp {
Packit Service 509fd4
			prefix = segRexpat
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Look for the edge to attach to
Packit Service 509fd4
		parent = n
Packit Service 509fd4
		n = n.getEdge(segTyp, label, segTail, prefix)
Packit Service 509fd4
Packit Service 509fd4
		// No edge, create one
Packit Service 509fd4
		if n == nil {
Packit Service 509fd4
			child := &node{label: label, tail: segTail, prefix: search}
Packit Service 509fd4
			hn := parent.addChild(child, search)
Packit Service 509fd4
			hn.setEndpoint(method, handler, pattern)
Packit Service 509fd4
Packit Service 509fd4
			return hn
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Found an edge to match the pattern
Packit Service 509fd4
Packit Service 509fd4
		if n.typ > ntStatic {
Packit Service 509fd4
			// We found a param node, trim the param from the search path and continue.
Packit Service 509fd4
			// This param/wild pattern segment would already be on the tree from a previous
Packit Service 509fd4
			// call to addChild when creating a new node.
Packit Service 509fd4
			search = search[segEndIdx:]
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Static nodes fall below here.
Packit Service 509fd4
		// Determine longest prefix of the search key on match.
Packit Service 509fd4
		commonPrefix := longestPrefix(search, n.prefix)
Packit Service 509fd4
		if commonPrefix == len(n.prefix) {
Packit Service 509fd4
			// the common prefix is as long as the current node's prefix we're attempting to insert.
Packit Service 509fd4
			// keep the search going.
Packit Service 509fd4
			search = search[commonPrefix:]
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Split the node
Packit Service 509fd4
		child := &node{
Packit Service 509fd4
			typ:    ntStatic,
Packit Service 509fd4
			prefix: search[:commonPrefix],
Packit Service 509fd4
		}
Packit Service 509fd4
		parent.replaceChild(search[0], segTail, child)
Packit Service 509fd4
Packit Service 509fd4
		// Restore the existing node
Packit Service 509fd4
		n.label = n.prefix[commonPrefix]
Packit Service 509fd4
		n.prefix = n.prefix[commonPrefix:]
Packit Service 509fd4
		child.addChild(n, n.prefix)
Packit Service 509fd4
Packit Service 509fd4
		// If the new key is a subset, set the method/handler on this node and finish.
Packit Service 509fd4
		search = search[commonPrefix:]
Packit Service 509fd4
		if len(search) == 0 {
Packit Service 509fd4
			child.setEndpoint(method, handler, pattern)
Packit Service 509fd4
			return child
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Create a new edge for the node
Packit Service 509fd4
		subchild := &node{
Packit Service 509fd4
			typ:    ntStatic,
Packit Service 509fd4
			label:  search[0],
Packit Service 509fd4
			prefix: search,
Packit Service 509fd4
		}
Packit Service 509fd4
		hn := child.addChild(subchild, search)
Packit Service 509fd4
		hn.setEndpoint(method, handler, pattern)
Packit Service 509fd4
		return hn
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// addChild appends the new `child` node to the tree using the `pattern` as the trie key.
Packit Service 509fd4
// For a URL router like chi's, we split the static, param, regexp and wildcard segments
Packit Service 509fd4
// into different nodes. In addition, addChild will recursively call itself until every
Packit Service 509fd4
// pattern segment is added to the url pattern tree as individual nodes, depending on type.
Packit Service 509fd4
func (n *node) addChild(child *node, prefix string) *node {
Packit Service 509fd4
	search := prefix
Packit Service 509fd4
Packit Service 509fd4
	// handler leaf node added to the tree is the child.
Packit Service 509fd4
	// this may be overridden later down the flow
Packit Service 509fd4
	hn := child
Packit Service 509fd4
Packit Service 509fd4
	// Parse next segment
Packit Service 509fd4
	segTyp, _, segRexpat, segTail, segStartIdx, segEndIdx := patNextSegment(search)
Packit Service 509fd4
Packit Service 509fd4
	// Add child depending on next up segment
Packit Service 509fd4
	switch segTyp {
Packit Service 509fd4
Packit Service 509fd4
	case ntStatic:
Packit Service 509fd4
		// Search prefix is all static (that is, has no params in path)
Packit Service 509fd4
		// noop
Packit Service 509fd4
Packit Service 509fd4
	default:
Packit Service 509fd4
		// Search prefix contains a param, regexp or wildcard
Packit Service 509fd4
Packit Service 509fd4
		if segTyp == ntRegexp {
Packit Service 509fd4
			rex, err := regexp.Compile(segRexpat)
Packit Service 509fd4
			if err != nil {
Packit Service 509fd4
				panic(fmt.Sprintf("chi: invalid regexp pattern '%s' in route param", segRexpat))
Packit Service 509fd4
			}
Packit Service 509fd4
			child.prefix = segRexpat
Packit Service 509fd4
			child.rex = rex
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		if segStartIdx == 0 {
Packit Service 509fd4
			// Route starts with a param
Packit Service 509fd4
			child.typ = segTyp
Packit Service 509fd4
Packit Service 509fd4
			if segTyp == ntCatchAll {
Packit Service 509fd4
				segStartIdx = -1
Packit Service 509fd4
			} else {
Packit Service 509fd4
				segStartIdx = segEndIdx
Packit Service 509fd4
			}
Packit Service 509fd4
			if segStartIdx < 0 {
Packit Service 509fd4
				segStartIdx = len(search)
Packit Service 509fd4
			}
Packit Service 509fd4
			child.tail = segTail // for params, we set the tail
Packit Service 509fd4
Packit Service 509fd4
			if segStartIdx != len(search) {
Packit Service 509fd4
				// add static edge for the remaining part, split the end.
Packit Service 509fd4
				// its not possible to have adjacent param nodes, so its certainly
Packit Service 509fd4
				// going to be a static node next.
Packit Service 509fd4
Packit Service 509fd4
				search = search[segStartIdx:] // advance search position
Packit Service 509fd4
Packit Service 509fd4
				nn := &node{
Packit Service 509fd4
					typ:    ntStatic,
Packit Service 509fd4
					label:  search[0],
Packit Service 509fd4
					prefix: search,
Packit Service 509fd4
				}
Packit Service 509fd4
				hn = child.addChild(nn, search)
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
		} else if segStartIdx > 0 {
Packit Service 509fd4
			// Route has some param
Packit Service 509fd4
Packit Service 509fd4
			// starts with a static segment
Packit Service 509fd4
			child.typ = ntStatic
Packit Service 509fd4
			child.prefix = search[:segStartIdx]
Packit Service 509fd4
			child.rex = nil
Packit Service 509fd4
Packit Service 509fd4
			// add the param edge node
Packit Service 509fd4
			search = search[segStartIdx:]
Packit Service 509fd4
Packit Service 509fd4
			nn := &node{
Packit Service 509fd4
				typ:   segTyp,
Packit Service 509fd4
				label: search[0],
Packit Service 509fd4
				tail:  segTail,
Packit Service 509fd4
			}
Packit Service 509fd4
			hn = child.addChild(nn, search)
Packit Service 509fd4
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	n.children[child.typ] = append(n.children[child.typ], child)
Packit Service 509fd4
	n.children[child.typ].Sort()
Packit Service 509fd4
	return hn
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) replaceChild(label, tail byte, child *node) {
Packit Service 509fd4
	for i := 0; i < len(n.children[child.typ]); i++ {
Packit Service 509fd4
		if n.children[child.typ][i].label == label && n.children[child.typ][i].tail == tail {
Packit Service 509fd4
			n.children[child.typ][i] = child
Packit Service 509fd4
			n.children[child.typ][i].label = label
Packit Service 509fd4
			n.children[child.typ][i].tail = tail
Packit Service 509fd4
			return
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	panic("chi: replacing missing child")
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node {
Packit Service 509fd4
	nds := n.children[ntyp]
Packit Service 509fd4
	for i := 0; i < len(nds); i++ {
Packit Service 509fd4
		if nds[i].label == label && nds[i].tail == tail {
Packit Service 509fd4
			if ntyp == ntRegexp && nds[i].prefix != prefix {
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
			return nds[i]
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) {
Packit Service 509fd4
	// Set the handler for the method type on the node
Packit Service 509fd4
	if n.endpoints == nil {
Packit Service 509fd4
		n.endpoints = make(endpoints, 0)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	paramKeys := patParamKeys(pattern)
Packit Service 509fd4
Packit Service 509fd4
	if method&mSTUB == mSTUB {
Packit Service 509fd4
		n.endpoints.Value(mSTUB).handler = handler
Packit Service 509fd4
	}
Packit Service 509fd4
	if method&mALL == mALL {
Packit Service 509fd4
		h := n.endpoints.Value(mALL)
Packit Service 509fd4
		h.handler = handler
Packit Service 509fd4
		h.pattern = pattern
Packit Service 509fd4
		h.paramKeys = paramKeys
Packit Service 509fd4
		for _, m := range methodMap {
Packit Service 509fd4
			h := n.endpoints.Value(m)
Packit Service 509fd4
			h.handler = handler
Packit Service 509fd4
			h.pattern = pattern
Packit Service 509fd4
			h.paramKeys = paramKeys
Packit Service 509fd4
		}
Packit Service 509fd4
	} else {
Packit Service 509fd4
		h := n.endpoints.Value(method)
Packit Service 509fd4
		h.handler = handler
Packit Service 509fd4
		h.pattern = pattern
Packit Service 509fd4
		h.paramKeys = paramKeys
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) FindRoute(rctx *Context, method methodTyp, path string) (*node, endpoints, http.Handler) {
Packit Service 509fd4
	// Reset the context routing pattern and params
Packit Service 509fd4
	rctx.routePattern = ""
Packit Service 509fd4
	rctx.routeParams.Keys = rctx.routeParams.Keys[:0]
Packit Service 509fd4
	rctx.routeParams.Values = rctx.routeParams.Values[:0]
Packit Service 509fd4
Packit Service 509fd4
	// Find the routing handlers for the path
Packit Service 509fd4
	rn := n.findRoute(rctx, method, path)
Packit Service 509fd4
	if rn == nil {
Packit Service 509fd4
		return nil, nil, nil
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// Record the routing params in the request lifecycle
Packit Service 509fd4
	rctx.URLParams.Keys = append(rctx.URLParams.Keys, rctx.routeParams.Keys...)
Packit Service 509fd4
	rctx.URLParams.Values = append(rctx.URLParams.Values, rctx.routeParams.Values...)
Packit Service 509fd4
Packit Service 509fd4
	// Record the routing pattern in the request lifecycle
Packit Service 509fd4
	if rn.endpoints[method].pattern != "" {
Packit Service 509fd4
		rctx.routePattern = rn.endpoints[method].pattern
Packit Service 509fd4
		rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.routePattern)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return rn, rn.endpoints, rn.endpoints[method].handler
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// Recursive edge traversal by checking all nodeTyp groups along the way.
Packit Service 509fd4
// It's like searching through a multi-dimensional radix trie.
Packit Service 509fd4
func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node {
Packit Service 509fd4
	nn := n
Packit Service 509fd4
	search := path
Packit Service 509fd4
Packit Service 509fd4
	for t, nds := range nn.children {
Packit Service 509fd4
		ntyp := nodeTyp(t)
Packit Service 509fd4
		if len(nds) == 0 {
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var xn *node
Packit Service 509fd4
		xsearch := search
Packit Service 509fd4
Packit Service 509fd4
		var label byte
Packit Service 509fd4
		if search != "" {
Packit Service 509fd4
			label = search[0]
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		switch ntyp {
Packit Service 509fd4
		case ntStatic:
Packit Service 509fd4
			xn = nds.findEdge(label)
Packit Service 509fd4
			if xn == nil || !strings.HasPrefix(xsearch, xn.prefix) {
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
			xsearch = xsearch[len(xn.prefix):]
Packit Service 509fd4
Packit Service 509fd4
		case ntParam, ntRegexp:
Packit Service 509fd4
			// short-circuit and return no matching route for empty param values
Packit Service 509fd4
			if xsearch == "" {
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			// serially loop through each node grouped by the tail delimiter
Packit Service 509fd4
			for idx := 0; idx < len(nds); idx++ {
Packit Service 509fd4
				xn = nds[idx]
Packit Service 509fd4
Packit Service 509fd4
				// label for param nodes is the delimiter byte
Packit Service 509fd4
				p := strings.IndexByte(xsearch, xn.tail)
Packit Service 509fd4
Packit Service 509fd4
				if p < 0 {
Packit Service 509fd4
					if xn.tail == '/' {
Packit Service 509fd4
						p = len(xsearch)
Packit Service 509fd4
					} else {
Packit Service 509fd4
						continue
Packit Service 509fd4
					}
Packit Service 509fd4
				}
Packit Service 509fd4
Packit Service 509fd4
				if ntyp == ntRegexp && xn.rex != nil {
Packit Service 509fd4
					if xn.rex.Match([]byte(xsearch[:p])) == false {
Packit Service 509fd4
						continue
Packit Service 509fd4
					}
Packit Service 509fd4
				} else if strings.IndexByte(xsearch[:p], '/') != -1 {
Packit Service 509fd4
					// avoid a match across path segments
Packit Service 509fd4
					continue
Packit Service 509fd4
				}
Packit Service 509fd4
Packit Service 509fd4
				rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p])
Packit Service 509fd4
				xsearch = xsearch[p:]
Packit Service 509fd4
				break
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
		default:
Packit Service 509fd4
			// catch-all nodes
Packit Service 509fd4
			rctx.routeParams.Values = append(rctx.routeParams.Values, search)
Packit Service 509fd4
			xn = nds[0]
Packit Service 509fd4
			xsearch = ""
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		if xn == nil {
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// did we find it yet?
Packit Service 509fd4
		if len(xsearch) == 0 {
Packit Service 509fd4
			if xn.isLeaf() {
Packit Service 509fd4
				h, _ := xn.endpoints[method]
Packit Service 509fd4
				if h != nil && h.handler != nil {
Packit Service 509fd4
					rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
Packit Service 509fd4
					return xn
Packit Service 509fd4
				}
Packit Service 509fd4
Packit Service 509fd4
				// flag that the routing context found a route, but not a corresponding
Packit Service 509fd4
				// supported method
Packit Service 509fd4
				rctx.methodNotAllowed = true
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// recursively find the next node..
Packit Service 509fd4
		fin := xn.findRoute(rctx, method, xsearch)
Packit Service 509fd4
		if fin != nil {
Packit Service 509fd4
			return fin
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Did not find final handler, let's remove the param here if it was set
Packit Service 509fd4
		if xn.typ > ntStatic {
Packit Service 509fd4
			if len(rctx.routeParams.Values) > 0 {
Packit Service 509fd4
				rctx.routeParams.Values = rctx.routeParams.Values[:len(rctx.routeParams.Values)-1]
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) findEdge(ntyp nodeTyp, label byte) *node {
Packit Service 509fd4
	nds := n.children[ntyp]
Packit Service 509fd4
	num := len(nds)
Packit Service 509fd4
	idx := 0
Packit Service 509fd4
Packit Service 509fd4
	switch ntyp {
Packit Service 509fd4
	case ntStatic, ntParam, ntRegexp:
Packit Service 509fd4
		i, j := 0, num-1
Packit Service 509fd4
		for i <= j {
Packit Service 509fd4
			idx = i + (j-i)/2
Packit Service 509fd4
			if label > nds[idx].label {
Packit Service 509fd4
				i = idx + 1
Packit Service 509fd4
			} else if label < nds[idx].label {
Packit Service 509fd4
				j = idx - 1
Packit Service 509fd4
			} else {
Packit Service 509fd4
				i = num // breaks cond
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
		if nds[idx].label != label {
Packit Service 509fd4
			return nil
Packit Service 509fd4
		}
Packit Service 509fd4
		return nds[idx]
Packit Service 509fd4
Packit Service 509fd4
	default: // catch all
Packit Service 509fd4
		return nds[idx]
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) isEmpty() bool {
Packit Service 509fd4
	for _, nds := range n.children {
Packit Service 509fd4
		if len(nds) > 0 {
Packit Service 509fd4
			return false
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return true
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) isLeaf() bool {
Packit Service 509fd4
	return n.endpoints != nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) findPattern(pattern string) bool {
Packit Service 509fd4
	nn := n
Packit Service 509fd4
	for _, nds := range nn.children {
Packit Service 509fd4
		if len(nds) == 0 {
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		n = nn.findEdge(nds[0].typ, pattern[0])
Packit Service 509fd4
		if n == nil {
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var idx int
Packit Service 509fd4
		var xpattern string
Packit Service 509fd4
Packit Service 509fd4
		switch n.typ {
Packit Service 509fd4
		case ntStatic:
Packit Service 509fd4
			idx = longestPrefix(pattern, n.prefix)
Packit Service 509fd4
			if idx < len(n.prefix) {
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
		case ntParam, ntRegexp:
Packit Service 509fd4
			idx = strings.IndexByte(pattern, '}') + 1
Packit Service 509fd4
Packit Service 509fd4
		case ntCatchAll:
Packit Service 509fd4
			idx = longestPrefix(pattern, "*")
Packit Service 509fd4
Packit Service 509fd4
		default:
Packit Service 509fd4
			panic("chi: unknown node type")
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		xpattern = pattern[idx:]
Packit Service 509fd4
		if len(xpattern) == 0 {
Packit Service 509fd4
			return true
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		return n.findPattern(xpattern)
Packit Service 509fd4
	}
Packit Service 509fd4
	return false
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) routes() []Route {
Packit Service 509fd4
	rts := []Route{}
Packit Service 509fd4
Packit Service 509fd4
	n.walk(func(eps endpoints, subroutes Routes) bool {
Packit Service 509fd4
		if eps[mSTUB] != nil && eps[mSTUB].handler != nil && subroutes == nil {
Packit Service 509fd4
			return false
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		// Group methodHandlers by unique patterns
Packit Service 509fd4
		pats := make(map[string]endpoints, 0)
Packit Service 509fd4
Packit Service 509fd4
		for mt, h := range eps {
Packit Service 509fd4
			if h.pattern == "" {
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
			p, ok := pats[h.pattern]
Packit Service 509fd4
			if !ok {
Packit Service 509fd4
				p = endpoints{}
Packit Service 509fd4
				pats[h.pattern] = p
Packit Service 509fd4
			}
Packit Service 509fd4
			p[mt] = h
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		for p, mh := range pats {
Packit Service 509fd4
			hs := make(map[string]http.Handler, 0)
Packit Service 509fd4
			if mh[mALL] != nil && mh[mALL].handler != nil {
Packit Service 509fd4
				hs["*"] = mh[mALL].handler
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			for mt, h := range mh {
Packit Service 509fd4
				if h.handler == nil {
Packit Service 509fd4
					continue
Packit Service 509fd4
				}
Packit Service 509fd4
				m := methodTypString(mt)
Packit Service 509fd4
				if m == "" {
Packit Service 509fd4
					continue
Packit Service 509fd4
				}
Packit Service 509fd4
				hs[m] = h.handler
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			rt := Route{p, hs, subroutes}
Packit Service 509fd4
			rts = append(rts, rt)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		return false
Packit Service 509fd4
	})
Packit Service 509fd4
Packit Service 509fd4
	return rts
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (n *node) walk(fn func(eps endpoints, subroutes Routes) bool) bool {
Packit Service 509fd4
	// Visit the leaf values if any
Packit Service 509fd4
	if (n.endpoints != nil || n.subroutes != nil) && fn(n.endpoints, n.subroutes) {
Packit Service 509fd4
		return true
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// Recurse on the children
Packit Service 509fd4
	for _, ns := range n.children {
Packit Service 509fd4
		for _, cn := range ns {
Packit Service 509fd4
			if cn.walk(fn) {
Packit Service 509fd4
				return true
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return false
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// patNextSegment returns the next segment details from a pattern:
Packit Service 509fd4
// node type, param key, regexp string, param tail byte, param starting index, param ending index
Packit Service 509fd4
func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) {
Packit Service 509fd4
	ps := strings.Index(pattern, "{")
Packit Service 509fd4
	ws := strings.Index(pattern, "*")
Packit Service 509fd4
Packit Service 509fd4
	if ps < 0 && ws < 0 {
Packit Service 509fd4
		return ntStatic, "", "", 0, 0, len(pattern) // we return the entire thing
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// Sanity check
Packit Service 509fd4
	if ps >= 0 && ws >= 0 && ws < ps {
Packit Service 509fd4
		panic("chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'")
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	var tail byte = '/' // Default endpoint tail to / byte
Packit Service 509fd4
Packit Service 509fd4
	if ps >= 0 {
Packit Service 509fd4
		// Param/Regexp pattern is next
Packit Service 509fd4
		nt := ntParam
Packit Service 509fd4
Packit Service 509fd4
		// Read to closing } taking into account opens and closes in curl count (cc)
Packit Service 509fd4
		cc := 0
Packit Service 509fd4
		pe := ps
Packit Service 509fd4
		for i, c := range pattern[ps:] {
Packit Service 509fd4
			if c == '{' {
Packit Service 509fd4
				cc++
Packit Service 509fd4
			} else if c == '}' {
Packit Service 509fd4
				cc--
Packit Service 509fd4
				if cc == 0 {
Packit Service 509fd4
					pe = ps + i
Packit Service 509fd4
					break
Packit Service 509fd4
				}
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
		if pe == ps {
Packit Service 509fd4
			panic("chi: route param closing delimiter '}' is missing")
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		key := pattern[ps+1 : pe]
Packit Service 509fd4
		pe++ // set end to next position
Packit Service 509fd4
Packit Service 509fd4
		if pe < len(pattern) {
Packit Service 509fd4
			tail = pattern[pe]
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var rexpat string
Packit Service 509fd4
		if idx := strings.Index(key, ":"); idx >= 0 {
Packit Service 509fd4
			nt = ntRegexp
Packit Service 509fd4
			rexpat = key[idx+1:]
Packit Service 509fd4
			key = key[:idx]
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		if len(rexpat) > 0 {
Packit Service 509fd4
			if rexpat[0] != '^' {
Packit Service 509fd4
				rexpat = "^" + rexpat
Packit Service 509fd4
			}
Packit Service 509fd4
			if rexpat[len(rexpat)-1] != '$' {
Packit Service 509fd4
				rexpat = rexpat + "$"
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		return nt, key, rexpat, tail, ps, pe
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// Wildcard pattern as finale
Packit Service 509fd4
	if ws < len(pattern)-1 {
Packit Service 509fd4
		panic("chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead")
Packit Service 509fd4
	}
Packit Service 509fd4
	return ntCatchAll, "*", "", 0, ws, len(pattern)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func patParamKeys(pattern string) []string {
Packit Service 509fd4
	pat := pattern
Packit Service 509fd4
	paramKeys := []string{}
Packit Service 509fd4
	for {
Packit Service 509fd4
		ptyp, paramKey, _, _, _, e := patNextSegment(pat)
Packit Service 509fd4
		if ptyp == ntStatic {
Packit Service 509fd4
			return paramKeys
Packit Service 509fd4
		}
Packit Service 509fd4
		for i := 0; i < len(paramKeys); i++ {
Packit Service 509fd4
			if paramKeys[i] == paramKey {
Packit Service 509fd4
				panic(fmt.Sprintf("chi: routing pattern '%s' contains duplicate param key, '%s'", pattern, paramKey))
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
		paramKeys = append(paramKeys, paramKey)
Packit Service 509fd4
		pat = pat[e:]
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// longestPrefix finds the length of the shared prefix
Packit Service 509fd4
// of two strings
Packit Service 509fd4
func longestPrefix(k1, k2 string) int {
Packit Service 509fd4
	max := len(k1)
Packit Service 509fd4
	if l := len(k2); l < max {
Packit Service 509fd4
		max = l
Packit Service 509fd4
	}
Packit Service 509fd4
	var i int
Packit Service 509fd4
	for i = 0; i < max; i++ {
Packit Service 509fd4
		if k1[i] != k2[i] {
Packit Service 509fd4
			break
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return i
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func methodTypString(method methodTyp) string {
Packit Service 509fd4
	for s, t := range methodMap {
Packit Service 509fd4
		if method == t {
Packit Service 509fd4
			return s
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return ""
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
type nodes []*node
Packit Service 509fd4
Packit Service 509fd4
// Sort the list of nodes by label
Packit Service 509fd4
func (ns nodes) Sort()              { sort.Sort(ns); ns.tailSort() }
Packit Service 509fd4
func (ns nodes) Len() int           { return len(ns) }
Packit Service 509fd4
func (ns nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
Packit Service 509fd4
func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label }
Packit Service 509fd4
Packit Service 509fd4
// tailSort pushes nodes with '/' as the tail to the end of the list for param nodes.
Packit Service 509fd4
// The list order determines the traversal order.
Packit Service 509fd4
func (ns nodes) tailSort() {
Packit Service 509fd4
	for i := len(ns) - 1; i >= 0; i-- {
Packit Service 509fd4
		if ns[i].typ > ntStatic && ns[i].tail == '/' {
Packit Service 509fd4
			ns.Swap(i, len(ns)-1)
Packit Service 509fd4
			return
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func (ns nodes) findEdge(label byte) *node {
Packit Service 509fd4
	num := len(ns)
Packit Service 509fd4
	idx := 0
Packit Service 509fd4
	i, j := 0, num-1
Packit Service 509fd4
	for i <= j {
Packit Service 509fd4
		idx = i + (j-i)/2
Packit Service 509fd4
		if label > ns[idx].label {
Packit Service 509fd4
			i = idx + 1
Packit Service 509fd4
		} else if label < ns[idx].label {
Packit Service 509fd4
			j = idx - 1
Packit Service 509fd4
		} else {
Packit Service 509fd4
			i = num // breaks cond
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	if ns[idx].label != label {
Packit Service 509fd4
		return nil
Packit Service 509fd4
	}
Packit Service 509fd4
	return ns[idx]
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// Route describes the details of a routing handler.
Packit Service 509fd4
type Route struct {
Packit Service 509fd4
	Pattern   string
Packit Service 509fd4
	Handlers  map[string]http.Handler
Packit Service 509fd4
	SubRoutes Routes
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WalkFunc is the type of the function called for each method and route visited by Walk.
Packit Service 509fd4
type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error
Packit Service 509fd4
Packit Service 509fd4
// Walk walks any router tree that implements Routes interface.
Packit Service 509fd4
func Walk(r Routes, walkFn WalkFunc) error {
Packit Service 509fd4
	return walk(r, walkFn, "")
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(http.Handler) http.Handler) error {
Packit Service 509fd4
	for _, route := range r.Routes() {
Packit Service 509fd4
		mws := make([]func(http.Handler) http.Handler, len(parentMw))
Packit Service 509fd4
		copy(mws, parentMw)
Packit Service 509fd4
		mws = append(mws, r.Middlewares()...)
Packit Service 509fd4
Packit Service 509fd4
		if route.SubRoutes != nil {
Packit Service 509fd4
			if err := walk(route.SubRoutes, walkFn, parentRoute+route.Pattern, mws...); err != nil {
Packit Service 509fd4
				return err
Packit Service 509fd4
			}
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		for method, handler := range route.Handlers {
Packit Service 509fd4
			if method == "*" {
Packit Service 509fd4
				// Ignore a "catchAll" method, since we pass down all the specific methods for each route.
Packit Service 509fd4
				continue
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			fullRoute := parentRoute + route.Pattern
Packit Service 509fd4
Packit Service 509fd4
			if chain, ok := handler.(*ChainHandler); ok {
Packit Service 509fd4
				if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil {
Packit Service 509fd4
					return err
Packit Service 509fd4
				}
Packit Service 509fd4
			} else {
Packit Service 509fd4
				if err := walkFn(method, fullRoute, handler, mws...); err != nil {
Packit Service 509fd4
					return err
Packit Service 509fd4
				}
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return nil
Packit Service 509fd4
}