github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/unmarshal_error.go (about)

     1  package s3
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/xml"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/aavshr/aws-sdk-go/aws"
    13  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    14  	"github.com/aavshr/aws-sdk-go/aws/request"
    15  	"github.com/aavshr/aws-sdk-go/private/protocol/xml/xmlutil"
    16  )
    17  
    18  type xmlErrorResponse struct {
    19  	XMLName xml.Name `xml:"Error"`
    20  	Code    string   `xml:"Code"`
    21  	Message string   `xml:"Message"`
    22  }
    23  
    24  func unmarshalError(r *request.Request) {
    25  	defer r.HTTPResponse.Body.Close()
    26  	defer io.Copy(ioutil.Discard, r.HTTPResponse.Body)
    27  
    28  	// Bucket exists in a different region, and request needs
    29  	// to be made to the correct region.
    30  	if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
    31  		msg := fmt.Sprintf(
    32  			"incorrect region, the bucket is not in '%s' region at endpoint '%s'",
    33  			aws.StringValue(r.Config.Region),
    34  			aws.StringValue(r.Config.Endpoint),
    35  		)
    36  		if v := r.HTTPResponse.Header.Get("x-amz-bucket-region"); len(v) != 0 {
    37  			msg += fmt.Sprintf(", bucket is in '%s' region", v)
    38  		}
    39  		r.Error = awserr.NewRequestFailure(
    40  			awserr.New("BucketRegionError", msg, nil),
    41  			r.HTTPResponse.StatusCode,
    42  			r.RequestID,
    43  		)
    44  		return
    45  	}
    46  
    47  	// Attempt to parse error from body if it is known
    48  	var errResp xmlErrorResponse
    49  	var err error
    50  	if r.HTTPResponse.StatusCode >= 200 && r.HTTPResponse.StatusCode < 300 {
    51  		err = s3unmarshalXMLError(&errResp, r.HTTPResponse.Body)
    52  	} else {
    53  		err = xmlutil.UnmarshalXMLError(&errResp, r.HTTPResponse.Body)
    54  	}
    55  
    56  	if err != nil {
    57  		var errorMsg string
    58  		if err == io.EOF {
    59  			errorMsg = "empty response payload"
    60  		} else {
    61  			errorMsg = "failed to unmarshal error message"
    62  		}
    63  
    64  		r.Error = awserr.NewRequestFailure(
    65  			awserr.New(request.ErrCodeSerialization,
    66  				errorMsg, err),
    67  			r.HTTPResponse.StatusCode,
    68  			r.RequestID,
    69  		)
    70  		return
    71  	}
    72  
    73  	// Fallback to status code converted to message if still no error code
    74  	if len(errResp.Code) == 0 {
    75  		statusText := http.StatusText(r.HTTPResponse.StatusCode)
    76  		errResp.Code = strings.Replace(statusText, " ", "", -1)
    77  		errResp.Message = statusText
    78  	}
    79  
    80  	r.Error = awserr.NewRequestFailure(
    81  		awserr.New(errResp.Code, errResp.Message, err),
    82  		r.HTTPResponse.StatusCode,
    83  		r.RequestID,
    84  	)
    85  }
    86  
    87  // A RequestFailure provides access to the S3 Request ID and Host ID values
    88  // returned from API operation errors. Getting the error as a string will
    89  // return the formated error with the same information as awserr.RequestFailure,
    90  // while also adding the HostID value from the response.
    91  type RequestFailure interface {
    92  	awserr.RequestFailure
    93  
    94  	// Host ID is the S3 Host ID needed for debug, and contacting support
    95  	HostID() string
    96  }
    97  
    98  // s3unmarshalXMLError is s3 specific xml error unmarshaler
    99  // for 200 OK errors and response payloads.
   100  // This function differs from the xmlUtil.UnmarshalXMLError
   101  // func. It does not ignore the EOF error and passes it up.
   102  // Related to bug fix for `s3 200 OK response with empty payload`
   103  func s3unmarshalXMLError(v interface{}, stream io.Reader) error {
   104  	var errBuf bytes.Buffer
   105  	body := io.TeeReader(stream, &errBuf)
   106  
   107  	err := xml.NewDecoder(body).Decode(v)
   108  	if err != nil && err != io.EOF {
   109  		return awserr.NewUnmarshalError(err,
   110  			"failed to unmarshal error message", errBuf.Bytes())
   111  	}
   112  
   113  	return err
   114  }