aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/v7/api-error-response.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/minio/minio-go/v7/api-error-response.go')
-rw-r--r--vendor/github.com/minio/minio-go/v7/api-error-response.go284
1 files changed, 284 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/api-error-response.go b/vendor/github.com/minio/minio-go/v7/api-error-response.go
new file mode 100644
index 0000000..7df211f
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/v7/api-error-response.go
@@ -0,0 +1,284 @@
1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2015-2020 MinIO, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package minio
19
20import (
21 "bytes"
22 "encoding/xml"
23 "fmt"
24 "io"
25 "net/http"
26 "strings"
27)
28
29/* **** SAMPLE ERROR RESPONSE ****
30<?xml version="1.0" encoding="UTF-8"?>
31<Error>
32 <Code>AccessDenied</Code>
33 <Message>Access Denied</Message>
34 <BucketName>bucketName</BucketName>
35 <Key>objectName</Key>
36 <RequestId>F19772218238A85A</RequestId>
37 <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
38</Error>
39*/
40
41// ErrorResponse - Is the typed error returned by all API operations.
42// ErrorResponse struct should be comparable since it is compared inside
43// golang http API (https://github.com/golang/go/issues/29768)
44type ErrorResponse struct {
45 XMLName xml.Name `xml:"Error" json:"-"`
46 Code string
47 Message string
48 BucketName string
49 Key string
50 Resource string
51 RequestID string `xml:"RequestId"`
52 HostID string `xml:"HostId"`
53
54 // Region where the bucket is located. This header is returned
55 // only in HEAD bucket and ListObjects response.
56 Region string
57
58 // Captures the server string returned in response header.
59 Server string
60
61 // Underlying HTTP status code for the returned error
62 StatusCode int `xml:"-" json:"-"`
63}
64
65// ToErrorResponse - Returns parsed ErrorResponse struct from body and
66// http headers.
67//
68// For example:
69//
70// import s3 "github.com/minio/minio-go/v7"
71// ...
72// ...
73// reader, stat, err := s3.GetObject(...)
74// if err != nil {
75// resp := s3.ToErrorResponse(err)
76// }
77// ...
78func ToErrorResponse(err error) ErrorResponse {
79 switch err := err.(type) {
80 case ErrorResponse:
81 return err
82 default:
83 return ErrorResponse{}
84 }
85}
86
87// Error - Returns S3 error string.
88func (e ErrorResponse) Error() string {
89 if e.Message == "" {
90 msg, ok := s3ErrorResponseMap[e.Code]
91 if !ok {
92 msg = fmt.Sprintf("Error response code %s.", e.Code)
93 }
94 return msg
95 }
96 return e.Message
97}
98
99// Common string for errors to report issue location in unexpected
100// cases.
101const (
102 reportIssue = "Please report this issue at https://github.com/minio/minio-go/issues."
103)
104
105// xmlDecodeAndBody reads the whole body up to 1MB and
106// tries to XML decode it into v.
107// The body that was read and any error from reading or decoding is returned.
108func xmlDecodeAndBody(bodyReader io.Reader, v interface{}) ([]byte, error) {
109 // read the whole body (up to 1MB)
110 const maxBodyLength = 1 << 20
111 body, err := io.ReadAll(io.LimitReader(bodyReader, maxBodyLength))
112 if err != nil {
113 return nil, err
114 }
115 return bytes.TrimSpace(body), xmlDecoder(bytes.NewReader(body), v)
116}
117
118// httpRespToErrorResponse returns a new encoded ErrorResponse
119// structure as error.
120func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) error {
121 if resp == nil {
122 msg := "Empty http response. " + reportIssue
123 return errInvalidArgument(msg)
124 }
125
126 errResp := ErrorResponse{
127 StatusCode: resp.StatusCode,
128 Server: resp.Header.Get("Server"),
129 }
130
131 errBody, err := xmlDecodeAndBody(resp.Body, &errResp)
132 // Xml decoding failed with no body, fall back to HTTP headers.
133 if err != nil {
134 switch resp.StatusCode {
135 case http.StatusNotFound:
136 if objectName == "" {
137 errResp = ErrorResponse{
138 StatusCode: resp.StatusCode,
139 Code: "NoSuchBucket",
140 Message: "The specified bucket does not exist.",
141 BucketName: bucketName,
142 }
143 } else {
144 errResp = ErrorResponse{
145 StatusCode: resp.StatusCode,
146 Code: "NoSuchKey",
147 Message: "The specified key does not exist.",
148 BucketName: bucketName,
149 Key: objectName,
150 }
151 }
152 case http.StatusForbidden:
153 errResp = ErrorResponse{
154 StatusCode: resp.StatusCode,
155 Code: "AccessDenied",
156 Message: "Access Denied.",
157 BucketName: bucketName,
158 Key: objectName,
159 }
160 case http.StatusConflict:
161 errResp = ErrorResponse{
162 StatusCode: resp.StatusCode,
163 Code: "Conflict",
164 Message: "Bucket not empty.",
165 BucketName: bucketName,
166 }
167 case http.StatusPreconditionFailed:
168 errResp = ErrorResponse{
169 StatusCode: resp.StatusCode,
170 Code: "PreconditionFailed",
171 Message: s3ErrorResponseMap["PreconditionFailed"],
172 BucketName: bucketName,
173 Key: objectName,
174 }
175 default:
176 msg := resp.Status
177 if len(errBody) > 0 {
178 msg = string(errBody)
179 if len(msg) > 1024 {
180 msg = msg[:1024] + "..."
181 }
182 }
183 errResp = ErrorResponse{
184 StatusCode: resp.StatusCode,
185 Code: resp.Status,
186 Message: msg,
187 BucketName: bucketName,
188 }
189 }
190 }
191
192 code := resp.Header.Get("x-minio-error-code")
193 if code != "" {
194 errResp.Code = code
195 }
196 desc := resp.Header.Get("x-minio-error-desc")
197 if desc != "" {
198 errResp.Message = strings.Trim(desc, `"`)
199 }
200
201 // Save hostID, requestID and region information
202 // from headers if not available through error XML.
203 if errResp.RequestID == "" {
204 errResp.RequestID = resp.Header.Get("x-amz-request-id")
205 }
206 if errResp.HostID == "" {
207 errResp.HostID = resp.Header.Get("x-amz-id-2")
208 }
209 if errResp.Region == "" {
210 errResp.Region = resp.Header.Get("x-amz-bucket-region")
211 }
212 if errResp.Code == "InvalidRegion" && errResp.Region != "" {
213 errResp.Message = fmt.Sprintf("Region does not match, expecting region ‘%s’.", errResp.Region)
214 }
215
216 return errResp
217}
218
219// errTransferAccelerationBucket - bucket name is invalid to be used with transfer acceleration.
220func errTransferAccelerationBucket(bucketName string) error {
221 return ErrorResponse{
222 StatusCode: http.StatusBadRequest,
223 Code: "InvalidArgument",
224 Message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’.",
225 BucketName: bucketName,
226 }
227}
228
229// errEntityTooLarge - Input size is larger than supported maximum.
230func errEntityTooLarge(totalSize, maxObjectSize int64, bucketName, objectName string) error {
231 msg := fmt.Sprintf("Your proposed upload size ‘%d’ exceeds the maximum allowed object size ‘%d’ for single PUT operation.", totalSize, maxObjectSize)
232 return ErrorResponse{
233 StatusCode: http.StatusBadRequest,
234 Code: "EntityTooLarge",
235 Message: msg,
236 BucketName: bucketName,
237 Key: objectName,
238 }
239}
240
241// errEntityTooSmall - Input size is smaller than supported minimum.
242func errEntityTooSmall(totalSize int64, bucketName, objectName string) error {
243 msg := fmt.Sprintf("Your proposed upload size ‘%d’ is below the minimum allowed object size ‘0B’ for single PUT operation.", totalSize)
244 return ErrorResponse{
245 StatusCode: http.StatusBadRequest,
246 Code: "EntityTooSmall",
247 Message: msg,
248 BucketName: bucketName,
249 Key: objectName,
250 }
251}
252
253// errUnexpectedEOF - Unexpected end of file reached.
254func errUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) error {
255 msg := fmt.Sprintf("Data read ‘%d’ is not equal to the size ‘%d’ of the input Reader.", totalRead, totalSize)
256 return ErrorResponse{
257 StatusCode: http.StatusBadRequest,
258 Code: "UnexpectedEOF",
259 Message: msg,
260 BucketName: bucketName,
261 Key: objectName,
262 }
263}
264
265// errInvalidArgument - Invalid argument response.
266func errInvalidArgument(message string) error {
267 return ErrorResponse{
268 StatusCode: http.StatusBadRequest,
269 Code: "InvalidArgument",
270 Message: message,
271 RequestID: "minio",
272 }
273}
274
275// errAPINotSupported - API not supported response
276// The specified API call is not supported
277func errAPINotSupported(message string) error {
278 return ErrorResponse{
279 StatusCode: http.StatusNotImplemented,
280 Code: "APINotSupported",
281 Message: message,
282 RequestID: "minio",
283 }
284}