The url.RawPath used when engine.UseRawPath is set to true. (#810)
This commit is contained in:
		
							
								
								
									
										25
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								gin.go
									
									
									
									
									
								
							@ -83,6 +83,13 @@ type (
 | 
				
			|||||||
		// #726 #755 If enabled, it will thrust some headers starting with
 | 
							// #726 #755 If enabled, it will thrust some headers starting with
 | 
				
			||||||
		// 'X-AppEngine...' for better integration with that PaaS.
 | 
							// 'X-AppEngine...' for better integration with that PaaS.
 | 
				
			||||||
		AppEngine bool
 | 
							AppEngine bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If enabled, the url.RawPath will be used to find parameters.
 | 
				
			||||||
 | 
							UseRawPath bool
 | 
				
			||||||
 | 
							// If true, the path value will be unescaped.
 | 
				
			||||||
 | 
							// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
 | 
				
			||||||
 | 
							// as url.Path gonna be used, which is already unescaped.
 | 
				
			||||||
 | 
							UnescapePathValues bool
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -94,6 +101,8 @@ var _ IRouter = &Engine{}
 | 
				
			|||||||
// - RedirectFixedPath:      false
 | 
					// - RedirectFixedPath:      false
 | 
				
			||||||
// - HandleMethodNotAllowed: false
 | 
					// - HandleMethodNotAllowed: false
 | 
				
			||||||
// - ForwardedByClientIP:    true
 | 
					// - ForwardedByClientIP:    true
 | 
				
			||||||
 | 
					// - UseRawPath:             false
 | 
				
			||||||
 | 
					// - UnescapePathValues:     true
 | 
				
			||||||
func New() *Engine {
 | 
					func New() *Engine {
 | 
				
			||||||
	debugPrintWARNINGNew()
 | 
						debugPrintWARNINGNew()
 | 
				
			||||||
	engine := &Engine{
 | 
						engine := &Engine{
 | 
				
			||||||
@ -107,6 +116,8 @@ func New() *Engine {
 | 
				
			|||||||
		HandleMethodNotAllowed: false,
 | 
							HandleMethodNotAllowed: false,
 | 
				
			||||||
		ForwardedByClientIP:    true,
 | 
							ForwardedByClientIP:    true,
 | 
				
			||||||
		AppEngine:              defaultAppEngine,
 | 
							AppEngine:              defaultAppEngine,
 | 
				
			||||||
 | 
							UseRawPath:             false,
 | 
				
			||||||
 | 
							UnescapePathValues:     true,
 | 
				
			||||||
		trees:                  make(methodTrees, 0, 9),
 | 
							trees:                  make(methodTrees, 0, 9),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	engine.RouterGroup.engine = engine
 | 
						engine.RouterGroup.engine = engine
 | 
				
			||||||
@ -284,7 +295,15 @@ func (engine *Engine) HandleContext(c *Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (engine *Engine) handleHTTPRequest(context *Context) {
 | 
					func (engine *Engine) handleHTTPRequest(context *Context) {
 | 
				
			||||||
	httpMethod := context.Request.Method
 | 
						httpMethod := context.Request.Method
 | 
				
			||||||
	path := context.Request.URL.Path
 | 
						var path string
 | 
				
			||||||
 | 
						var unescape bool
 | 
				
			||||||
 | 
						if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
 | 
				
			||||||
 | 
							path = context.Request.URL.RawPath
 | 
				
			||||||
 | 
							unescape = engine.UnescapePathValues
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							path = context.Request.URL.Path
 | 
				
			||||||
 | 
							unescape = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Find root of the tree for the given HTTP method
 | 
						// Find root of the tree for the given HTTP method
 | 
				
			||||||
	t := engine.trees
 | 
						t := engine.trees
 | 
				
			||||||
@ -292,7 +311,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
 | 
				
			|||||||
		if t[i].method == httpMethod {
 | 
							if t[i].method == httpMethod {
 | 
				
			||||||
			root := t[i].root
 | 
								root := t[i].root
 | 
				
			||||||
			// Find route in tree
 | 
								// Find route in tree
 | 
				
			||||||
			handlers, params, tsr := root.getValue(path, context.Params)
 | 
								handlers, params, tsr := root.getValue(path, context.Params, unescape)
 | 
				
			||||||
			if handlers != nil {
 | 
								if handlers != nil {
 | 
				
			||||||
				context.handlers = handlers
 | 
									context.handlers = handlers
 | 
				
			||||||
				context.Params = params
 | 
									context.Params = params
 | 
				
			||||||
@ -317,7 +336,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
 | 
				
			|||||||
	if engine.HandleMethodNotAllowed {
 | 
						if engine.HandleMethodNotAllowed {
 | 
				
			||||||
		for _, tree := range engine.trees {
 | 
							for _, tree := range engine.trees {
 | 
				
			||||||
			if tree.method != httpMethod {
 | 
								if tree.method != httpMethod {
 | 
				
			||||||
				if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
 | 
									if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
 | 
				
			||||||
					context.handlers = engine.allNoMethod
 | 
										context.handlers = engine.allNoMethod
 | 
				
			||||||
					serveError(context, 405, default405Body)
 | 
										serveError(context, 405, default405Body)
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
 | 
				
			|||||||
@ -400,3 +400,42 @@ func TestRouterNotFound(t *testing.T) {
 | 
				
			|||||||
	w = performRequest(router, "GET", "/")
 | 
						w = performRequest(router, "GET", "/")
 | 
				
			||||||
	assert.Equal(t, w.Code, 404)
 | 
						assert.Equal(t, w.Code, 404)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRouteRawPath(t *testing.T) {
 | 
				
			||||||
 | 
						route := New()
 | 
				
			||||||
 | 
						route.UseRawPath = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						route.POST("/project/:name/build/:num", func(c *Context) {
 | 
				
			||||||
 | 
							name := c.Params.ByName("name")
 | 
				
			||||||
 | 
							num := c.Params.ByName("num")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, c.Param("name"), name)
 | 
				
			||||||
 | 
							assert.Equal(t, c.Param("num"), num)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "Some/Other/Project", name)
 | 
				
			||||||
 | 
							assert.Equal(t, "222", num)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222")
 | 
				
			||||||
 | 
						assert.Equal(t, w.Code, 200)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRouteRawPathNoUnescape(t *testing.T) {
 | 
				
			||||||
 | 
						route := New()
 | 
				
			||||||
 | 
						route.UseRawPath = true
 | 
				
			||||||
 | 
						route.UnescapePathValues = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						route.POST("/project/:name/build/:num", func(c *Context) {
 | 
				
			||||||
 | 
							name := c.Params.ByName("name")
 | 
				
			||||||
 | 
							num := c.Params.ByName("num")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, c.Param("name"), name)
 | 
				
			||||||
 | 
							assert.Equal(t, c.Param("num"), num)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, "Some%2FOther%2FProject", name)
 | 
				
			||||||
 | 
							assert.Equal(t, "333", num)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333")
 | 
				
			||||||
 | 
						assert.Equal(t, w.Code, 200)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								tree.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								tree.go
									
									
									
									
									
								
							@ -5,6 +5,7 @@
 | 
				
			|||||||
package gin
 | 
					package gin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode"
 | 
						"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
 | 
					// 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
 | 
					// made if a handle exists with an extra (without the) trailing slash for the
 | 
				
			||||||
// given path.
 | 
					// given path.
 | 
				
			||||||
func (n *node) getValue(path string, po Params) (handlers HandlersChain, p Params, tsr bool) {
 | 
					func (n *node) getValue(path string, po Params, unescape bool) (handlers HandlersChain, p Params, tsr bool) {
 | 
				
			||||||
	p = po
 | 
						p = po
 | 
				
			||||||
walk: // Outer loop for walking the tree
 | 
					walk: // Outer loop for walking the tree
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
@ -406,7 +407,15 @@ walk: // Outer loop for walking the tree
 | 
				
			|||||||
					i := len(p)
 | 
										i := len(p)
 | 
				
			||||||
					p = p[:i+1] // expand slice within preallocated capacity
 | 
										p = p[:i+1] // expand slice within preallocated capacity
 | 
				
			||||||
					p[i].Key = n.path[1:]
 | 
										p[i].Key = n.path[1:]
 | 
				
			||||||
					p[i].Value = path[:end]
 | 
										val := path[:end]
 | 
				
			||||||
 | 
										if unescape {
 | 
				
			||||||
 | 
											var err error
 | 
				
			||||||
 | 
											if p[i].Value, err = url.QueryUnescape(val); err != nil {
 | 
				
			||||||
 | 
												p[i].Value = val // fallback, in case of error
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											p[i].Value = val
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// we need to go deeper!
 | 
										// we need to go deeper!
 | 
				
			||||||
					if end < len(path) {
 | 
										if end < len(path) {
 | 
				
			||||||
@ -440,7 +449,14 @@ walk: // Outer loop for walking the tree
 | 
				
			|||||||
					i := len(p)
 | 
										i := len(p)
 | 
				
			||||||
					p = p[:i+1] // expand slice within preallocated capacity
 | 
										p = p[:i+1] // expand slice within preallocated capacity
 | 
				
			||||||
					p[i].Key = n.path[2:]
 | 
										p[i].Key = n.path[2:]
 | 
				
			||||||
 | 
										if unescape {
 | 
				
			||||||
 | 
											var err error
 | 
				
			||||||
 | 
											if p[i].Value, err = url.QueryUnescape(path); err != nil {
 | 
				
			||||||
 | 
												p[i].Value = path // fallback, in case of error
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
						p[i].Value = path
 | 
											p[i].Value = path
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					handlers = n.handlers
 | 
										handlers = n.handlers
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										56
									
								
								tree_test.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								tree_test.go
									
									
									
									
									
								
							@ -37,9 +37,14 @@ type testRequests []struct {
 | 
				
			|||||||
	ps         Params
 | 
						ps         Params
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkRequests(t *testing.T, tree *node, requests testRequests) {
 | 
					func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) {
 | 
				
			||||||
 | 
						unescape := false
 | 
				
			||||||
 | 
						if len(unescapes) >= 1 {
 | 
				
			||||||
 | 
							unescape = unescapes[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, request := range requests {
 | 
						for _, request := range requests {
 | 
				
			||||||
		handler, ps, _ := tree.getValue(request.path, nil)
 | 
							handler, ps, _ := tree.getValue(request.path, nil, unescape)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if handler == nil {
 | 
							if handler == nil {
 | 
				
			||||||
			if !request.nilHandler {
 | 
								if !request.nilHandler {
 | 
				
			||||||
@ -197,6 +202,45 @@ func TestTreeWildcard(t *testing.T) {
 | 
				
			|||||||
	checkMaxParams(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, "")
 | 
				
			||||||
 | 
						unescape := true
 | 
				
			||||||
 | 
						checkRequests(t, tree, testRequests{
 | 
				
			||||||
 | 
							{"/", false, "/", nil},
 | 
				
			||||||
 | 
							{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
 | 
				
			||||||
 | 
							{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
 | 
				
			||||||
 | 
							{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
 | 
				
			||||||
 | 
							{"/src/some/file+test.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file test.png"}}},
 | 
				
			||||||
 | 
							{"/src/some/file++++%%%%test.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file++++%%%%test.png"}}},
 | 
				
			||||||
 | 
							{"/src/some/file%2Ftest.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file/test.png"}}},
 | 
				
			||||||
 | 
							{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng in ünìcodé"}}},
 | 
				
			||||||
 | 
							{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
 | 
				
			||||||
 | 
							{"/info/slash%2Fgordon", false, "/info/:user", Params{Param{"user", "slash/gordon"}}},
 | 
				
			||||||
 | 
							{"/info/slash%2Fgordon/project/Project%20%231", false, "/info/:user/project/:project", Params{Param{"user", "slash/gordon"}, Param{"project", "Project #1"}}},
 | 
				
			||||||
 | 
							{"/info/slash%%%%", false, "/info/:user", Params{Param{"user", "slash%%%%"}}},
 | 
				
			||||||
 | 
							{"/info/slash%%%%2Fgordon/project/Project%%%%20%231", false, "/info/:user/project/:project", Params{Param{"user", "slash%%%%2Fgordon"}, Param{"project", "Project%%%%20%231"}}},
 | 
				
			||||||
 | 
						}, unescape)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						checkPriorities(t, tree)
 | 
				
			||||||
 | 
						checkMaxParams(t, tree)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func catchPanic(testFunc func()) (recv interface{}) {
 | 
					func catchPanic(testFunc func()) (recv interface{}) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		recv = recover()
 | 
							recv = recover()
 | 
				
			||||||
@ -430,7 +474,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
 | 
				
			|||||||
		"/doc/",
 | 
							"/doc/",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, route := range tsrRoutes {
 | 
						for _, route := range tsrRoutes {
 | 
				
			||||||
		handler, _, tsr := tree.getValue(route, nil)
 | 
							handler, _, tsr := tree.getValue(route, nil, false)
 | 
				
			||||||
		if handler != nil {
 | 
							if handler != nil {
 | 
				
			||||||
			t.Fatalf("non-nil handler for TSR route '%s", route)
 | 
								t.Fatalf("non-nil handler for TSR route '%s", route)
 | 
				
			||||||
		} else if !tsr {
 | 
							} else if !tsr {
 | 
				
			||||||
@ -447,7 +491,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
 | 
				
			|||||||
		"/api/world/abc",
 | 
							"/api/world/abc",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, route := range noTsrRoutes {
 | 
						for _, route := range noTsrRoutes {
 | 
				
			||||||
		handler, _, tsr := tree.getValue(route, nil)
 | 
							handler, _, tsr := tree.getValue(route, nil, false)
 | 
				
			||||||
		if handler != nil {
 | 
							if handler != nil {
 | 
				
			||||||
			t.Fatalf("non-nil handler for No-TSR route '%s", route)
 | 
								t.Fatalf("non-nil handler for No-TSR route '%s", route)
 | 
				
			||||||
		} else if tsr {
 | 
							} else if tsr {
 | 
				
			||||||
@ -466,7 +510,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) {
 | 
				
			|||||||
		t.Fatalf("panic inserting test route: %v", recv)
 | 
							t.Fatalf("panic inserting test route: %v", recv)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handler, _, tsr := tree.getValue("/", nil)
 | 
						handler, _, tsr := tree.getValue("/", nil, false)
 | 
				
			||||||
	if handler != nil {
 | 
						if handler != nil {
 | 
				
			||||||
		t.Fatalf("non-nil handler")
 | 
							t.Fatalf("non-nil handler")
 | 
				
			||||||
	} else if tsr {
 | 
						} else if tsr {
 | 
				
			||||||
@ -617,7 +661,7 @@ func TestTreeInvalidNodeType(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// normal lookup
 | 
						// normal lookup
 | 
				
			||||||
	recv := catchPanic(func() {
 | 
						recv := catchPanic(func() {
 | 
				
			||||||
		tree.getValue("/test", nil)
 | 
							tree.getValue("/test", nil, false)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if rs, ok := recv.(string); !ok || rs != panicMsg {
 | 
						if rs, ok := recv.(string); !ok || rs != panicMsg {
 | 
				
			||||||
		t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
 | 
							t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user