Source File
routing_tree.go
Belonging Package
net/http
// Copyright 2023 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// This file implements a decision tree for fast matching of requests to// patterns.//// The root of the tree branches on the host of the request.// The next level branches on the method.// The remaining levels branch on consecutive segments of the path.//// The "more specific wins" precedence rule can result in backtracking.// For example, given the patterns// /a/b/z// /a/{x}/c// we will first try to match the path "/a/b/c" with /a/b/z, and// when that fails we will try against /a/{x}/c.package httpimport ()// A routingNode is a node in the decision tree.// The same struct is used for leaf and interior nodes.type routingNode struct {// A leaf node holds a single pattern and the Handler it was registered// with.pattern *patternhandler Handler// An interior node maps parts of the incoming request to child nodes.// special children keys:// "/" trailing slash (resulting from {$})// "" single wildcardchildren mapping[string, *routingNode]multiChild *routingNode // child with multi wildcardemptyChild *routingNode // optimization: child with key ""}// addPattern adds a pattern and its associated Handler to the tree// at root.func ( *routingNode) ( *pattern, Handler) {// First level of tree is host.:= .addChild(.host)// Second level of tree is method.= .addChild(.method)// Remaining levels are path..addSegments(.segments, , )}// addSegments adds the given segments to the tree rooted at n.// If there are no segments, then n is a leaf node that holds// the given pattern and handler.func ( *routingNode) ( []segment, *pattern, Handler) {if len() == 0 {.set(, )return}:= [0]if .multi {if len() != 1 {panic("multi wildcard not last")}:= &routingNode{}.multiChild =.set(, )} else if .wild {.addChild("").([1:], , )} else {.addChild(.s).([1:], , )}}// set sets the pattern and handler for n, which// must be a leaf node.func ( *routingNode) ( *pattern, Handler) {if .pattern != nil || .handler != nil {panic("non-nil leaf fields")}.pattern =.handler =}// addChild adds a child node with the given key to n// if one does not exist, and returns the child.func ( *routingNode) ( string) *routingNode {if == "" {if .emptyChild == nil {.emptyChild = &routingNode{}}return .emptyChild}if := .findChild(); != nil {return}:= &routingNode{}.children.add(, )return}// findChild returns the child of n with the given key, or nil// if there is no child with that key.func ( *routingNode) ( string) *routingNode {if == "" {return .emptyChild}, := .children.find()return}// match returns the leaf node under root that matches the arguments, and a list// of values for pattern wildcards in the order that the wildcards appear.// For example, if the request path is "/a/b/c" and the pattern is "/{x}/b/{y}",// then the second return value will be []string{"a", "c"}.func ( *routingNode) (, , string) (*routingNode, []string) {if != "" {// There is a host. If there is a pattern that specifies that host and it// matches, we are done. If the pattern doesn't match, fall through to// try patterns with no host.if , := .findChild().matchMethodAndPath(, ); != nil {return ,}}return .emptyChild.matchMethodAndPath(, )}// matchMethodAndPath matches the method and path.// Its return values are the same as [routingNode.match].// The receiver should be a child of the root.func ( *routingNode) (, string) (*routingNode, []string) {if == nil {return nil, nil}if , := .findChild().matchPath(, nil); != nil {// Exact match of method name.return ,}if == "HEAD" {// GET matches HEAD too.if , := .findChild("GET").matchPath(, nil); != nil {return ,}}// No exact match; try patterns with no method.return .emptyChild.matchPath(, nil)}// matchPath matches a path.// Its return values are the same as [routingNode.match].// matchPath calls itself recursively. The matches argument holds the wildcard matches// found so far.func ( *routingNode) ( string, []string) (*routingNode, []string) {if == nil {return nil, nil}// If path is empty, then we are done.// If n is a leaf node, we found a match; return it.// If n is an interior node (which means it has a nil pattern),// then we failed to match.if == "" {if .pattern == nil {return nil, nil}return ,}// Get the first segment of path., := firstSegment()// First try matching against patterns that have a literal for this position.// We know by construction that such patterns are more specific than those// with a wildcard at this position (they are either more specific, equivalent,// or overlap, and we ruled out the first two when the patterns were registered).if , := .findChild().(, ); != nil {return ,}// If matching a literal fails, try again with patterns that have a single// wildcard (represented by an empty string in the child mapping).// Again, by construction, patterns with a single wildcard must be more specific than// those with a multi wildcard.// We skip this step if the segment is a trailing slash, because single wildcards// don't match trailing slashes.if != "/" {if , := .emptyChild.(, append(, )); != nil {return ,}}// Lastly, match the pattern (there can be at most one) that has a multi// wildcard in this position to the rest of the path.if := .multiChild; != nil {// Don't record a match for a nameless wildcard (which arises from a// trailing slash in the pattern).if .pattern.lastSegment().s != "" {= append(, pathUnescape([1:])) // remove initial slash}return ,}return nil, nil}// firstSegment splits path into its first segment, and the rest.// The path must begin with "/".// If path consists of only a slash, firstSegment returns ("/", "").// The segment is returned unescaped, if possible.func ( string) (, string) {if == "/" {return "/", ""}= [1:] // drop initial slash:= strings.IndexByte(, '/')if < 0 {= len()}return pathUnescape([:]), [:]}// matchingMethods adds to methodSet all the methods that would result in a// match if passed to routingNode.match with the given host and path.func ( *routingNode) (, string, map[string]bool) {if != "" {.findChild().matchingMethodsPath(, )}.emptyChild.matchingMethodsPath(, )if ["GET"] {["HEAD"] = true}}func ( *routingNode) ( string, map[string]bool) {if == nil {return}.children.eachPair(func( string, *routingNode) bool {if , := .matchPath(, nil); != nil {[] = true}return true})// Don't look at the empty child. If there were an empty// child, it would match on any method, but we only// call this when we fail to match on a method.}
![]() |
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |