123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /*
- * MinIO Go Library for Amazon S3 Compatible Cloud Storage
- * Copyright 2015-2020 MinIO, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package minio
- import (
- "encoding/xml"
- "fmt"
- "net/http"
- )
- /* **** SAMPLE ERROR RESPONSE ****
- <?xml version="1.0" encoding="UTF-8"?>
- <Error>
- <Code>AccessDenied</Code>
- <Message>Access Denied</Message>
- <BucketName>bucketName</BucketName>
- <Key>objectName</Key>
- <RequestId>F19772218238A85A</RequestId>
- <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
- </Error>
- */
- // ErrorResponse - Is the typed error returned by all API operations.
- // ErrorResponse struct should be comparable since it is compared inside
- // golang http API (https://github.com/golang/go/issues/29768)
- type ErrorResponse struct {
- XMLName xml.Name `xml:"Error" json:"-"`
- Code string
- Message string
- BucketName string
- Key string
- RequestID string `xml:"RequestId"`
- HostID string `xml:"HostId"`
- // Region where the bucket is located. This header is returned
- // only in HEAD bucket and ListObjects response.
- Region string
- // Captures the server string returned in response header.
- Server string
- // Underlying HTTP status code for the returned error
- StatusCode int `xml:"-" json:"-"`
- }
- // ToErrorResponse - Returns parsed ErrorResponse struct from body and
- // http headers.
- //
- // For example:
- //
- // import s3 "github.com/minio/minio-go/v7"
- // ...
- // ...
- // reader, stat, err := s3.GetObject(...)
- // if err != nil {
- // resp := s3.ToErrorResponse(err)
- // }
- // ...
- func ToErrorResponse(err error) ErrorResponse {
- switch err := err.(type) {
- case ErrorResponse:
- return err
- default:
- return ErrorResponse{}
- }
- }
- // Error - Returns S3 error string.
- func (e ErrorResponse) Error() string {
- if e.Message == "" {
- msg, ok := s3ErrorResponseMap[e.Code]
- if !ok {
- msg = fmt.Sprintf("Error response code %s.", e.Code)
- }
- return msg
- }
- return e.Message
- }
- // Common string for errors to report issue location in unexpected
- // cases.
- const (
- reportIssue = "Please report this issue at https://github.com/minio/minio-go/issues."
- )
- // httpRespToErrorResponse returns a new encoded ErrorResponse
- // structure as error.
- func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) error {
- if resp == nil {
- msg := "Empty http response. " + reportIssue
- return errInvalidArgument(msg)
- }
- errResp := ErrorResponse{
- StatusCode: resp.StatusCode,
- Server: resp.Header.Get("Server"),
- }
- err := xmlDecoder(resp.Body, &errResp)
- // Xml decoding failed with no body, fall back to HTTP headers.
- if err != nil {
- switch resp.StatusCode {
- case http.StatusNotFound:
- if objectName == "" {
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: "NoSuchBucket",
- Message: "The specified bucket does not exist.",
- BucketName: bucketName,
- }
- } else {
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: "NoSuchKey",
- Message: "The specified key does not exist.",
- BucketName: bucketName,
- Key: objectName,
- }
- }
- case http.StatusForbidden:
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: "AccessDenied",
- Message: "Access Denied.",
- BucketName: bucketName,
- Key: objectName,
- }
- case http.StatusConflict:
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: "Conflict",
- Message: "Bucket not empty.",
- BucketName: bucketName,
- }
- case http.StatusPreconditionFailed:
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: "PreconditionFailed",
- Message: s3ErrorResponseMap["PreconditionFailed"],
- BucketName: bucketName,
- Key: objectName,
- }
- default:
- errResp = ErrorResponse{
- StatusCode: resp.StatusCode,
- Code: resp.Status,
- Message: resp.Status,
- BucketName: bucketName,
- }
- }
- }
- // Save hostID, requestID and region information
- // from headers if not available through error XML.
- if errResp.RequestID == "" {
- errResp.RequestID = resp.Header.Get("x-amz-request-id")
- }
- if errResp.HostID == "" {
- errResp.HostID = resp.Header.Get("x-amz-id-2")
- }
- if errResp.Region == "" {
- errResp.Region = resp.Header.Get("x-amz-bucket-region")
- }
- if errResp.Code == "InvalidRegion" && errResp.Region != "" {
- errResp.Message = fmt.Sprintf("Region does not match, expecting region ‘%s’.", errResp.Region)
- }
- return errResp
- }
- // errTransferAccelerationBucket - bucket name is invalid to be used with transfer acceleration.
- func errTransferAccelerationBucket(bucketName string) error {
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "InvalidArgument",
- Message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’.",
- BucketName: bucketName,
- }
- }
- // errEntityTooLarge - Input size is larger than supported maximum.
- func errEntityTooLarge(totalSize, maxObjectSize int64, bucketName, objectName string) error {
- msg := fmt.Sprintf("Your proposed upload size ‘%d’ exceeds the maximum allowed object size ‘%d’ for single PUT operation.", totalSize, maxObjectSize)
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "EntityTooLarge",
- Message: msg,
- BucketName: bucketName,
- Key: objectName,
- }
- }
- // errEntityTooSmall - Input size is smaller than supported minimum.
- func errEntityTooSmall(totalSize int64, bucketName, objectName string) error {
- msg := fmt.Sprintf("Your proposed upload size ‘%d’ is below the minimum allowed object size ‘0B’ for single PUT operation.", totalSize)
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "EntityTooSmall",
- Message: msg,
- BucketName: bucketName,
- Key: objectName,
- }
- }
- // errUnexpectedEOF - Unexpected end of file reached.
- func errUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) error {
- msg := fmt.Sprintf("Data read ‘%d’ is not equal to the size ‘%d’ of the input Reader.", totalRead, totalSize)
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "UnexpectedEOF",
- Message: msg,
- BucketName: bucketName,
- Key: objectName,
- }
- }
- // errInvalidBucketName - Invalid bucket name response.
- func errInvalidBucketName(message string) error {
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "InvalidBucketName",
- Message: message,
- RequestID: "minio",
- }
- }
- // errInvalidObjectName - Invalid object name response.
- func errInvalidObjectName(message string) error {
- return ErrorResponse{
- StatusCode: http.StatusNotFound,
- Code: "NoSuchKey",
- Message: message,
- RequestID: "minio",
- }
- }
- // errInvalidArgument - Invalid argument response.
- func errInvalidArgument(message string) error {
- return ErrorResponse{
- StatusCode: http.StatusBadRequest,
- Code: "InvalidArgument",
- Message: message,
- RequestID: "minio",
- }
- }
- // errAPINotSupported - API not supported response
- // The specified API call is not supported
- func errAPINotSupported(message string) error {
- return ErrorResponse{
- StatusCode: http.StatusNotImplemented,
- Code: "APINotSupported",
- Message: message,
- RequestID: "minio",
- }
- }
|