Repository: minio/minio-go
Pull request #1113 (2019-05-29 00:32:54)

Listbuckets API: Retry with region returned by s3 server
In case of AuthorizationHeaderMalformed error, retry with the region
returned by the S3 server.

This allows listBuckets to work with S3 servers that does not support
signing with us-east-1 by default


Patch:
diff --git a/api-list.go b/api-list.go
index b9c0f5d..2fb0b62 100644
--- a/api-list.go
+++ b/api-list.go
@@ -41,6 +41,23 @@ import (
 func (c Client) ListBuckets() ([]BucketInfo, error) {
 	// Execute GET on service.
 	resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{contentSHA256Hex: emptySHA256Hex})
+
+	if err != nil {
+		closeResponse(resp)
+		return nil, err
+	}
+	if resp != nil {
+		if resp.StatusCode != http.StatusOK {
+			errResponse := ToErrorResponse(httpRespToErrorResponse(resp, "", ""))
+			closeResponse(resp)
+			if errResponse.Code != "AuthorizationHeaderMalformed" && errResponse.Code != "InvalidRegion" {
+				return nil, httpRespToErrorResponse(resp, "", "")
+			}
+			// Retry with the region returned by the server.
+			resp, err = c.executeMethod(context.Background(), "GET", requestMetadata{contentSHA256Hex: emptySHA256Hex, bucketLocation: errResponse.Region})
+		}
+	}
+
 	defer closeResponse(resp)
 	if err != nil {
 		return nil, err
diff --git a/api.go b/api.go
index 0c358f3..ece498f 100644
--- a/api.go
+++ b/api.go
@@ -103,7 +103,7 @@ type Options struct {
 // Global constants.
 const (
 	libraryName    = "minio-go"
-	libraryVersion = "v6.0.26"
+	libraryVersion = "v6.0.27"
 )
 
 // User Agent should always following the below style.
diff --git a/pkg/credentials/iam_aws.go b/pkg/credentials/iam_aws.go
index 3107852..5732f2e 100644
--- a/pkg/credentials/iam_aws.go
+++ b/pkg/credentials/iam_aws.go
@@ -53,7 +53,7 @@ type IAM struct {
 const (
 	defaultIAMRoleEndpoint      = "http://169.254.169.254"
 	defaultECSRoleEndpoint      = "http://169.254.170.2"
-	defaultIAMSecurityCredsPath = "/latest/meta-data/iam/security-credentials"
+	defaultIAMSecurityCredsPath = "/latest/meta-data/iam/security-credentials/"
 )
 
 // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
diff --git a/retry.go b/retry.go
index 94176b0..2c608ba 100644
--- a/retry.go
+++ b/retry.go
@@ -111,6 +111,9 @@ func isHTTPReqErrorRetryable(err error) bool {
 		} else if strings.Contains(err.Error(), "net/http: HTTP/1.x transport connection broken") {
 			// If error is transport connection broken, retry.
 			return true
+		} else if strings.Contains(err.Error(), "net/http: timeout awaiting response headers") {
+			// Retry errors due to server not sending the response before timeout
+			return true
 		}
 	}
 	return false


Test patch:
diff --git a/pkg/credentials/iam_aws_test.go b/pkg/credentials/iam_aws_test.go
index b33f9bd..90f9806 100644
--- a/pkg/credentials/iam_aws_test.go
+++ b/pkg/credentials/iam_aws_test.go
@@ -67,7 +67,7 @@ func initTestServerNoRoles() *httptest.Server {
 
 func initTestServer(expireOn string, failAssume bool) *httptest.Server {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if r.URL.Path == "/latest/meta-data/iam/security-credentials" {
+		if r.URL.Path == "/latest/meta-data/iam/security-credentials/" {
 			fmt.Fprintln(w, "RoleName")
 		} else if r.URL.Path == "/latest/meta-data/iam/security-credentials/RoleName" {
 			if failAssume {