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  }