From 76fe6efa8a935c23ea94770ad7677ba639c8ac0d Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Thu, 27 May 2021 15:37:59 -0700 Subject: [PATCH] aws/request: Fix SDK's handling of endpoints ending in slash (#3926) Fixes the SDK's handling of endpoints that end in a slash (`/`) causing signature validation errors due to Request.HTTPRequest.URL.Path not matching what was sent in the request. --- CHANGELOG_PENDING.md | 2 + aws/request/request.go | 17 ++++++++- aws/request/request_1_8_test.go | 68 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a1927a39ca..bb7cfb9e8a5 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -3,3 +3,5 @@ ### SDK Enhancements ### SDK Bugs +* `aws/request`: Fix handling of endpoints with trailing slashes + * Fixes the SDK's handling of endpoint URLs that contain a trailing slash when the API operation's modeled path is suffixed. Also ensures any endpoint URL query string is squashed consistently. diff --git a/aws/request/request.go b/aws/request/request.go index d597c6ead55..fb0a68fce3e 100644 --- a/aws/request/request.go +++ b/aws/request/request.go @@ -129,12 +129,27 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers, httpReq, _ := http.NewRequest(method, "", nil) var err error - httpReq.URL, err = url.Parse(clientInfo.Endpoint + operation.HTTPPath) + httpReq.URL, err = url.Parse(clientInfo.Endpoint) if err != nil { httpReq.URL = &url.URL{} err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err) } + if len(operation.HTTPPath) != 0 { + opHTTPPath := operation.HTTPPath + var opQueryString string + if idx := strings.Index(opHTTPPath, "?"); idx >= 0 { + opQueryString = opHTTPPath[idx+1:] + opHTTPPath = opHTTPPath[:idx] + } + + if strings.HasSuffix(httpReq.URL.Path, "/") && strings.HasPrefix(opHTTPPath, "/") { + opHTTPPath = opHTTPPath[1:] + } + httpReq.URL.Path += opHTTPPath + httpReq.URL.RawQuery = opQueryString + } + r := &Request{ Config: cfg, ClientInfo: clientInfo, diff --git a/aws/request/request_1_8_test.go b/aws/request/request_1_8_test.go index ad9ab1526ee..1f0aa7c7ca5 100644 --- a/aws/request/request_1_8_test.go +++ b/aws/request/request_1_8_test.go @@ -84,3 +84,71 @@ func TestRequest_FollowPUTRedirects(t *testing.T) { t.Errorf("expect %d endpoint hits, got %d", e, a) } } + +func TestNewRequest_JoinEndpointWithOperationPathQuery(t *testing.T) { + cases := map[string]struct { + HTTPPath string + Endpoint *string + ExpectQuery string + ExpectPath string + }{ + "no op HTTP Path": { + HTTPPath: "", + Endpoint: aws.String("https://foo.bar.aws/foo?bar=Baz"), + ExpectPath: "/foo", + ExpectQuery: "bar=Baz", + }, + "no trailing slash": { + HTTPPath: "/", + Endpoint: aws.String("https://foo.bar.aws"), + ExpectPath: "/", + ExpectQuery: "", + }, + "set query": { + HTTPPath: "/?Foo=bar", + Endpoint: aws.String("https://foo.bar.aws"), + ExpectPath: "/", + ExpectQuery: "Foo=bar", + }, + "squash query": { + HTTPPath: "/?Foo=bar", + Endpoint: aws.String("https://foo.bar.aws/?bar=Foo"), + ExpectPath: "/", + ExpectQuery: "Foo=bar", + }, + "trailing slash": { + HTTPPath: "/", + Endpoint: aws.String("https://foo.bar.aws/"), + ExpectPath: "/", + ExpectQuery: "", + }, + "trailing slash set query": { + HTTPPath: "/?Foo=bar", + Endpoint: aws.String("https://foo.bar.aws/"), + ExpectPath: "/", + ExpectQuery: "Foo=bar", + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + client := awstesting.NewClient(&aws.Config{ + Endpoint: c.Endpoint, + }) + + client.Handlers.Clear() + r := client.NewRequest(&request.Operation{ + Name: "FooBar", + HTTPMethod: "GET", + HTTPPath: c.HTTPPath, + }, nil, nil) + + if e, a := c.ExpectPath, r.HTTPRequest.URL.Path; e != a { + t.Errorf("expect %v path, got %v", e, a) + } + if e, a := c.ExpectQuery, r.HTTPRequest.URL.RawQuery; e != a { + t.Errorf("expect %v query, got %v", e, a) + } + }) + } +}