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  }