github.com/minio/minio-go/v6@v6.0.57/api-error-response.go (about) 1 /* 2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage 3 * Copyright 2015-2017 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 18 package minio 19 20 import ( 21 "encoding/xml" 22 "fmt" 23 "net/http" 24 ) 25 26 /* **** SAMPLE ERROR RESPONSE **** 27 <?xml version="1.0" encoding="UTF-8"?> 28 <Error> 29 <Code>AccessDenied</Code> 30 <Message>Access Denied</Message> 31 <BucketName>bucketName</BucketName> 32 <Key>objectName</Key> 33 <RequestId>F19772218238A85A</RequestId> 34 <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId> 35 </Error> 36 */ 37 38 // ErrorResponse - Is the typed error returned by all API operations. 39 // ErrorResponse struct should be comparable since it is compared inside 40 // golang http API (https://github.com/golang/go/issues/29768) 41 type ErrorResponse struct { 42 XMLName xml.Name `xml:"Error" json:"-"` 43 Code string 44 Message string 45 BucketName string 46 Key string 47 RequestID string `xml:"RequestId"` 48 HostID string `xml:"HostId"` 49 50 // Region where the bucket is located. This header is returned 51 // only in HEAD bucket and ListObjects response. 52 Region string 53 54 // Captures the server string returned in response header. 55 Server string 56 57 // Underlying HTTP status code for the returned error 58 StatusCode int `xml:"-" json:"-"` 59 } 60 61 // ToErrorResponse - Returns parsed ErrorResponse struct from body and 62 // http headers. 63 // 64 // For example: 65 // 66 // import s3 "github.com/minio/minio-go/v6" 67 // ... 68 // ... 69 // reader, stat, err := s3.GetObject(...) 70 // if err != nil { 71 // resp := s3.ToErrorResponse(err) 72 // } 73 // ... 74 func ToErrorResponse(err error) ErrorResponse { 75 switch err := err.(type) { 76 case ErrorResponse: 77 return err 78 default: 79 return ErrorResponse{} 80 } 81 } 82 83 // Error - Returns S3 error string. 84 func (e ErrorResponse) Error() string { 85 if e.Message == "" { 86 msg, ok := s3ErrorResponseMap[e.Code] 87 if !ok { 88 msg = fmt.Sprintf("Error response code %s.", e.Code) 89 } 90 return msg 91 } 92 return e.Message 93 } 94 95 // Common string for errors to report issue location in unexpected 96 // cases. 97 const ( 98 reportIssue = "Please report this issue at https://github.com/minio/minio-go/issues." 99 ) 100 101 // httpRespToErrorResponse returns a new encoded ErrorResponse 102 // structure as error. 103 func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) error { 104 if resp == nil { 105 msg := "Response is empty. " + reportIssue 106 return ErrInvalidArgument(msg) 107 } 108 109 errResp := ErrorResponse{ 110 StatusCode: resp.StatusCode, 111 Server: resp.Header.Get("Server"), 112 } 113 114 err := xmlDecoder(resp.Body, &errResp) 115 // Xml decoding failed with no body, fall back to HTTP headers. 116 if err != nil { 117 switch resp.StatusCode { 118 case http.StatusNotFound: 119 if objectName == "" { 120 errResp = ErrorResponse{ 121 StatusCode: resp.StatusCode, 122 Code: "NoSuchBucket", 123 Message: "The specified bucket does not exist.", 124 BucketName: bucketName, 125 } 126 } else { 127 errResp = ErrorResponse{ 128 StatusCode: resp.StatusCode, 129 Code: "NoSuchKey", 130 Message: "The specified key does not exist.", 131 BucketName: bucketName, 132 Key: objectName, 133 } 134 } 135 case http.StatusForbidden: 136 errResp = ErrorResponse{ 137 StatusCode: resp.StatusCode, 138 Code: "AccessDenied", 139 Message: "Access Denied.", 140 BucketName: bucketName, 141 Key: objectName, 142 } 143 case http.StatusConflict: 144 errResp = ErrorResponse{ 145 StatusCode: resp.StatusCode, 146 Code: "Conflict", 147 Message: "Bucket not empty.", 148 BucketName: bucketName, 149 } 150 case http.StatusPreconditionFailed: 151 errResp = ErrorResponse{ 152 StatusCode: resp.StatusCode, 153 Code: "PreconditionFailed", 154 Message: s3ErrorResponseMap["PreconditionFailed"], 155 BucketName: bucketName, 156 Key: objectName, 157 } 158 default: 159 errResp = ErrorResponse{ 160 StatusCode: resp.StatusCode, 161 Code: resp.Status, 162 Message: resp.Status, 163 BucketName: bucketName, 164 } 165 } 166 } 167 168 // Save hostID, requestID and region information 169 // from headers if not available through error XML. 170 if errResp.RequestID == "" { 171 errResp.RequestID = resp.Header.Get("x-amz-request-id") 172 } 173 if errResp.HostID == "" { 174 errResp.HostID = resp.Header.Get("x-amz-id-2") 175 } 176 if errResp.Region == "" { 177 errResp.Region = resp.Header.Get("x-amz-bucket-region") 178 } 179 if errResp.Code == "InvalidRegion" && errResp.Region != "" { 180 errResp.Message = fmt.Sprintf("Region does not match, expecting region ‘%s’.", errResp.Region) 181 } 182 183 return errResp 184 } 185 186 // ErrTransferAccelerationBucket - bucket name is invalid to be used with transfer acceleration. 187 func ErrTransferAccelerationBucket(bucketName string) error { 188 return ErrorResponse{ 189 StatusCode: http.StatusBadRequest, 190 Code: "InvalidArgument", 191 Message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’.", 192 BucketName: bucketName, 193 } 194 } 195 196 // ErrEntityTooLarge - Input size is larger than supported maximum. 197 func ErrEntityTooLarge(totalSize, maxObjectSize int64, bucketName, objectName string) error { 198 msg := fmt.Sprintf("Your proposed upload size ‘%d’ exceeds the maximum allowed object size ‘%d’ for single PUT operation.", totalSize, maxObjectSize) 199 return ErrorResponse{ 200 StatusCode: http.StatusBadRequest, 201 Code: "EntityTooLarge", 202 Message: msg, 203 BucketName: bucketName, 204 Key: objectName, 205 } 206 } 207 208 // ErrEntityTooSmall - Input size is smaller than supported minimum. 209 func ErrEntityTooSmall(totalSize int64, bucketName, objectName string) error { 210 msg := fmt.Sprintf("Your proposed upload size ‘%d’ is below the minimum allowed object size ‘0B’ for single PUT operation.", totalSize) 211 return ErrorResponse{ 212 StatusCode: http.StatusBadRequest, 213 Code: "EntityTooSmall", 214 Message: msg, 215 BucketName: bucketName, 216 Key: objectName, 217 } 218 } 219 220 // ErrUnexpectedEOF - Unexpected end of file reached. 221 func ErrUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) error { 222 msg := fmt.Sprintf("Data read ‘%d’ is not equal to the size ‘%d’ of the input Reader.", totalRead, totalSize) 223 return ErrorResponse{ 224 StatusCode: http.StatusBadRequest, 225 Code: "UnexpectedEOF", 226 Message: msg, 227 BucketName: bucketName, 228 Key: objectName, 229 } 230 } 231 232 // ErrInvalidBucketName - Invalid bucket name response. 233 func ErrInvalidBucketName(message string) error { 234 return ErrorResponse{ 235 StatusCode: http.StatusBadRequest, 236 Code: "InvalidBucketName", 237 Message: message, 238 RequestID: "minio", 239 } 240 } 241 242 // ErrInvalidObjectName - Invalid object name response. 243 func ErrInvalidObjectName(message string) error { 244 return ErrorResponse{ 245 StatusCode: http.StatusNotFound, 246 Code: "NoSuchKey", 247 Message: message, 248 RequestID: "minio", 249 } 250 } 251 252 // ErrInvalidObjectPrefix - Invalid object prefix response is 253 // similar to object name response. 254 var ErrInvalidObjectPrefix = ErrInvalidObjectName 255 256 // ErrInvalidArgument - Invalid argument response. 257 func ErrInvalidArgument(message string) error { 258 return ErrorResponse{ 259 StatusCode: http.StatusBadRequest, 260 Code: "InvalidArgument", 261 Message: message, 262 RequestID: "minio", 263 } 264 } 265 266 // ErrNoSuchBucketPolicy - No Such Bucket Policy response 267 // The specified bucket does not have a bucket policy. 268 func ErrNoSuchBucketPolicy(message string) error { 269 return ErrorResponse{ 270 StatusCode: http.StatusNotFound, 271 Code: "NoSuchBucketPolicy", 272 Message: message, 273 RequestID: "minio", 274 } 275 } 276 277 // ErrAPINotSupported - API not supported response 278 // The specified API call is not supported 279 func ErrAPINotSupported(message string) error { 280 return ErrorResponse{ 281 StatusCode: http.StatusNotImplemented, 282 Code: "APINotSupported", 283 Message: message, 284 RequestID: "minio", 285 } 286 }