@@ -80,6 +80,16 @@ func longestCommonPrefix(a, b string) int {
80
80
return i
81
81
}
82
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
+
83
93
func countParams (path string ) uint16 {
84
94
var n uint16
85
95
s := bytesconv .StringToBytes (path )
@@ -103,7 +113,7 @@ type node struct {
103
113
wildChild bool
104
114
nType nodeType
105
115
priority uint32
106
- children []* node
116
+ children []* node // child nodes, at most 1 :param style node at the end of the array
107
117
handlers HandlersChain
108
118
fullPath string
109
119
}
@@ -177,36 +187,9 @@ walk:
177
187
// Make new node a child of this node
178
188
if i < len (path ) {
179
189
path = path [i :]
180
-
181
- if n .wildChild {
182
- parentFullPathIndex += len (n .path )
183
- n = n .children [0 ]
184
- n .priority ++
185
-
186
- // Check if the wildcard matches
187
- if len (path ) >= len (n .path ) && n .path == path [:len (n .path )] &&
188
- // Adding a child to a catchAll is not possible
189
- n .nType != catchAll &&
190
- // Check for longer wildcard, e.g. :name and :names
191
- (len (n .path ) >= len (path ) || path [len (n .path )] == '/' ) {
192
- continue walk
193
- }
194
-
195
- pathSeg := path
196
- if n .nType != catchAll {
197
- pathSeg = strings .SplitN (path , "/" , 2 )[0 ]
198
- }
199
- prefix := fullPath [:strings .Index (fullPath , pathSeg )] + n .path
200
- panic ("'" + pathSeg +
201
- "' in new path '" + fullPath +
202
- "' conflicts with existing wildcard '" + n .path +
203
- "' in existing prefix '" + prefix +
204
- "'" )
205
- }
206
-
207
190
c := path [0 ]
208
191
209
- // slash after param
192
+ // '/' after param
210
193
if n .nType == param && c == '/' && len (n .children ) == 1 {
211
194
parentFullPathIndex += len (n .path )
212
195
n = n .children [0 ]
@@ -225,21 +208,47 @@ walk:
225
208
}
226
209
227
210
// Otherwise insert it
228
- if c != ':' && c != '*' {
211
+ if c != ':' && c != '*' && n . nType != catchAll {
229
212
// []byte for proper unicode char conversion, see #65
230
213
n .indices += bytesconv .BytesToString ([]byte {c })
231
214
child := & node {
232
215
fullPath : fullPath ,
233
216
}
234
- n .children = append ( n . children , child )
217
+ n .addChild ( child )
235
218
n .incrementChildPrio (len (n .indices ) - 1 )
236
219
n = child
220
+ } else if n .wildChild {
221
+ // inserting a wildcard node, need to check if it conflicts with the existing wildcard
222
+ n = n .children [len (n .children )- 1 ]
223
+ n .priority ++
224
+
225
+ // Check if the wildcard matches
226
+ if len (path ) >= len (n .path ) && n .path == path [:len (n .path )] &&
227
+ // Adding a child to a catchAll is not possible
228
+ n .nType != catchAll &&
229
+ // Check for longer wildcard, e.g. :name and :names
230
+ (len (n .path ) >= len (path ) || path [len (n .path )] == '/' ) {
231
+ continue walk
232
+ }
233
+
234
+ // Wildcard conflict
235
+ pathSeg := path
236
+ if n .nType != catchAll {
237
+ pathSeg = strings .SplitN (pathSeg , "/" , 2 )[0 ]
238
+ }
239
+ prefix := fullPath [:strings .Index (fullPath , pathSeg )] + n .path
240
+ panic ("'" + pathSeg +
241
+ "' in new path '" + fullPath +
242
+ "' conflicts with existing wildcard '" + n .path +
243
+ "' in existing prefix '" + prefix +
244
+ "'" )
237
245
}
246
+
238
247
n .insertChild (path , fullPath , handlers )
239
248
return
240
249
}
241
250
242
- // Otherwise and handle to current node
251
+ // Otherwise add handle to current node
243
252
if n .handlers != nil {
244
253
panic ("handlers are already registered for path '" + fullPath + "'" )
245
254
}
@@ -293,27 +302,20 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
293
302
panic ("wildcards must be named with a non-empty name in path '" + fullPath + "'" )
294
303
}
295
304
296
- // Check if this node has existing children which would be
297
- // unreachable if we insert the wildcard here
298
- if len (n .children ) > 0 {
299
- panic ("wildcard segment '" + wildcard +
300
- "' conflicts with existing children in path '" + fullPath + "'" )
301
- }
302
-
303
305
if wildcard [0 ] == ':' { // param
304
306
if i > 0 {
305
307
// Insert prefix before the current wildcard
306
308
n .path = path [:i ]
307
309
path = path [i :]
308
310
}
309
311
310
- n .wildChild = true
311
312
child := & node {
312
313
nType : param ,
313
314
path : wildcard ,
314
315
fullPath : fullPath ,
315
316
}
316
- n .children = []* node {child }
317
+ n .addChild (child )
318
+ n .wildChild = true
317
319
n = child
318
320
n .priority ++
319
321
@@ -326,7 +328,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
326
328
priority : 1 ,
327
329
fullPath : fullPath ,
328
330
}
329
- n .children = [] * node { child }
331
+ n .addChild ( child )
330
332
n = child
331
333
continue
332
334
}
@@ -360,7 +362,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
360
362
fullPath : fullPath ,
361
363
}
362
364
363
- n .children = [] * node { child }
365
+ n .addChild ( child )
364
366
n .indices = string ('/' )
365
367
n = child
366
368
n .priority ++
@@ -404,27 +406,28 @@ walk: // Outer loop for walking the tree
404
406
if len (path ) > len (prefix ) {
405
407
if path [:len (prefix )] == prefix {
406
408
path = path [len (prefix ):]
407
- // If this node does not have a wildcard (param or catchAll)
408
- // child, we can just look up the next child node and continue
409
- // to walk down the tree
410
- if ! n .wildChild {
411
- idxc := path [0 ]
412
- for i , c := range []byte (n .indices ) {
413
- if c == idxc {
414
- n = n .children [i ]
415
- continue walk
416
- }
409
+
410
+ // Try all the non-wildcard children first by matching the indices
411
+ idxc := path [0 ]
412
+ for i , c := range []byte (n .indices ) {
413
+ if c == idxc {
414
+ n = n .children [i ]
415
+ continue walk
417
416
}
417
+ }
418
418
419
+ // If there is no wildcard pattern, recommend a redirection
420
+ if ! n .wildChild {
419
421
// Nothing found.
420
422
// We can recommend to redirect to the same URL without a
421
423
// trailing slash if a leaf exists for that path.
422
424
value .tsr = (path == "/" && n .handlers != nil )
423
425
return
424
426
}
425
427
426
- // Handle wildcard child
427
- n = n .children [0 ]
428
+ // Handle wildcard child, which is always at the end of the array
429
+ n = n .children [len (n .children )- 1 ]
430
+
428
431
switch n .nType {
429
432
case param :
430
433
// Find param end (either '/' or path end)
0 commit comments