]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package s3 |
2 | ||
3 | import ( | |
4 | "encoding/xml" | |
5 | "fmt" | |
6 | "io" | |
7 | "io/ioutil" | |
8 | "net/http" | |
9 | "strings" | |
10 | ||
11 | "github.com/aws/aws-sdk-go/aws" | |
12 | "github.com/aws/aws-sdk-go/aws/awserr" | |
13 | "github.com/aws/aws-sdk-go/aws/request" | |
14 | ) | |
15 | ||
16 | type xmlErrorResponse struct { | |
17 | XMLName xml.Name `xml:"Error"` | |
18 | Code string `xml:"Code"` | |
19 | Message string `xml:"Message"` | |
20 | } | |
21 | ||
22 | func unmarshalError(r *request.Request) { | |
23 | defer r.HTTPResponse.Body.Close() | |
24 | defer io.Copy(ioutil.Discard, r.HTTPResponse.Body) | |
25 | ||
26 | hostID := r.HTTPResponse.Header.Get("X-Amz-Id-2") | |
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 | r.Error = requestFailure{ | |
32 | RequestFailure: awserr.NewRequestFailure( | |
33 | awserr.New("BucketRegionError", | |
34 | fmt.Sprintf("incorrect region, the bucket is not in '%s' region", | |
35 | aws.StringValue(r.Config.Region)), | |
36 | nil), | |
37 | r.HTTPResponse.StatusCode, | |
38 | r.RequestID, | |
39 | ), | |
40 | hostID: hostID, | |
41 | } | |
42 | return | |
43 | } | |
44 | ||
45 | var errCode, errMsg string | |
46 | ||
47 | // Attempt to parse error from body if it is known | |
48 | resp := &xmlErrorResponse{} | |
49 | err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp) | |
50 | if err != nil && err != io.EOF { | |
51 | errCode = "SerializationError" | |
52 | errMsg = "failed to decode S3 XML error response" | |
53 | } else { | |
54 | errCode = resp.Code | |
55 | errMsg = resp.Message | |
56 | err = nil | |
57 | } | |
58 | ||
59 | // Fallback to status code converted to message if still no error code | |
60 | if len(errCode) == 0 { | |
61 | statusText := http.StatusText(r.HTTPResponse.StatusCode) | |
62 | errCode = strings.Replace(statusText, " ", "", -1) | |
63 | errMsg = statusText | |
64 | } | |
65 | ||
66 | r.Error = requestFailure{ | |
67 | RequestFailure: awserr.NewRequestFailure( | |
68 | awserr.New(errCode, errMsg, err), | |
69 | r.HTTPResponse.StatusCode, | |
70 | r.RequestID, | |
71 | ), | |
72 | hostID: hostID, | |
73 | } | |
74 | } | |
75 | ||
76 | // A RequestFailure provides access to the S3 Request ID and Host ID values | |
77 | // returned from API operation errors. Getting the error as a string will | |
78 | // return the formated error with the same information as awserr.RequestFailure, | |
79 | // while also adding the HostID value from the response. | |
80 | type RequestFailure interface { | |
81 | awserr.RequestFailure | |
82 | ||
83 | // Host ID is the S3 Host ID needed for debug, and contacting support | |
84 | HostID() string | |
85 | } | |
86 | ||
87 | type requestFailure struct { | |
88 | awserr.RequestFailure | |
89 | ||
90 | hostID string | |
91 | } | |
92 | ||
93 | func (r requestFailure) Error() string { | |
94 | extra := fmt.Sprintf("status code: %d, request id: %s, host id: %s", | |
95 | r.StatusCode(), r.RequestID(), r.hostID) | |
96 | return awserr.SprintError(r.Code(), r.Message(), extra, r.OrigErr()) | |
97 | } | |
98 | func (r requestFailure) String() string { | |
99 | return r.Error() | |
100 | } | |
101 | func (r requestFailure) HostID() string { | |
102 | return r.hostID | |
103 | } |