-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
aws/request: Fix for Go 1.8 request incorrectly sent with body #991
Conversation
Go 1.8 tightened and clarified the rules code needs to use when building requests with the http package. Go 1.8 removed the automatic detection of if the Request.Body was empty, or actually had bytes in it. The SDK always sets the Request.Body even if it is empty and should not actually be sent. This is incorrect. Go 1.8 did add a http.NoBody value that the SDK can use to tell the http client that the request really should be sent without a body. The Request.Body cannot be set to nil, which is preferable, because the field is exported and could introduce nil pointer dereferences for users of the SDK if they used that field. Related golang/go#18257 Fix aws#984
r.Error = awserr.New("SerializationError", "failed to seek request body", err) | ||
return | ||
} | ||
_, err = r.Body.Seek(r.BodyStart, io.SeekStart) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove copy/paste error handling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking adding a util function that does all this and returns the size of the body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah included this in the refactor
r.ResetBody() | ||
|
||
if v, ok := r.HTTPRequest.Body.(*offsetReader); !ok || v == nil { | ||
t.Errorf("expected request body to be set to reader, got %#v", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update error messages to be more obvious what failed. This and other tests added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few comments
func resetBody(r *Request) { | ||
r.safeBody = newOffsetReader(r.Body, r.BodyStart) | ||
|
||
seekable := false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seekable := true
seekable = v.IsSeeker() | ||
case *aws.ReaderSeekerCloser: | ||
seekable = v.IsSeeker() | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can get rid of this
r.Error = awserr.New("SerializationError", "failed to seek request body", err) | ||
return | ||
} | ||
_, err = r.Body.Seek(r.BodyStart, io.SeekStart) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking adding a util function that does all this and returns the size of the body.
// of the SDK if they used that field. | ||
// | ||
// Related golang/go#18257 | ||
func resetBody(r *Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we feel about returning an error? I feel like when we get an error in here, it'll at least clean it up some, rather than mutating the request's error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ended up refactoring this logic simplifying this
"testing" | ||
) | ||
|
||
func TestResetBody_WithBodyContents(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have tests to test the non-seekable case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will add
We have run a bunch of S3-related tests on 0a88972 with go1.8beta1 and it seems that it works correctly. In current version we see issues you mentioned in #984 (comment): "0" symbol in the body where body is supposed to be empty (e.g. HEAD request) appears because |
@pin thanks for testing against this change. I think you're correct. The SDK pre-sets the request to a io.ReadSeeker, and later populates this with connects. The SDK was relying on a undocumented feature in Go where Go's http client would figure out if the request should be sent with a request body or not based on the length of the body. In the case of the SDK, AWS services do not seem to like GET, HEAD, or DELETE HTTP requests with chunked encoding bodies, even if those body chunks are 0 length. The request will fails with a timeout. I'm working on reaching out internally to see if that issue can be addressed in a broader way server side to ignore the body instead of timing out the socket. But, for now this change should fix the SDK's behavior. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just the one comment
return | ||
} | ||
|
||
if l == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 this looks a lot better
@@ -321,7 +331,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi | |||
// If the request is not presigned the body should be attached to it. This | |||
// prevents the confusion of wanting to send a signed request without | |||
// the body the request was signed for attached. | |||
if !ctx.isPresign { | |||
if !v4.DisableRequestBodyOverwrite && !ctx.isPresign { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!(v4.DisableRequestBodyOverwrite || ctx.isPresign)
Don't know if the Go compiler does De Morgan's laws under the hood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated thanks
Adds resolver function for loading S3 client specific config
Go 1.8 tightened and clarified the rules code needs to use when building
requests with the http package. Go 1.8 removed the automatic detection
of if the Request.Body was empty, or actually had bytes in it. The SDK
always sets the Request.Body even if it is empty and should not actually
be sent. This is incorrect.
Go 1.8 did add a http.NoBody value that the SDK can use to tell the http
client that the request really should be sent without a body. The
Request.Body cannot be set to nil, which is preferable, because the
field is exported and could introduce nil pointer dereferences for users
of the SDK if they used that field.
This change also deprecates the
aws.ReaderSeekerCloser
type. Thistype has a bug in its design that hides the fact the underlying reader
is not also a seeker. This obfication, leads to unexpected bugs. If using
this type for operations such as
S3.PutObject
it is suggested to uses3manager.Upload
instead. Thes3manager.Upload
takes anio.Reader
to make streaming to S3 easier.
Related golang/go#18257
Fix #984