From ca4c84fec5ef36f8643e2b887543ae4f428c3b2e Mon Sep 17 00:00:00 2001 From: Fabio Kung Date: Thu, 7 Apr 2016 16:45:04 -0700 Subject: [PATCH] response.Body can be nil when requests time out This was generating panics on retries, since Body will be empty. --- aws/request/request.go | 8 +++++--- aws/request/request_test.go | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/aws/request/request.go b/aws/request/request.go index a4b70545a2d..13ce8fdd851 100644 --- a/aws/request/request.go +++ b/aws/request/request.go @@ -253,9 +253,11 @@ func (r *Request) Send() error { Proto: r.HTTPRequest.Proto, ContentLength: r.HTTPRequest.ContentLength, } - // Closing response body. Since we are setting a new request to send off, this - // response will get squashed and leaked. - r.HTTPResponse.Body.Close() + if r.HTTPResponse.Body != nil { + // Closing response body. Since we are setting a new request to send off, this + // response will get squashed and leaked. + r.HTTPResponse.Body.Close() + } } r.Sign() diff --git a/aws/request/request_test.go b/aws/request/request_test.go index 1739d7a01a2..917ca34a2e6 100644 --- a/aws/request/request_test.go +++ b/aws/request/request_test.go @@ -3,6 +3,7 @@ package request_test import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -303,3 +304,40 @@ func TestRequestThrottleRetries(t *testing.T) { "Expect delay to be within range, i:%d, v:%s, min:%s, max:%s", i, v, min, max) } } + +// test that retries occur for request timeouts when response.Body can be nil +func TestRequestRecoverTimeoutWithNilBody(t *testing.T) { + reqNum := 0 + reqs := []*http.Response{ + {StatusCode: 0, Body: nil}, // body can be nil when requests time out + {StatusCode: 200, Body: body(`{"data":"valid"}`)}, + } + errors := []error{ + errors.New("timeout"), nil, + } + + s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10)) + s.Handlers.Validate.Clear() + s.Handlers.Unmarshal.PushBack(unmarshal) + s.Handlers.UnmarshalError.PushBack(unmarshalError) + s.Handlers.AfterRetry.Clear() // force retry on all errors + s.Handlers.AfterRetry.PushBack(func(r *request.Request) { + if r.Error != nil { + r.Error = nil + r.Retryable = aws.Bool(true) + r.RetryCount++ + } + }) + s.Handlers.Send.Clear() // mock sending + s.Handlers.Send.PushBack(func(r *request.Request) { + r.HTTPResponse = reqs[reqNum] + r.Error = errors[reqNum] + reqNum++ + }) + out := &testData{} + r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out) + err := r.Send() + assert.Nil(t, err) + assert.Equal(t, 1, int(r.RetryCount)) + assert.Equal(t, "valid", out.Data) +}