@@ -17,6 +17,7 @@ import (
1717var (
1818 strColon = []byte (":" )
1919 strStar = []byte ("*" )
20+ strSlash = []byte ("/" )
2021)
2122
2223// Param is a single URL parameter, consisting of a key and a value.
@@ -98,6 +99,11 @@ func countParams(path string) uint16 {
9899 return n
99100}
100101
102+ func countSections (path string ) uint16 {
103+ s := bytesconv .StringToBytes (path )
104+ return uint16 (bytes .Count (s , strSlash ))
105+ }
106+
101107type nodeType uint8
102108
103109const (
@@ -394,16 +400,19 @@ type nodeValue struct {
394400 fullPath string
395401}
396402
403+ type skippedNode struct {
404+ path string
405+ node * node
406+ paramsCount int16
407+ }
408+
397409// Returns the handle registered with the given path (key). The values of
398410// wildcards are saved to a map.
399411// If no handle can be found, a TSR (trailing slash redirect) recommendation is
400412// made if a handle exists with an extra (without the) trailing slash for the
401413// given path.
402- func (n * node ) getValue (path string , params * Params , unescape bool ) (value nodeValue ) {
403- var (
404- skippedPath string
405- latestNode = n // Caching the latest node
406- )
414+ func (n * node ) getValue (path string , params * Params , skippedNodes * []skippedNode , unescape bool ) (value nodeValue ) {
415+ var globalParamsCount int16
407416
408417walk: // Outer loop for walking the tree
409418 for {
@@ -418,15 +427,20 @@ walk: // Outer loop for walking the tree
418427 if c == idxc {
419428 // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
420429 if n .wildChild {
421- skippedPath = prefix + path
422- latestNode = & node {
423- path : n .path ,
424- wildChild : n .wildChild ,
425- nType : n .nType ,
426- priority : n .priority ,
427- children : n .children ,
428- handlers : n .handlers ,
429- fullPath : n .fullPath ,
430+ index := len (* skippedNodes )
431+ * skippedNodes = (* skippedNodes )[:index + 1 ]
432+ (* skippedNodes )[index ] = skippedNode {
433+ path : prefix + path ,
434+ node : & node {
435+ path : n .path ,
436+ wildChild : n .wildChild ,
437+ nType : n .nType ,
438+ priority : n .priority ,
439+ children : n .children ,
440+ handlers : n .handlers ,
441+ fullPath : n .fullPath ,
442+ },
443+ paramsCount : globalParamsCount ,
430444 }
431445 }
432446
@@ -435,10 +449,22 @@ walk: // Outer loop for walking the tree
435449 }
436450 }
437451 // If the path at the end of the loop is not equal to '/' and the current node has no child nodes
438- // the current node needs to be equal to the latest matching node
439- matched := path != "/" && ! n .wildChild
440- if matched {
441- n = latestNode
452+ // the current node needs to roll back to last vaild skippedNode
453+
454+ if path != "/" && ! n .wildChild {
455+ for l := len (* skippedNodes ); l > 0 ; {
456+ skippedNode := (* skippedNodes )[l - 1 ]
457+ * skippedNodes = (* skippedNodes )[:l - 1 ]
458+ if strings .HasSuffix (skippedNode .path , path ) {
459+ path = skippedNode .path
460+ n = skippedNode .node
461+ if value .params != nil {
462+ * value .params = (* value .params )[:skippedNode .paramsCount ]
463+ }
464+ globalParamsCount = skippedNode .paramsCount
465+ continue walk
466+ }
467+ }
442468 }
443469
444470 // If there is no wildcard pattern, recommend a redirection
@@ -452,18 +478,12 @@ walk: // Outer loop for walking the tree
452478
453479 // Handle wildcard child, which is always at the end of the array
454480 n = n .children [len (n .children )- 1 ]
481+ globalParamsCount ++
455482
456483 switch n .nType {
457484 case param :
458485 // fix truncate the parameter
459486 // tree_test.go line: 204
460- if matched {
461- path = prefix + path
462- // The saved path is used after the prefix route is intercepted by matching
463- if n .indices == "/" {
464- path = skippedPath [1 :]
465- }
466- }
467487
468488 // Find param end (either '/' or path end)
469489 end := 0
@@ -549,9 +569,22 @@ walk: // Outer loop for walking the tree
549569
550570 if path == prefix {
551571 // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
552- // the current node needs to be equal to the latest matching node
553- if latestNode .wildChild && n .handlers == nil && path != "/" {
554- n = latestNode .children [len (latestNode .children )- 1 ]
572+ // the current node needs to roll back to last vaild skippedNode
573+ if n .handlers == nil && path != "/" {
574+ for l := len (* skippedNodes ); l > 0 ; {
575+ skippedNode := (* skippedNodes )[l - 1 ]
576+ * skippedNodes = (* skippedNodes )[:l - 1 ]
577+ if strings .HasSuffix (skippedNode .path , path ) {
578+ path = skippedNode .path
579+ n = skippedNode .node
580+ if value .params != nil {
581+ * value .params = (* value .params )[:skippedNode .paramsCount ]
582+ }
583+ globalParamsCount = skippedNode .paramsCount
584+ continue walk
585+ }
586+ }
587+ // n = latestNode.children[len(latestNode.children)-1]
555588 }
556589 // We should have reached the node containing the handle.
557590 // Check if this node has a handle registered.
@@ -582,19 +615,21 @@ walk: // Outer loop for walking the tree
582615 return
583616 }
584617
585- if path != "/" && len (skippedPath ) > 0 && strings .HasSuffix (skippedPath , path ) {
586- path = skippedPath
587- // Reduce the number of cycles
588- n , latestNode = latestNode , n
589- // skippedPath cannot execute
590- // example:
591- // * /:cc/cc
592- // call /a/cc expectations:match/200 Actual:match/200
593- // call /a/dd expectations:unmatch/404 Actual: panic
594- // call /addr/dd/aa expectations:unmatch/404 Actual: panic
595- // skippedPath: It can only be executed if the secondary route is not found
596- skippedPath = ""
597- continue walk
618+ // roll back to last vaild skippedNode
619+ if path != "/" {
620+ for l := len (* skippedNodes ); l > 0 ; {
621+ skippedNode := (* skippedNodes )[l - 1 ]
622+ * skippedNodes = (* skippedNodes )[:l - 1 ]
623+ if strings .HasSuffix (skippedNode .path , path ) {
624+ path = skippedNode .path
625+ n = skippedNode .node
626+ if value .params != nil {
627+ * value .params = (* value .params )[:skippedNode .paramsCount ]
628+ }
629+ globalParamsCount = skippedNode .paramsCount
630+ continue walk
631+ }
632+ }
598633 }
599634
600635 // Nothing found. We can recommend to redirect to the same URL with an
0 commit comments