unmarshal_error.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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. hostID := r.HTTPResponse.Header.Get("X-Amz-Id-2")
  22. // Bucket exists in a different region, and request needs
  23. // to be made to the correct region.
  24. if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
  25. r.Error = requestFailure{
  26. RequestFailure: awserr.NewRequestFailure(
  27. awserr.New("BucketRegionError",
  28. fmt.Sprintf("incorrect region, the bucket is not in '%s' region",
  29. aws.StringValue(r.Config.Region)),
  30. nil),
  31. r.HTTPResponse.StatusCode,
  32. r.RequestID,
  33. ),
  34. hostID: hostID,
  35. }
  36. return
  37. }
  38. var errCode, errMsg string
  39. // Attempt to parse error from body if it is known
  40. resp := &xmlErrorResponse{}
  41. err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
  42. if err != nil && err != io.EOF {
  43. errCode = "SerializationError"
  44. errMsg = "failed to decode S3 XML error response"
  45. } else {
  46. errCode = resp.Code
  47. errMsg = resp.Message
  48. err = nil
  49. }
  50. // Fallback to status code converted to message if still no error code
  51. if len(errCode) == 0 {
  52. statusText := http.StatusText(r.HTTPResponse.StatusCode)
  53. errCode = strings.Replace(statusText, " ", "", -1)
  54. errMsg = statusText
  55. }
  56. r.Error = requestFailure{
  57. RequestFailure: awserr.NewRequestFailure(
  58. awserr.New(errCode, errMsg, err),
  59. r.HTTPResponse.StatusCode,
  60. r.RequestID,
  61. ),
  62. hostID: hostID,
  63. }
  64. }
  65. // A RequestFailure provides access to the S3 Request ID and Host ID values
  66. // returned from API operation errors. Getting the error as a string will
  67. // return the formated error with the same information as awserr.RequestFailure,
  68. // while also adding the HostID value from the response.
  69. type RequestFailure interface {
  70. awserr.RequestFailure
  71. // Host ID is the S3 Host ID needed for debug, and contacting support
  72. HostID() string
  73. }
  74. type requestFailure struct {
  75. awserr.RequestFailure
  76. hostID string
  77. }
  78. func (r requestFailure) Error() string {
  79. extra := fmt.Sprintf("status code: %d, request id: %s, host id: %s",
  80. r.StatusCode(), r.RequestID(), r.hostID)
  81. return awserr.SprintError(r.Code(), r.Message(), extra, r.OrigErr())
  82. }
  83. func (r requestFailure) String() string {
  84. return r.Error()
  85. }
  86. func (r requestFailure) HostID() string {
  87. return r.hostID
  88. }