aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/azure-storage-blob-go/2018-03-28/azblob/zc_storage_error.go
blob: 03178b247abd10c878ca64764fd7d443ef1a36bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package azblob

import (
    "bytes"
    "encoding/xml"
    "fmt"
    "net/http"
    "sort"

    "github.com/Azure/azure-pipeline-go/pipeline"
)

func init() {
    // wire up our custom error handling constructor
    responseErrorFactory = newStorageError
}

// ServiceCodeType is a string identifying a storage service error.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/status-and-error-codes2
type ServiceCodeType string

// StorageError identifies a responder-generated network or response parsing error.
type StorageError interface {
    // ResponseError implements error's Error(), net.Error's Temporary() and Timeout() methods & Response().
    ResponseError

    // ServiceCode returns a service error code. Your code can use this to make error recovery decisions.
    ServiceCode() ServiceCodeType
}

// storageError is the internal struct that implements the public StorageError interface.
type storageError struct {
    responseError
    serviceCode ServiceCodeType
    details     map[string]string
}

// newStorageError creates an error object that implements the error interface.
func newStorageError(cause error, response *http.Response, description string) error {
    return &storageError{
        responseError: responseError{
            ErrorNode:   pipeline.ErrorNode{}.Initialize(cause, 3),
            response:    response,
            description: description,
        },
    }
}

// ServiceCode returns service-error information. The caller may examine these values but should not modify any of them.
func (e *storageError) ServiceCode() ServiceCodeType { return e.serviceCode }

// Error implements the error interface's Error method to return a string representation of the error.
func (e *storageError) Error() string {
    b := &bytes.Buffer{}
    fmt.Fprintf(b, "===== RESPONSE ERROR (ServiceCode=%s) =====\n", e.serviceCode)
    fmt.Fprintf(b, "Description=%s, Details: ", e.description)
    if len(e.details) == 0 {
        b.WriteString("(none)\n")
    } else {
        b.WriteRune('\n')
        keys := make([]string, 0, len(e.details))
        // Alphabetize the details
        for k := range e.details {
            keys = append(keys, k)
        }
        sort.Strings(keys)
        for _, k := range keys {
            fmt.Fprintf(b, "   %s: %+v\n", k, e.details[k])
        }
    }
    req := pipeline.Request{Request: e.response.Request}.Copy() // Make a copy of the response's request
    pipeline.WriteRequestWithResponse(b, prepareRequestForLogging(req), e.response, nil)
    return e.ErrorNode.Error(b.String())
}

// Temporary returns true if the error occurred due to a temporary condition (including an HTTP status of 500 or 503).
func (e *storageError) Temporary() bool {
    if e.response != nil {
        if (e.response.StatusCode == http.StatusInternalServerError) || (e.response.StatusCode == http.StatusServiceUnavailable) {
            return true
        }
    }
    return e.ErrorNode.Temporary()
}

// UnmarshalXML performs custom unmarshalling of XML-formatted Azure storage request errors.
func (e *storageError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
    tokName := ""
    var t xml.Token
    for t, err = d.Token(); err == nil; t, err = d.Token() {
        switch tt := t.(type) {
        case xml.StartElement:
            tokName = tt.Name.Local
            break
        case xml.CharData:
            switch tokName {
            case "Code":
                e.serviceCode = ServiceCodeType(tt)
            case "Message":
                e.description = string(tt)
            default:
                if e.details == nil {
                    e.details = map[string]string{}
                }
                e.details[tokName] = string(tt)
            }
        }
    }
    return nil
}