github.com/aavshr/aws-sdk-go@v1.41.3/private/protocol/restjson/unmarshal_error.go (about) 1 package restjson 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "strings" 9 10 "github.com/aavshr/aws-sdk-go/aws/awserr" 11 "github.com/aavshr/aws-sdk-go/aws/request" 12 "github.com/aavshr/aws-sdk-go/private/protocol" 13 "github.com/aavshr/aws-sdk-go/private/protocol/json/jsonutil" 14 "github.com/aavshr/aws-sdk-go/private/protocol/rest" 15 ) 16 17 const ( 18 errorTypeHeader = "X-Amzn-Errortype" 19 errorMessageHeader = "X-Amzn-Errormessage" 20 ) 21 22 // UnmarshalTypedError provides unmarshaling errors API response errors 23 // for both typed and untyped errors. 24 type UnmarshalTypedError struct { 25 exceptions map[string]func(protocol.ResponseMetadata) error 26 } 27 28 // NewUnmarshalTypedError returns an UnmarshalTypedError initialized for the 29 // set of exception names to the error unmarshalers 30 func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata) error) *UnmarshalTypedError { 31 return &UnmarshalTypedError{ 32 exceptions: exceptions, 33 } 34 } 35 36 // UnmarshalError attempts to unmarshal the HTTP response error as a known 37 // error type. If unable to unmarshal the error type, the generic SDK error 38 // type will be used. 39 func (u *UnmarshalTypedError) UnmarshalError( 40 resp *http.Response, 41 respMeta protocol.ResponseMetadata, 42 ) (error, error) { 43 44 code := resp.Header.Get(errorTypeHeader) 45 msg := resp.Header.Get(errorMessageHeader) 46 47 body := resp.Body 48 if len(code) == 0 { 49 // If unable to get code from HTTP headers have to parse JSON message 50 // to determine what kind of exception this will be. 51 var buf bytes.Buffer 52 var jsonErr jsonErrorResponse 53 teeReader := io.TeeReader(resp.Body, &buf) 54 err := jsonutil.UnmarshalJSONError(&jsonErr, teeReader) 55 if err != nil { 56 return nil, err 57 } 58 59 body = ioutil.NopCloser(&buf) 60 code = jsonErr.Code 61 msg = jsonErr.Message 62 } 63 64 // If code has colon separators remove them so can compare against modeled 65 // exception names. 66 code = strings.SplitN(code, ":", 2)[0] 67 68 if fn, ok := u.exceptions[code]; ok { 69 // If exception code is know, use associated constructor to get a value 70 // for the exception that the JSON body can be unmarshaled into. 71 v := fn(respMeta) 72 if err := jsonutil.UnmarshalJSONCaseInsensitive(v, body); err != nil { 73 return nil, err 74 } 75 76 if err := rest.UnmarshalResponse(resp, v, true); err != nil { 77 return nil, err 78 } 79 80 return v, nil 81 } 82 83 // fallback to unmodeled generic exceptions 84 return awserr.NewRequestFailure( 85 awserr.New(code, msg, nil), 86 respMeta.StatusCode, 87 respMeta.RequestID, 88 ), nil 89 } 90 91 // UnmarshalErrorHandler is a named request handler for unmarshaling restjson 92 // protocol request errors 93 var UnmarshalErrorHandler = request.NamedHandler{ 94 Name: "awssdk.restjson.UnmarshalError", 95 Fn: UnmarshalError, 96 } 97 98 // UnmarshalError unmarshals a response error for the REST JSON protocol. 99 func UnmarshalError(r *request.Request) { 100 defer r.HTTPResponse.Body.Close() 101 102 var jsonErr jsonErrorResponse 103 err := jsonutil.UnmarshalJSONError(&jsonErr, r.HTTPResponse.Body) 104 if err != nil { 105 r.Error = awserr.NewRequestFailure( 106 awserr.New(request.ErrCodeSerialization, 107 "failed to unmarshal response error", err), 108 r.HTTPResponse.StatusCode, 109 r.RequestID, 110 ) 111 return 112 } 113 114 code := r.HTTPResponse.Header.Get(errorTypeHeader) 115 if code == "" { 116 code = jsonErr.Code 117 } 118 msg := r.HTTPResponse.Header.Get(errorMessageHeader) 119 if msg == "" { 120 msg = jsonErr.Message 121 } 122 123 code = strings.SplitN(code, ":", 2)[0] 124 r.Error = awserr.NewRequestFailure( 125 awserr.New(code, jsonErr.Message, nil), 126 r.HTTPResponse.StatusCode, 127 r.RequestID, 128 ) 129 } 130 131 type jsonErrorResponse struct { 132 Code string `json:"code"` 133 Message string `json:"message"` 134 }