1package s3shared
2
3import (
4	"encoding/xml"
5	"fmt"
6	"io"
7	"net/http"
8	"strings"
9)
10
11// ErrorComponents represents the error response fields
12// that will be deserialized from an xml error response body
13type ErrorComponents struct {
14	Code      string `xml:"Code"`
15	Message   string `xml:"Message"`
16	RequestID string `xml:"RequestId"`
17	HostID    string `xml:"HostId"`
18}
19
20// GetUnwrappedErrorResponseComponents returns the error fields from an xml error response body
21func GetUnwrappedErrorResponseComponents(r io.Reader) (ErrorComponents, error) {
22	var errComponents ErrorComponents
23	if err := xml.NewDecoder(r).Decode(&errComponents); err != nil && err != io.EOF {
24		return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response : %w", err)
25	}
26	return errComponents, nil
27}
28
29// GetWrappedErrorResponseComponents returns the error fields from an xml error response body
30// in which error code, and message are wrapped by a <Error> tag
31func GetWrappedErrorResponseComponents(r io.Reader) (ErrorComponents, error) {
32	var errComponents struct {
33		Code      string `xml:"Error>Code"`
34		Message   string `xml:"Error>Message"`
35		RequestID string `xml:"RequestId"`
36		HostID    string `xml:"HostId"`
37	}
38
39	if err := xml.NewDecoder(r).Decode(&errComponents); err != nil && err != io.EOF {
40		return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response : %w", err)
41	}
42
43	return ErrorComponents{
44		Code:      errComponents.Code,
45		Message:   errComponents.Message,
46		RequestID: errComponents.RequestID,
47		HostID:    errComponents.HostID,
48	}, nil
49}
50
51// GetErrorResponseComponents retrieves error components according to passed in options
52func GetErrorResponseComponents(r io.Reader, options ErrorResponseDeserializerOptions) (ErrorComponents, error) {
53	var errComponents ErrorComponents
54	var err error
55
56	if options.IsWrappedWithErrorTag {
57		errComponents, err = GetWrappedErrorResponseComponents(r)
58	} else {
59		errComponents, err = GetUnwrappedErrorResponseComponents(r)
60	}
61
62	if err != nil {
63		return ErrorComponents{}, err
64	}
65
66	// If an error code or message is not retrieved, it is derived from the http status code
67	// eg, for S3 service, we derive err code and message, if none is found
68	if options.UseStatusCode && len(errComponents.Code) == 0 &&
69		len(errComponents.Message) == 0 {
70		// derive code and message from status code
71		statusText := http.StatusText(options.StatusCode)
72		errComponents.Code = strings.Replace(statusText, " ", "", -1)
73		errComponents.Message = statusText
74	}
75	return errComponents, nil
76}
77
78// ErrorResponseDeserializerOptions represents error response deserializer options for s3 and s3-control service
79type ErrorResponseDeserializerOptions struct {
80	// UseStatusCode denotes if status code should be used to retrieve error code, msg
81	UseStatusCode bool
82
83	// StatusCode is status code of error response
84	StatusCode int
85
86	//IsWrappedWithErrorTag represents if error response's code, msg is wrapped within an
87	// additional <Error> tag
88	IsWrappedWithErrorTag bool
89}
90