github.com/minio/madmin-go/v3@v3.0.51/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  	"net/http"
    29  	"unicode/utf8"
    30  )
    31  
    32  /* **** SAMPLE ERROR RESPONSE ****
    33  <?xml version="1.0" encoding="UTF-8"?>
    34  <Error>
    35     <Code>AccessDenied</Code>
    36     <Message>Access Denied</Message>
    37     <BucketName>bucketName</BucketName>
    38     <Key>objectName</Key>
    39     <RequestId>F19772218238A85A</RequestId>
    40     <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
    41  </Error>
    42  */
    43  
    44  // ErrorResponse - Is the typed error returned by all API operations.
    45  type ErrorResponse struct {
    46  	XMLName    xml.Name `xml:"Error" json:"-"`
    47  	Code       string
    48  	Message    string
    49  	BucketName string
    50  	Key        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  
    59  // Error - Returns HTTP error string
    60  func (e ErrorResponse) Error() string {
    61  	return e.Message
    62  }
    63  
    64  const (
    65  	reportIssue = "Please report this issue at https://github.com/minio/minio/issues."
    66  )
    67  
    68  // httpRespToErrorResponse returns a new encoded ErrorResponse
    69  // structure as error.
    70  func httpRespToErrorResponse(resp *http.Response) error {
    71  	if resp == nil || resp.Body == nil {
    72  		msg := "Response is empty. " + reportIssue
    73  		return ErrInvalidArgument(msg)
    74  	}
    75  
    76  	defer closeResponse(resp)
    77  	// Limit to 100K
    78  	body, err := io.ReadAll(io.LimitReader(resp.Body, 100<<10))
    79  	if err != nil {
    80  		return ErrorResponse{
    81  			Code:    resp.Status,
    82  			Message: fmt.Sprintf("Failed to read server response: %s.", err),
    83  		}
    84  	}
    85  
    86  	var errResp ErrorResponse
    87  	// Decode the json error
    88  	err = json.Unmarshal(body, &errResp)
    89  	if err != nil {
    90  		// We might get errors as XML, try that.
    91  		xmlErr := xml.Unmarshal(body, &errResp)
    92  
    93  		if xmlErr != nil {
    94  			bodyString := string(body)
    95  			if !utf8.Valid(body) {
    96  				bodyString = hex.EncodeToString(body)
    97  			}
    98  			if len(bodyString) > 1024 {
    99  				bodyString = bodyString[:1021] + "..."
   100  			}
   101  			return ErrorResponse{
   102  				Code:    resp.Status,
   103  				Message: fmt.Sprintf("Failed to parse server response (%s): %s", err.Error(), bodyString),
   104  			}
   105  		}
   106  	}
   107  	return errResp
   108  }
   109  
   110  // ToErrorResponse - Returns parsed ErrorResponse struct from body and
   111  // http headers.
   112  //
   113  // For example:
   114  //
   115  //	import admin "github.com/minio/madmin-go/v3"
   116  //	...
   117  //	...
   118  //	ss, err := adm.ServiceStatus(...)
   119  //	if err != nil {
   120  //	   resp := admin.ToErrorResponse(err)
   121  //	}
   122  //	...
   123  func ToErrorResponse(err error) ErrorResponse {
   124  	switch err := err.(type) {
   125  	case ErrorResponse:
   126  		return err
   127  	default:
   128  		return ErrorResponse{}
   129  	}
   130  }
   131  
   132  // ErrInvalidArgument - Invalid argument response.
   133  func ErrInvalidArgument(message string) error {
   134  	return ErrorResponse{
   135  		Code:      "InvalidArgument",
   136  		Message:   message,
   137  		RequestID: "minio",
   138  	}
   139  }