mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-10-18 23:45:08 +00:00
158 lines
4.3 KiB
Go
158 lines
4.3 KiB
Go
package restjson
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
|
"github.com/aws/aws-sdk-go/private/protocol"
|
|
"github.com/aws/aws-sdk-go/private/protocol/json/jsonutil"
|
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
|
)
|
|
|
|
const (
|
|
errorTypeHeader = "X-Amzn-Errortype"
|
|
errorMessageHeader = "X-Amzn-Errormessage"
|
|
)
|
|
|
|
// UnmarshalTypedError provides unmarshaling errors API response errors
|
|
// for both typed and untyped errors.
|
|
type UnmarshalTypedError struct {
|
|
exceptions map[string]func(protocol.ResponseMetadata) error
|
|
}
|
|
|
|
// NewUnmarshalTypedError returns an UnmarshalTypedError initialized for the
|
|
// set of exception names to the error unmarshalers
|
|
func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata) error) *UnmarshalTypedError {
|
|
return &UnmarshalTypedError{
|
|
exceptions: exceptions,
|
|
}
|
|
}
|
|
|
|
// UnmarshalError attempts to unmarshal the HTTP response error as a known
|
|
// error type. If unable to unmarshal the error type, the generic SDK error
|
|
// type will be used.
|
|
func (u *UnmarshalTypedError) UnmarshalError(
|
|
resp *http.Response,
|
|
respMeta protocol.ResponseMetadata,
|
|
) (error, error) {
|
|
code, msg, err := unmarshalErrorInfo(resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fn, ok := u.exceptions[code]
|
|
if !ok {
|
|
return awserr.NewRequestFailure(
|
|
awserr.New(code, msg, nil),
|
|
respMeta.StatusCode,
|
|
respMeta.RequestID,
|
|
), nil
|
|
}
|
|
|
|
v := fn(respMeta)
|
|
if err := jsonutil.UnmarshalJSONCaseInsensitive(v, resp.Body); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := rest.UnmarshalResponse(resp, v, true); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
// UnmarshalErrorHandler is a named request handler for unmarshaling restjson
|
|
// protocol request errors
|
|
var UnmarshalErrorHandler = request.NamedHandler{
|
|
Name: "awssdk.restjson.UnmarshalError",
|
|
Fn: UnmarshalError,
|
|
}
|
|
|
|
// UnmarshalError unmarshals a response error for the REST JSON protocol.
|
|
func UnmarshalError(r *request.Request) {
|
|
defer r.HTTPResponse.Body.Close()
|
|
|
|
code, msg, err := unmarshalErrorInfo(r.HTTPResponse)
|
|
if err != nil {
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New(request.ErrCodeSerialization, "failed to unmarshal response error", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
return
|
|
}
|
|
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New(code, msg, nil),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
}
|
|
|
|
type jsonErrorResponse struct {
|
|
Type string `json:"__type"`
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (j *jsonErrorResponse) SanitizedCode() string {
|
|
code := j.Code
|
|
if len(j.Type) > 0 {
|
|
code = j.Type
|
|
}
|
|
return sanitizeCode(code)
|
|
}
|
|
|
|
// Remove superfluous components from a restJson error code.
|
|
// - If a : character is present, then take only the contents before the
|
|
// first : character in the value.
|
|
// - If a # character is present, then take only the contents after the first
|
|
// # character in the value.
|
|
//
|
|
// All of the following error values resolve to FooError:
|
|
// - FooError
|
|
// - FooError:http://internal.amazon.com/coral/com.amazon.coral.validate/
|
|
// - aws.protocoltests.restjson#FooError
|
|
// - aws.protocoltests.restjson#FooError:http://internal.amazon.com/coral/com.amazon.coral.validate/
|
|
func sanitizeCode(code string) string {
|
|
noColon := strings.SplitN(code, ":", 2)[0]
|
|
hashSplit := strings.SplitN(noColon, "#", 2)
|
|
return hashSplit[len(hashSplit)-1]
|
|
}
|
|
|
|
// attempt to garner error details from the response, preferring header values
|
|
// when present
|
|
func unmarshalErrorInfo(resp *http.Response) (code string, msg string, err error) {
|
|
code = sanitizeCode(resp.Header.Get(errorTypeHeader))
|
|
msg = resp.Header.Get(errorMessageHeader)
|
|
if len(code) > 0 && len(msg) > 0 {
|
|
return
|
|
}
|
|
|
|
// a modeled error will have to be re-deserialized later, so the body must
|
|
// be preserved
|
|
var buf bytes.Buffer
|
|
tee := io.TeeReader(resp.Body, &buf)
|
|
defer func() { resp.Body = ioutil.NopCloser(&buf) }()
|
|
|
|
var jsonErr jsonErrorResponse
|
|
if decodeErr := json.NewDecoder(tee).Decode(&jsonErr); decodeErr != nil && decodeErr != io.EOF {
|
|
err = awserr.NewUnmarshalError(decodeErr, "failed to decode response body", buf.Bytes())
|
|
return
|
|
}
|
|
|
|
if len(code) == 0 {
|
|
code = jsonErr.SanitizedCode()
|
|
}
|
|
if len(msg) == 0 {
|
|
msg = jsonErr.Message
|
|
}
|
|
return
|
|
}
|