Skip to content

Commit cb8cad4

Browse files
skotambkarjasdel
authored andcommitted
aws: Adds sdk error checking when seeking readers (#379)
Adds missing sdk error checking when seeking readers. Also adds support for nonseekable io.Reader and support for streamed payloads for unsigned body request. Fixes #371
1 parent c4fe3c8 commit cb8cad4

File tree

31 files changed

+902
-156
lines changed

31 files changed

+902
-156
lines changed

CHANGELOG_PENDING.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
### SDK Features
22

33
### SDK Enhancements
4-
* `aws/endpoints`: Expose DNSSuffix for partitions ([#368](https://github.com/aws/aws-sdk-go/pull/368))
4+
* `aws/endpoints`: Expose DNSSuffix for partitions ([#369](https://github.com/aws/aws-sdk-go-v2/pull/369))
55
* Exposes the underlying partition metadata's DNSSuffix value via the `DNSSuffix` method on the endpoint's `Partition` type. This allows access to the partition's DNS suffix, e.g. "amazon.com".
6-
* Fixes [#347](https://github.com/aws/aws-sdk-go/issues/347)
6+
* Fixes [#347](https://github.com/aws/aws-sdk-go-v2/issues/347)
77
* `private/protocol`: Add support for parsing fractional timestamp ([#367](https://github.com/aws/aws-sdk-go-v2/pull/367))
88
* Fixes the SDK's ability to parse fractional unix timestamp values and adds tests.
99
* Fixes [#365](https://github.com/aws/aws-sdk-go-v2/issues/365)
10-
* `aws/ec2metadata`: Add marketplaceProductCodes to EC2 Instance Identity Document
10+
* `aws/ec2metadata`: Add marketplaceProductCodes to EC2 Instance Identity Document ([#374](https://github.com/aws/aws-sdk-go-v2/pull/374))
1111
* Adds `MarketplaceProductCodes` to the EC2 Instance Metadata's Identity Document. The ec2metadata client will now retrieve these values if they are available.
1212
* Related to: [aws/aws-sdk-go#2781](https://github.com/aws/aws-sdk-go/issues/2781)
1313

1414
### SDK Bugs
1515
* `aws`: Fixes bug in calculating throttled retry delay ([#373](https://github.com/aws/aws-sdk-go-v2/pull/373))
1616
* The `Retry-After` duration specified in the request is now added to the Retry delay for throttled exception. Adds test for retry delays for throttled exceptions. Fixes bug where the throttled retry's math was off.
1717
* Fixes [#45](https://github.com/aws/aws-sdk-go-v2/issues/45)
18-
18+
* `aws` : Adds missing sdk error checking when seeking readers [#379](https://github.com/aws/aws-sdk-go-v2/pull/379).
19+
* Adds support for nonseekable io.Reader. Adds support for streamed payloads for unsigned body request.
20+
* Fixes [#371](https://github.com/aws/aws-sdk-go-v2/issues/371)

aws/client_logger.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,26 @@ func (reader *teeReaderCloser) Close() error {
4343

4444
func logRequest(r *Request) {
4545
logBody := r.Config.LogLevel.Matches(LogDebugWithHTTPBody)
46+
bodySeekable := IsReaderSeekable(r.Body)
47+
4648
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
4749
if err != nil {
4850
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.Metadata.ServiceName, r.Operation.Name, err))
4951
return
5052
}
5153

5254
if logBody {
53-
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
54-
// Body as a NoOpCloser and will not be reset after read by the HTTP
55-
// client reader.
56-
r.ResetBody()
55+
if !bodySeekable {
56+
r.SetReaderBody(ReadSeekCloser(r.HTTPRequest.Body))
57+
}
58+
59+
// Reset the request body because dumpRequest will re-wrap the
60+
// r.HTTPRequest's Body as a NoOpCloser and will not be reset
61+
// after read by the HTTP client reader.
62+
if err := r.Error; err != nil {
63+
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.Metadata.ServiceName, r.Operation.Name, err))
64+
return
65+
}
5766
}
5867

5968
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.Metadata.ServiceName, r.Operation.Name, string(dumpedBody)))

aws/client_logger_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package aws
22

33
import (
44
"bytes"
5+
"fmt"
56
"io"
7+
"io/ioutil"
8+
"reflect"
9+
"runtime"
610
"testing"
711
)
812

@@ -55,3 +59,87 @@ func TestLogWriter(t *testing.T) {
5559
t.Errorf("Expected %q, but received %q", expected, lw.buf.String())
5660
}
5761
}
62+
63+
func TestLogRequest(t *testing.T) {
64+
cases := []struct {
65+
Body io.ReadSeeker
66+
ExpectBody []byte
67+
LogLevel LogLevel
68+
}{
69+
{
70+
Body: ReadSeekCloser(bytes.NewBuffer([]byte("body content"))),
71+
ExpectBody: []byte("body content"),
72+
},
73+
{
74+
Body: ReadSeekCloser(bytes.NewBuffer([]byte("body content"))),
75+
LogLevel: LogDebugWithHTTPBody,
76+
ExpectBody: []byte("body content"),
77+
},
78+
{
79+
Body: bytes.NewReader([]byte("body content")),
80+
ExpectBody: []byte("body content"),
81+
},
82+
{
83+
Body: bytes.NewReader([]byte("body content")),
84+
LogLevel: LogDebugWithHTTPBody,
85+
ExpectBody: []byte("body content"),
86+
},
87+
}
88+
89+
for i, c := range cases {
90+
var logW bytes.Buffer
91+
req := New(
92+
Config{
93+
EndpointResolver: ResolveWithEndpointURL("https://endpoint"),
94+
Credentials: AnonymousCredentials,
95+
Logger: &bufLogger{w: &logW},
96+
LogLevel: c.LogLevel,
97+
Region: "mock-region",
98+
},
99+
Metadata{
100+
EndpointsID: "https://mock-service.mock-region.amazonaws.com",
101+
},
102+
testHandlers(),
103+
nil,
104+
&Operation{
105+
Name: "APIName",
106+
HTTPMethod: "POST",
107+
HTTPPath: "/",
108+
},
109+
struct{}{}, nil,
110+
)
111+
req.SetReaderBody(c.Body)
112+
req.Build()
113+
114+
logRequest(req)
115+
116+
b, err := ioutil.ReadAll(req.HTTPRequest.Body)
117+
if err != nil {
118+
t.Fatalf("%d, expect to read SDK request Body", i)
119+
}
120+
121+
if e, a := c.ExpectBody, b; !reflect.DeepEqual(e, a) {
122+
t.Errorf("%d, expect %v body, got %v", i, e, a)
123+
}
124+
}
125+
}
126+
127+
type bufLogger struct {
128+
w *bytes.Buffer
129+
}
130+
131+
func (l *bufLogger) Log(args ...interface{}) {
132+
fmt.Fprintln(l.w, args...)
133+
}
134+
135+
func testHandlers() Handlers {
136+
var handlers Handlers
137+
handler := NamedHandler{
138+
Name: "core.SDKVersionUserAgentHandler",
139+
Fn: MakeAddToUserAgentHandler(SDKName, SDKVersion,
140+
runtime.Version(), runtime.GOOS, runtime.GOARCH),
141+
}
142+
handlers.Build.PushBackNamed(handler)
143+
144+
return handlers
145+
}

aws/defaults/handlers.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,19 @@ var BuildContentLengthHandler = aws.NamedHandler{Name: "core.BuildContentLengthH
4040
case lener:
4141
length = int64(body.Len())
4242
case io.Seeker:
43-
r.BodyStart, _ = body.Seek(0, 1)
44-
end, _ := body.Seek(0, 2)
45-
body.Seek(r.BodyStart, 0) // make sure to seek back to original location
43+
var err error
44+
r.BodyStart, err = body.Seek(0, io.SeekCurrent)
45+
if err != nil {
46+
r.Error = awserr.New(aws.ErrCodeSerialization, "failed to determine start of the request body", err)
47+
}
48+
end, err := body.Seek(0, io.SeekEnd)
49+
if err != nil {
50+
r.Error = awserr.New(aws.ErrCodeSerialization, "failed to determine end of the request body", err)
51+
}
52+
_, err = body.Seek(r.BodyStart, io.SeekStart) // make sure to seek back to original location
53+
if err != nil {
54+
r.Error = awserr.New(aws.ErrCodeSerialization, "failed to seek back to the original location", err)
55+
}
4656
length = end - r.BodyStart
4757
default:
4858
panic("Cannot get length of body, must provide `ContentLength`")

aws/offset_reader.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ type offsetReader struct {
1313
closed bool
1414
}
1515

16-
func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader {
16+
func newOffsetReader(buf io.ReadSeeker, offset int64) (*offsetReader, error) {
1717
reader := &offsetReader{}
18-
buf.Seek(offset, 0)
19-
18+
_, err := buf.Seek(offset, io.SeekStart)
19+
if err != nil {
20+
return nil, err
21+
}
2022
reader.buf = buf
21-
return reader
23+
return reader, nil
2224
}
2325

2426
// Close will close the instance of the offset reader's access to
@@ -52,7 +54,9 @@ func (o *offsetReader) Seek(offset int64, whence int) (int64, error) {
5254

5355
// CloseAndCopy will return a new offsetReader with a copy of the old buffer
5456
// and close the old buffer.
55-
func (o *offsetReader) CloseAndCopy(offset int64) *offsetReader {
56-
o.Close()
57+
func (o *offsetReader) CloseAndCopy(offset int64) (*offsetReader, error) {
58+
if err := o.Close(); err != nil {
59+
return nil, err
60+
}
5761
return newOffsetReader(o.buf, offset)
5862
}

aws/offset_reader_test.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestOffsetReaderRead(t *testing.T) {
2121
t.Errorf("expect %v, got %v", e, a)
2222
}
2323
if err != nil {
24-
t.Errorf("expect no error, got %v", err)
24+
t.Fatalf("expect no error, got %v", err)
2525
}
2626
if e, a := buf, tempBuf; !bytes.Equal(e, a) {
2727
t.Errorf("expect %v, got %v", e, a)
@@ -30,27 +30,30 @@ func TestOffsetReaderRead(t *testing.T) {
3030

3131
func TestOffsetReaderSeek(t *testing.T) {
3232
buf := []byte("testData")
33-
reader := newOffsetReader(bytes.NewReader(buf), 0)
33+
reader, err := newOffsetReader(bytes.NewReader(buf), 0)
34+
if err != nil {
35+
t.Fatalf("expect no error, got %v", err)
36+
}
3437

35-
orig, err := reader.Seek(0, 1)
38+
orig, err := reader.Seek(0, io.SeekCurrent)
3639
if err != nil {
37-
t.Errorf("expect no error, got %v", err)
40+
t.Fatalf("expect no error, got %v", err)
3841
}
3942
if e, a := int64(0), orig; e != a {
4043
t.Errorf("expect %v, got %v", e, a)
4144
}
4245

43-
n, err := reader.Seek(0, 2)
46+
n, err := reader.Seek(0, io.SeekEnd)
4447
if err != nil {
45-
t.Errorf("expect no error, got %v", err)
48+
t.Fatalf("expect no error, got %v", err)
4649
}
4750
if e, a := int64(len(buf)), n; e != a {
4851
t.Errorf("expect %v, got %v", e, a)
4952
}
5053

51-
n, err = reader.Seek(orig, 0)
54+
n, err = reader.Seek(orig, io.SeekStart)
5255
if err != nil {
53-
t.Errorf("expect no error, got %v", err)
56+
t.Fatalf("expect no error, got %v", err)
5457
}
5558
if e, a := int64(0), n; e != a {
5659
t.Errorf("expect %v, got %v", e, a)
@@ -81,8 +84,10 @@ func TestOffsetReaderCloseAndCopy(t *testing.T) {
8184
tempBuf := make([]byte, len(buf))
8285
reader := &offsetReader{buf: bytes.NewReader(buf)}
8386

84-
newReader := reader.CloseAndCopy(0)
85-
87+
newReader, err := reader.CloseAndCopy(0)
88+
if err != nil {
89+
t.Fatalf("expect no error, got %v", err)
90+
}
8691
n, err := reader.Read(tempBuf)
8792
if e, a := 0, n; e != a {
8893
t.Errorf("expect %v, got %v", e, a)
@@ -96,7 +101,7 @@ func TestOffsetReaderCloseAndCopy(t *testing.T) {
96101
t.Errorf("expect %v, got %v", e, a)
97102
}
98103
if err != nil {
99-
t.Errorf("expect no error, got %v", err)
104+
t.Fatalf("expect no error, got %v", err)
100105
}
101106
if e, a := buf, tempBuf; !bytes.Equal(e, a) {
102107
t.Errorf("expect %v, got %v", e, a)
@@ -108,13 +113,16 @@ func TestOffsetReaderCloseAndCopyOffset(t *testing.T) {
108113
tempBuf := make([]byte, len(buf))
109114
reader := &offsetReader{buf: bytes.NewReader(buf)}
110115

111-
newReader := reader.CloseAndCopy(4)
116+
newReader, err := reader.CloseAndCopy(4)
117+
if err != nil {
118+
t.Fatalf("expect no error, got %v", err)
119+
}
112120
n, err := newReader.Read(tempBuf)
113121
if e, a := n, len(buf)-4; e != a {
114122
t.Errorf("expect %v, got %v", e, a)
115123
}
116124
if err != nil {
117-
t.Errorf("expect no error, got %v", err)
125+
t.Fatalf("expect no error, got %v", err)
118126
}
119127

120128
expected := []byte{'D', 'a', 't', 'a', 0, 0, 0, 0}

0 commit comments

Comments
 (0)