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

Deprecate url path cleaning during http request serialization #4832

Merged
merged 11 commits into from
May 18, 2023
4 changes: 4 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
### SDK Enhancements

### SDK Bugs
* `rest`: Remove unnecessary path normalization behavior.
* This behavior would incorrectly mutate request paths with URI-encoded characters, potentially resulting in misrouted requests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two spaces here to indent instead of 4. IIRC the code that handles this is incredibly picky, best to be safe.

* `config`: Deprecate `DisableRestProtocolURICleaning` config setting.
* This setting no longer has any effect. REST-protocol paths will now never be normalized after serialization.
26 changes: 2 additions & 24 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,8 @@ type Config struct {
// and specify a Retryer instead.
SleepDelay func(time.Duration)

// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
// Will default to false. This would only be used for empty directory names in s3 requests.
//
// Example:
// sess := session.Must(session.NewSession(&aws.Config{
// DisableRestProtocolURICleaning: aws.Bool(true),
// }))
//
// svc := s3.New(sess)
// out, err := svc.GetObject(&s3.GetObjectInput {
// Bucket: aws.String("bucketname"),
// Key: aws.String("//foo//bar//moo"),
// })
// Deprecated: This setting no longer has any effect.
// RESTful paths are no longer cleaned after request serialization.
DisableRestProtocolURICleaning *bool

// EnableEndpointDiscovery will allow for endpoint discovery on operations that
Expand Down Expand Up @@ -497,13 +486,6 @@ func (c *Config) WithLowerCaseHeaderMaps(t bool) *Config {
return c
}

// WithDisableRestProtocolURICleaning sets a config DisableRestProtocolURICleaning value
// returning a Config pointer for chaining.
func (c *Config) WithDisableRestProtocolURICleaning(t bool) *Config {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't remove this (breaking change).

Do please add the same deprecation message as we did on the field itself, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, just over-understand deprecate

c.DisableRestProtocolURICleaning = &t
return c
}

// MergeIn merges the passed in configs into the existing config object.
func (c *Config) MergeIn(cfgs ...*Config) {
for _, other := range cfgs {
Expand Down Expand Up @@ -608,10 +590,6 @@ func mergeInConfig(dst *Config, other *Config) {
dst.SleepDelay = other.SleepDelay
}

if other.DisableRestProtocolURICleaning != nil {
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
}

if other.EnforceShouldRetryCheck != nil {
dst.EnforceShouldRetryCheck = other.EnforceShouldRetryCheck
}
Expand Down
31 changes: 15 additions & 16 deletions aws/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,21 @@ func TestCopyReturnsNewInstance(t *testing.T) {
var mergeTestZeroValueConfig = Config{}

var mergeTestConfig = Config{
Credentials: testCredentials,
Endpoint: String("MergeTestEndpoint"),
Region: String("MERGE_TEST_AWS_REGION"),
DisableSSL: Bool(true),
HTTPClient: http.DefaultClient,
LogLevel: LogLevel(LogDebug),
Logger: NewDefaultLogger(),
MaxRetries: Int(10),
DisableParamValidation: Bool(true),
DisableComputeChecksums: Bool(true),
DisableEndpointHostPrefix: Bool(true),
EnableEndpointDiscovery: Bool(true),
EnforceShouldRetryCheck: Bool(true),
DisableRestProtocolURICleaning: Bool(true),
S3ForcePathStyle: Bool(true),
LowerCaseHeaderMaps: Bool(true),
Credentials: testCredentials,
Endpoint: String("MergeTestEndpoint"),
Region: String("MERGE_TEST_AWS_REGION"),
DisableSSL: Bool(true),
HTTPClient: http.DefaultClient,
LogLevel: LogLevel(LogDebug),
Logger: NewDefaultLogger(),
MaxRetries: Int(10),
DisableParamValidation: Bool(true),
DisableComputeChecksums: Bool(true),
DisableEndpointHostPrefix: Bool(true),
EnableEndpointDiscovery: Bool(true),
EnforceShouldRetryCheck: Bool(true),
S3ForcePathStyle: Bool(true),
LowerCaseHeaderMaps: Bool(true),
}

var mergeTests = []struct {
Expand Down
17 changes: 0 additions & 17 deletions private/protocol/rest/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"math"
"net/http"
"net/url"
"path"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -134,9 +133,6 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
}

r.HTTPRequest.URL.RawQuery = query.Encode()
if !aws.BoolValue(r.Config.DisableRestProtocolURICleaning) {
cleanPath(r.HTTPRequest.URL)
}
}

func buildBody(r *request.Request, v reflect.Value) {
Expand Down Expand Up @@ -244,19 +240,6 @@ func buildQueryString(query url.Values, v reflect.Value, name string, tag reflec
return nil
}

func cleanPath(u *url.URL) {
hasSlash := strings.HasSuffix(u.Path, "/")

// clean up path, removing duplicate `/`
u.Path = path.Clean(u.Path)
u.RawPath = path.Clean(u.RawPath)

if hasSlash && !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
u.RawPath += "/"
}
}

// EscapePath escapes part of a URL path in Amazon style
func EscapePath(path string, encodeSep bool) string {
var buf bytes.Buffer
Expand Down
56 changes: 47 additions & 9 deletions private/protocol/rest/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,55 @@ import (
"github.com/aws/aws-sdk-go/aws/request"
)

func TestCleanPath(t *testing.T) {
uri := &url.URL{
Path: "//foo//bar",
Scheme: "https",
Host: "host",
func TestBuildURI(t *testing.T) {
cases := map[string]struct {
Topic *string
ExpectPath string
ExpectRawPath string
}{
"path without prefix slash": {
Topic: aws.String("devices/123/test"),
isaiahvita marked this conversation as resolved.
Show resolved Hide resolved
ExpectPath: "/topics/devices/123/test",
ExpectRawPath: "/topics/devices%2F123%2Ftest",
},
"path containing single prefix slashes": {
Topic: aws.String("/devices/123/test"),
ExpectPath: "/topics//devices/123/test",
ExpectRawPath: "/topics/%2Fdevices%2F123%2Ftest",
},
"path containing prefix multi-slashes": {
Topic: aws.String("///devices/123/test"),
ExpectPath: "/topics////devices/123/test",
ExpectRawPath: "/topics/%2F%2F%2Fdevices%2F123%2Ftest",
},
}
cleanPath(uri)

expected := "https://host/foo/bar"
if a, e := uri.String(), expected; a != e {
t.Errorf("expect %q URI, got %q", e, a)
for _, c := range cases {
uri := &url.URL{
Path: "/topics/{topic}",
Scheme: "https",
Host: "host",
}

req := &request.Request{
HTTPRequest: &http.Request{
URL: uri,
},
Params: &struct {
Topic *string `location:"uri" locationName:"topic" type:"string" required:"true"`
}{
c.Topic,
},
}

Build(req)

if a, e := uri.Path, c.ExpectPath; a != e {
t.Errorf("expect %q Path, got %q", e, a)
}
if a, e := uri.RawPath, c.ExpectRawPath; a != e {
t.Errorf("expect %q RawPath, got %q", e, a)
}
}
}

Expand Down