Browse Source

Allow SkipClean to be evaluated on each route independently.

pull/463/head
George Vilches 7 years ago
parent
commit
98c7272ce6
  1. 40
      mux.go
  2. 34
      mux_test.go
  3. 17
      regexp.go
  4. 1
      route.go

40
mux.go

@ -116,6 +116,8 @@ func copyRouteConf(r routeConf) routeConf {
c.matchers = append(c.matchers, m) c.matchers = append(c.matchers, m)
} }
c.skipClean = r.skipClean
return c return c
} }
@ -173,26 +175,6 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
// When there is a match, the route variables can be retrieved calling // When there is a match, the route variables can be retrieved calling
// mux.Vars(request). // mux.Vars(request).
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !r.skipClean {
path := req.URL.Path
if r.useEncodedPath {
path = req.URL.EscapedPath()
}
// Clean path to canonical form and redirect.
if p := cleanPath(path); p != path {
// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
// http://code.google.com/p/go/issues/detail?id=5252
url := *req.URL
url.Path = p
p = url.String()
w.Header().Set("Location", p)
w.WriteHeader(http.StatusMovedPermanently)
return
}
}
var match RouteMatch var match RouteMatch
var handler http.Handler var handler http.Handler
if r.Match(req, &match) { if r.Match(req, &match) {
@ -209,6 +191,17 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler = http.NotFoundHandler() handler = http.NotFoundHandler()
} }
if match.OnlyMatchedCleanPath {
// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
// http://code.google.com/p/go/issues/detail?id=5252
url := *req.URL
url.Path = match.CleanPath
w.Header().Set("Location", url.String())
w.WriteHeader(http.StatusMovedPermanently)
return
}
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
} }
@ -413,6 +406,13 @@ type RouteMatch struct {
Handler http.Handler Handler http.Handler
Vars map[string]string Vars map[string]string
// Cleaned version of the path being matched.
CleanPath string
// true if a valid route match occurs, but only on the cleaned version
// of a URL.
OnlyMatchedCleanPath bool
// MatchErr is set to appropriate matching error // MatchErr is set to appropriate matching error
// It is set to ErrMethodMismatch if there is a mismatch in // It is set to ErrMethodMismatch if there is a mismatch in
// the request method and route method // the request method and route method

34
mux_test.go

@ -1995,6 +1995,40 @@ func TestSkipClean(t *testing.T) {
} }
} }
func TestSkipCleanSubrouter(t *testing.T) {
func1 := func(w http.ResponseWriter, r *http.Request) {}
func2 := func(w http.ResponseWriter, r *http.Request) {}
r := NewRouter().SkipClean(true)
r.HandleFunc("/api/", func1).Name("func2")
subRouter := r.PathPrefix("/v0").Subrouter().SkipClean(false)
subRouter.HandleFunc("/action/do/", func2).Name("func2")
req, _ := http.NewRequest("GET", "http://localhost/api/?abc=def", nil)
res := NewRecorder()
r.ServeHTTP(res, req)
if len(res.HeaderMap["Location"]) != 0 {
t.Errorf("Req 1: Shouldn't redirect since route is already clean")
}
req, _ = http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
res = NewRecorder()
r.ServeHTTP(res, req)
if len(res.HeaderMap["Location"]) != 0 {
t.Errorf("Req 2: Shouldn't redirect since skip clean is disabled")
}
req, _ = http.NewRequest("GET", "http://localhost/v0/action//do/?ghi=jkl", nil)
res = NewRecorder()
r.ServeHTTP(res, req)
if len(res.HeaderMap["Location"]) == 0 {
t.Errorf("Req 3: Should redirect since skip clean is enabled for subroute")
}
}
// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW // https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
func TestSubrouterHeader(t *testing.T) { func TestSubrouterHeader(t *testing.T) {
expected := "func1 response" expected := "func1 response"

17
regexp.go

@ -17,6 +17,7 @@ import (
type routeRegexpOptions struct { type routeRegexpOptions struct {
strictSlash bool strictSlash bool
useEncodedPath bool useEncodedPath bool
skipClean bool
} }
type regexpType int type regexpType int
@ -170,7 +171,21 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
if r.options.useEncodedPath { if r.options.useEncodedPath {
path = req.URL.EscapedPath() path = req.URL.EscapedPath()
} }
return r.regexp.MatchString(path) if !r.options.skipClean {
// Only clean the path when needed, and cache the result for later use.
if match.CleanPath == "" {
// Clean path to canonical form for testing purposes.
match.CleanPath = cleanPath(path)
}
path = match.CleanPath
}
isMatch := r.regexp.MatchString(path)
if isMatch {
if !r.options.skipClean && req.URL.Path != match.CleanPath {
match.OnlyMatchedCleanPath = true
}
}
return isMatch
} }
return r.regexp.MatchString(getHost(req)) return r.regexp.MatchString(getHost(req))

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,
skipClean: r.skipClean,
}) })
if err != nil { if err != nil {
return err return err

Loading…
Cancel
Save