Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix raw path handling in strip prefix #2382

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions middlewares/stripPrefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ type StripPrefix struct {

func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, prefix := range s.Prefixes {
if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
r.URL.Path = "/" + strings.TrimPrefix(p, "/")
if strings.HasPrefix(r.URL.Path, prefix) {
r.URL.Path = stripPrefix(r.URL.Path, prefix)
if r.URL.RawPath != "" {
r.URL.RawPath = stripPrefix(r.URL.RawPath, prefix)
}
s.serveRequest(w, r, strings.TrimSpace(prefix))
return
}
Expand All @@ -35,3 +38,11 @@ func (s *StripPrefix) serveRequest(w http.ResponseWriter, r *http.Request, prefi
func (s *StripPrefix) SetHandler(Handler http.Handler) {
s.Handler = Handler
}

func stripPrefix(s, prefix string) string {
return ensureLeadingSlash(strings.TrimPrefix(s, prefix))
}

func ensureLeadingSlash(str string) string {
return "/" + strings.TrimPrefix(str, "/")
}
3 changes: 3 additions & 0 deletions middlewares/stripPrefixRegex.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func (s *StripPrefixRegex) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

r.URL.Path = r.URL.Path[len(prefix.Path):]
if r.URL.RawPath != "" {
r.URL.RawPath = r.URL.RawPath[len(prefix.Path):]
}
r.Header.Add(ForwardedPrefixHeader, prefix.Path)
r.RequestURI = r.URL.RequestURI()
s.Handler.ServeHTTP(w, r)
Expand Down
13 changes: 11 additions & 2 deletions middlewares/stripPrefixRegex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import (
)

func TestStripPrefixRegex(t *testing.T) {

testPrefixRegex := []string{"/a/api/", "/b/{regex}/", "/c/{category}/{id:[0-9]+}/"}

tests := []struct {
path string
expectedStatusCode int
expectedPath string
expectedRawPath string
expectedHeader string
}{
{
Expand Down Expand Up @@ -61,16 +61,24 @@ func TestStripPrefixRegex(t *testing.T) {
path: "/c/api/abc/test4",
expectedStatusCode: http.StatusNotFound,
},
{
path: "/a/api/a%2Fb",
expectedStatusCode: http.StatusOK,
expectedPath: "a/b",
expectedRawPath: "a%2Fb",
expectedHeader: "/a/api/",
},
}

for _, test := range tests {
test := test
t.Run(test.path, func(t *testing.T) {
t.Parallel()

var actualPath, actualHeader string
var actualPath, actualRawPath, actualHeader string
handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
actualPath = r.URL.Path
actualRawPath = r.URL.RawPath
actualHeader = r.Header.Get(ForwardedPrefixHeader)
})
handler := NewStripPrefixRegex(handlerPath, testPrefixRegex)
Expand All @@ -82,6 +90,7 @@ func TestStripPrefixRegex(t *testing.T) {

assert.Equal(t, test.expectedStatusCode, resp.Code, "Unexpected status code.")
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader)
})
}
Expand Down
22 changes: 20 additions & 2 deletions middlewares/stripPrefix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestStripPrefix(t *testing.T) {
path string
expectedStatusCode int
expectedPath string
expectedRawPath string
expectedHeader string
}{
{
Expand Down Expand Up @@ -94,18 +95,28 @@ func TestStripPrefix(t *testing.T) {
expectedPath: "/us",
expectedHeader: "/stat",
},
{
desc: "raw path is also stripped",
prefixes: []string{"/stat"},
path: "/stat/a%2Fb",
expectedStatusCode: http.StatusOK,
expectedPath: "/a/b",
expectedRawPath: "/a%2Fb",
expectedHeader: "/stat",
},
}

for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

var actualPath, actualHeader, requestURI string
var actualPath, actualRawPath, actualHeader, requestURI string
handler := &StripPrefix{
Prefixes: test.prefixes,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
actualPath = r.URL.Path
actualRawPath = r.URL.RawPath
actualHeader = r.Header.Get(ForwardedPrefixHeader)
requestURI = r.RequestURI
}),
Expand All @@ -118,8 +129,15 @@ func TestStripPrefix(t *testing.T) {

assert.Equal(t, test.expectedStatusCode, resp.Code, "Unexpected status code.")
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader)
assert.Equal(t, test.expectedPath, requestURI, "Unexpected request URI.")

expectedURI := test.expectedPath
if test.expectedRawPath != "" {
// go HTTP uses the raw path when existent in the RequestURI
expectedURI = test.expectedRawPath
}
assert.Equal(t, expectedURI, requestURI, "Unexpected request URI.")
})
}
}