Browse Source

Add a flag to unescape variable values

pull/662/head
Francesco Mari 4 years ago
parent
commit
81d61cff64
  1. 10
      mux.go
  2. 17
      mux_test.go
  3. 20
      regexp.go
  4. 1
      route.go

10
mux.go

@ -84,6 +84,8 @@ type routeConf struct {
// will not redirect // will not redirect
skipClean bool skipClean bool
unescapeVars bool
// Manager for the variables from host and path. // Manager for the variables from host and path.
regexp routeRegexpGroup regexp routeRegexpGroup
@ -260,6 +262,14 @@ func (r *Router) SkipClean(value bool) *Router {
return r return r
} }
// UnescapeVars tells the router to escape the route variables before invoking
// the handler. This is useful when used together with UseEncodedPath to avoid
// un-escaping the variable values in the handler.
func (r *Router) UnescapeVars(value bool) *Router {
r.unescapeVars = value
return r
}
// UseEncodedPath tells the router to match the encoded original path // UseEncodedPath tells the router to match the encoded original path
// to the routes. // to the routes.
// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".

17
mux_test.go

@ -1540,13 +1540,10 @@ func TestStrictSlash(t *testing.T) {
} }
func TestUseEncodedPath(t *testing.T) { func TestUseEncodedPath(t *testing.T) {
r := NewRouter()
r.UseEncodedPath()
tests := []routeTest{ tests := []routeTest{
{ {
title: "Router with useEncodedPath, URL with encoded slash does match", title: "Router with useEncodedPath, URL with encoded slash does match",
route: r.NewRoute().Path("/v1/{v1}/v2"), route: NewRouter().UseEncodedPath().NewRoute().Path("/v1/{v1}/v2"),
request: newRequest("GET", "http://localhost/v1/1%2F2/v2"), request: newRequest("GET", "http://localhost/v1/1%2F2/v2"),
vars: map[string]string{"v1": "1%2F2"}, vars: map[string]string{"v1": "1%2F2"},
host: "", host: "",
@ -1554,9 +1551,19 @@ func TestUseEncodedPath(t *testing.T) {
pathTemplate: `/v1/{v1}/v2`, pathTemplate: `/v1/{v1}/v2`,
shouldMatch: true, shouldMatch: true,
}, },
{
title: "Router with useEncodedPath, URL with encoded slash does match, variables decoded",
route: NewRouter().UseEncodedPath().UnescapeVars(true).NewRoute().Path("/v1/{v1}/v2"),
request: newRequest("GET", "http://localhost/v1/1%2F2/v2"),
vars: map[string]string{"v1": "1/2"},
host: "",
path: "/v1/1%2F2/v2",
pathTemplate: `/v1/{v1}/v2`,
shouldMatch: true,
},
{ {
title: "Router with useEncodedPath, URL with encoded slash doesn't match", title: "Router with useEncodedPath, URL with encoded slash doesn't match",
route: r.NewRoute().Path("/v1/1/2/v2"), route: NewRouter().UseEncodedPath().NewRoute().Path("/v1/1/2/v2"),
request: newRequest("GET", "http://localhost/v1/1%2F2/v2"), request: newRequest("GET", "http://localhost/v1/1%2F2/v2"),
vars: map[string]string{"v1": "1%2F2"}, vars: map[string]string{"v1": "1%2F2"},
host: "", host: "",

20
regexp.go

@ -17,6 +17,7 @@ import (
type routeRegexpOptions struct { type routeRegexpOptions struct {
strictSlash bool strictSlash bool
useEncodedPath bool useEncodedPath bool
unescapeVars bool
} }
type regexpType int type regexpType int
@ -204,7 +205,11 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
if r.regexpType == regexpTypeQuery { if r.regexpType == regexpTypeQuery {
value = url.QueryEscape(value) value = url.QueryEscape(value)
} }
urlValues[k] = value if r.options.unescapeVars {
urlValues[k] = url.PathEscape(value)
} else {
urlValues[k] = value
}
} }
rv := fmt.Sprintf(r.reverse, urlValues...) rv := fmt.Sprintf(r.reverse, urlValues...)
if !r.regexp.MatchString(rv) { if !r.regexp.MatchString(rv) {
@ -345,6 +350,9 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
matches := v.path.regexp.FindStringSubmatchIndex(path) matches := v.path.regexp.FindStringSubmatchIndex(path)
if len(matches) > 0 { if len(matches) > 0 {
extractVars(path, matches, v.path.varsN, m.Vars) extractVars(path, matches, v.path.varsN, m.Vars)
if r.unescapeVars {
unescapeVars(m.Vars)
}
// Check if we should redirect. // Check if we should redirect.
if v.path.options.strictSlash { if v.path.options.strictSlash {
p1 := strings.HasSuffix(path, "/") p1 := strings.HasSuffix(path, "/")
@ -386,3 +394,13 @@ func extractVars(input string, matches []int, names []string, output map[string]
output[name] = input[matches[2*i+2]:matches[2*i+3]] output[name] = input[matches[2*i+2]:matches[2*i+3]]
} }
} }
func unescapeVars(vars map[string]string) {
for k, v := range vars {
if decoded, err := url.PathUnescape(v); err != nil {
vars[k] = v
} else {
vars[k] = decoded
}
}
}

1
route.go

@ -186,6 +186,7 @@ func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
strictSlash: r.strictSlash, strictSlash: r.strictSlash,
useEncodedPath: r.useEncodedPath, useEncodedPath: r.useEncodedPath,
unescapeVars: r.unescapeVars,
}) })
if err != nil { if err != nil {
return err return err

Loading…
Cancel
Save