2015-03-27 23:21:30 +02:00
package echo
2015-03-01 19:45:13 +02:00
2019-10-03 19:30:46 +02:00
import (
"net/http"
)
2018-10-14 17:16:58 +02:00
2015-03-01 19:45:13 +02:00
type (
2016-03-20 00:47:20 +02:00
// Router is the registry of all registered routes for an `Echo` instance for
// request matching and URL path parameter parsing.
2015-05-23 05:26:52 +02:00
Router struct {
2015-10-06 15:48:33 +02:00
tree * node
2017-05-17 01:29:33 +02:00
routes map [ string ] * Route
2015-10-06 15:48:33 +02:00
echo * Echo
2015-03-01 19:45:13 +02:00
}
node struct {
2021-03-06 01:43:59 +02:00
kind kind
label byte
prefix string
parent * node
staticChildren children
ppath string
pnames [ ] string
methodHandler * methodHandler
paramChild * node
anyChild * node
2021-04-27 09:55:31 +02:00
// isLeaf indicates that node does not have child routes
isLeaf bool
// isHandler indicates that node has at least one handler registered to it
isHandler bool
2015-10-06 15:48:33 +02:00
}
2019-10-03 19:30:46 +02:00
kind uint8
children [ ] * node
2015-10-06 15:48:33 +02:00
methodHandler struct {
2018-03-13 21:38:42 +02: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 19:45:13 +02:00
}
)
2015-04-24 16:44:30 +02:00
const (
2021-03-06 01:43:59 +02:00
staticKind kind = iota
paramKind
anyKind
2020-11-21 04:48:16 +02:00
paramLabel = byte ( ':' )
anyLabel = byte ( '*' )
2015-04-24 16:44:30 +02:00
)
2021-04-27 09:55:31 +02:00
func ( m * methodHandler ) isHandler ( ) bool {
return m . connect != nil ||
m . delete != nil ||
m . get != nil ||
m . head != nil ||
m . options != nil ||
m . patch != nil ||
m . post != nil ||
m . propfind != nil ||
m . put != nil ||
m . trace != nil ||
m . report != nil
}
2016-03-20 00:47:20 +02:00
// NewRouter returns a new Router instance.
2015-07-27 17:43:11 +02:00
func NewRouter ( e * Echo ) * Router {
return & Router {
2015-10-06 15:48:33 +02:00
tree : & node {
methodHandler : new ( methodHandler ) ,
} ,
2017-05-17 01:29:33 +02:00
routes : map [ string ] * Route { } ,
2015-10-06 15:48:33 +02:00
echo : e ,
2015-03-01 19:45:13 +02:00
}
}
2016-03-20 00:47:20 +02:00
// Add registers a new route for method and path with matching handler.
2016-10-22 05:36:49 +02:00
func ( r * Router ) Add ( method , path string , h HandlerFunc ) {
2016-04-12 07:53:31 +02:00
// Validate path
if path == "" {
2019-04-30 07:54:49 +02:00
path = "/"
2016-04-12 07:53:31 +02:00
}
if path [ 0 ] != '/' {
path = "/" + path
}
2018-02-21 21:38:22 +02:00
pnames := [ ] string { } // Param names
ppath := path // Pristine path
2015-04-24 16:44:30 +02:00
2021-04-27 09:55:31 +02:00
if h == nil && r . echo . Logger != nil {
// FIXME: in future we should return error
r . echo . Logger . Errorf ( "Adding route without handler function: %v:%v" , method , path )
}
2021-03-06 01:43:59 +02:00
for i , lcpIndex := 0 , len ( path ) ; i < lcpIndex ; i ++ {
2015-03-01 19:45:13 +02:00
if path [ i ] == ':' {
2015-04-24 16:44:30 +02:00
j := i + 1
2021-03-06 01:43:59 +02:00
r . insert ( method , path [ : i ] , nil , staticKind , "" , nil )
for ; i < lcpIndex && path [ i ] != '/' ; i ++ {
2015-03-01 19:45:13 +02:00
}
2015-04-24 16:44:30 +02:00
pnames = append ( pnames , path [ j : i ] )
path = path [ : j ] + path [ i : ]
2021-03-06 01:43:59 +02:00
i , lcpIndex = j , len ( path )
2015-04-24 16:44:30 +02:00
2021-03-06 01:43:59 +02:00
if i == lcpIndex {
2021-04-27 09:55:31 +02:00
// path node is last fragment of route path. ie. `/users/:id`
2021-03-06 01:43:59 +02:00
r . insert ( method , path [ : i ] , h , paramKind , ppath , pnames )
2019-04-29 07:22:35 +02:00
} else {
2021-03-06 01:43:59 +02:00
r . insert ( method , path [ : i ] , nil , paramKind , "" , nil )
2015-03-01 19:45:13 +02:00
}
2015-03-07 07:55:51 +02:00
} else if path [ i ] == '*' {
2021-03-06 01:43:59 +02:00
r . insert ( method , path [ : i ] , nil , staticKind , "" , nil )
2016-10-11 03:36:48 +02:00
pnames = append ( pnames , "*" )
2021-03-06 01:43:59 +02:00
r . insert ( method , path [ : i + 1 ] , h , anyKind , ppath , pnames )
2015-03-01 19:45:13 +02:00
}
}
2015-06-04 00:18:27 +02:00
2021-03-06 01:43:59 +02:00
r . insert ( method , path , h , staticKind , ppath , pnames )
2015-03-01 19:45:13 +02:00
}
2016-10-22 05:36:49 +02:00
func ( r * Router ) insert ( method , path string , h HandlerFunc , t kind , ppath string , pnames [ ] string ) {
2015-06-04 00:18:27 +02:00
// Adjust max param
2021-03-06 01:43:59 +02:00
paramLen := len ( pnames )
if * r . echo . maxParam < paramLen {
* r . echo . maxParam = paramLen
2015-06-04 00:18:27 +02:00
}
2021-03-06 01:43:59 +02:00
currentNode := r . tree // Current node as root
if currentNode == nil {
2017-06-03 02:55:59 +02:00
panic ( "echo: invalid method" )
2015-07-24 21:03:36 +02:00
}
2015-03-01 19:45:13 +02:00
search := path
for {
2021-03-06 01:43:59 +02:00
searchLen := len ( search )
prefixLen := len ( currentNode . prefix )
lcpLen := 0
// LCP - Longest Common Prefix (https://en.wikipedia.org/wiki/LCP_array)
max := prefixLen
if searchLen < max {
max = searchLen
2015-06-06 00:08:32 +02:00
}
2021-03-06 01:43:59 +02:00
for ; lcpLen < max && search [ lcpLen ] == currentNode . prefix [ lcpLen ] ; lcpLen ++ {
2015-06-06 00:08:32 +02:00
}
2015-03-01 19:45:13 +02:00
2021-03-06 01:43:59 +02:00
if lcpLen == 0 {
2015-03-01 19:45:13 +02:00
// At root node
2021-03-06 01:43:59 +02:00
currentNode . label = search [ 0 ]
currentNode . prefix = search
2015-03-01 19:45:13 +02:00
if h != nil {
2021-03-06 01:43:59 +02:00
currentNode . kind = t
currentNode . addHandler ( method , h )
currentNode . ppath = ppath
currentNode . pnames = pnames
2015-03-01 19:45:13 +02:00
}
2021-04-27 09:55:31 +02:00
currentNode . isLeaf = currentNode . staticChildren == nil && currentNode . paramChild == nil && currentNode . anyChild == nil
2021-03-06 01:43:59 +02:00
} else if lcpLen < prefixLen {
2015-04-08 23:40:49 +02:00
// Split node
2021-03-06 01:43:59 +02:00
n := newNode (
currentNode . kind ,
currentNode . prefix [ lcpLen : ] ,
currentNode ,
currentNode . staticChildren ,
currentNode . methodHandler ,
currentNode . ppath ,
currentNode . pnames ,
currentNode . paramChild ,
currentNode . anyChild ,
)
2019-10-16 21:52:10 +02:00
// Update parent path for all children to new node
2021-03-06 01:43:59 +02:00
for _ , child := range currentNode . staticChildren {
2019-10-16 21:52:10 +02:00
child . parent = n
}
2021-03-06 01:43:59 +02:00
if currentNode . paramChild != nil {
currentNode . paramChild . parent = n
2020-11-21 04:48:16 +02:00
}
2021-03-06 01:43:59 +02:00
if currentNode . anyChild != nil {
currentNode . anyChild . parent = n
2020-11-21 04:48:16 +02:00
}
2019-10-16 21:52:10 +02:00
2015-03-01 19:45:13 +02:00
// Reset parent node
2021-03-06 01:43:59 +02:00
currentNode . kind = staticKind
currentNode . label = currentNode . prefix [ 0 ]
currentNode . prefix = currentNode . prefix [ : lcpLen ]
currentNode . staticChildren = nil
currentNode . methodHandler = new ( methodHandler )
currentNode . ppath = ""
currentNode . pnames = nil
currentNode . paramChild = nil
currentNode . anyChild = nil
2021-04-27 09:55:31 +02:00
currentNode . isLeaf = false
currentNode . isHandler = false
2015-03-01 19:45:13 +02:00
2020-11-21 04:48:16 +02:00
// Only Static children could reach here
2021-03-06 01:43:59 +02:00
currentNode . addStaticChild ( n )
2015-06-06 00:08:32 +02:00
2021-03-06 01:43:59 +02:00
if lcpLen == searchLen {
2015-03-01 19:45:13 +02:00
// At parent node
2021-03-06 01:43:59 +02:00
currentNode . kind = t
currentNode . addHandler ( method , h )
currentNode . ppath = ppath
currentNode . pnames = pnames
2015-03-01 19:45:13 +02:00
} else {
2015-04-08 23:40:49 +02:00
// Create child node
2021-03-06 01:43:59 +02:00
n = newNode ( t , search [ lcpLen : ] , currentNode , nil , new ( methodHandler ) , ppath , pnames , nil , nil )
2015-10-06 15:48:33 +02:00
n . addHandler ( method , h )
2020-11-21 04:48:16 +02:00
// Only Static children could reach here
2021-03-06 01:43:59 +02:00
currentNode . addStaticChild ( n )
2015-03-01 19:45:13 +02:00
}
2021-04-27 09:55:31 +02:00
currentNode . isLeaf = currentNode . staticChildren == nil && currentNode . paramChild == nil && currentNode . anyChild == nil
2021-03-06 01:43:59 +02:00
} else if lcpLen < searchLen {
search = search [ lcpLen : ]
c := currentNode . findChildWithLabel ( search [ 0 ] )
2015-04-14 06:57:36 +02:00
if c != nil {
2015-04-08 23:40:49 +02:00
// Go deeper
2021-03-06 01:43:59 +02:00
currentNode = c
2015-04-08 23:40:49 +02:00
continue
2015-03-01 19:45:13 +02:00
}
2015-04-08 23:40:49 +02:00
// Create child node
2021-03-06 01:43:59 +02:00
n := newNode ( t , search , currentNode , nil , new ( methodHandler ) , ppath , pnames , nil , nil )
2015-10-06 15:48:33 +02:00
n . addHandler ( method , h )
2020-11-21 04:48:16 +02:00
switch t {
2021-03-06 01:43:59 +02:00
case staticKind :
currentNode . addStaticChild ( n )
case paramKind :
currentNode . paramChild = n
case anyKind :
currentNode . anyChild = n
2020-11-21 04:48:16 +02:00
}
2021-04-27 09:55:31 +02:00
currentNode . isLeaf = currentNode . staticChildren == nil && currentNode . paramChild == nil && currentNode . anyChild == nil
2015-03-01 19:45:13 +02:00
} else {
// Node already exists
if h != nil {
2021-03-06 01:43:59 +02:00
currentNode . addHandler ( method , h )
currentNode . ppath = ppath
if len ( currentNode . pnames ) == 0 { // Issue #729
currentNode . pnames = pnames
2016-11-16 06:09:52 +02:00
}
2015-03-01 19:45:13 +02:00
}
}
2015-04-08 23:40:49 +02:00
return
2015-03-01 19:45:13 +02:00
}
}
2020-11-21 04:48:16 +02:00
func newNode ( t kind , pre string , p * node , sc children , mh * methodHandler , ppath string , pnames [ ] string , paramChildren , anyChildren * node ) * node {
2015-04-26 21:44:38 +02:00
return & node {
2021-03-06 01:43:59 +02:00
kind : t ,
label : pre [ 0 ] ,
prefix : pre ,
parent : p ,
staticChildren : sc ,
ppath : ppath ,
pnames : pnames ,
methodHandler : mh ,
paramChild : paramChildren ,
anyChild : anyChildren ,
2021-04-27 09:55:31 +02:00
isLeaf : sc == nil && paramChildren == nil && anyChildren == nil ,
isHandler : mh . isHandler ( ) ,
2015-03-01 19:45:13 +02:00
}
2015-04-26 21:44:38 +02:00
}
2020-11-21 04:48:16 +02:00
func ( n * node ) addStaticChild ( c * node ) {
2021-03-06 01:43:59 +02:00
n . staticChildren = append ( n . staticChildren , c )
2015-04-01 17:05:54 +02:00
}
2020-11-21 04:48:16 +02:00
func ( n * node ) findStaticChild ( l byte ) * node {
2021-03-06 01:43:59 +02:00
for _ , c := range n . staticChildren {
2020-11-21 04:48:16 +02:00
if c . label == l {
2015-04-24 16:44:30 +02:00
return c
}
}
return nil
}
2015-06-06 00:08:32 +02:00
func ( n * node ) findChildWithLabel ( l byte ) * node {
2021-03-06 01:43:59 +02:00
for _ , c := range n . staticChildren {
2015-06-06 00:08:32 +02:00
if c . label == l {
2015-04-24 16:44:30 +02:00
return c
}
}
2020-11-21 04:48:16 +02:00
if l == paramLabel {
2021-03-06 01:43:59 +02:00
return n . paramChild
2020-11-21 04:48:16 +02:00
}
if l == anyLabel {
2021-03-06 01:43:59 +02:00
return n . anyChild
2015-04-24 16:44:30 +02:00
}
return nil
}
2016-04-02 23:19:39 +02:00
func ( n * node ) addHandler ( method string , h HandlerFunc ) {
2015-10-06 15:48:33 +02:00
switch method {
2018-10-14 17:16:58 +02:00
case http . MethodConnect :
2018-03-13 21:38:42 +02:00
n . methodHandler . connect = h
2018-10-14 17:16:58 +02:00
case http . MethodDelete :
2018-03-13 21:38:42 +02:00
n . methodHandler . delete = h
2018-10-14 17:16:58 +02:00
case http . MethodGet :
2015-10-06 15:48:33 +02:00
n . methodHandler . get = h
2018-10-14 17:16:58 +02:00
case http . MethodHead :
2018-03-13 21:38:42 +02:00
n . methodHandler . head = h
2018-10-14 17:16:58 +02:00
case http . MethodOptions :
2018-03-13 21:38:42 +02:00
n . methodHandler . options = h
2018-10-14 17:16:58 +02:00
case http . MethodPatch :
2018-03-13 21:38:42 +02:00
n . methodHandler . patch = h
2018-10-14 17:16:58 +02:00
case http . MethodPost :
2015-10-06 15:48:33 +02:00
n . methodHandler . post = h
2018-03-13 21:38:42 +02:00
case PROPFIND :
n . methodHandler . propfind = h
2018-10-14 17:16:58 +02:00
case http . MethodPut :
2015-10-06 15:48:33 +02:00
n . methodHandler . put = h
2018-10-14 17:16:58 +02:00
case http . MethodTrace :
2015-10-06 15:48:33 +02:00
n . methodHandler . trace = h
2019-06-09 18:11:18 +02:00
case REPORT :
n . methodHandler . report = h
2015-10-06 15:48:33 +02:00
}
2021-04-27 09:55:31 +02:00
if h != nil {
n . isHandler = true
} else {
n . isHandler = n . methodHandler . isHandler ( )
}
2015-10-06 15:48:33 +02:00
}
2016-04-02 23:19:39 +02:00
func ( n * node ) findHandler ( method string ) HandlerFunc {
2015-10-06 15:48:33 +02:00
switch method {
2018-10-14 17:16:58 +02:00
case http . MethodConnect :
2018-03-13 21:38:42 +02:00
return n . methodHandler . connect
2018-10-14 17:16:58 +02:00
case http . MethodDelete :
2018-03-13 21:38:42 +02:00
return n . methodHandler . delete
2018-10-14 17:16:58 +02:00
case http . MethodGet :
2015-10-06 15:48:33 +02:00
return n . methodHandler . get
2018-10-14 17:16:58 +02:00
case http . MethodHead :
2018-03-13 21:38:42 +02:00
return n . methodHandler . head
2018-10-14 17:16:58 +02:00
case http . MethodOptions :
2018-03-13 21:38:42 +02:00
return n . methodHandler . options
2018-10-14 17:16:58 +02:00
case http . MethodPatch :
2018-03-13 21:38:42 +02:00
return n . methodHandler . patch
2018-10-14 17:16:58 +02:00
case http . MethodPost :
2015-10-06 15:48:33 +02:00
return n . methodHandler . post
2018-03-13 21:38:42 +02:00
case PROPFIND :
return n . methodHandler . propfind
2018-10-14 17:16:58 +02:00
case http . MethodPut :
2015-10-06 15:48:33 +02:00
return n . methodHandler . put
2018-10-14 17:16:58 +02:00
case http . MethodTrace :
2015-10-06 15:48:33 +02:00
return n . methodHandler . trace
2019-06-09 18:11:18 +02:00
case REPORT :
return n . methodHandler . report
2015-10-06 15:48:33 +02:00
default :
return nil
2015-07-24 21:03:36 +02:00
}
}
2016-03-30 23:47:04 +02:00
func ( n * node ) checkMethodNotAllowed ( ) HandlerFunc {
2015-11-22 19:26:11 +02:00
for _ , m := range methods {
if h := n . findHandler ( m ) ; h != nil {
2016-07-05 17:43:46 +02:00
return MethodNotAllowedHandler
2015-11-22 19:26:11 +02:00
}
}
2016-07-05 17:43:46 +02:00
return NotFoundHandler
2015-11-22 19:26:11 +02:00
}
2017-02-02 21:50:17 +02:00
// Find lookup a handler registered for method and path. It also parses URL for path
2016-03-20 00:47:20 +02:00
// parameters and load them into context.
//
// For performance:
//
2016-05-04 02:23:31 +02:00
// - Get context from `Echo#AcquireContext()`
2016-03-20 00:47:20 +02:00
// - Reset it `Context#Reset()`
2016-05-04 02:23:31 +02:00
// - Return it `Echo#ReleaseContext()`.
2017-04-10 22:10:31 +02:00
func ( r * Router ) Find ( method , path string , c Context ) {
ctx := c . ( * context )
ctx . path = path
2021-03-06 01:43:59 +02:00
currentNode := r . tree // Current node as root
2015-09-01 17:03:01 +02:00
2015-05-10 07:06:13 +02:00
var (
2021-04-27 09:55:31 +02:00
previousBestMatchNode * node
matchedHandler HandlerFunc
2021-03-06 01:43:59 +02:00
// search stores the remaining path to check for match. By each iteration we move from start of path to end of the path
// and search value gets shorter and shorter.
2021-03-02 20:56:40 +02:00
search = path
searchIndex = 0
2021-03-06 01:43:59 +02:00
paramIndex int // Param counter
paramValues = ctx . pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
2015-05-10 07:06:13 +02:00
)
2015-03-01 19:45:13 +02: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 ) {
2021-03-06 01:43:59 +02:00
previous := currentNode
currentNode = previous . parent
valid = currentNode != nil
2021-03-02 20:56:40 +02:00
// Next node type by priority
2021-04-27 09:55:31 +02:00
if previous . kind == anyKind {
nextNodeKind = staticKind
} else {
nextNodeKind = previous . kind + 1
}
2021-03-02 20:56:40 +02:00
2021-03-06 01:43:59 +02:00
if fromKind == staticKind {
2021-03-02 20:56:40 +02:00
// when backtracking is done from static kind block we did not change search so nothing to restore
return
2015-03-01 19:45:13 +02: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.
2021-03-06 01:43:59 +02:00
if previous . kind == staticKind {
2021-03-02 20:56:40 +02:00
searchIndex -= len ( previous . prefix )
} else {
2021-03-06 01:43:59 +02:00
paramIndex --
2021-03-02 20:56:40 +02:00
// 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
2021-03-06 01:43:59 +02:00
searchIndex -= len ( paramValues [ paramIndex ] )
2021-04-27 09:55:31 +02:00
paramValues [ paramIndex ] = ""
2021-03-02 20:56:40 +02:00
}
search = path [ searchIndex : ]
return
}
2021-03-06 01:43:59 +02:00
// Router tree is implemented by longest common prefix array (LCP array) https://en.wikipedia.org/wiki/LCP_array
// Tree search is implemented as for loop where one loop iteration is divided into 3 separate blocks
// Each of these blocks checks specific kind of node (static/param/any). Order of blocks reflex their priority in routing.
// Search order/priority is: static > param > any.
//
// Note: backtracking in tree is implemented by replacing/switching currentNode to previous node
// and hoping to (goto statement) next block by priority to check if it is the match.
2021-03-02 20:56:40 +02:00
for {
2021-03-06 01:43:59 +02:00
prefixLen := 0 // Prefix length
lcpLen := 0 // LCP (longest common prefix) length
2015-05-10 07:06:13 +02:00
2021-03-06 01:43:59 +02:00
if currentNode . kind == staticKind {
searchLen := len ( search )
prefixLen = len ( currentNode . prefix )
2015-06-06 00:08:32 +02:00
2021-03-06 01:43:59 +02:00
// LCP - Longest Common Prefix (https://en.wikipedia.org/wiki/LCP_array)
max := prefixLen
if searchLen < max {
max = searchLen
2015-06-06 00:08:32 +02:00
}
2021-03-06 01:43:59 +02:00
for ; lcpLen < max && search [ lcpLen ] == currentNode . prefix [ lcpLen ] ; lcpLen ++ {
2015-06-06 00:08:32 +02:00
}
2015-05-10 07:06:13 +02:00
}
2015-04-13 22:12:30 +02:00
2021-03-06 01:43:59 +02:00
if lcpLen != prefixLen {
2021-03-02 20:56:40 +02:00
// No matching prefix, let's backtrack to the first possible alternative node of the decision path
2021-03-06 01:43:59 +02:00
nk , ok := backtrackToNextNodeKind ( staticKind )
2021-03-02 20:56:40 +02:00
if ! ok {
return // No other possibilities on the decision path
2021-03-06 01:43:59 +02:00
} else if nk == paramKind {
2021-03-02 20:56:40 +02:00
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
2021-03-06 01:43:59 +02:00
//} else if nk == anyKind {
2021-03-02 20:56:40 +02:00
// goto Any
} else {
// Not found (this should never be possible for static node we are looking currently)
2021-04-27 09:55:31 +02:00
break
2021-01-03 20:35:00 +02:00
}
2020-02-24 18:26:49 +02:00
}
2021-03-02 20:56:40 +02:00
// The full prefix has matched, remove the prefix from the remaining search
2021-03-06 01:43:59 +02:00
search = search [ lcpLen : ]
searchIndex = searchIndex + lcpLen
2021-03-02 20:56:40 +02:00
2021-04-27 09:55:31 +02:00
// Finish routing if no remaining search and we are on a node with handler and matching method type
if search == "" && currentNode . isHandler {
// check if current node has handler registered for http method we are looking for. we store currentNode as
// best matching in case we do no find no more routes matching this path+method
if previousBestMatchNode == nil {
previousBestMatchNode = currentNode
}
if h := currentNode . findHandler ( method ) ; h != nil {
matchedHandler = h
break
}
2015-04-11 20:10:19 +02:00
}
2015-03-01 19:45:13 +02:00
2015-04-11 19:09:41 +02:00
// Static node
2021-03-02 20:56:40 +02:00
if search != "" {
2021-03-06 01:43:59 +02:00
if child := currentNode . findStaticChild ( search [ 0 ] ) ; child != nil {
currentNode = child
2021-03-02 20:56:40 +02:00
continue
2015-05-10 07:06:13 +02:00
}
2015-04-11 19:09:41 +02:00
}
2015-04-04 19:44:48 +02:00
2015-09-15 22:14:30 +02:00
Param :
2020-02-19 17:10:57 +02:00
// Param node
2021-03-06 01:43:59 +02:00
if child := currentNode . paramChild ; search != "" && child != nil {
currentNode = child
2021-04-27 09:55:31 +02:00
i := 0
l := len ( search )
if currentNode . isLeaf {
// when param node does not have any children then param node should act similarly to any node - consider all remaining search as match
i = l
} else {
for ; i < l && search [ i ] != '/' ; i ++ {
}
2015-03-01 19:45:13 +02:00
}
2021-04-27 09:55:31 +02:00
2021-03-06 01:43:59 +02:00
paramValues [ paramIndex ] = search [ : i ]
paramIndex ++
2015-04-11 19:09:41 +02:00
search = search [ i : ]
2021-03-02 20:56:40 +02:00
searchIndex = searchIndex + i
2015-04-11 19:09:41 +02:00
continue
}
2015-04-04 19:44:48 +02:00
2016-03-04 08:06:47 +02:00
Any :
2020-02-19 17:10:57 +02:00
// Any node
2021-03-06 01:43:59 +02:00
if child := currentNode . anyChild ; child != nil {
// If any node is found, use remaining path for paramValues
currentNode = child
paramValues [ len ( currentNode . pnames ) - 1 ] = search
2021-04-27 09:55:31 +02:00
// update indexes/search in case we need to backtrack when no handler match is found
paramIndex ++
searchIndex += + len ( search )
search = ""
// check if current node has handler registered for http method we are looking for. we store currentNode as
// best matching in case we do no find no more routes matching this path+method
if previousBestMatchNode == nil {
previousBestMatchNode = currentNode
}
if h := currentNode . findHandler ( method ) ; h != nil {
matchedHandler = h
break
}
2020-02-19 17:10:57 +02:00
}
2021-03-02 20:56:40 +02:00
// Let's backtrack to the first possible alternative node of the decision path
2021-03-06 01:43:59 +02:00
nk , ok := backtrackToNextNodeKind ( anyKind )
2021-03-02 20:56:40 +02:00
if ! ok {
2021-04-27 09:55:31 +02:00
break // No other possibilities on the decision path
2021-03-06 01:43:59 +02:00
} else if nk == paramKind {
2021-03-02 20:56:40 +02:00
goto Param
2021-03-06 01:43:59 +02:00
} else if nk == anyKind {
2021-03-02 20:56:40 +02:00
goto Any
} else {
// Not found
2021-04-27 09:55:31 +02:00
break
2015-04-12 22:04:41 +02:00
}
2015-03-01 19:45:13 +02:00
}
2015-09-24 23:14:01 +02:00
2021-04-27 09:55:31 +02:00
if currentNode == nil && previousBestMatchNode == nil {
return // nothing matched at all
}
2015-11-22 19:26:11 +02:00
2021-04-27 09:55:31 +02:00
if matchedHandler != nil {
ctx . handler = matchedHandler
} else {
// use previous match as basis. although we have no matching handler we have path match.
// so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404)
currentNode = previousBestMatchNode
2021-03-06 01:43:59 +02:00
ctx . handler = currentNode . checkMethodNotAllowed ( )
2015-10-06 15:48:33 +02:00
}
2021-04-27 09:55:31 +02:00
ctx . path = currentNode . ppath
ctx . pnames = currentNode . pnames
2015-09-24 23:14:01 +02:00
return
2015-03-01 19:45:13 +02:00
}