Skip to content

Commit 7f53248

Browse files
authored
Prevent double escaping of path on redirect (#61)
* Prevent double escaping of path on redirect Fixes #59
1 parent 8cc36ee commit 7f53248

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

router.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func redirect(w http.ResponseWriter, r *http.Request, newPath string, statusCode
109109
func (t *TreeMux) lookup(w http.ResponseWriter, r *http.Request) (result LookupResult, found bool) {
110110
result.StatusCode = http.StatusNotFound
111111
path := r.RequestURI
112+
unescapedPath := r.URL.Path
112113
pathLen := len(path)
113114
if pathLen > 0 && t.PathSource == RequestURI {
114115
rawQueryLen := len(r.URL.RawQuery)
@@ -128,14 +129,15 @@ func (t *TreeMux) lookup(w http.ResponseWriter, r *http.Request) (result LookupR
128129
trailingSlash := path[pathLen-1] == '/' && pathLen > 1
129130
if trailingSlash && t.RedirectTrailingSlash {
130131
path = path[:pathLen-1]
132+
unescapedPath = unescapedPath[:len(unescapedPath)-1]
131133
}
132134

133135
n, handler, params := t.root.search(r.Method, path[1:])
134136
if n == nil {
135137
if t.RedirectCleanPath {
136138
// Path was not found. Try cleaning it up and search again.
137139
// TODO Test this
138-
cleanPath := Clean(path)
140+
cleanPath := Clean(unescapedPath)
139141
n, handler, params = t.root.search(r.Method, cleanPath[1:])
140142
if n == nil {
141143
// Still nothing found.
@@ -169,11 +171,11 @@ func (t *TreeMux) lookup(w http.ResponseWriter, r *http.Request) (result LookupR
169171
var h HandlerFunc
170172
if n.addSlash {
171173
// Need to add a slash.
172-
h = redirectHandler(path+"/", statusCode)
174+
h = redirectHandler(unescapedPath+"/", statusCode)
173175
} else if path != "/" {
174176
// We need to remove the slash. This was already done at the
175177
// beginning of the function.
176-
h = redirectHandler(path, statusCode)
178+
h = redirectHandler(unescapedPath, statusCode)
177179
}
178180

179181
if h != nil {

router_test.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func panicHandler(w http.ResponseWriter, r *http.Request, params map[string]stri
2222

2323
func newRequest(method, path string, body io.Reader) (*http.Request, error) {
2424
r, _ := http.NewRequest(method, path, body)
25-
u, _ := url.Parse(path)
25+
u, _ := url.ParseRequestURI(path)
2626
r.URL = u
2727
r.RequestURI = path
2828
return r, nil
@@ -428,7 +428,7 @@ func testRedirect(t *testing.T, defaultBehavior, getBehavior, postBehavior Redir
428428
t.Errorf("/noslash/ expected code %d, saw %d", expectedCode, w.Code)
429429
}
430430
if expectedCode != http.StatusNoContent && w.Header().Get("Location") != "/noslash" {
431-
t.Errorf("/noslash/ was not redirected to /noslash")
431+
t.Errorf("/noslash/ was redirected to `%s` instead of /noslash", w.Header().Get("Location"))
432432
}
433433

434434
r, _ = newRequest(method, "//noslash/", nil)
@@ -1043,6 +1043,36 @@ func TestLookup(t *testing.T) {
10431043
tryLookup("POST", "/user/dimfeld/", true, http.StatusTemporaryRedirect)
10441044
}
10451045

1046+
func TestRedirectEscapedPath(t *testing.T) {
1047+
router := New()
1048+
1049+
testHandler := func(w http.ResponseWriter, r *http.Request, params map[string]string) {}
1050+
1051+
router.GET("/:escaped/", testHandler)
1052+
1053+
w := httptest.NewRecorder()
1054+
u, err := url.Parse("/Test P@th")
1055+
if err != nil {
1056+
t.Error(err)
1057+
return
1058+
}
1059+
1060+
r, _ := newRequest("GET", u.String(), nil)
1061+
1062+
router.ServeHTTP(w, r)
1063+
1064+
if w.Code != http.StatusMovedPermanently {
1065+
t.Errorf("Expected status 301 but saw %d", w.Code)
1066+
}
1067+
1068+
path := w.Header().Get("Location")
1069+
expected := "/Test%20P@th/"
1070+
if path != expected {
1071+
t.Errorf("Given path wasn't escaped correctly.\n"+
1072+
"Expected: %q\nBut got: %q", expected, path)
1073+
}
1074+
}
1075+
10461076
func BenchmarkRouterSimple(b *testing.B) {
10471077
router := New()
10481078

0 commit comments

Comments
 (0)