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 }