Skip to content

Commit d266c29

Browse files
committed
update router tree
1 parent c6ed41d commit d266c29

File tree

3 files changed

+91
-47
lines changed

3 files changed

+91
-47
lines changed

muxapi.go

+41
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,47 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14+
//
15+
//
16+
//
17+
// The registered path, against which the router matches incoming requests, can
18+
// contain two types of parameters:
19+
// Syntax Type
20+
// :name named parameter
21+
// *name catch-all parameter
22+
//
23+
// Named parameters are dynamic path segments. They match anything until the
24+
// next '/' or the path end:
25+
// Path: /blog/:category/:post
26+
//
27+
// Requests:
28+
// /blog/go/request-routers match: category="go", post="request-routers"
29+
// /blog/go/request-routers/ no match, but the router would redirect
30+
// /blog/go/ no match
31+
// /blog/go/request-routers/comments no match
32+
//
33+
// Catch-all parameters match anything until the path end, including the
34+
// directory index (the '/' before the catch-all). Since they match anything
35+
// until the end, catch-all parameters must always be the final path element.
36+
// Path: /files/*filepath
37+
//
38+
// Requests:
39+
// /files/ match: filepath="/"
40+
// /files/LICENSE match: filepath="/LICENSE"
41+
// /files/templates/article.html match: filepath="/templates/article.html"
42+
// /files no match, but the router would redirect
43+
//
44+
// The value of parameters is saved as a slice of the Param struct, consisting
45+
// each of a key and a value. The slice is passed to the Handle func as a third
46+
// parameter.
47+
// There are two ways to retrieve the value of a parameter:
48+
// // by the name of the parameter
49+
// user := ps.ByName("user") // defined by :user or *user
50+
//
51+
// // by the index of the parameter. This way you can also get the name (key)
52+
// thirdKey := ps[2].Key // the name of the 3rd parameter
53+
// thirdValue := ps[2].Value // the value of the 3rd parameter
54+
//
1455

1556
package faygo
1657

tree.go

+10-47
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,6 @@
1-
// The router matches incoming requests by the request method and the path.
2-
// If a handle is registered for this path and method, the router delegates the
3-
// request to that function.
4-
// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
5-
// register handles, for all other methods router.Handle can be used.
6-
//
7-
// The registered path, against which the router matches incoming requests, can
8-
// contain two types of parameters:
9-
// Syntax Type
10-
// :name named parameter
11-
// *name catch-all parameter
12-
//
13-
// Named parameters are dynamic path segments. They match anything until the
14-
// next '/' or the path end:
15-
// Path: /blog/:category/:post
16-
//
17-
// Requests:
18-
// /blog/go/request-routers match: category="go", post="request-routers"
19-
// /blog/go/request-routers/ no match, but the router would redirect
20-
// /blog/go/ no match
21-
// /blog/go/request-routers/comments no match
22-
//
23-
// Catch-all parameters match anything until the path end, including the
24-
// directory index (the '/' before the catch-all). Since they match anything
25-
// until the end, catch-all parameters must always be the final path element.
26-
// Path: /files/*filepath
27-
//
28-
// Requests:
29-
// /files/ match: filepath="/"
30-
// /files/LICENSE match: filepath="/LICENSE"
31-
// /files/templates/article.html match: filepath="/templates/article.html"
32-
// /files no match, but the router would redirect
33-
//
34-
// The value of parameters is saved as a slice of the Param struct, consisting
35-
// each of a key and a value. The slice is passed to the Handle func as a third
36-
// parameter.
37-
// There are two ways to retrieve the value of a parameter:
38-
// // by the name of the parameter
39-
// user := ps.ByName("user") // defined by :user or *user
40-
//
41-
// // by the index of the parameter. This way you can also get the name (key)
42-
// thirdKey := ps[2].Key // the name of the 3rd parameter
43-
// thirdValue := ps[2].Value // the value of the 3rd parameter
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+
// in the LICENSE file.
444

455
package faygo
466

@@ -140,9 +100,7 @@ func (n *node) incrementChildPrio(pos int) int {
140100
newPos := pos
141101
for newPos > 0 && n.children[newPos-1].priority < prio {
142102
// swap node positions
143-
tmpN := n.children[newPos-1]
144-
n.children[newPos-1] = n.children[newPos]
145-
n.children[newPos] = tmpN
103+
n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1]
146104

147105
newPos--
148106
}
@@ -230,7 +188,12 @@ func (n *node) addRoute(path string, handle Handle) {
230188
continue walk
231189
} else {
232190
// Wildcard conflict
233-
pathSeg := strings.SplitN(path, "/", 2)[0]
191+
var pathSeg string
192+
if n.nType == catchAll {
193+
pathSeg = path
194+
} else {
195+
pathSeg = strings.SplitN(path, "/", 2)[0]
196+
}
234197
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
235198
panic("'" + pathSeg +
236199
"' in new path '" + fullPath +

tree_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package faygo
77
import (
88
"fmt"
99
"reflect"
10+
"regexp"
1011
"strings"
1112
"testing"
1213
)
@@ -656,3 +657,42 @@ func TestTreeInvalidNodeType(t *testing.T) {
656657
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
657658
}
658659
}
660+
661+
func TestTreeWildcardConflictEx(t *testing.T) {
662+
conflicts := [...]struct {
663+
route string
664+
segPath string
665+
existPath string
666+
existSegPath string
667+
}{
668+
{"/who/are/foo", "/foo", `/who/are/\*you`, `/\*you`},
669+
{"/who/are/foo/", "/foo/", `/who/are/\*you`, `/\*you`},
670+
{"/who/are/foo/bar", "/foo/bar", `/who/are/\*you`, `/\*you`},
671+
{"/conxxx", "xxx", `/con:tact`, `:tact`},
672+
{"/conooo/xxx", "ooo", `/con:tact`, `:tact`},
673+
}
674+
675+
for _, conflict := range conflicts {
676+
// I have to re-create a 'tree', because the 'tree' will be
677+
// in an inconsistent state when the loop recovers from the
678+
// panic which threw by 'addRoute' function.
679+
tree := &node{}
680+
routes := [...]string{
681+
"/con:tact",
682+
"/who/are/*you",
683+
"/who/foo/hello",
684+
}
685+
686+
for _, route := range routes {
687+
tree.addRoute(route, fakeHandler(route))
688+
}
689+
690+
recv := catchPanic(func() {
691+
tree.addRoute(conflict.route, fakeHandler(conflict.route))
692+
})
693+
694+
if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'", conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) {
695+
t.Fatalf("invalid wildcard conflict error (%v)", recv)
696+
}
697+
}
698+
}

0 commit comments

Comments
 (0)