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

Requests Retrying is broken if the error was caused due to a client timeout #947

Closed
hallmaxw opened this issue Nov 17, 2016 · 3 comments
Closed
Labels
bug This issue is a bug. investigating This issue is being investigated and/or work is in progress to resolve the issue.

Comments

@hallmaxw
Copy link

This logic in Request.Send() makes it impossible to retry if a Send handler fails due to a client timeout.

Here's what an error returned by the standard library's Client.Do() looks like:

Get http://127.0.0.1:52299/: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

I'd expect to be able to retry a request if it failed due to a timeout I set. Here's a unit test I wrote to check this:

package main

import (
	"errors"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/client/metadata"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/request"
	"net"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func TestRequestRetryClientTimeout(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		// sleep for 2 seconds to ensure the client's request times out
		time.Sleep(2 * time.Second)
		rw.Write([]byte("ok"))
	}))
	defer ts.Close()

	var handlers request.Handlers
	handlers.Send.PushBackNamed(corehandlers.SendHandler)
	handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)

	// count the number of times we try to send the request
	sendCount := 0
	handlers.Send.PushFront(func(r *request.Request) {
		sendCount++
	})

	// uncomment this to fix the bug
	// handlers.Send.PushBack(fixTimeoutError)

	numMaxRetries := 2
	op := &request.Operation{
		Name:       "Test",
		HTTPMethod: "GET",
		HTTPPath:   "/",
	}
	c := &http.Client{
		Timeout: 1 * time.Second,
	}
	// setup a request to send to our test server
	config := aws.NewConfig().
		WithHTTPClient(c).
		WithSleepDelay(time.Sleep)
	clientInfo := metadata.ClientInfo{Endpoint: ts.URL}
	retryer := client.DefaultRetryer{NumMaxRetries: numMaxRetries}
	req := request.New(*config, clientInfo, handlers, retryer, op, nil, nil)
	req.SetStringBody("testing")

	err := req.Send()
	t.Logf("Error received when from Send(): %v", err)

	expectedSendCount := numMaxRetries + 1
	if sendCount != expectedSendCount {
		t.Errorf("Expected the request to be sent %d times but it was sent %d times", expectedSendCount, sendCount)
	}
}

// Handler that fixes the error handling. I shouldn't have to do this
func fixTimeoutError(r *request.Request) {
	origErr := getOriginalError(r.Error)
	switch typedErr := origErr.(type) {
	case net.Error:
		if typedErr.Timeout() {
			r.Error = errors.New("timeout")
		}
	}
}

func getOriginalError(err error) error {
	switch typedError := err.(type) {
	case awserr.Error:
		return typedError.OrigErr()
	default:
		return err
	}
}

Was this intended when support for cancelling HTTP requests was added? (this commit)

@jasdel
Copy link
Contributor

jasdel commented Nov 17, 2016

Thanks for letting us know about this issue, and example. I think you're correct. This change was added to capture users's canceling a request, ensuring the SDK did not attempt to retry a canceled. request. I think we can add additional logic around this correctly allow retries of Timeout, but not cancels.

@jasdel
Copy link
Contributor

jasdel commented Nov 28, 2016

The SDK should be able to take advantage of the net package's Error interface for determining if the request can be retried. https://golang.org/pkg/net/#Error

@jasdel jasdel added the bug This issue is a bug. label Nov 29, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
@xibz
Copy link
Contributor

xibz commented Dec 6, 2016

Hello @mbh621, I have a PR, #981, out that addresses this issue. Please let us know if you have any more issues!

xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
@jasdel jasdel added the investigating This issue is being investigated and/or work is in progress to resolve the issue. label Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
xibz added a commit to xibz/aws-sdk-go that referenced this issue Dec 6, 2016
@xibz xibz closed this as completed in #981 Dec 12, 2016
xibz added a commit that referenced this issue Dec 12, 2016
* Fixing bug #947

* Updating tests

* Updating tests

* Refactoring to work with any version

* Update request.go
@awstools awstools mentioned this issue Dec 15, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug. investigating This issue is being investigated and/or work is in progress to resolve the issue.
Projects
None yet
Development

No branches or pull requests

3 participants