diff --git a/doc/go1.16.html b/doc/go1.16.html
index 4753cf914d..09e974d07c 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -112,3 +112,17 @@ Do not send CLs removing the interior tags from such phrases.
TODO
+
+
+ In the net/http package, the
+ behavior of StripPrefix
+ has been changed to strip the prefix from the request URL's
+ RawPath field in addition to its Path field.
+ In past releases, only the Path field was trimmed, and so if the
+ request URL contained any escaped characters the URL would be modified to
+ have mismatched Path and RawPath fields.
+ In Go 1.16, StripPrefix trims both fields.
+ If there are escaped characters in the prefix part of the request URL the
+ handler serves a 404 instead of its previous behavior of invoking the
+ underlying handler with a mismatched Path/RawPath pair.
+
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 5f56932778..635bf5dfc9 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -2849,29 +2849,47 @@ func TestStripPrefix(t *testing.T) {
defer afterTest(t)
h := HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("X-Path", r.URL.Path)
+ w.Header().Set("X-RawPath", r.URL.RawPath)
})
- ts := httptest.NewServer(StripPrefix("/foo", h))
+ ts := httptest.NewServer(StripPrefix("/foo/bar", h))
defer ts.Close()
c := ts.Client()
- res, err := c.Get(ts.URL + "/foo/bar")
- if err != nil {
- t.Fatal(err)
+ cases := []struct {
+ reqPath string
+ path string // If empty we want a 404.
+ rawPath string
+ }{
+ {"/foo/bar/qux", "/qux", ""},
+ {"/foo/bar%2Fqux", "/qux", "%2Fqux"},
+ {"/foo%2Fbar/qux", "", ""}, // Escaped prefix does not match.
+ {"/bar", "", ""}, // No prefix match.
}
- if g, e := res.Header.Get("X-Path"), "/bar"; g != e {
- t.Errorf("test 1: got %s, want %s", g, e)
+ for _, tc := range cases {
+ t.Run(tc.reqPath, func(t *testing.T) {
+ res, err := c.Get(ts.URL + tc.reqPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if tc.path == "" {
+ if res.StatusCode != StatusNotFound {
+ t.Errorf("got %q, want 404 Not Found", res.Status)
+ }
+ return
+ }
+ if res.StatusCode != StatusOK {
+ t.Fatalf("got %q, want 200 OK", res.Status)
+ }
+ if g, w := res.Header.Get("X-Path"), tc.path; g != w {
+ t.Errorf("got Path %q, want %q", g, w)
+ }
+ if g, w := res.Header.Get("X-RawPath"), tc.rawPath; g != w {
+ t.Errorf("got RawPath %q, want %q", g, w)
+ }
+ })
}
- res.Body.Close()
-
- res, err = Get(ts.URL + "/bar")
- if err != nil {
- t.Fatal(err)
- }
- if g, e := res.StatusCode, 404; g != e {
- t.Errorf("test 2: got status %v, want %v", g, e)
- }
- res.Body.Close()
}
// https://golang.org/issue/18952.
diff --git a/src/net/http/server.go b/src/net/http/server.go
index d41b5f6f48..ed5de350a9 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -2062,22 +2062,26 @@ func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", Sta
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
-// StripPrefix returns a handler that serves HTTP requests
-// by removing the given prefix from the request URL's Path
-// and invoking the handler h. StripPrefix handles a
-// request for a path that doesn't begin with prefix by
-// replying with an HTTP 404 not found error.
+// StripPrefix returns a handler that serves HTTP requests by removing the
+// given prefix from the request URL's Path (and RawPath if set) and invoking
+// the handler h. StripPrefix handles a request for a path that doesn't begin
+// with prefix by replying with an HTTP 404 not found error. The prefix must
+// match exactly: if the prefix in the request contains escaped characters
+// the reply is also an HTTP 404 not found error.
func StripPrefix(prefix string, h Handler) Handler {
if prefix == "" {
return h
}
return HandlerFunc(func(w ResponseWriter, r *Request) {
- if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
+ p := strings.TrimPrefix(r.URL.Path, prefix)
+ rp := strings.TrimPrefix(r.URL.RawPath, prefix)
+ if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) {
r2 := new(Request)
*r2 = *r
r2.URL = new(url.URL)
*r2.URL = *r.URL
r2.URL.Path = p
+ r2.URL.RawPath = rp
h.ServeHTTP(w, r2)
} else {
NotFound(w, r)