| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 | package echotype (	// Router is the registry of all registered routes for an `Echo` instance for	// request matching and URL path parameter parsing.	Router struct {		tree   *node		routes map[string]*Route		echo   *Echo	}	node struct {		kind          kind		label         byte		prefix        string		parent        *node		children      children		ppath         string		pnames        []string		methodHandler *methodHandler	}	kind          uint8	children      []*node	methodHandler struct {		connect  HandlerFunc		delete   HandlerFunc		get      HandlerFunc		head     HandlerFunc		options  HandlerFunc		patch    HandlerFunc		post     HandlerFunc		propfind HandlerFunc		put      HandlerFunc		trace    HandlerFunc	})const (	skind kind = iota	pkind	akind)// NewRouter returns a new Router instance.func NewRouter(e *Echo) *Router {	return &Router{		tree: &node{			methodHandler: new(methodHandler),		},		routes: map[string]*Route{},		echo:   e,	}}// Add registers a new route for method and path with matching handler.func (r *Router) Add(method, path string, h HandlerFunc) {	// Validate path	if path == "" {		panic("echo: path cannot be empty")	}	if path[0] != '/' {		path = "/" + path	}	pnames := []string{} // Param names	ppath := path        // Pristine path	for i, l := 0, len(path); i < l; i++ {		if path[i] == ':' {			j := i + 1			r.insert(method, path[:i], nil, skind, "", nil)			for ; i < l && path[i] != '/'; i++ {			}			pnames = append(pnames, path[j:i])			path = path[:j] + path[i:]			i, l = j, len(path)			if i == l {				r.insert(method, path[:i], h, pkind, ppath, pnames)				return			}			r.insert(method, path[:i], nil, pkind, ppath, pnames)		} else if path[i] == '*' {			r.insert(method, path[:i], nil, skind, "", nil)			pnames = append(pnames, "*")			r.insert(method, path[:i+1], h, akind, ppath, pnames)			return		}	}	r.insert(method, path, h, skind, ppath, pnames)}func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {	// Adjust max param	l := len(pnames)	if *r.echo.maxParam < l {		*r.echo.maxParam = l	}	cn := r.tree // Current node as root	if cn == nil {		panic("echo: invalid method")	}	search := path	for {		sl := len(search)		pl := len(cn.prefix)		l := 0		// LCP		max := pl		if sl < max {			max = sl		}		for ; l < max && search[l] == cn.prefix[l]; l++ {		}		if l == 0 {			// At root node			cn.label = search[0]			cn.prefix = search			if h != nil {				cn.kind = t				cn.addHandler(method, h)				cn.ppath = ppath				cn.pnames = pnames			}		} else if l < pl {			// Split node			n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)			// Reset parent node			cn.kind = skind			cn.label = cn.prefix[0]			cn.prefix = cn.prefix[:l]			cn.children = nil			cn.methodHandler = new(methodHandler)			cn.ppath = ""			cn.pnames = nil			cn.addChild(n)			if l == sl {				// At parent node				cn.kind = t				cn.addHandler(method, h)				cn.ppath = ppath				cn.pnames = pnames			} else {				// Create child node				n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)				n.addHandler(method, h)				cn.addChild(n)			}		} else if l < sl {			search = search[l:]			c := cn.findChildWithLabel(search[0])			if c != nil {				// Go deeper				cn = c				continue			}			// Create child node			n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)			n.addHandler(method, h)			cn.addChild(n)		} else {			// Node already exists			if h != nil {				cn.addHandler(method, h)				cn.ppath = ppath				if len(cn.pnames) == 0 { // Issue #729					cn.pnames = pnames				}			}		}		return	}}func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {	return &node{		kind:          t,		label:         pre[0],		prefix:        pre,		parent:        p,		children:      c,		ppath:         ppath,		pnames:        pnames,		methodHandler: mh,	}}func (n *node) addChild(c *node) {	n.children = append(n.children, c)}func (n *node) findChild(l byte, t kind) *node {	for _, c := range n.children {		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 {			return c		}	}	return nil}func (n *node) findChildByKind(t kind) *node {	for _, c := range n.children {		if c.kind == t {			return c		}	}	return nil}func (n *node) addHandler(method string, h HandlerFunc) {	switch method {	case CONNECT:		n.methodHandler.connect = h	case DELETE:		n.methodHandler.delete = h	case GET:		n.methodHandler.get = h	case HEAD:		n.methodHandler.head = h	case OPTIONS:		n.methodHandler.options = h	case PATCH:		n.methodHandler.patch = h	case POST:		n.methodHandler.post = h	case PROPFIND:		n.methodHandler.propfind = h	case PUT:		n.methodHandler.put = h	case TRACE:		n.methodHandler.trace = h	}}func (n *node) findHandler(method string) HandlerFunc {	switch method {	case CONNECT:		return n.methodHandler.connect	case DELETE:		return n.methodHandler.delete	case GET:		return n.methodHandler.get	case HEAD:		return n.methodHandler.head	case OPTIONS:		return n.methodHandler.options	case PATCH:		return n.methodHandler.patch	case POST:		return n.methodHandler.post	case PROPFIND:		return n.methodHandler.propfind	case PUT:		return n.methodHandler.put	case TRACE:		return n.methodHandler.trace	default:		return nil	}}func (n *node) checkMethodNotAllowed() HandlerFunc {	for _, m := range methods {		if h := n.findHandler(m); h != nil {			return MethodNotAllowedHandler		}	}	return NotFoundHandler}// Find lookup a handler registered for method and path. It also parses URL for path// parameters and load them into context.//// For performance://// - Get context from `Echo#AcquireContext()`// - Reset it `Context#Reset()`// - Return it `Echo#ReleaseContext()`.func (r *Router) Find(method, path string, c Context) {	ctx := c.(*context)	ctx.path = path	cn := r.tree // Current node as root	var (		search  = path		child   *node         // Child node		n       int           // Param counter		nk      kind          // Next kind		nn      *node         // Next node		ns      string        // Next search		pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice	)	// Search order static > param > any	for {		if search == "" {			goto End		}		pl := 0 // Prefix length		l := 0  // LCP length		if cn.label != ':' {			sl := len(search)			pl = len(cn.prefix)			// LCP			max := pl			if sl < max {				max = sl			}			for ; l < max && search[l] == cn.prefix[l]; l++ {			}		}		if l == pl {			// Continue search			search = search[l:]		} else {			cn = nn			search = ns			if nk == pkind {				goto Param			} else if nk == akind {				goto Any			}			// Not found			return		}		if search == "" {			goto End		}		// Static node		if child = cn.findChild(search[0], skind); child != nil {			// Save next			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623				nk = pkind				nn = cn				ns = search			}			cn = child			continue		}		// Param node	Param:		if child = cn.findChildByKind(pkind); child != nil {			// Issue #378			if len(pvalues) == n {				continue			}			// Save next			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623				nk = akind				nn = cn				ns = search			}			cn = child			i, l := 0, len(search)			for ; i < l && search[i] != '/'; i++ {			}			pvalues[n] = search[:i]			n++			search = search[i:]			continue		}		// Any node	Any:		if cn = cn.findChildByKind(akind); cn == nil {			if nn != nil {				cn = nn				nn = cn.parent // Next (Issue #954)				search = ns				if nk == pkind {					goto Param				} else if nk == akind {					goto Any				}			}			// Not found			return		}		pvalues[len(cn.pnames)-1] = search		goto End	}End:	ctx.handler = cn.findHandler(method)	ctx.path = cn.ppath	ctx.pnames = cn.pnames	// NOTE: Slow zone...	if ctx.handler == nil {		ctx.handler = cn.checkMethodNotAllowed()		// Dig further for any, might have an empty value for *, e.g.		// serving a directory. Issue #207.		if cn = cn.findChildByKind(akind); cn == nil {			return		}		if h := cn.findHandler(method); h != nil {			ctx.handler = h		} else {			ctx.handler = cn.checkMethodNotAllowed()		}		ctx.path = cn.ppath		ctx.pnames = cn.pnames		pvalues[len(cn.pnames)-1] = ""	}	return}
 |