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 }