github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gin/tree.go (about) 1 // Copyright 2013 Julien Schmidt. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be found 3 // at https://github.com/julienschmidt/httprouter/blob/master/LICENSE 4 5 package gin 6 7 import ( 8 "bytes" 9 "net/url" 10 "strings" 11 "unicode" 12 "unicode/utf8" 13 14 "github.com/hellobchain/third_party/gin/internal/bytesconv" 15 ) 16 17 var ( 18 strColon = []byte(":") 19 strStar = []byte("*") 20 ) 21 22 // Param is a single URL parameter, consisting of a key and a value. 23 type Param struct { 24 Key string 25 Value string 26 } 27 28 // Params is a Param-slice, as returned by the router. 29 // The slice is ordered, the first URL parameter is also the first slice value. 30 // It is therefore safe to read values by the index. 31 type Params []Param 32 33 // Get returns the value of the first Param which key matches the given name. 34 // If no matching Param is found, an empty string is returned. 35 func (ps Params) Get(name string) (string, bool) { 36 for _, entry := range ps { 37 if entry.Key == name { 38 return entry.Value, true 39 } 40 } 41 return "", false 42 } 43 44 // ByName returns the value of the first Param which key matches the given name. 45 // If no matching Param is found, an empty string is returned. 46 func (ps Params) ByName(name string) (va string) { 47 va, _ = ps.Get(name) 48 return 49 } 50 51 type methodTree struct { 52 method string 53 root *node 54 } 55 56 type methodTrees []methodTree 57 58 func (trees methodTrees) get(method string) *node { 59 for _, tree := range trees { 60 if tree.method == method { 61 return tree.root 62 } 63 } 64 return nil 65 } 66 67 func min(a, b int) int { 68 if a <= b { 69 return a 70 } 71 return b 72 } 73 74 func longestCommonPrefix(a, b string) int { 75 i := 0 76 max := min(len(a), len(b)) 77 for i < max && a[i] == b[i] { 78 i++ 79 } 80 return i 81 } 82 83 // addChild will add a child node, keeping wildcards at the end 84 func (n *node) addChild(child *node) { 85 if n.wildChild && len(n.children) > 0 { 86 wildcardChild := n.children[len(n.children)-1] 87 n.children = append(n.children[:len(n.children)-1], child, wildcardChild) 88 } else { 89 n.children = append(n.children, child) 90 } 91 } 92 93 func countParams(path string) uint16 { 94 var n uint16 95 s := bytesconv.StringToBytes(path) 96 n += uint16(bytes.Count(s, strColon)) 97 n += uint16(bytes.Count(s, strStar)) 98 return n 99 } 100 101 type nodeType uint8 102 103 const ( 104 static nodeType = iota // default 105 root 106 param 107 catchAll 108 ) 109 110 type node struct { 111 path string 112 indices string 113 wildChild bool 114 nType nodeType 115 priority uint32 116 children []*node // child nodes, at most 1 :param style node at the end of the array 117 handlers HandlersChain 118 fullPath string 119 } 120 121 type skip struct { 122 path string 123 paramNode *node 124 } 125 126 // Increments priority of the given child and reorders if necessary 127 func (n *node) incrementChildPrio(pos int) int { 128 cs := n.children 129 cs[pos].priority++ 130 prio := cs[pos].priority 131 132 // Adjust position (move to front) 133 newPos := pos 134 for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { 135 // Swap node positions 136 cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] 137 } 138 139 // Build new index char string 140 if newPos != pos { 141 n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty 142 n.indices[pos:pos+1] + // The index char we move 143 n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos' 144 } 145 146 return newPos 147 } 148 149 // addRoute adds a node with the given handle to the path. 150 // Not concurrency-safe! 151 func (n *node) addRoute(path string, handlers HandlersChain) { 152 fullPath := path 153 n.priority++ 154 155 // Empty tree 156 if len(n.path) == 0 && len(n.children) == 0 { 157 n.insertChild(path, fullPath, handlers) 158 n.nType = root 159 return 160 } 161 162 parentFullPathIndex := 0 163 164 walk: 165 for { 166 // Find the longest common prefix. 167 // This also implies that the common prefix contains no ':' or '*' 168 // since the existing key can't contain those chars. 169 i := longestCommonPrefix(path, n.path) 170 171 // Split edge 172 if i < len(n.path) { 173 child := node{ 174 path: n.path[i:], 175 wildChild: n.wildChild, 176 indices: n.indices, 177 children: n.children, 178 handlers: n.handlers, 179 priority: n.priority - 1, 180 fullPath: n.fullPath, 181 } 182 183 n.children = []*node{&child} 184 // []byte for proper unicode char conversion, see #65 185 n.indices = bytesconv.BytesToString([]byte{n.path[i]}) 186 n.path = path[:i] 187 n.handlers = nil 188 n.wildChild = false 189 n.fullPath = fullPath[:parentFullPathIndex+i] 190 } 191 192 // Make new node a child of this node 193 if i < len(path) { 194 path = path[i:] 195 c := path[0] 196 197 // '/' after param 198 if n.nType == param && c == '/' && len(n.children) == 1 { 199 parentFullPathIndex += len(n.path) 200 n = n.children[0] 201 n.priority++ 202 continue walk 203 } 204 205 // Check if a child with the next path byte exists 206 for i, max := 0, len(n.indices); i < max; i++ { 207 if c == n.indices[i] { 208 parentFullPathIndex += len(n.path) 209 i = n.incrementChildPrio(i) 210 n = n.children[i] 211 continue walk 212 } 213 } 214 215 // Otherwise insert it 216 if c != ':' && c != '*' && n.nType != catchAll { 217 // []byte for proper unicode char conversion, see #65 218 n.indices += bytesconv.BytesToString([]byte{c}) 219 child := &node{ 220 fullPath: fullPath, 221 } 222 n.addChild(child) 223 n.incrementChildPrio(len(n.indices) - 1) 224 n = child 225 } else if n.wildChild { 226 // inserting a wildcard node, need to check if it conflicts with the existing wildcard 227 n = n.children[len(n.children)-1] 228 n.priority++ 229 230 // Check if the wildcard matches 231 if len(path) >= len(n.path) && n.path == path[:len(n.path)] && 232 // Adding a child to a catchAll is not possible 233 n.nType != catchAll && 234 // Check for longer wildcard, e.g. :name and :names 235 (len(n.path) >= len(path) || path[len(n.path)] == '/') { 236 continue walk 237 } 238 239 // Wildcard conflict 240 pathSeg := path 241 if n.nType != catchAll { 242 pathSeg = strings.SplitN(pathSeg, "/", 2)[0] 243 } 244 prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path 245 panic("'" + pathSeg + 246 "' in new path '" + fullPath + 247 "' conflicts with existing wildcard '" + n.path + 248 "' in existing prefix '" + prefix + 249 "'") 250 } 251 252 n.insertChild(path, fullPath, handlers) 253 return 254 } 255 256 // Otherwise add handle to current node 257 if n.handlers != nil { 258 panic("handlers are already registered for path '" + fullPath + "'") 259 } 260 n.handlers = handlers 261 n.fullPath = fullPath 262 return 263 } 264 } 265 266 // Search for a wildcard segment and check the name for invalid characters. 267 // Returns -1 as index, if no wildcard was found. 268 func findWildcard(path string) (wildcard string, i int, valid bool) { 269 // Find start 270 for start, c := range []byte(path) { 271 // A wildcard starts with ':' (param) or '*' (catch-all) 272 if c != ':' && c != '*' { 273 continue 274 } 275 276 // Find end and check for invalid characters 277 valid = true 278 for end, c := range []byte(path[start+1:]) { 279 switch c { 280 case '/': 281 return path[start : start+1+end], start, valid 282 case ':', '*': 283 valid = false 284 } 285 } 286 return path[start:], start, valid 287 } 288 return "", -1, false 289 } 290 291 func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { 292 for { 293 // Find prefix until first wildcard 294 wildcard, i, valid := findWildcard(path) 295 if i < 0 { // No wildcard found 296 break 297 } 298 299 // The wildcard name must not contain ':' and '*' 300 if !valid { 301 panic("only one wildcard per path segment is allowed, has: '" + 302 wildcard + "' in path '" + fullPath + "'") 303 } 304 305 // check if the wildcard has a name 306 if len(wildcard) < 2 { 307 panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") 308 } 309 310 if wildcard[0] == ':' { // param 311 if i > 0 { 312 // Insert prefix before the current wildcard 313 n.path = path[:i] 314 path = path[i:] 315 } 316 317 child := &node{ 318 nType: param, 319 path: wildcard, 320 fullPath: fullPath, 321 } 322 n.addChild(child) 323 n.wildChild = true 324 n = child 325 n.priority++ 326 327 // if the path doesn't end with the wildcard, then there 328 // will be another non-wildcard subpath starting with '/' 329 if len(wildcard) < len(path) { 330 path = path[len(wildcard):] 331 332 child := &node{ 333 priority: 1, 334 fullPath: fullPath, 335 } 336 n.addChild(child) 337 n = child 338 continue 339 } 340 341 // Otherwise we're done. Insert the handle in the new leaf 342 n.handlers = handlers 343 return 344 } 345 346 // catchAll 347 if i+len(wildcard) != len(path) { 348 panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") 349 } 350 351 if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { 352 panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'") 353 } 354 355 // currently fixed width 1 for '/' 356 i-- 357 if path[i] != '/' { 358 panic("no / before catch-all in path '" + fullPath + "'") 359 } 360 361 n.path = path[:i] 362 363 // First node: catchAll node with empty path 364 child := &node{ 365 wildChild: true, 366 nType: catchAll, 367 fullPath: fullPath, 368 } 369 370 n.addChild(child) 371 n.indices = string('/') 372 n = child 373 n.priority++ 374 375 // second node: node holding the variable 376 child = &node{ 377 path: path[i:], 378 nType: catchAll, 379 handlers: handlers, 380 priority: 1, 381 fullPath: fullPath, 382 } 383 n.children = []*node{child} 384 385 return 386 } 387 388 // If no wildcard was found, simply insert the path and handle 389 n.path = path 390 n.handlers = handlers 391 n.fullPath = fullPath 392 } 393 394 // nodeValue holds return values of (*Node).getValue method 395 type nodeValue struct { 396 handlers HandlersChain 397 params *Params 398 tsr bool 399 fullPath string 400 } 401 402 // Returns the handle registered with the given path (key). The values of 403 // wildcards are saved to a map. 404 // If no handle can be found, a TSR (trailing slash redirect) recommendation is 405 // made if a handle exists with an extra (without the) trailing slash for the 406 // given path. 407 func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { 408 var skipped *skip 409 410 walk: // Outer loop for walking the tree 411 for { 412 prefix := n.path 413 if len(path) > len(prefix) { 414 if path[:len(prefix)] == prefix { 415 path = path[len(prefix):] 416 417 // Try all the non-wildcard children first by matching the indices 418 idxc := path[0] 419 for i, c := range []byte(n.indices) { 420 if c == idxc { 421 if strings.HasPrefix(n.children[len(n.children)-1].path, ":") { 422 skipped = &skip{ 423 path: prefix + path, 424 paramNode: &node{ 425 path: n.path, 426 wildChild: n.wildChild, 427 nType: n.nType, 428 priority: n.priority, 429 children: n.children, 430 handlers: n.handlers, 431 fullPath: n.fullPath, 432 }, 433 } 434 } 435 436 n = n.children[i] 437 continue walk 438 } 439 } 440 441 // If there is no wildcard pattern, recommend a redirection 442 if !n.wildChild { 443 // Nothing found. 444 // We can recommend to redirect to the same URL without a 445 // trailing slash if a leaf exists for that path. 446 value.tsr = (path == "/" && n.handlers != nil) 447 return 448 } 449 450 // Handle wildcard child, which is always at the end of the array 451 n = n.children[len(n.children)-1] 452 453 switch n.nType { 454 case param: 455 // Find param end (either '/' or path end) 456 end := 0 457 for end < len(path) && path[end] != '/' { 458 end++ 459 } 460 461 // Save param value 462 if params != nil { 463 if value.params == nil { 464 value.params = params 465 } 466 // Expand slice within preallocated capacity 467 i := len(*value.params) 468 *value.params = (*value.params)[:i+1] 469 val := path[:end] 470 if unescape { 471 if v, err := url.QueryUnescape(val); err == nil { 472 val = v 473 } 474 } 475 (*value.params)[i] = Param{ 476 Key: n.path[1:], 477 Value: val, 478 } 479 } 480 481 // we need to go deeper! 482 if end < len(path) { 483 if len(n.children) > 0 { 484 path = path[end:] 485 n = n.children[0] 486 continue walk 487 } 488 489 // ... but we can't 490 value.tsr = (len(path) == end+1) 491 return 492 } 493 494 if value.handlers = n.handlers; value.handlers != nil { 495 value.fullPath = n.fullPath 496 return 497 } 498 if len(n.children) == 1 { 499 // No handle found. Check if a handle for this path + a 500 // trailing slash exists for TSR recommendation 501 n = n.children[0] 502 value.tsr = (n.path == "/" && n.handlers != nil) 503 } 504 return 505 506 case catchAll: 507 // Save param value 508 if params != nil { 509 if value.params == nil { 510 value.params = params 511 } 512 // Expand slice within preallocated capacity 513 i := len(*value.params) 514 *value.params = (*value.params)[:i+1] 515 val := path 516 if unescape { 517 if v, err := url.QueryUnescape(path); err == nil { 518 val = v 519 } 520 } 521 (*value.params)[i] = Param{ 522 Key: n.path[2:], 523 Value: val, 524 } 525 } 526 527 value.handlers = n.handlers 528 value.fullPath = n.fullPath 529 return 530 531 default: 532 panic("invalid node type") 533 } 534 } 535 } 536 537 if path == prefix { 538 // We should have reached the node containing the handle. 539 // Check if this node has a handle registered. 540 if value.handlers = n.handlers; value.handlers != nil { 541 value.fullPath = n.fullPath 542 return 543 } 544 545 // If there is no handle for this route, but this route has a 546 // wildcard child, there must be a handle for this path with an 547 // additional trailing slash 548 if path == "/" && n.wildChild && n.nType != root { 549 value.tsr = true 550 return 551 } 552 553 // No handle found. Check if a handle for this path + a 554 // trailing slash exists for trailing slash recommendation 555 for i, c := range []byte(n.indices) { 556 if c == '/' { 557 n = n.children[i] 558 value.tsr = (len(n.path) == 1 && n.handlers != nil) || 559 (n.nType == catchAll && n.children[0].handlers != nil) 560 return 561 } 562 } 563 564 return 565 } 566 567 if path != "/" && skipped != nil && strings.HasSuffix(skipped.path, path) { 568 path = skipped.path 569 n = skipped.paramNode 570 skipped = nil 571 continue walk 572 } 573 574 // Nothing found. We can recommend to redirect to the same URL with an 575 // extra trailing slash if a leaf exists for that path 576 value.tsr = (path == "/") || 577 (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && 578 path == prefix[:len(prefix)-1] && n.handlers != nil) 579 return 580 } 581 } 582 583 // Makes a case-insensitive lookup of the given path and tries to find a handler. 584 // It can optionally also fix trailing slashes. 585 // It returns the case-corrected path and a bool indicating whether the lookup 586 // was successful. 587 func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) { 588 const stackBufSize = 128 589 590 // Use a static sized buffer on the stack in the common case. 591 // If the path is too long, allocate a buffer on the heap instead. 592 buf := make([]byte, 0, stackBufSize) 593 if length := len(path) + 1; length > stackBufSize { 594 buf = make([]byte, 0, length) 595 } 596 597 ciPath := n.findCaseInsensitivePathRec( 598 path, 599 buf, // Preallocate enough memory for new path 600 [4]byte{}, // Empty rune buffer 601 fixTrailingSlash, 602 ) 603 604 return ciPath, ciPath != nil 605 } 606 607 // Shift bytes in array by n bytes left 608 func shiftNRuneBytes(rb [4]byte, n int) [4]byte { 609 switch n { 610 case 0: 611 return rb 612 case 1: 613 return [4]byte{rb[1], rb[2], rb[3], 0} 614 case 2: 615 return [4]byte{rb[2], rb[3]} 616 case 3: 617 return [4]byte{rb[3]} 618 default: 619 return [4]byte{} 620 } 621 } 622 623 // Recursive case-insensitive lookup function used by n.findCaseInsensitivePath 624 func (n *node) findCaseInsensitivePathRec(path string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) []byte { 625 npLen := len(n.path) 626 627 walk: // Outer loop for walking the tree 628 for len(path) >= npLen && (npLen == 0 || strings.EqualFold(path[1:npLen], n.path[1:])) { 629 // Add common prefix to result 630 oldPath := path 631 path = path[npLen:] 632 ciPath = append(ciPath, n.path...) 633 634 if len(path) == 0 { 635 // We should have reached the node containing the handle. 636 // Check if this node has a handle registered. 637 if n.handlers != nil { 638 return ciPath 639 } 640 641 // No handle found. 642 // Try to fix the path by adding a trailing slash 643 if fixTrailingSlash { 644 for i, c := range []byte(n.indices) { 645 if c == '/' { 646 n = n.children[i] 647 if (len(n.path) == 1 && n.handlers != nil) || 648 (n.nType == catchAll && n.children[0].handlers != nil) { 649 return append(ciPath, '/') 650 } 651 return nil 652 } 653 } 654 } 655 return nil 656 } 657 658 // If this node does not have a wildcard (param or catchAll) child, 659 // we can just look up the next child node and continue to walk down 660 // the tree 661 if !n.wildChild { 662 // Skip rune bytes already processed 663 rb = shiftNRuneBytes(rb, npLen) 664 665 if rb[0] != 0 { 666 // Old rune not finished 667 idxc := rb[0] 668 for i, c := range []byte(n.indices) { 669 if c == idxc { 670 // continue with child node 671 n = n.children[i] 672 npLen = len(n.path) 673 continue walk 674 } 675 } 676 } else { 677 // Process a new rune 678 var rv rune 679 680 // Find rune start. 681 // Runes are up to 4 byte long, 682 // -4 would definitely be another rune. 683 var off int 684 for max := min(npLen, 3); off < max; off++ { 685 if i := npLen - off; utf8.RuneStart(oldPath[i]) { 686 // read rune from cached path 687 rv, _ = utf8.DecodeRuneInString(oldPath[i:]) 688 break 689 } 690 } 691 692 // Calculate lowercase bytes of current rune 693 lo := unicode.ToLower(rv) 694 utf8.EncodeRune(rb[:], lo) 695 696 // Skip already processed bytes 697 rb = shiftNRuneBytes(rb, off) 698 699 idxc := rb[0] 700 for i, c := range []byte(n.indices) { 701 // Lowercase matches 702 if c == idxc { 703 // must use a recursive approach since both the 704 // uppercase byte and the lowercase byte might exist 705 // as an index 706 if out := n.children[i].findCaseInsensitivePathRec( 707 path, ciPath, rb, fixTrailingSlash, 708 ); out != nil { 709 return out 710 } 711 break 712 } 713 } 714 715 // If we found no match, the same for the uppercase rune, 716 // if it differs 717 if up := unicode.ToUpper(rv); up != lo { 718 utf8.EncodeRune(rb[:], up) 719 rb = shiftNRuneBytes(rb, off) 720 721 idxc := rb[0] 722 for i, c := range []byte(n.indices) { 723 // Uppercase matches 724 if c == idxc { 725 // Continue with child node 726 n = n.children[i] 727 npLen = len(n.path) 728 continue walk 729 } 730 } 731 } 732 } 733 734 // Nothing found. We can recommend to redirect to the same URL 735 // without a trailing slash if a leaf exists for that path 736 if fixTrailingSlash && path == "/" && n.handlers != nil { 737 return ciPath 738 } 739 return nil 740 } 741 742 n = n.children[0] 743 switch n.nType { 744 case param: 745 // Find param end (either '/' or path end) 746 end := 0 747 for end < len(path) && path[end] != '/' { 748 end++ 749 } 750 751 // Add param value to case insensitive path 752 ciPath = append(ciPath, path[:end]...) 753 754 // We need to go deeper! 755 if end < len(path) { 756 if len(n.children) > 0 { 757 // Continue with child node 758 n = n.children[0] 759 npLen = len(n.path) 760 path = path[end:] 761 continue 762 } 763 764 // ... but we can't 765 if fixTrailingSlash && len(path) == end+1 { 766 return ciPath 767 } 768 return nil 769 } 770 771 if n.handlers != nil { 772 return ciPath 773 } 774 775 if fixTrailingSlash && len(n.children) == 1 { 776 // No handle found. Check if a handle for this path + a 777 // trailing slash exists 778 n = n.children[0] 779 if n.path == "/" && n.handlers != nil { 780 return append(ciPath, '/') 781 } 782 } 783 784 return nil 785 786 case catchAll: 787 return append(ciPath, path...) 788 789 default: 790 panic("invalid node type") 791 } 792 } 793 794 // Nothing found. 795 // Try to fix the path by adding / removing a trailing slash 796 if fixTrailingSlash { 797 if path == "/" { 798 return ciPath 799 } 800 if len(path)+1 == npLen && n.path[len(path)] == '/' && 801 strings.EqualFold(path[1:], n.path[1:len(path)]) && n.handlers != nil { 802 return append(ciPath, n.path...) 803 } 804 } 805 return nil 806 }