2015-03-27 14:21:30 -07:00
package echo
2015-03-01 09:45:13 -08:00
2019-10-03 19:30:46 +02:00
import (
"net/http"
)
2018-10-14 17:16:58 +02:00
2015-03-01 09:45:13 -08:00
type (
2016-03-19 15:47:20 -07:00
// Router is the registry of all registered routes for an `Echo` instance for
// request matching and URL path parameter parsing.
2015-05-22 20:26:52 -07:00
Router struct {
2015-10-06 06:48:33 -07:00
tree * node
2017-05-16 16:29:33 -07:00
routes map [ string ] * Route
2015-10-06 06:48:33 -07:00
echo * Echo
2015-03-01 09:45:13 -08:00
}
node struct {
2020-11-21 02:48:16 +00:00
kind kind
label byte
prefix string
parent * node
staticChildrens children
ppath string
pnames [ ] string
methodHandler * methodHandler
paramChildren * node
anyChildren * node
2015-10-06 06:48:33 -07:00
}
2019-10-03 19:30:46 +02:00
kind uint8
children [ ] * node
2015-10-06 06:48:33 -07:00
methodHandler struct {
2018-03-13 12:38:42 -07:00
connect HandlerFunc
delete HandlerFunc
get HandlerFunc
head HandlerFunc
options HandlerFunc
patch HandlerFunc
post HandlerFunc
propfind HandlerFunc
put HandlerFunc
trace HandlerFunc
2019-06-09 18:11:18 +02:00
report HandlerFunc
2015-03-01 09:45:13 -08:00
}
)
2015-04-24 07:44:30 -07:00
const (
2015-10-07 15:10:40 -07:00
skind kind = iota
pkind
2016-03-03 22:06:47 -08:00
akind
2020-11-21 02:48:16 +00:00
paramLabel = byte ( ':' )
anyLabel = byte ( '*' )
2015-04-24 07:44:30 -07:00
)
2016-03-19 15:47:20 -07:00
// NewRouter returns a new Router instance.
2015-07-27 08:43:11 -07:00
func NewRouter ( e * Echo ) * Router {
return & Router {
2015-10-06 06:48:33 -07:00
tree : & node {
methodHandler : new ( methodHandler ) ,
} ,
2017-05-16 16:29:33 -07:00
routes : map [ string ] * Route { } ,
2015-10-06 06:48:33 -07:00
echo : e ,
2015-03-01 09:45:13 -08:00
}
}
2016-03-19 15:47:20 -07:00
// Add registers a new route for method and path with matching handler.
2016-10-21 20:36:49 -07:00
func ( r * Router ) Add ( method , path string , h HandlerFunc ) {
2016-04-11 22:53:31 -07:00
// Validate path
if path == "" {
2019-04-29 22:54:49 -07:00
path = "/"
2016-04-11 22:53:31 -07:00
}
if path [ 0 ] != '/' {
path = "/" + path
}
2018-02-21 11:38:22 -08:00
pnames := [ ] string { } // Param names
ppath := path // Pristine path
2015-04-24 07:44:30 -07:00
2015-04-05 14:21:03 -07:00
for i , l := 0 , len ( path ) ; i < l ; i ++ {
2015-03-01 09:45:13 -08:00
if path [ i ] == ':' {
2015-04-24 07:44:30 -07:00
j := i + 1
2016-10-21 20:36:49 -07:00
r . insert ( method , path [ : i ] , nil , skind , "" , nil )
2018-04-11 22:34:01 -07:00
for ; i < l && path [ i ] != '/' ; i ++ {
2015-03-01 09:45:13 -08:00
}
2015-04-24 07:44:30 -07:00
pnames = append ( pnames , path [ j : i ] )
path = path [ : j ] + path [ i : ]
i , l = j , len ( path )
2015-03-01 09:45:13 -08:00
if i == l {
2016-10-21 20:36:49 -07:00
r . insert ( method , path [ : i ] , h , pkind , ppath , pnames )
2019-04-28 22:22:35 -07:00
} else {
r . insert ( method , path [ : i ] , nil , pkind , "" , nil )
2015-03-01 09:45:13 -08:00
}
2015-03-06 21:55:51 -08:00
} else if path [ i ] == '*' {
2016-10-21 20:36:49 -07:00
r . insert ( method , path [ : i ] , nil , skind , "" , nil )
2016-10-10 18:36:48 -07:00
pnames = append ( pnames , "*" )
2016-10-21 20:36:49 -07:00
r . insert ( method , path [ : i + 1 ] , h , akind , ppath , pnames )
2015-03-01 09:45:13 -08:00
}
}
2015-06-03 15:18:27 -07:00
2016-10-21 20:36:49 -07:00
r . insert ( method , path , h , skind , ppath , pnames )
2015-03-01 09:45:13 -08:00
}
2016-10-21 20:36:49 -07:00
func ( r * Router ) insert ( method , path string , h HandlerFunc , t kind , ppath string , pnames [ ] string ) {
2015-06-03 15:18:27 -07:00
// Adjust max param
l := len ( pnames )
2016-10-21 20:36:49 -07:00
if * r . echo . maxParam < l {
* r . echo . maxParam = l
2015-06-03 15:18:27 -07:00
}
2015-10-06 06:48:33 -07:00
cn := r . tree // Current node as root
2015-07-24 12:03:36 -07:00
if cn == nil {
2017-06-02 17:55:59 -07:00
panic ( "echo: invalid method" )
2015-07-24 12:03:36 -07:00
}
2015-03-01 09:45:13 -08:00
search := path
for {
sl := len ( search )
pl := len ( cn . prefix )
2015-06-05 15:08:32 -07:00
l := 0
// LCP
max := pl
if sl < max {
max = sl
}
for ; l < max && search [ l ] == cn . prefix [ l ] ; l ++ {
}
2015-03-01 09:45:13 -08:00
if l == 0 {
// At root node
cn . label = search [ 0 ]
cn . prefix = search
if h != nil {
2015-10-07 15:10:40 -07:00
cn . kind = t
2015-10-06 06:48:33 -07:00
cn . addHandler ( method , h )
2015-11-12 20:23:14 -08:00
cn . ppath = ppath
2015-04-24 07:44:30 -07:00
cn . pnames = pnames
2015-03-01 09:45:13 -08:00
}
} else if l < pl {
2015-04-08 14:40:49 -07:00
// Split node
2020-11-21 02:48:16 +00:00
n := newNode ( cn . kind , cn . prefix [ l : ] , cn , cn . staticChildrens , cn . methodHandler , cn . ppath , cn . pnames , cn . paramChildren , cn . anyChildren )
2015-03-01 09:45:13 -08:00
2019-10-16 21:52:10 +02:00
// Update parent path for all children to new node
2020-11-21 02:48:16 +00:00
for _ , child := range cn . staticChildrens {
2019-10-16 21:52:10 +02:00
child . parent = n
}
2020-11-21 02:48:16 +00:00
if cn . paramChildren != nil {
cn . paramChildren . parent = n
}
if cn . anyChildren != nil {
cn . anyChildren . parent = n
}
2019-10-16 21:52:10 +02:00
2015-03-01 09:45:13 -08:00
// Reset parent node
2015-10-07 15:10:40 -07:00
cn . kind = skind
2015-03-01 09:45:13 -08:00
cn . label = cn . prefix [ 0 ]
cn . prefix = cn . prefix [ : l ]
2020-11-21 02:48:16 +00:00
cn . staticChildrens = nil
2015-10-06 06:48:33 -07:00
cn . methodHandler = new ( methodHandler )
2015-11-12 20:23:14 -08:00
cn . ppath = ""
2015-04-24 07:44:30 -07:00
cn . pnames = nil
2020-11-21 02:48:16 +00:00
cn . paramChildren = nil
cn . anyChildren = nil
2015-03-01 09:45:13 -08:00
2020-11-21 02:48:16 +00:00
// Only Static children could reach here
cn . addStaticChild ( n )
2015-06-05 15:08:32 -07:00
2015-03-01 09:45:13 -08:00
if l == sl {
// At parent node
2015-10-07 15:10:40 -07:00
cn . kind = t
2015-10-06 06:48:33 -07:00
cn . addHandler ( method , h )
2015-11-12 20:23:14 -08:00
cn . ppath = ppath
2015-04-24 07:44:30 -07:00
cn . pnames = pnames
2015-03-01 09:45:13 -08:00
} else {
2015-04-08 14:40:49 -07:00
// Create child node
2020-11-21 02:48:16 +00:00
n = newNode ( t , search [ l : ] , cn , nil , new ( methodHandler ) , ppath , pnames , nil , nil )
2015-10-06 06:48:33 -07:00
n . addHandler ( method , h )
2020-11-21 02:48:16 +00:00
// Only Static children could reach here
cn . addStaticChild ( n )
2015-03-01 09:45:13 -08:00
}
} else if l < sl {
search = search [ l : ]
2015-06-05 15:08:32 -07:00
c := cn . findChildWithLabel ( search [ 0 ] )
2015-04-13 21:57:36 -07:00
if c != nil {
2015-04-08 14:40:49 -07:00
// Go deeper
2015-04-13 21:57:36 -07:00
cn = c
2015-04-08 14:40:49 -07:00
continue
2015-03-01 09:45:13 -08:00
}
2015-04-08 14:40:49 -07:00
// Create child node
2020-11-21 02:48:16 +00:00
n := newNode ( t , search , cn , nil , new ( methodHandler ) , ppath , pnames , nil , nil )
2015-10-06 06:48:33 -07:00
n . addHandler ( method , h )
2020-11-21 02:48:16 +00:00
switch t {
case skind :
cn . addStaticChild ( n )
case pkind :
cn . paramChildren = n
case akind :
cn . anyChildren = n
}
2015-03-01 09:45:13 -08:00
} else {
// Node already exists
if h != nil {
2015-10-06 06:48:33 -07:00
cn . addHandler ( method , h )
2016-03-08 19:40:25 -08:00
cn . ppath = ppath
2016-11-15 20:09:52 -08:00
if len ( cn . pnames ) == 0 { // Issue #729
cn . pnames = pnames
}
2015-03-01 09:45:13 -08:00
}
}
2015-04-08 14:40:49 -07:00
return
2015-03-01 09:45:13 -08:00
}
}
2020-11-21 02:48:16 +00:00
func newNode ( t kind , pre string , p * node , sc children , mh * methodHandler , ppath string , pnames [ ] string , paramChildren , anyChildren * node ) * node {
2015-04-26 12:44:38 -07:00
return & node {
2020-11-21 02:48:16 +00:00
kind : t ,
label : pre [ 0 ] ,
prefix : pre ,
parent : p ,
staticChildrens : sc ,
ppath : ppath ,
pnames : pnames ,
methodHandler : mh ,
paramChildren : paramChildren ,
anyChildren : anyChildren ,
2015-03-01 09:45:13 -08:00
}
2015-04-26 12:44:38 -07:00
}
2020-11-21 02:48:16 +00:00
func ( n * node ) addStaticChild ( c * node ) {
n . staticChildrens = append ( n . staticChildrens , c )
2015-04-01 08:05:54 -07:00
}
2020-11-21 02:48:16 +00:00
func ( n * node ) findStaticChild ( l byte ) * node {
for _ , c := range n . staticChildrens {
if c . label == l {
2015-04-24 07:44:30 -07:00
return c
}
}
return nil
}
2015-06-05 15:08:32 -07:00
func ( n * node ) findChildWithLabel ( l byte ) * node {
2020-11-21 02:48:16 +00:00
for _ , c := range n . staticChildrens {
2015-06-05 15:08:32 -07:00
if c . label == l {
2015-04-24 07:44:30 -07:00
return c
}
}
2020-11-21 02:48:16 +00:00
if l == paramLabel {
return n . paramChildren
}
if l == anyLabel {
return n . anyChildren
2015-04-24 07:44:30 -07:00
}
return nil
}
2016-04-02 14:19:39 -07:00
func ( n * node ) addHandler ( method string , h HandlerFunc ) {
2015-10-06 06:48:33 -07:00
switch method {
2018-10-14 17:16:58 +02:00
case http . MethodConnect :
2018-03-13 12:38:42 -07:00
n . methodHandler . connect = h
2018-10-14 17:16:58 +02:00
case http . MethodDelete :
2018-03-13 12:38:42 -07:00
n . methodHandler . delete = h
2018-10-14 17:16:58 +02:00
case http . MethodGet :
2015-10-06 06:48:33 -07:00
n . methodHandler . get = h
2018-10-14 17:16:58 +02:00
case http . MethodHead :
2018-03-13 12:38:42 -07:00
n . methodHandler . head = h
2018-10-14 17:16:58 +02:00
case http . MethodOptions :
2018-03-13 12:38:42 -07:00
n . methodHandler . options = h
2018-10-14 17:16:58 +02:00
case http . MethodPatch :
2018-03-13 12:38:42 -07:00
n . methodHandler . patch = h
2018-10-14 17:16:58 +02:00
case http . MethodPost :
2015-10-06 06:48:33 -07:00
n . methodHandler . post = h
2018-03-13 12:38:42 -07:00
case PROPFIND :
n . methodHandler . propfind = h
2018-10-14 17:16:58 +02:00
case http . MethodPut :
2015-10-06 06:48:33 -07:00
n . methodHandler . put = h
2018-10-14 17:16:58 +02:00
case http . MethodTrace :
2015-10-06 06:48:33 -07:00
n . methodHandler . trace = h
2019-06-09 18:11:18 +02:00
case REPORT :
n . methodHandler . report = h
2015-10-06 06:48:33 -07:00
}
}
2016-04-02 14:19:39 -07:00
func ( n * node ) findHandler ( method string ) HandlerFunc {
2015-10-06 06:48:33 -07:00
switch method {
2018-10-14 17:16:58 +02:00
case http . MethodConnect :
2018-03-13 12:38:42 -07:00
return n . methodHandler . connect
2018-10-14 17:16:58 +02:00
case http . MethodDelete :
2018-03-13 12:38:42 -07:00
return n . methodHandler . delete
2018-10-14 17:16:58 +02:00
case http . MethodGet :
2015-10-06 06:48:33 -07:00
return n . methodHandler . get
2018-10-14 17:16:58 +02:00
case http . MethodHead :
2018-03-13 12:38:42 -07:00
return n . methodHandler . head
2018-10-14 17:16:58 +02:00
case http . MethodOptions :
2018-03-13 12:38:42 -07:00
return n . methodHandler . options
2018-10-14 17:16:58 +02:00
case http . MethodPatch :
2018-03-13 12:38:42 -07:00
return n . methodHandler . patch
2018-10-14 17:16:58 +02:00
case http . MethodPost :
2015-10-06 06:48:33 -07:00
return n . methodHandler . post
2018-03-13 12:38:42 -07:00
case PROPFIND :
return n . methodHandler . propfind
2018-10-14 17:16:58 +02:00
case http . MethodPut :
2015-10-06 06:48:33 -07:00
return n . methodHandler . put
2018-10-14 17:16:58 +02:00
case http . MethodTrace :
2015-10-06 06:48:33 -07:00
return n . methodHandler . trace
2019-06-09 18:11:18 +02:00
case REPORT :
return n . methodHandler . report
2015-10-06 06:48:33 -07:00
default :
return nil
2015-07-24 12:03:36 -07:00
}
}
2016-03-30 14:47:04 -07:00
func ( n * node ) checkMethodNotAllowed ( ) HandlerFunc {
2015-11-22 09:26:11 -08:00
for _ , m := range methods {
if h := n . findHandler ( m ) ; h != nil {
2016-07-05 08:43:46 -07:00
return MethodNotAllowedHandler
2015-11-22 09:26:11 -08:00
}
}
2016-07-05 08:43:46 -07:00
return NotFoundHandler
2015-11-22 09:26:11 -08:00
}
2017-02-02 17:50:17 -02:00
// Find lookup a handler registered for method and path. It also parses URL for path
2016-03-19 15:47:20 -07:00
// parameters and load them into context.
//
// For performance:
//
2016-05-03 17:23:31 -07:00
// - Get context from `Echo#AcquireContext()`
2016-03-19 15:47:20 -07:00
// - Reset it `Context#Reset()`
2016-05-03 17:23:31 -07:00
// - Return it `Echo#ReleaseContext()`.
2017-04-10 13:10:31 -07:00
func ( r * Router ) Find ( method , path string , c Context ) {
ctx := c . ( * context )
ctx . path = path
2015-10-06 06:48:33 -07:00
cn := r . tree // Current node as root
2015-09-01 08:03:01 -07:00
2015-05-09 22:06:13 -07:00
var (
2021-03-02 20:56:40 +02:00
search = path
searchIndex = 0
n int // Param counter
pvalues = ctx . pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
2015-05-09 22:06:13 -07:00
)
2015-03-01 09:45:13 -08:00
2021-03-02 20:56:40 +02:00
// Backtracking is needed when a dead end (leaf node) is reached in the router tree.
// To backtrack the current node will be changed to the parent node and the next kind for the
// router logic will be returned based on fromKind or kind of the dead end node (static > param > any).
// For example if there is no static node match we should check parent next sibling by kind (param).
// Backtracking itself does not check if there is a next sibling, this is done by the router logic.
backtrackToNextNodeKind := func ( fromKind kind ) ( nextNodeKind kind , valid bool ) {
previous := cn
cn = previous . parent
valid = cn != nil
// Next node type by priority
// NOTE: With the current implementation we never backtrack from an `any` route, so `previous.kind` is
// always `static` or `any`
// If this is changed then for any route next kind would be `static` and this statement should be changed
nextNodeKind = previous . kind + 1
if fromKind == skind {
// when backtracking is done from static kind block we did not change search so nothing to restore
return
2015-03-01 09:45:13 -08:00
}
2021-03-02 20:56:40 +02:00
// restore search to value it was before we move to current node we are backtracking from.
if previous . kind == skind {
searchIndex -= len ( previous . prefix )
} else {
n --
// for param/any node.prefix value is always `:` so we can not deduce searchIndex from that and must use pValue
// for that index as it would also contain part of path we cut off before moving into node we are backtracking from
searchIndex -= len ( pvalues [ n ] )
}
search = path [ searchIndex : ]
return
}
// Search order static > param > any
for {
2015-05-09 22:06:13 -07:00
pl := 0 // Prefix length
l := 0 // LCP length
if cn . label != ':' {
2015-06-05 15:08:32 -07:00
sl := len ( search )
2015-05-09 22:06:13 -07:00
pl = len ( cn . prefix )
2015-06-05 15:08:32 -07:00
// LCP
max := pl
2015-06-09 20:06:51 -07:00
if sl < max {
2015-06-05 15:08:32 -07:00
max = sl
}
for ; l < max && search [ l ] == cn . prefix [ l ] ; l ++ {
}
2015-05-09 22:06:13 -07:00
}
2015-04-13 13:12:30 -07:00
2021-03-02 20:56:40 +02:00
if l != pl {
// No matching prefix, let's backtrack to the first possible alternative node of the decision path
nk , ok := backtrackToNextNodeKind ( skind )
if ! ok {
return // No other possibilities on the decision path
} else if nk == pkind {
goto Param
// NOTE: this case (backtracking from static node to previous any node) can not happen by current any matching logic. Any node is end of search currently
//} else if nk == akind {
// goto Any
} else {
// Not found (this should never be possible for static node we are looking currently)
return
2021-01-03 19:35:00 +01:00
}
2020-02-24 17:26:49 +01:00
}
2021-03-02 20:56:40 +02:00
// The full prefix has matched, remove the prefix from the remaining search
search = search [ l : ]
searchIndex = searchIndex + l
// Finish routing if no remaining search and we are on an leaf node
if search == "" && cn . ppath != "" {
break
2015-04-11 11:10:19 -07:00
}
2015-03-01 09:45:13 -08:00
2015-04-11 10:09:41 -07:00
// Static node
2021-03-02 20:56:40 +02:00
if search != "" {
if child := cn . findStaticChild ( search [ 0 ] ) ; child != nil {
cn = child
continue
2015-05-09 22:06:13 -07:00
}
2015-04-11 10:09:41 -07:00
}
2015-04-04 10:44:48 -07:00
2015-09-15 13:14:30 -07:00
Param :
2020-02-19 16:10:57 +01:00
// Param node
2021-03-02 20:56:40 +02:00
if child := cn . paramChildren ; search != "" && child != nil {
2017-04-10 13:10:31 -07:00
cn = child
2015-04-11 10:09:41 -07:00
i , l := 0 , len ( search )
2018-04-11 22:34:01 -07:00
for ; i < l && search [ i ] != '/' ; i ++ {
2015-03-01 09:45:13 -08:00
}
2016-04-16 15:53:27 -07:00
pvalues [ n ] = search [ : i ]
2015-04-26 09:48:49 -07:00
n ++
2015-04-11 10:09:41 -07:00
search = search [ i : ]
2021-03-02 20:56:40 +02:00
searchIndex = searchIndex + i
2015-04-11 10:09:41 -07:00
continue
}
2015-04-04 10:44:48 -07:00
2016-03-03 22:06:47 -08:00
Any :
2020-02-19 16:10:57 +01:00
// Any node
2021-03-02 20:56:40 +02:00
if child := cn . anyChildren ; child != nil {
2020-02-19 16:10:57 +01:00
// If any node is found, use remaining path for pvalues
2021-03-02 20:56:40 +02:00
cn = child
2020-02-19 16:10:57 +01:00
pvalues [ len ( cn . pnames ) - 1 ] = search
break
}
2021-03-02 20:56:40 +02:00
// Let's backtrack to the first possible alternative node of the decision path
nk , ok := backtrackToNextNodeKind ( akind )
if ! ok {
return // No other possibilities on the decision path
} else if nk == pkind {
goto Param
} else if nk == akind {
goto Any
} else {
// Not found
return
2015-04-12 13:04:41 -07:00
}
2015-03-01 09:45:13 -08:00
}
2015-09-24 14:14:01 -07:00
2017-04-10 13:10:31 -07:00
ctx . handler = cn . findHandler ( method )
ctx . path = cn . ppath
ctx . pnames = cn . pnames
2015-11-22 09:26:11 -08:00
2017-04-10 13:10:31 -07:00
if ctx . handler == nil {
ctx . handler = cn . checkMethodNotAllowed ( )
2015-10-06 06:48:33 -07:00
}
2015-09-24 14:14:01 -07:00
return
2015-03-01 09:45:13 -08:00
}