in runtime/router/trie.go [129:247]
func (t *tnode) set(path string, value http.Handler, lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted bool) error {
// find the longest common prefix
var shorterLength, i int
keyLength, pathLength := len(t.key), len(path)
if keyLength > pathLength {
shorterLength = pathLength
} else {
shorterLength = keyLength
}
for i < shorterLength && t.key[i] == path[i] {
i++
}
// Find the first character that differs between "path" and this node's key, if it exists.
// If we encounter a colon wildcard, ensure that the wildcard in path matches the wildcard
// in this node's key for that segment. The segment is a colon wildcard only when the colon
// is immediately after slash, e.g. "/:foo", "/x/:y". "/a:b" is not a colon wildcard segment.
var keyMatchIdx, pathMatchIdx int
for keyMatchIdx < keyLength && pathMatchIdx < pathLength {
if t.isSetWildCardPattern(path, keyMatchIdx, pathMatchIdx, lastKeyCharSlash, lastPathCharSlash, isWhitelisted) {
keyStartIdx, pathStartIdx := keyMatchIdx, pathMatchIdx
same := t.key[keyMatchIdx] == path[pathMatchIdx]
for keyMatchIdx < keyLength && t.key[keyMatchIdx] != '/' {
keyMatchIdx++
}
for pathMatchIdx < pathLength && path[pathMatchIdx] != '/' {
pathMatchIdx++
}
if same && (keyMatchIdx-keyStartIdx) != (pathMatchIdx-pathStartIdx) {
return ¶mMismatch{
t.key[keyStartIdx:keyMatchIdx],
path[pathStartIdx:pathMatchIdx],
t.key,
}
}
} else if t.key[keyMatchIdx] == path[pathMatchIdx] {
keyMatchIdx++
pathMatchIdx++
} else {
break
}
lastKeyCharSlash = t.key[keyMatchIdx-1] == '/'
lastPathCharSlash = path[pathMatchIdx-1] == '/'
}
// If the node key is fully matched, we match the rest path with children nodes to see if a value
// already exists for the path.
if keyMatchIdx == keyLength {
for _, c := range t.children {
if _, _, err := c.get(path[pathMatchIdx:], lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted); err == nil {
return errExist
}
}
}
// node key is longer than longest common prefix
if i < keyLength {
// key/path suffix being "*" means a conflict
if path[i:] == "*" || t.key[i:] == "*" {
return errExist
}
// split the node key, add new node with node key minus longest common prefix
split := &tnode{
key: t.key[i:],
value: t.value,
children: t.children,
}
t.key = t.key[:i]
t.value = nil
t.children = []*tnode{split}
// path is equal to longest common prefix
// set value on current node after split
if i == pathLength {
t.value = value
} else {
// path is longer than longest common prefix
// add new node with path minus longest common prefix
newNode := &tnode{
key: path[i:],
value: value,
}
t.addChildren(newNode, lastPathCharSlash)
}
}
// node key is equal to longest common prefix
if i == keyLength {
// path is equal to longest common prefix
if i == pathLength {
// node is guaranteed to have zero value,
// otherwise it would have caused errExist earlier
t.value = value
} else {
// path is longer than node key, try to recurse on node children
for _, c := range t.children {
if c.key[0] == path[i] {
lastKeyCharSlash = i > 0 && t.key[i-1] == '/'
lastPathCharSlash = i > 0 && path[i-1] == '/'
err := c.set(path[i:], value, lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted)
if e, ok := err.(*paramMismatch); ok {
e.existingPath = t.key + e.existingPath
return e
}
return err
}
}
// no children to recurse, add node with path minus longest common path
newNode := &tnode{
key: path[i:],
value: value,
}
t.addChildren(newNode, lastPathCharSlash)
}
}
return nil
}