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 }