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 }