github.com/minio/madmin-go/v2@v2.2.1/api-error-response.go (about)

     1  //
     2  // Copyright (c) 2015-2022 MinIO, Inc.
     3  //
     4  // This file is part of MinIO Object Storage stack
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Affero General Public License as
     8  // published by the Free Software Foundation, either version 3 of the
     9  // License, or (at your option) any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU Affero General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Affero General Public License
    17  // along with this program. If not, see <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  package madmin
    21  
    22  import (
    23  	"encoding/hex"
    24  	"encoding/json"
    25  	"encoding/xml"
    26  	"fmt"
    27  	"io"
    28  	"io/ioutil"
    29  	"net/http"
    30  	"unicode/utf8"
    31  )
    32  
    33  /* **** SAMPLE ERROR RESPONSE ****
    34  <?xml version="1.0" encoding="UTF-8"?>
    35  <Error>
    36     <Code>AccessDenied</Code>
    37     <Message>Access Denied</Message>
    38     <BucketName>bucketName</BucketName>
    39     <Key>objectName</Key>
    40     <RequestId>F19772218238A85A</RequestId>
    41     <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
    42  </Error>
    43  */
    44  
    45  // ErrorResponse - Is the typed error returned by all API operations.
    46  type ErrorResponse struct {
    47  	XMLName    xml.Name `xml:"Error" json:"-"`
    48  	Code       string
    49  	Message    string
    50  	BucketName string
    51  	Key        string
    52  	RequestID  string `xml:"RequestId"`
    53  	HostID     string `xml:"HostId"`
    54  
    55  	// Region where the bucket is located. This header is returned
    56  	// only in HEAD bucket and ListObjects response.
    57  	Region string
    58  }
    59  
    60  // Error - Returns HTTP error string
    61  func (e ErrorResponse) Error() string {
    62  	return e.Message
    63  }
    64  
    65  const (
    66  	reportIssue = "Please report this issue at https://github.com/minio/minio/issues."
    67  )
    68  
    69  // httpRespToErrorResponse returns a new encoded ErrorResponse
    70  // structure as error.
    71  func httpRespToErrorResponse(resp *http.Response) error {
    72  	if resp == nil || resp.Body == nil {
    73  		msg := "Response is empty. " + reportIssue
    74  		return ErrInvalidArgument(msg)
    75  	}
    76  
    77  	defer closeResponse(resp)
    78  	// Limit to 100K
    79  	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 100<<10))
    80  	if err != nil {
    81  		return ErrorResponse{
    82  			Code:    resp.Status,
    83  			Message: fmt.Sprintf("Failed to read server response: %s.", err),
    84  		}
    85  	}
    86  
    87  	var errResp ErrorResponse
    88  	// Decode the json error
    89  	err = json.Unmarshal(body, &errResp)
    90  	if err != nil {
    91  		// We might get errors as XML, try that.
    92  		xmlErr := xml.Unmarshal(body, &errResp)
    93  
    94  		if xmlErr != nil {
    95  			bodyString := string(body)
    96  			if !utf8.Valid(body) {
    97  				bodyString = hex.EncodeToString(body)
    98  			}
    99  			if len(bodyString) > 1024 {
   100  				bodyString = bodyString[:1021] + "..."
   101  			}
   102  			return ErrorResponse{
   103  				Code:    resp.Status,
   104  				Message: fmt.Sprintf("Failed to parse server response (%s): %s", err.Error(), bodyString),
   105  			}
   106  		}
   107  	}
   108  	return errResp
   109  }
   110  
   111  // ToErrorResponse - Returns parsed ErrorResponse struct from body and
   112  // http headers.
   113  //
   114  // For example:
   115  //
   116  //	import admin "github.com/minio/madmin-go/v2"
   117  //	...
   118  //	...
   119  //	ss, err := adm.ServiceStatus(...)
   120  //	if err != nil {
   121  //	   resp := admin.ToErrorResponse(err)
   122  //	}
   123  //	...
   124  func ToErrorResponse(err error) ErrorResponse {
   125  	switch err := err.(type) {
   126  	case ErrorResponse:
   127  		return err
   128  	default:
   129  		return ErrorResponse{}
   130  	}
   131  }
   132  
   133  // ErrInvalidArgument - Invalid argument response.
   134  func ErrInvalidArgument(message string) error {
   135  	return ErrorResponse{
   136  		Code:      "InvalidArgument",
   137  		Message:   message,
   138  		RequestID: "minio",
   139  	}
   140  }