unmarshal_error.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package s3
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "net/http"
  8. "strings"
  9. "github.com/aws/aws-sdk-go/aws"
  10. "github.com/aws/aws-sdk-go/aws/awserr"
  11. "github.com/aws/aws-sdk-go/aws/request"
  12. )
  13. type xmlErrorResponse struct {
  14. XMLName xml.Name `xml:"Error"`
  15. Code string `xml:"Code"`
  16. Message string `xml:"Message"`
  17. }
  18. func unmarshalError(r *request.Request) {
  19. defer r.HTTPResponse.Body.Close()
  20. defer io.Copy(ioutil.Discard, r.HTTPResponse.Body)
  21. // Bucket exists in a different region, and request needs
  22. // to be made to the correct region.
  23. if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
  24. msg := fmt.Sprintf(
  25. "incorrect region, the bucket is not in '%s' region at endpoint '%s'",
  26. aws.StringValue(r.Config.Region),
  27. aws.StringValue(r.Config.Endpoint),
  28. )
  29. if v := r.HTTPResponse.Header.Get("x-amz-bucket-region"); len(v) != 0 {
  30. msg += fmt.Sprintf(", bucket is in '%s' region", v)
  31. }
  32. r.Error = awserr.NewRequestFailure(
  33. awserr.New("BucketRegionError", msg, nil),
  34. r.HTTPResponse.StatusCode,
  35. r.RequestID,
  36. )
  37. return
  38. }
  39. var errCode, errMsg string
  40. // Attempt to parse error from body if it is known
  41. resp := &xmlErrorResponse{}
  42. err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
  43. if err != nil && err != io.EOF {
  44. errCode = "SerializationError"
  45. errMsg = "failed to decode S3 XML error response"
  46. } else {
  47. errCode = resp.Code
  48. errMsg = resp.Message
  49. err = nil
  50. }
  51. // Fallback to status code converted to message if still no error code
  52. if len(errCode) == 0 {
  53. statusText := http.StatusText(r.HTTPResponse.StatusCode)
  54. errCode = strings.Replace(statusText, " ", "", -1)
  55. errMsg = statusText
  56. }
  57. r.Error = awserr.NewRequestFailure(
  58. awserr.New(errCode, errMsg, err),
  59. r.HTTPResponse.StatusCode,
  60. r.RequestID,
  61. )
  62. }
  63. // A RequestFailure provides access to the S3 Request ID and Host ID values
  64. // returned from API operation errors. Getting the error as a string will
  65. // return the formated error with the same information as awserr.RequestFailure,
  66. // while also adding the HostID value from the response.
  67. type RequestFailure interface {
  68. awserr.RequestFailure
  69. // Host ID is the S3 Host ID needed for debug, and contacting support
  70. HostID() string
  71. }