aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/go-autorest/autorest/client.go
blob: b5f94b5c3c75d1a93f5a308344e7f03f37a3be2f (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package autorest

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/cookiejar"
    "runtime"
    "time"
)

const (
    // DefaultPollingDelay is a reasonable delay between polling requests.
    DefaultPollingDelay = 60 * time.Second

    // DefaultPollingDuration is a reasonable total polling duration.
    DefaultPollingDuration = 15 * time.Minute

    // DefaultRetryAttempts is number of attempts for retry status codes (5xx).
    DefaultRetryAttempts = 3
)

var (
    // defaultUserAgent builds a string containing the Go version, system archityecture and OS,
    // and the go-autorest version.
    defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
        runtime.Version(),
        runtime.GOARCH,
        runtime.GOOS,
        Version(),
    )

    statusCodesForRetry = []int{
        http.StatusRequestTimeout,      // 408
        http.StatusInternalServerError, // 500
        http.StatusBadGateway,          // 502
        http.StatusServiceUnavailable,  // 503
        http.StatusGatewayTimeout,      // 504
    }
)

const (
    requestFormat = `HTTP Request Begin ===================================================
%s
===================================================== HTTP Request End
`
    responseFormat = `HTTP Response Begin ===================================================
%s
===================================================== HTTP Response End
`
)

// Response serves as the base for all responses from generated clients. It provides access to the
// last http.Response.
type Response struct {
    *http.Response `json:"-"`
}

// LoggingInspector implements request and response inspectors that log the full request and
// response to a supplied log.
type LoggingInspector struct {
    Logger *log.Logger
}

// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The
// body is restored after being emitted.
//
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
// important. It is best used to trace JSON or similar body values.
func (li LoggingInspector) WithInspection() PrepareDecorator {
    return func(p Preparer) Preparer {
        return PreparerFunc(func(r *http.Request) (*http.Request, error) {
            var body, b bytes.Buffer

            defer r.Body.Close()

            r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
            if err := r.Write(&b); err != nil {
                return nil, fmt.Errorf("Failed to write response: %v", err)
            }

            li.Logger.Printf(requestFormat, b.String())

            r.Body = ioutil.NopCloser(&body)
            return p.Prepare(r)
        })
    }
}

// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The
// body is restored after being emitted.
//
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
// important. It is best used to trace JSON or similar body values.
func (li LoggingInspector) ByInspecting() RespondDecorator {
    return func(r Responder) Responder {
        return ResponderFunc(func(resp *http.Response) error {
            var body, b bytes.Buffer
            defer resp.Body.Close()
            resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
            if err := resp.Write(&b); err != nil {
                return fmt.Errorf("Failed to write response: %v", err)
            }

            li.Logger.Printf(responseFormat, b.String())

            resp.Body = ioutil.NopCloser(&body)
            return r.Respond(resp)
        })
    }
}

// Client is the base for autorest generated clients. It provides default, "do nothing"
// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the
// standard, undecorated http.Client as a default Sender.
//
// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and
// return responses that compose with Response.
//
// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom
// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit
// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence
// sending the request by providing a decorated Sender.
type Client struct {
    Authorizer        Authorizer
    Sender            Sender
    RequestInspector  PrepareDecorator
    ResponseInspector RespondDecorator

    // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header
    PollingDelay time.Duration

    // PollingDuration sets the maximum polling time after which an error is returned.
    PollingDuration time.Duration

    // RetryAttempts sets the default number of retry attempts for client.
    RetryAttempts int

    // RetryDuration sets the delay duration for retries.
    RetryDuration time.Duration

    // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent
    // through the Do method.
    UserAgent string

    Jar http.CookieJar
}

// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
// string.
func NewClientWithUserAgent(ua string) Client {
    c := Client{
        PollingDelay:    DefaultPollingDelay,
        PollingDuration: DefaultPollingDuration,
        RetryAttempts:   DefaultRetryAttempts,
        RetryDuration:   30 * time.Second,
        UserAgent:       defaultUserAgent,
    }
    c.AddToUserAgent(ua)
    return c
}

// AddToUserAgent adds an extension to the current user agent
func (c *Client) AddToUserAgent(extension string) error {
    if extension != "" {
        c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
        return nil
    }
    return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
}

// Do implements the Sender interface by invoking the active Sender after applying authorization.
// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent
// is set, apply set the User-Agent header.
func (c Client) Do(r *http.Request) (*http.Response, error) {
    if r.UserAgent() == "" {
        r, _ = Prepare(r,
            WithUserAgent(c.UserAgent))
    }
    r, err := Prepare(r,
        c.WithInspection(),
        c.WithAuthorization())
    if err != nil {
        return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
    }
    resp, err := SendWithSender(c.sender(), r,
        DoRetryForStatusCodes(c.RetryAttempts, c.RetryDuration, statusCodesForRetry...))
    Respond(resp,
        c.ByInspecting())
    return resp, err
}

// sender returns the Sender to which to send requests.
func (c Client) sender() Sender {
    if c.Sender == nil {
        j, _ := cookiejar.New(nil)
        return &http.Client{Jar: j}
    }
    return c.Sender
}

// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator
// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer.
func (c Client) WithAuthorization() PrepareDecorator {
    return c.authorizer().WithAuthorization()
}

// authorizer returns the Authorizer to use.
func (c Client) authorizer() Authorizer {
    if c.Authorizer == nil {
        return NullAuthorizer{}
    }
    return c.Authorizer
}

// WithInspection is a convenience method that passes the request to the supplied RequestInspector,
// if present, or returns the WithNothing PrepareDecorator otherwise.
func (c Client) WithInspection() PrepareDecorator {
    if c.RequestInspector == nil {
        return WithNothing()
    }
    return c.RequestInspector
}

// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector,
// if present, or returns the ByIgnoring RespondDecorator otherwise.
func (c Client) ByInspecting() RespondDecorator {
    if c.ResponseInspector == nil {
        return ByIgnoring()
    }
    return c.ResponseInspector
}