]>
Commit | Line | Data |
---|---|---|
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 | "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" | |
15 | ) | |
16 | ||
17 | type xmlErrorResponse struct { | |
18 | XMLName xml.Name `xml:"Error"` | |
19 | Code string `xml:"Code"` | |
20 | Message string `xml:"Message"` | |
21 | } | |
22 | ||
23 | func unmarshalError(r *request.Request) { | |
24 | defer r.HTTPResponse.Body.Close() | |
25 | defer io.Copy(ioutil.Discard, r.HTTPResponse.Body) | |
26 | ||
27 | // Bucket exists in a different region, and request needs | |
28 | // to be made to the correct region. | |
29 | if r.HTTPResponse.StatusCode == http.StatusMovedPermanently { | |
30 | msg := fmt.Sprintf( | |
31 | "incorrect region, the bucket is not in '%s' region at endpoint '%s'", | |
32 | aws.StringValue(r.Config.Region), | |
33 | aws.StringValue(r.Config.Endpoint), | |
34 | ) | |
35 | if v := r.HTTPResponse.Header.Get("x-amz-bucket-region"); len(v) != 0 { | |
36 | msg += fmt.Sprintf(", bucket is in '%s' region", v) | |
37 | } | |
38 | r.Error = awserr.NewRequestFailure( | |
39 | awserr.New("BucketRegionError", msg, nil), | |
40 | r.HTTPResponse.StatusCode, | |
41 | r.RequestID, | |
42 | ) | |
43 | return | |
44 | } | |
45 | ||
46 | // Attempt to parse error from body if it is known | |
47 | var errResp xmlErrorResponse | |
48 | err := xmlutil.UnmarshalXMLError(&errResp, r.HTTPResponse.Body) | |
49 | if err == io.EOF { | |
50 | // Only capture the error if an unmarshal error occurs that is not EOF, | |
51 | // because S3 might send an error without a error message which causes | |
52 | // the XML unmarshal to fail with EOF. | |
53 | err = nil | |
54 | } | |
55 | if err != nil { | |
56 | r.Error = awserr.NewRequestFailure( | |
57 | awserr.New(request.ErrCodeSerialization, | |
58 | "failed to unmarshal error message", err), | |
59 | r.HTTPResponse.StatusCode, | |
60 | r.RequestID, | |
61 | ) | |
62 | return | |
63 | } | |
64 | ||
65 | // Fallback to status code converted to message if still no error code | |
66 | if len(errResp.Code) == 0 { | |
67 | statusText := http.StatusText(r.HTTPResponse.StatusCode) | |
68 | errResp.Code = strings.Replace(statusText, " ", "", -1) | |
69 | errResp.Message = statusText | |
70 | } | |
71 | ||
72 | r.Error = awserr.NewRequestFailure( | |
73 | awserr.New(errResp.Code, errResp.Message, err), | |
74 | r.HTTPResponse.StatusCode, | |
75 | r.RequestID, | |
76 | ) | |
77 | } | |
78 | ||
79 | // A RequestFailure provides access to the S3 Request ID and Host ID values | |
80 | // returned from API operation errors. Getting the error as a string will | |
81 | // return the formated error with the same information as awserr.RequestFailure, | |
82 | // while also adding the HostID value from the response. | |
83 | type RequestFailure interface { | |
84 | awserr.RequestFailure | |
85 | ||
86 | // Host ID is the S3 Host ID needed for debug, and contacting support | |
87 | HostID() string | |
88 | } |