Skip to content

Commit

Permalink
Add literal colon support (#1432)
Browse files Browse the repository at this point in the history
  • Loading branch information
wssccc committed Apr 22, 2022
1 parent 444e156 commit 9007250
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 0 deletions.
24 changes: 24 additions & 0 deletions gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
)

const defaultMultipartMemory = 32 << 20 // 32 MB
const escapedColon = "\\:"
const colon = ":"
const backslash = "\\"

var (
default404Body = []byte("404 page not found")
Expand Down Expand Up @@ -373,6 +376,7 @@ func (engine *Engine) Run(addr ...string) (err error) {
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
}
engine.updateRouteTrees()

address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
Expand Down Expand Up @@ -469,6 +473,26 @@ func (engine *Engine) validateHeader(header string) (clientIP string, valid bool
return "", false
}

// updateRouteTree do update to the route tree recursively
func updateRouteTree(n *node) {
n.path = strings.ReplaceAll(n.path, escapedColon, colon)
n.fullPath = strings.ReplaceAll(n.fullPath, escapedColon, colon)
n.indices = strings.ReplaceAll(n.indices, backslash, colon)
if n.children == nil {
return
}
for _, child := range n.children {
updateRouteTree(child)
}
}

// updateRouteTrees do update to the route trees
func (engine *Engine) updateRouteTrees() {
for _, tree := range engine.trees {
updateRouteTree(tree.root)
}
}

// parseIP parse a string representation of an IP and returns a net.IP with the
// minimum byte representation or nil if input is invalid.
func parseIP(ip string) net.IP {
Expand Down
22 changes: 22 additions & 0 deletions gin_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,25 @@ func TestTreeRunDynamicRouting(t *testing.T) {
func isWindows() bool {
return runtime.GOOS == "windows"
}

func TestEscapedColon(t *testing.T) {
router := New()
f := func(u string) {
router.GET(u, func(c *Context) { c.String(http.StatusOK, u) })
}
f("/r/r\\:r")
f("/r/r:r")
f("/r/r/:r")
f("/r/r/\\:r")
f("/r/r/r\\:r")

router.updateRouteTrees()
ts := httptest.NewServer(router)
defer ts.Close()

testRequest(t, ts.URL+"/r/r123", "", "/r/r:r")
testRequest(t, ts.URL+"/r/r:r", "", "/r/r\\:r")
testRequest(t, ts.URL+"/r/r/r123", "", "/r/r/:r")
testRequest(t, ts.URL+"/r/r/:r", "", "/r/r/\\:r")
testRequest(t, ts.URL+"/r/r/r:r", "", "/r/r/r\\:r")
}
11 changes: 11 additions & 0 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,18 @@ walk:
// Returns -1 as index, if no wildcard was found.
func findWildcard(path string) (wildcard string, i int, valid bool) {
// Find start
escapeColon := false
for start, c := range []byte(path) {
if escapeColon {
if c == ':' {
escapeColon = false
continue
}
}
if c == '\\' {
escapeColon = true
continue
}
// A wildcard starts with ':' (param) or '*' (catch-all)
if c != ':' && c != '*' {
continue
Expand Down
24 changes: 24 additions & 0 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func TestTreeWildcard(t *testing.T) {
"/get/abc/123abg/:param",
"/get/abc/123abf/:param",
"/get/abc/123abfff/:param",
"/get/abc/escaped_colon/test\\:param",
}
for _, route := range routes {
tree.addRoute(route, fakeHandler(route))
Expand Down Expand Up @@ -315,6 +316,7 @@ func TestTreeWildcard(t *testing.T) {
{"/get/abc/123abg/test", false, "/get/abc/123abg/:param", Params{Param{Key: "param", Value: "test"}}},
{"/get/abc/123abf/testss", false, "/get/abc/123abf/:param", Params{Param{Key: "param", Value: "testss"}}},
{"/get/abc/123abfff/te", false, "/get/abc/123abfff/:param", Params{Param{Key: "param", Value: "te"}}},
{"/get/abc/escaped_colon/test\\:param", false, "/get/abc/escaped_colon/test\\:param", nil},
})

checkPriorities(t, tree)
Expand Down Expand Up @@ -417,6 +419,9 @@ func TestTreeWildcardConflict(t *testing.T) {
{"/user_:name", false},
{"/id:id", false},
{"/id/:id", false},
{"/escape/test\\:d1", false},
{"/escape/test\\:d2", false},
{"/escape/test:param", false},
}
testRoutes(t, routes)
}
Expand Down Expand Up @@ -921,3 +926,22 @@ func TestTreeWildcardConflictEx(t *testing.T) {
}
}
}

func TestTreeInvalidEscape(t *testing.T) {
routes := map[string]bool{
"/r1/r": true,
"/r2/:r": true,
"/r3/\\:r": true,
"/r4/\\\\:r": false,
"/r5/\\~:r": false,
}
tree := &node{}
for route, valid := range routes {
recv := catchPanic(func() {
tree.addRoute(route, fakeHandler(route))
})
if recv == nil != valid {
t.Fatalf("%s should be %t but got %v", route, valid, recv)
}
}
}

0 comments on commit 9007250

Please sign in to comment.