github.com/cloudwego/kitex@v0.9.0/pkg/generic/descriptor/tree.go (about)

     1  /*
     2   * Copyright 2013 Julien Schmidt. All rights reserved.
     3   * Use of this source code is governed by a BSD-style license that can be found
     4   * in the LICENSE file.
     5   *
     6   * This file may have been modified by CloudWeGo authors. All CloudWeGo
     7   * Modifications are Copyright 2021 CloudWeGo Authors.
     8   */
     9  
    10  package descriptor
    11  
    12  import (
    13  	"fmt"
    14  	"net/url"
    15  	"strings"
    16  )
    17  
    18  func countParams(path string) uint16 {
    19  	var n uint
    20  	for i := range []byte(path) {
    21  		switch path[i] {
    22  		case ':', '*':
    23  			n++
    24  		}
    25  	}
    26  	return uint16(n)
    27  }
    28  
    29  type nodeType uint8
    30  
    31  const (
    32  	static nodeType = iota // default
    33  	param
    34  	catchAll
    35  	paramLabel = byte(':')
    36  	anyLabel   = byte('*')
    37  	slash      = "/"
    38  	nilString  = ""
    39  )
    40  
    41  type (
    42  	node struct {
    43  		nType    nodeType
    44  		label    byte
    45  		prefix   string
    46  		parent   *node
    47  		children children
    48  		// original path
    49  		ppath string
    50  		// param names
    51  		pnames     []string
    52  		function   *FunctionDescriptor
    53  		paramChild *node
    54  		anyChild   *node
    55  		// isLeaf indicates that node does not have child routes
    56  		isLeaf bool
    57  	}
    58  	children []*node
    59  )
    60  
    61  func checkPathValid(path string) {
    62  	if path == nilString {
    63  		panic("empty path")
    64  	}
    65  	if path[0] != '/' {
    66  		panic("path must begin with '/'")
    67  	}
    68  	for i, c := range []byte(path) {
    69  		switch c {
    70  		case ':':
    71  			if (i < len(path)-1 && path[i+1] == '/') || i == (len(path)-1) {
    72  				panic("wildcards must be named with a non-empty name in path '" + path + "'")
    73  			}
    74  			i++
    75  			for ; i < len(path) && path[i] != '/'; i++ {
    76  				if path[i] == ':' || path[i] == '*' {
    77  					panic("only one wildcard per path segment is allowed, find multi in path '" + path + "'")
    78  				}
    79  			}
    80  		case '*':
    81  			if i == len(path)-1 {
    82  				panic("wildcards must be named with a non-empty name in path '" + path + "'")
    83  			}
    84  			if i > 0 && path[i-1] != '/' {
    85  				panic(" no / before wildcards in path " + path)
    86  			}
    87  			for ; i < len(path); i++ {
    88  				if path[i] == '/' {
    89  					panic("catch-all routes are only allowed at the end of the path in path '" + path + "'")
    90  				}
    91  			}
    92  		}
    93  	}
    94  }
    95  
    96  // addRoute adds a node with the given function to the path.
    97  // Not concurrency-safe!
    98  func (n *node) addRoute(path string, function *FunctionDescriptor) {
    99  	checkPathValid(path)
   100  
   101  	var (
   102  		pnames []string // Param names
   103  		ppath  = path   // Pristine path
   104  	)
   105  
   106  	if function == nil {
   107  		panic(fmt.Sprintf("adding route without handler function: %v", path))
   108  	}
   109  
   110  	// Add the front static route part of a non-static route
   111  	for i, lcpIndex := 0, len(path); i < lcpIndex; i++ {
   112  		// param route
   113  		if path[i] == paramLabel {
   114  			j := i + 1
   115  
   116  			n.insert(path[:i], nil, static, nilString, nil)
   117  			for ; i < lcpIndex && path[i] != '/'; i++ {
   118  			}
   119  
   120  			pnames = append(pnames, path[j:i])
   121  			path = path[:j] + path[i:]
   122  			i, lcpIndex = j, len(path)
   123  
   124  			if i == lcpIndex {
   125  				// path node is last fragment of route path. ie. `/users/:id`
   126  				n.insert(path[:i], function, param, ppath, pnames)
   127  				return
   128  			} else {
   129  				n.insert(path[:i], nil, param, nilString, pnames)
   130  			}
   131  		} else if path[i] == anyLabel {
   132  			n.insert(path[:i], nil, static, nilString, nil)
   133  			pnames = append(pnames, path[i+1:])
   134  			n.insert(path[:i+1], function, catchAll, ppath, pnames)
   135  			return
   136  		}
   137  	}
   138  	n.insert(path, function, static, ppath, pnames)
   139  }
   140  
   141  func (n *node) insert(path string, function *FunctionDescriptor, t nodeType, ppath string, pnames []string) {
   142  	currentNode := n
   143  	search := path
   144  
   145  	for {
   146  		searchLen := len(search)
   147  		prefixLen := len(currentNode.prefix)
   148  		lcpLen := 0
   149  
   150  		max := prefixLen
   151  		if searchLen < max {
   152  			max = searchLen
   153  		}
   154  		for ; lcpLen < max && search[lcpLen] == currentNode.prefix[lcpLen]; lcpLen++ {
   155  		}
   156  
   157  		if lcpLen == 0 {
   158  			currentNode.label = search[0]
   159  			currentNode.prefix = search
   160  			if function != nil {
   161  				currentNode.nType = t
   162  				currentNode.function = function
   163  				currentNode.ppath = ppath
   164  				currentNode.pnames = pnames
   165  			}
   166  			currentNode.isLeaf = currentNode.children == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
   167  		} else if lcpLen < prefixLen {
   168  			// Split node
   169  			n := newNode(
   170  				currentNode.nType,
   171  				currentNode.prefix[lcpLen:],
   172  				currentNode,
   173  				currentNode.children,
   174  				currentNode.function,
   175  				currentNode.ppath,
   176  				currentNode.pnames,
   177  				currentNode.paramChild,
   178  				currentNode.anyChild,
   179  			)
   180  			// Update parent path for all children to new node
   181  			for _, child := range currentNode.children {
   182  				child.parent = n
   183  			}
   184  			if currentNode.paramChild != nil {
   185  				currentNode.paramChild.parent = n
   186  			}
   187  			if currentNode.anyChild != nil {
   188  				currentNode.anyChild.parent = n
   189  			}
   190  
   191  			// Reset parent node
   192  			currentNode.nType = static
   193  			currentNode.label = currentNode.prefix[0]
   194  			currentNode.prefix = currentNode.prefix[:lcpLen]
   195  			currentNode.children = nil
   196  			currentNode.function = nil
   197  			currentNode.ppath = nilString
   198  			currentNode.pnames = nil
   199  			currentNode.paramChild = nil
   200  			currentNode.anyChild = nil
   201  			currentNode.isLeaf = false
   202  
   203  			// Only Static children could reach here
   204  			currentNode.children = append(currentNode.children, n)
   205  
   206  			if lcpLen == searchLen {
   207  				// At parent node
   208  				currentNode.nType = t
   209  				currentNode.function = function
   210  				currentNode.ppath = ppath
   211  				currentNode.pnames = pnames
   212  			} else {
   213  				// Create child node
   214  				n = newNode(t, search[lcpLen:], currentNode, nil, function, ppath, pnames, nil, nil)
   215  				// Only Static children could reach here
   216  				currentNode.children = append(currentNode.children, n)
   217  			}
   218  			currentNode.isLeaf = currentNode.children == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
   219  		} else if lcpLen < searchLen {
   220  			search = search[lcpLen:]
   221  			c := currentNode.findChildWithLabel(search[0])
   222  			if c != nil {
   223  				// Go deeper
   224  				currentNode = c
   225  				continue
   226  			}
   227  			// Create child node
   228  			n := newNode(t, search, currentNode, nil, function, ppath, pnames, nil, nil)
   229  			switch t {
   230  			case static:
   231  				currentNode.children = append(currentNode.children, n)
   232  			case param:
   233  				currentNode.paramChild = n
   234  			case catchAll:
   235  				currentNode.anyChild = n
   236  			}
   237  			currentNode.isLeaf = currentNode.children == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
   238  		} else {
   239  			// Node already exists
   240  			if currentNode.function != nil && function != nil {
   241  				panic("handlers are already registered for path '" + ppath + "'")
   242  			}
   243  
   244  			if function != nil {
   245  				currentNode.function = function
   246  				currentNode.ppath = ppath
   247  				if len(currentNode.pnames) == 0 {
   248  					currentNode.pnames = pnames
   249  				}
   250  			}
   251  		}
   252  		return
   253  	}
   254  }
   255  
   256  // Returns the function registered with the given path (key). The values of
   257  // wildcards are saved to a map.
   258  // If no function can be found, a TSR (trailing slash redirect) recommendation is
   259  // made if a function exists with an extra (without the) trailing slash for the
   260  // given path.
   261  func (n *node) getValue(path string, params func() *Params, unescape bool) (function *FunctionDescriptor, ps *Params, tsr bool) {
   262  	var (
   263  		cn          = n    // current node
   264  		search      = path // current path
   265  		searchIndex = 0
   266  		paramIndex  int
   267  	)
   268  
   269  	backtrackToNextNodeType := func(fromNodeType nodeType) (nextNodeType nodeType, valid bool) {
   270  		previous := cn
   271  		cn = previous.parent
   272  		valid = cn != nil
   273  
   274  		// Next node type by priority
   275  		if previous.nType == catchAll {
   276  			nextNodeType = static
   277  		} else {
   278  			nextNodeType = previous.nType + 1
   279  		}
   280  
   281  		if fromNodeType == static {
   282  			// when backtracking is done from static type block we did not change search so nothing to restore
   283  			return
   284  		}
   285  
   286  		// restore search to value it was before we move to current node we are backtracking from.
   287  		if previous.nType == static {
   288  			searchIndex -= len(previous.prefix)
   289  		} else {
   290  			paramIndex--
   291  			// for param/any node.prefix value is always `:`/`*` so we cannot deduce searchIndex from that and must use pValue
   292  			// for that index as it would also contain part of path we cut off before moving into node we are backtracking from
   293  			searchIndex -= len(ps.params[paramIndex].Value)
   294  			ps.params = ps.params[:paramIndex]
   295  		}
   296  		search = path[searchIndex:]
   297  		return
   298  	}
   299  
   300  	// search order: static > param > any
   301  	for {
   302  		if cn.nType == static {
   303  			if len(search) >= len(cn.prefix) && cn.prefix == search[:len(cn.prefix)] {
   304  				// Continue search
   305  				search = search[len(cn.prefix):]
   306  				searchIndex = searchIndex + len(cn.prefix)
   307  			} else {
   308  				// not equal
   309  				if (len(cn.prefix) == len(search)+1) &&
   310  					(cn.prefix[len(search)]) == '/' && cn.prefix[:len(search)] == search && (cn.function != nil || cn.anyChild != nil) {
   311  					tsr = true
   312  				}
   313  				// No matching prefix, let's backtrack to the first possible alternative node of the decision path
   314  				nk, ok := backtrackToNextNodeType(static)
   315  				if !ok {
   316  					return // No other possibilities on the decision path
   317  				} else if nk == param {
   318  					goto Param
   319  				} else {
   320  					// Not found (this should never be possible for static node we are looking currently)
   321  					break
   322  				}
   323  			}
   324  		}
   325  		if search == nilString && cn.function != nil {
   326  			function = cn.function
   327  			break
   328  		}
   329  
   330  		// Static node
   331  		if search != nilString {
   332  			// If it can execute that logic, there is handler registered on the current node and search is `/`.
   333  			if search == "/" && cn.function != nil {
   334  				tsr = true
   335  			}
   336  			if child := cn.findChild(search[0]); child != nil {
   337  				cn = child
   338  				continue
   339  			}
   340  		}
   341  
   342  		if search == nilString {
   343  			if cd := cn.findChild('/'); cd != nil && (cd.function != nil || cd.anyChild != nil) {
   344  				tsr = true
   345  			}
   346  		}
   347  
   348  	Param:
   349  		// Param node
   350  		if child := cn.paramChild; search != nilString && child != nil {
   351  			cn = child
   352  			i := strings.Index(search, slash)
   353  			if i == -1 {
   354  				i = len(search)
   355  			}
   356  			if ps == nil {
   357  				ps = params()
   358  			}
   359  			val := search[:i]
   360  			if unescape {
   361  				if v, err := url.QueryUnescape(val); err == nil {
   362  					val = v
   363  				}
   364  			}
   365  			ps.params = ps.params[:paramIndex+1]
   366  			ps.params[paramIndex].Value = val
   367  			paramIndex++
   368  			search = search[i:]
   369  			searchIndex = searchIndex + i
   370  			if search == nilString {
   371  				if cd := cn.findChild('/'); cd != nil && (cd.function != nil || cd.anyChild != nil) {
   372  					tsr = true
   373  				}
   374  			}
   375  			continue
   376  		}
   377  	Any:
   378  		// Any node
   379  		if child := cn.anyChild; child != nil {
   380  			// If any node is found, use remaining path for paramValues
   381  			cn = child
   382  			if ps == nil {
   383  				ps = params()
   384  			}
   385  			index := len(cn.pnames) - 1
   386  			val := search
   387  			if unescape {
   388  				if v, err := url.QueryUnescape(val); err == nil {
   389  					val = v
   390  				}
   391  			}
   392  			ps.params = ps.params[:paramIndex+1]
   393  			ps.params[index].Value = val
   394  			// update indexes/search in case we need to backtrack when no handler match is found
   395  			paramIndex++
   396  			searchIndex += len(search)
   397  			search = nilString
   398  			function = cn.function
   399  			break
   400  		}
   401  
   402  		// Let's backtrack to the first possible alternative node of the decision path
   403  		nk, ok := backtrackToNextNodeType(catchAll)
   404  		if !ok {
   405  			break // No other possibilities on the decision path
   406  		} else if nk == param {
   407  			goto Param
   408  		} else if nk == catchAll {
   409  			goto Any
   410  		} else {
   411  			// Not found
   412  			break
   413  		}
   414  	}
   415  
   416  	if cn != nil {
   417  		for i, name := range cn.pnames {
   418  			ps.params[i].Key = name
   419  		}
   420  	}
   421  
   422  	return
   423  }
   424  
   425  func (n *node) findChild(l byte) *node {
   426  	for _, c := range n.children {
   427  		if c.label == l {
   428  			return c
   429  		}
   430  	}
   431  	return nil
   432  }
   433  
   434  func (n *node) findChildWithLabel(l byte) *node {
   435  	for _, c := range n.children {
   436  		if c.label == l {
   437  			return c
   438  		}
   439  	}
   440  	if l == paramLabel {
   441  		return n.paramChild
   442  	}
   443  	if l == anyLabel {
   444  		return n.anyChild
   445  	}
   446  	return nil
   447  }
   448  
   449  func newNode(t nodeType, pre string, p *node, child children, f *FunctionDescriptor, ppath string, pnames []string, paramChildren, anyChildren *node) *node {
   450  	return &node{
   451  		nType:      t,
   452  		label:      pre[0],
   453  		prefix:     pre,
   454  		parent:     p,
   455  		children:   child,
   456  		ppath:      ppath,
   457  		pnames:     pnames,
   458  		function:   f,
   459  		paramChild: paramChildren,
   460  		anyChild:   anyChildren,
   461  		isLeaf:     child == nil && paramChildren == nil && anyChildren == nil,
   462  	}
   463  }