6 // Router is the registry of all registered routes for an `Echo` instance for
7 // request matching and URL path parameter parsing.
10 routes map[string]*Route
21 methodHandler *methodHandler
25 methodHandler struct {
45 // NewRouter returns a new Router instance.
46 func NewRouter(e *Echo) *Router {
49 methodHandler: new(methodHandler),
51 routes: map[string]*Route{},
56 // Add registers a new route for method and path with matching handler.
57 func (r *Router) Add(method, path string, h HandlerFunc) {
60 panic("echo: path cannot be empty")
65 pnames := []string{} // Param names
66 ppath := path // Pristine path
68 for i, l := 0, len(path); i < l; i++ {
72 r.insert(method, path[:i], nil, skind, "", nil)
73 for ; i < l && path[i] != '/'; i++ {
76 pnames = append(pnames, path[j:i])
77 path = path[:j] + path[i:]
81 r.insert(method, path[:i], h, pkind, ppath, pnames)
84 r.insert(method, path[:i], nil, pkind, "", nil)
85 } else if path[i] == '*' {
86 r.insert(method, path[:i], nil, skind, "", nil)
87 pnames = append(pnames, "*")
88 r.insert(method, path[:i+1], h, akind, ppath, pnames)
93 r.insert(method, path, h, skind, ppath, pnames)
96 func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
99 if *r.echo.maxParam < l {
103 cn := r.tree // Current node as root
105 panic("echo: invalid method")
119 for ; l < max && search[l] == cn.prefix[l]; l++ {
128 cn.addHandler(method, h)
134 n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
138 cn.label = cn.prefix[0]
139 cn.prefix = cn.prefix[:l]
141 cn.methodHandler = new(methodHandler)
150 cn.addHandler(method, h)
155 n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
156 n.addHandler(method, h)
161 c := cn.findChildWithLabel(search[0])
168 n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
169 n.addHandler(method, h)
172 // Node already exists
174 cn.addHandler(method, h)
176 if len(cn.pnames) == 0 { // Issue #729
185 func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
198 func (n *node) addChild(c *node) {
199 n.children = append(n.children, c)
202 func (n *node) findChild(l byte, t kind) *node {
203 for _, c := range n.children {
204 if c.label == l && c.kind == t {
211 func (n *node) findChildWithLabel(l byte) *node {
212 for _, c := range n.children {
220 func (n *node) findChildByKind(t kind) *node {
221 for _, c := range n.children {
229 func (n *node) addHandler(method string, h HandlerFunc) {
231 case http.MethodConnect:
232 n.methodHandler.connect = h
233 case http.MethodDelete:
234 n.methodHandler.delete = h
236 n.methodHandler.get = h
237 case http.MethodHead:
238 n.methodHandler.head = h
239 case http.MethodOptions:
240 n.methodHandler.options = h
241 case http.MethodPatch:
242 n.methodHandler.patch = h
243 case http.MethodPost:
244 n.methodHandler.post = h
246 n.methodHandler.propfind = h
248 n.methodHandler.put = h
249 case http.MethodTrace:
250 n.methodHandler.trace = h
254 func (n *node) findHandler(method string) HandlerFunc {
256 case http.MethodConnect:
257 return n.methodHandler.connect
258 case http.MethodDelete:
259 return n.methodHandler.delete
261 return n.methodHandler.get
262 case http.MethodHead:
263 return n.methodHandler.head
264 case http.MethodOptions:
265 return n.methodHandler.options
266 case http.MethodPatch:
267 return n.methodHandler.patch
268 case http.MethodPost:
269 return n.methodHandler.post
271 return n.methodHandler.propfind
273 return n.methodHandler.put
274 case http.MethodTrace:
275 return n.methodHandler.trace
281 func (n *node) checkMethodNotAllowed() HandlerFunc {
282 for _, m := range methods {
283 if h := n.findHandler(m); h != nil {
284 return MethodNotAllowedHandler
287 return NotFoundHandler
290 // Find lookup a handler registered for method and path. It also parses URL for path
291 // parameters and load them into context.
295 // - Get context from `Echo#AcquireContext()`
296 // - Reset it `Context#Reset()`
297 // - Return it `Echo#ReleaseContext()`.
298 func (r *Router) Find(method, path string, c Context) {
301 cn := r.tree // Current node as root
305 child *node // Child node
306 n int // Param counter
308 nn *node // Next node
309 ns string // Next search
310 pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
313 // Search order static > param > any
319 pl := 0 // Prefix length
331 for ; l < max && search[l] == cn.prefix[l]; l++ {
343 } else if nk == akind {
355 if child = cn.findChild(search[0], skind); child != nil {
357 if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
368 if child = cn.findChildByKind(pkind); child != nil {
370 if len(pvalues) == n {
375 if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
382 i, l := 0, len(search)
383 for ; i < l && search[i] != '/'; i++ {
385 pvalues[n] = search[:i]
393 if cn = cn.findChildByKind(akind); cn == nil {
396 nn = cn.parent // Next (Issue #954)
400 } else if nk == akind {
407 pvalues[len(cn.pnames)-1] = search
411 ctx.handler = cn.findHandler(method)
413 ctx.pnames = cn.pnames
415 // NOTE: Slow zone...
416 if ctx.handler == nil {
417 ctx.handler = cn.checkMethodNotAllowed()
419 // Dig further for any, might have an empty value for *, e.g.
420 // serving a directory. Issue #207.
421 if cn = cn.findChildByKind(akind); cn == nil {
424 if h := cn.findHandler(method); h != nil {
427 ctx.handler = cn.checkMethodNotAllowed()
430 ctx.pnames = cn.pnames
431 pvalues[len(cn.pnames)-1] = ""