github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/http/routing_tree.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements a decision tree for fast matching of requests to
     6  // patterns.
     7  //
     8  // The root of the tree branches on the host of the request.
     9  // The next level branches on the method.
    10  // The remaining levels branch on consecutive segments of the path.
    11  //
    12  // The "more specific wins" precedence rule can result in backtracking.
    13  // For example, given the patterns
    14  //     /a/b/z
    15  //     /a/{x}/c
    16  // we will first try to match the path "/a/b/c" with /a/b/z, and
    17  // when that fails we will try against /a/{x}/c.
    18  
    19  package http
    20  
    21  import (
    22  	"strings"
    23  )
    24  
    25  // A routingNode is a node in the decision tree.
    26  // The same struct is used for leaf and interior nodes.
    27  type routingNode struct {
    28  	// A leaf node holds a single pattern and the Handler it was registered
    29  	// with.
    30  	pattern *pattern
    31  	handler Handler
    32  
    33  	// An interior node maps parts of the incoming request to child nodes.
    34  	// special children keys:
    35  	//     "/"	trailing slash (resulting from {$})
    36  	//	   ""   single wildcard
    37  	//	   "*"  multi wildcard
    38  	children   mapping[string, *routingNode]
    39  	emptyChild *routingNode // optimization: child with key ""
    40  }
    41  
    42  // addPattern adds a pattern and its associated Handler to the tree
    43  // at root.
    44  func (root *routingNode) addPattern(p *pattern, h Handler) {
    45  	// First level of tree is host.
    46  	n := root.addChild(p.host)
    47  	// Second level of tree is method.
    48  	n = n.addChild(p.method)
    49  	// Remaining levels are path.
    50  	n.addSegments(p.segments, p, h)
    51  }
    52  
    53  // addSegments adds the given segments to the tree rooted at n.
    54  // If there are no segments, then n is a leaf node that holds
    55  // the given pattern and handler.
    56  func (n *routingNode) addSegments(segs []segment, p *pattern, h Handler) {
    57  	if len(segs) == 0 {
    58  		n.set(p, h)
    59  		return
    60  	}
    61  	seg := segs[0]
    62  	if seg.multi {
    63  		if len(segs) != 1 {
    64  			panic("multi wildcard not last")
    65  		}
    66  		n.addChild("*").set(p, h)
    67  	} else if seg.wild {
    68  		n.addChild("").addSegments(segs[1:], p, h)
    69  	} else {
    70  		n.addChild(seg.s).addSegments(segs[1:], p, h)
    71  	}
    72  }
    73  
    74  // set sets the pattern and handler for n, which
    75  // must be a leaf node.
    76  func (n *routingNode) set(p *pattern, h Handler) {
    77  	if n.pattern != nil || n.handler != nil {
    78  		panic("non-nil leaf fields")
    79  	}
    80  	n.pattern = p
    81  	n.handler = h
    82  }
    83  
    84  // addChild adds a child node with the given key to n
    85  // if one does not exist, and returns the child.
    86  func (n *routingNode) addChild(key string) *routingNode {
    87  	if key == "" {
    88  		if n.emptyChild == nil {
    89  			n.emptyChild = &routingNode{}
    90  		}
    91  		return n.emptyChild
    92  	}
    93  	if c := n.findChild(key); c != nil {
    94  		return c
    95  	}
    96  	c := &routingNode{}
    97  	n.children.add(key, c)
    98  	return c
    99  }
   100  
   101  // findChild returns the child of n with the given key, or nil
   102  // if there is no child with that key.
   103  func (n *routingNode) findChild(key string) *routingNode {
   104  	if key == "" {
   105  		return n.emptyChild
   106  	}
   107  	r, _ := n.children.find(key)
   108  	return r
   109  }
   110  
   111  // match returns the leaf node under root that matches the arguments, and a list
   112  // of values for pattern wildcards in the order that the wildcards appear.
   113  // For example, if the request path is "/a/b/c" and the pattern is "/{x}/b/{y}",
   114  // then the second return value will be []string{"a", "c"}.
   115  func (root *routingNode) match(host, method, path string) (*routingNode, []string) {
   116  	if host != "" {
   117  		// There is a host. If there is a pattern that specifies that host and it
   118  		// matches, we are done. If the pattern doesn't match, fall through to
   119  		// try patterns with no host.
   120  		if l, m := root.findChild(host).matchMethodAndPath(method, path); l != nil {
   121  			return l, m
   122  		}
   123  	}
   124  	return root.emptyChild.matchMethodAndPath(method, path)
   125  }
   126  
   127  // matchMethodAndPath matches the method and path.
   128  // Its return values are the same as [routingNode.match].
   129  // The receiver should be a child of the root.
   130  func (n *routingNode) matchMethodAndPath(method, path string) (*routingNode, []string) {
   131  	if n == nil {
   132  		return nil, nil
   133  	}
   134  	if l, m := n.findChild(method).matchPath(path, nil); l != nil {
   135  		// Exact match of method name.
   136  		return l, m
   137  	}
   138  	if method == "HEAD" {
   139  		// GET matches HEAD too.
   140  		if l, m := n.findChild("GET").matchPath(path, nil); l != nil {
   141  			return l, m
   142  		}
   143  	}
   144  	// No exact match; try patterns with no method.
   145  	return n.emptyChild.matchPath(path, nil)
   146  }
   147  
   148  // matchPath matches a path.
   149  // Its return values are the same as [routingNode.match].
   150  // matchPath calls itself recursively. The matches argument holds the wildcard matches
   151  // found so far.
   152  func (n *routingNode) matchPath(path string, matches []string) (*routingNode, []string) {
   153  	if n == nil {
   154  		return nil, nil
   155  	}
   156  	// If path is empty, then we are done.
   157  	// If n is a leaf node, we found a match; return it.
   158  	// If n is an interior node (which means it has a nil pattern),
   159  	// then we failed to match.
   160  	if path == "" {
   161  		if n.pattern == nil {
   162  			return nil, nil
   163  		}
   164  		return n, matches
   165  	}
   166  	// Get the first segment of path.
   167  	seg, rest := firstSegment(path)
   168  	// First try matching against patterns that have a literal for this position.
   169  	// We know by construction that such patterns are more specific than those
   170  	// with a wildcard at this position (they are either more specific, equivalent,
   171  	// or overlap, and we ruled out the first two when the patterns were registered).
   172  	if n, m := n.findChild(seg).matchPath(rest, matches); n != nil {
   173  		return n, m
   174  	}
   175  	// If matching a literal fails, try again with patterns that have a single
   176  	// wildcard (represented by an empty string in the child mapping).
   177  	// Again, by construction, patterns with a single wildcard must be more specific than
   178  	// those with a multi wildcard.
   179  	// We skip this step if the segment is a trailing slash, because single wildcards
   180  	// don't match trailing slashes.
   181  	if seg != "/" {
   182  		if n, m := n.emptyChild.matchPath(rest, append(matches, seg)); n != nil {
   183  			return n, m
   184  		}
   185  	}
   186  	// Lastly, match the pattern (there can be at most one) that has a multi
   187  	// wildcard in this position to the rest of the path.
   188  	if c := n.findChild("*"); c != nil {
   189  		// Don't record a match for a nameless wildcard (which arises from a
   190  		// trailing slash in the pattern).
   191  		if c.pattern.lastSegment().s != "" {
   192  			matches = append(matches, pathUnescape(path[1:])) // remove initial slash
   193  		}
   194  		return c, matches
   195  	}
   196  	return nil, nil
   197  }
   198  
   199  // firstSegment splits path into its first segment, and the rest.
   200  // The path must begin with "/".
   201  // If path consists of only a slash, firstSegment returns ("/", "").
   202  // The segment is returned unescaped, if possible.
   203  func firstSegment(path string) (seg, rest string) {
   204  	if path == "/" {
   205  		return "/", ""
   206  	}
   207  	path = path[1:] // drop initial slash
   208  	i := strings.IndexByte(path, '/')
   209  	if i < 0 {
   210  		i = len(path)
   211  	}
   212  	return pathUnescape(path[:i]), path[i:]
   213  }
   214  
   215  // matchingMethods adds to methodSet all the methods that would result in a
   216  // match if passed to routingNode.match with the given host and path.
   217  func (root *routingNode) matchingMethods(host, path string, methodSet map[string]bool) {
   218  	if host != "" {
   219  		root.findChild(host).matchingMethodsPath(path, methodSet)
   220  	}
   221  	root.emptyChild.matchingMethodsPath(path, methodSet)
   222  	if methodSet["GET"] {
   223  		methodSet["HEAD"] = true
   224  	}
   225  }
   226  
   227  func (n *routingNode) matchingMethodsPath(path string, set map[string]bool) {
   228  	if n == nil {
   229  		return
   230  	}
   231  	n.children.eachPair(func(method string, c *routingNode) bool {
   232  		if p, _ := c.matchPath(path, nil); p != nil {
   233  			set[method] = true
   234  		}
   235  		return true
   236  	})
   237  	// Don't look at the empty child. If there were an empty
   238  	// child, it would match on any method, but we only
   239  	// call this when we fail to match on a method.
   240  }