From 96591c0b8caeee7c785fc807f3990f1ec2fdc8c5 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 22 Sep 2017 22:35:20 -0400 Subject: [PATCH] Preserve leading and trailing slashes on proxy subpaths Kubernetes-commit: fb37e062e1babbf28d1d483354b0dd5bd3bee1b0 --- pkg/endpoints/apiserver_test.go | 22 ++++++++++++++++------ pkg/endpoints/handlers/rest.go | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/pkg/endpoints/apiserver_test.go b/pkg/endpoints/apiserver_test.go index 0c4730dba..880546abb 100644 --- a/pkg/endpoints/apiserver_test.go +++ b/pkg/endpoints/apiserver_test.go @@ -1953,15 +1953,25 @@ func TestGetWithOptions(t *testing.T) { requestURL: "/namespaces/default/simple/id?param1=test1¶m2=test2", expectedPath: "", }, + { + name: "with root slash", + requestURL: "/namespaces/default/simple/id/?param1=test1¶m2=test2", + expectedPath: "/", + }, { name: "with path", requestURL: "/namespaces/default/simple/id/a/different/path?param1=test1¶m2=test2", - expectedPath: "a/different/path", + expectedPath: "/a/different/path", + }, + { + name: "with path with trailing slash", + requestURL: "/namespaces/default/simple/id/a/different/path/?param1=test1¶m2=test2", + expectedPath: "/a/different/path/", }, { name: "as subresource", requestURL: "/namespaces/default/simple/id/subresource/another/different/path?param1=test1¶m2=test2", - expectedPath: "another/different/path", + expectedPath: "/another/different/path", }, { name: "cluster-scoped basic", @@ -1973,13 +1983,13 @@ func TestGetWithOptions(t *testing.T) { name: "cluster-scoped basic with path", rootScoped: true, requestURL: "/simple/id/a/cluster/path?param1=test1¶m2=test2", - expectedPath: "a/cluster/path", + expectedPath: "/a/cluster/path", }, { name: "cluster-scoped basic as subresource", rootScoped: true, requestURL: "/simple/id/subresource/another/cluster/path?param1=test1¶m2=test2", - expectedPath: "another/cluster/path", + expectedPath: "/another/cluster/path", }, } @@ -2374,7 +2384,7 @@ func TestConnectWithOptions(t *testing.T) { func TestConnectWithOptionsAndPath(t *testing.T) { responseText := "Hello World" itemID := "theID" - testPath := "a/b/c/def" + testPath := "/a/b/c/def" connectStorage := &ConnecterRESTStorage{ connectHandler: &OutputConnect{ response: responseText, @@ -2390,7 +2400,7 @@ func TestConnectWithOptionsAndPath(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect/" + testPath + "?param1=value1¶m2=value2") + resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect" + testPath + "?param1=value1¶m2=value2") if err != nil { t.Errorf("unexpected error: %v", err) diff --git a/pkg/endpoints/handlers/rest.go b/pkg/endpoints/handlers/rest.go index f238d447d..e23be1ef4 100644 --- a/pkg/endpoints/handlers/rest.go +++ b/pkg/endpoints/handlers/rest.go @@ -212,7 +212,20 @@ func getRequestOptions(req *http.Request, scope RequestScope, into runtime.Objec if isSubresource { startingIndex = 3 } - newQuery[subpathKey] = []string{strings.Join(requestInfo.Parts[startingIndex:], "/")} + + p := strings.Join(requestInfo.Parts[startingIndex:], "/") + + // ensure non-empty subpaths correctly reflect a leading slash + if len(p) > 0 && !strings.HasPrefix(p, "/") { + p = "/" + p + } + + // ensure subpaths correctly reflect the presence of a trailing slash on the original request + if strings.HasSuffix(requestInfo.Path, "/") && !strings.HasSuffix(p, "/") { + p += "/" + } + + newQuery[subpathKey] = []string{p} query = newQuery } return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into)