mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-16 21:32:11 +08:00
Merge b018f7ceb5f8aacb636b1b637b5f092cfd788334 into f4dec22c5097ba3d7635363b0a8d59766cb8aae6
This commit is contained in:
commit
f0b5ce404e
10
gin.go
10
gin.go
@ -80,6 +80,9 @@ type (
|
||||
HandleMethodNotAllowed bool
|
||||
ForwardedByClientIP bool
|
||||
|
||||
UseRawPath bool
|
||||
UnescapePathValues bool
|
||||
|
||||
// #726 #755 If enabled, it will thrust some headers starting with
|
||||
// 'X-AppEngine...' for better integration with that PaaS.
|
||||
AppEngine bool
|
||||
@ -285,6 +288,9 @@ func (engine *Engine) HandleContext(c *Context) {
|
||||
func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||
httpMethod := context.Request.Method
|
||||
path := context.Request.URL.Path
|
||||
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
|
||||
path = context.Request.URL.RawPath
|
||||
}
|
||||
|
||||
// Find root of the tree for the given HTTP method
|
||||
t := engine.trees
|
||||
@ -292,7 +298,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||
if t[i].method == httpMethod {
|
||||
root := t[i].root
|
||||
// Find route in tree
|
||||
handlers, params, tsr := root.getValue(path, context.Params)
|
||||
handlers, params, tsr := root.getValue(path, context.Params, engine.UnescapePathValues)
|
||||
if handlers != nil {
|
||||
context.handlers = handlers
|
||||
context.Params = params
|
||||
@ -317,7 +323,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||
if engine.HandleMethodNotAllowed {
|
||||
for _, tree := range engine.trees {
|
||||
if tree.method != httpMethod {
|
||||
if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
|
||||
if handlers, _, _ := tree.root.getValue(path, nil, engine.UnescapePathValues); handlers != nil {
|
||||
context.handlers = engine.allNoMethod
|
||||
serveError(context, 405, default405Body)
|
||||
return
|
||||
|
9
tree.go
9
tree.go
@ -5,6 +5,7 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
@ -363,7 +364,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
|
||||
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
||||
// made if a handle exists with an extra (without the) trailing slash for the
|
||||
// given path.
|
||||
func (n *node) getValue(path string, po Params) (handlers HandlersChain, p Params, tsr bool) {
|
||||
func (n *node) getValue(path string, po Params, unescapeValues bool) (handlers HandlersChain, p Params, tsr bool) {
|
||||
p = po
|
||||
walk: // Outer loop for walking the tree
|
||||
for {
|
||||
@ -407,6 +408,12 @@ walk: // Outer loop for walking the tree
|
||||
p = p[:i+1] // expand slice within preallocated capacity
|
||||
p[i].Key = n.path[1:]
|
||||
p[i].Value = path[:end]
|
||||
// as it is a path parameter, unescape value
|
||||
if unescapeValues {
|
||||
if val, err := url.QueryUnescape(path[:end]); err == nil {
|
||||
p[i].Value = val
|
||||
}
|
||||
}
|
||||
|
||||
// we need to go deeper!
|
||||
if end < len(path) {
|
||||
|
106
tree_test.go
106
tree_test.go
@ -35,11 +35,12 @@ type testRequests []struct {
|
||||
nilHandler bool
|
||||
route string
|
||||
ps Params
|
||||
unescape bool
|
||||
}
|
||||
|
||||
func checkRequests(t *testing.T, tree *node, requests testRequests) {
|
||||
for _, request := range requests {
|
||||
handler, ps, _ := tree.getValue(request.path, nil)
|
||||
handler, ps, _ := tree.getValue(request.path, nil, request.unescape)
|
||||
|
||||
if handler == nil {
|
||||
if !request.nilHandler {
|
||||
@ -134,17 +135,17 @@ func TestTreeAddAndGet(t *testing.T) {
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/a", false, "/a", nil},
|
||||
{"/", true, "", nil},
|
||||
{"/hi", false, "/hi", nil},
|
||||
{"/contact", false, "/contact", nil},
|
||||
{"/co", false, "/co", nil},
|
||||
{"/con", true, "", nil}, // key mismatch
|
||||
{"/cona", true, "", nil}, // key mismatch
|
||||
{"/no", true, "", nil}, // no matching child
|
||||
{"/ab", false, "/ab", nil},
|
||||
{"/α", false, "/α", nil},
|
||||
{"/β", false, "/β", nil},
|
||||
{"/a", false, "/a", nil, false},
|
||||
{"/", true, "", nil, false},
|
||||
{"/hi", false, "/hi", nil, false},
|
||||
{"/contact", false, "/contact", nil, false},
|
||||
{"/co", false, "/co", nil, false},
|
||||
{"/con", true, "", nil, false}, // key mismatch
|
||||
{"/cona", true, "", nil, false}, // key mismatch
|
||||
{"/no", true, "", nil, false}, // no matching child
|
||||
{"/ab", false, "/ab", nil, false},
|
||||
{"/α", false, "/α", nil, false},
|
||||
{"/β", false, "/β", nil, false},
|
||||
})
|
||||
|
||||
checkPriorities(t, tree)
|
||||
@ -177,20 +178,55 @@ func TestTreeWildcard(t *testing.T) {
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/", false, "/", nil},
|
||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
|
||||
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}},
|
||||
{"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||
{"/search/", false, "/search/", nil},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||
{"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}},
|
||||
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}},
|
||||
{"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}},
|
||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
|
||||
{"/", false, "/", nil, false},
|
||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}, false},
|
||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}, false},
|
||||
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}, false},
|
||||
{"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}, false},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}, false},
|
||||
{"/search/", false, "/search/", nil, false},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}, false},
|
||||
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}, false},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}, false},
|
||||
{"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}, false},
|
||||
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}, false},
|
||||
{"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}, false},
|
||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}, false},
|
||||
})
|
||||
|
||||
checkPriorities(t, tree)
|
||||
checkMaxParams(t, tree)
|
||||
}
|
||||
|
||||
func TestUnescapeParameters(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/",
|
||||
"/cmd/:tool/:sub",
|
||||
"/cmd/:tool/",
|
||||
"/src/*filepath",
|
||||
"/search/:query",
|
||||
"/files/:dir/*filepath",
|
||||
"/info/:user/project/:project",
|
||||
"/info/:user",
|
||||
}
|
||||
for _, route := range routes {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/", false, "/", nil, true},
|
||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}, true},
|
||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}, true},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}, true},
|
||||
{"/src/some/file+test.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file+test.png"}}, true},
|
||||
{"/src/some/file%2Ftest.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file%2Ftest.png"}}, true},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng in ünìcodé"}}, true},
|
||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}, true},
|
||||
{"/info/slash%2Fgordon", false, "/info/:user", Params{Param{"user", "slash/gordon"}}, true},
|
||||
})
|
||||
|
||||
checkPriorities(t, tree)
|
||||
@ -298,11 +334,11 @@ func TestTreeDupliatePath(t *testing.T) {
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/", false, "/", nil},
|
||||
{"/doc/", false, "/doc/", nil},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||
{"/", false, "/", nil, false},
|
||||
{"/doc/", false, "/doc/", nil, false},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}, false},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}, false},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}, false},
|
||||
})
|
||||
}
|
||||
|
||||
@ -430,7 +466,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||
"/doc/",
|
||||
}
|
||||
for _, route := range tsrRoutes {
|
||||
handler, _, tsr := tree.getValue(route, nil)
|
||||
handler, _, tsr := tree.getValue(route, nil, false)
|
||||
if handler != nil {
|
||||
t.Fatalf("non-nil handler for TSR route '%s", route)
|
||||
} else if !tsr {
|
||||
@ -447,7 +483,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||
"/api/world/abc",
|
||||
}
|
||||
for _, route := range noTsrRoutes {
|
||||
handler, _, tsr := tree.getValue(route, nil)
|
||||
handler, _, tsr := tree.getValue(route, nil, false)
|
||||
if handler != nil {
|
||||
t.Fatalf("non-nil handler for No-TSR route '%s", route)
|
||||
} else if tsr {
|
||||
@ -466,7 +502,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) {
|
||||
t.Fatalf("panic inserting test route: %v", recv)
|
||||
}
|
||||
|
||||
handler, _, tsr := tree.getValue("/", nil)
|
||||
handler, _, tsr := tree.getValue("/", nil, false)
|
||||
if handler != nil {
|
||||
t.Fatalf("non-nil handler")
|
||||
} else if tsr {
|
||||
@ -617,7 +653,7 @@ func TestTreeInvalidNodeType(t *testing.T) {
|
||||
|
||||
// normal lookup
|
||||
recv := catchPanic(func() {
|
||||
tree.getValue("/test", nil)
|
||||
tree.getValue("/test", nil, false)
|
||||
})
|
||||
if rs, ok := recv.(string); !ok || rs != panicMsg {
|
||||
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
|
||||
|
Loading…
x
Reference in New Issue
Block a user