~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/Azure/azure-sdk-for-go/core/http/client.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2009 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
// HTTP client. See RFC 2616.
 
6
//
 
7
// This is the high-level Client interface.
 
8
// The low-level implementation is in transport.go.
 
9
 
 
10
package http
 
11
 
 
12
import (
 
13
        "encoding/base64"
 
14
        "errors"
 
15
        "fmt"
 
16
        "io"
 
17
        "io/ioutil"
 
18
        "log"
 
19
        "net/url"
 
20
        "strings"
 
21
        "sync"
 
22
        "time"
 
23
)
 
24
 
 
25
// A Client is an HTTP client. Its zero value (DefaultClient) is a
 
26
// usable client that uses DefaultTransport.
 
27
//
 
28
// The Client's Transport typically has internal state (cached TCP
 
29
// connections), so Clients should be reused instead of created as
 
30
// needed. Clients are safe for concurrent use by multiple goroutines.
 
31
//
 
32
// A Client is higher-level than a RoundTripper (such as Transport)
 
33
// and additionally handles HTTP details such as cookies and
 
34
// redirects.
 
35
type Client struct {
 
36
        // Transport specifies the mechanism by which individual
 
37
        // HTTP requests are made.
 
38
        // If nil, DefaultTransport is used.
 
39
        Transport RoundTripper
 
40
 
 
41
        // CheckRedirect specifies the policy for handling redirects.
 
42
        // If CheckRedirect is not nil, the client calls it before
 
43
        // following an HTTP redirect. The arguments req and via are
 
44
        // the upcoming request and the requests made already, oldest
 
45
        // first. If CheckRedirect returns an error, the Client's Get
 
46
        // method returns both the previous Response and
 
47
        // CheckRedirect's error (wrapped in a url.Error) instead of
 
48
        // issuing the Request req.
 
49
        //
 
50
        // If CheckRedirect is nil, the Client uses its default policy,
 
51
        // which is to stop after 10 consecutive requests.
 
52
        CheckRedirect func(req *Request, via []*Request) error
 
53
 
 
54
        // Jar specifies the cookie jar.
 
55
        // If Jar is nil, cookies are not sent in requests and ignored
 
56
        // in responses.
 
57
        Jar CookieJar
 
58
 
 
59
        // Timeout specifies a time limit for requests made by this
 
60
        // Client. The timeout includes connection time, any
 
61
        // redirects, and reading the response body. The timer remains
 
62
        // running after Get, Head, Post, or Do return and will
 
63
        // interrupt reading of the Response.Body.
 
64
        //
 
65
        // A Timeout of zero means no timeout.
 
66
        //
 
67
        // The Client's Transport must support the CancelRequest
 
68
        // method or Client will return errors when attempting to make
 
69
        // a request with Get, Head, Post, or Do. Client's default
 
70
        // Transport (DefaultTransport) supports CancelRequest.
 
71
        Timeout time.Duration
 
72
}
 
73
 
 
74
// DefaultClient is the default Client and is used by Get, Head, and Post.
 
75
var DefaultClient = &Client{}
 
76
 
 
77
// RoundTripper is an interface representing the ability to execute a
 
78
// single HTTP transaction, obtaining the Response for a given Request.
 
79
//
 
80
// A RoundTripper must be safe for concurrent use by multiple
 
81
// goroutines.
 
82
type RoundTripper interface {
 
83
        // RoundTrip executes a single HTTP transaction, returning
 
84
        // the Response for the request req.  RoundTrip should not
 
85
        // attempt to interpret the response.  In particular,
 
86
        // RoundTrip must return err == nil if it obtained a response,
 
87
        // regardless of the response's HTTP status code.  A non-nil
 
88
        // err should be reserved for failure to obtain a response.
 
89
        // Similarly, RoundTrip should not attempt to handle
 
90
        // higher-level protocol details such as redirects,
 
91
        // authentication, or cookies.
 
92
        //
 
93
        // RoundTrip should not modify the request, except for
 
94
        // consuming and closing the Body, including on errors. The
 
95
        // request's URL and Header fields are guaranteed to be
 
96
        // initialized.
 
97
        RoundTrip(*Request) (*Response, error)
 
98
}
 
99
 
 
100
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
 
101
// return true if the string includes a port.
 
102
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
 
103
 
 
104
// Used in Send to implement io.ReadCloser by bundling together the
 
105
// bufio.Reader through which we read the response, and the underlying
 
106
// network connection.
 
107
type readClose struct {
 
108
        io.Reader
 
109
        io.Closer
 
110
}
 
111
 
 
112
func (c *Client) send(req *Request) (*Response, error) {
 
113
        if c.Jar != nil {
 
114
                for _, cookie := range c.Jar.Cookies(req.URL) {
 
115
                        req.AddCookie(cookie)
 
116
                }
 
117
        }
 
118
        resp, err := send(req, c.transport())
 
119
        if err != nil {
 
120
                return nil, err
 
121
        }
 
122
        if c.Jar != nil {
 
123
                if rc := resp.Cookies(); len(rc) > 0 {
 
124
                        c.Jar.SetCookies(req.URL, rc)
 
125
                }
 
126
        }
 
127
        return resp, err
 
128
}
 
129
 
 
130
// Do sends an HTTP request and returns an HTTP response, following
 
131
// policy (e.g. redirects, cookies, auth) as configured on the client.
 
132
//
 
133
// An error is returned if caused by client policy (such as
 
134
// CheckRedirect), or if there was an HTTP protocol error.
 
135
// A non-2xx response doesn't cause an error.
 
136
//
 
137
// When err is nil, resp always contains a non-nil resp.Body.
 
138
//
 
139
// Callers should close resp.Body when done reading from it. If
 
140
// resp.Body is not closed, the Client's underlying RoundTripper
 
141
// (typically Transport) may not be able to re-use a persistent TCP
 
142
// connection to the server for a subsequent "keep-alive" request.
 
143
//
 
144
// The request Body, if non-nil, will be closed by the underlying
 
145
// Transport, even on errors.
 
146
//
 
147
// Generally Get, Post, or PostForm will be used instead of Do.
 
148
func (c *Client) Do(req *Request) (resp *Response, err error) {
 
149
        if req.Method == "GET" || req.Method == "HEAD" {
 
150
                return c.doFollowingRedirects(req, shouldRedirectGet)
 
151
        }
 
152
        if req.Method == "POST" || req.Method == "PUT" {
 
153
                return c.doFollowingRedirects(req, shouldRedirectPost)
 
154
        }
 
155
        return c.send(req)
 
156
}
 
157
 
 
158
func (c *Client) transport() RoundTripper {
 
159
        if c.Transport != nil {
 
160
                return c.Transport
 
161
        }
 
162
        return DefaultTransport
 
163
}
 
164
 
 
165
// send issues an HTTP request.
 
166
// Caller should close resp.Body when done reading from it.
 
167
func send(req *Request, t RoundTripper) (resp *Response, err error) {
 
168
        if t == nil {
 
169
                req.closeBody()
 
170
                return nil, errors.New("http: no Client.Transport or DefaultTransport")
 
171
        }
 
172
 
 
173
        if req.URL == nil {
 
174
                req.closeBody()
 
175
                return nil, errors.New("http: nil Request.URL")
 
176
        }
 
177
 
 
178
        if req.RequestURI != "" {
 
179
                req.closeBody()
 
180
                return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
 
181
        }
 
182
 
 
183
        // Most the callers of send (Get, Post, et al) don't need
 
184
        // Headers, leaving it uninitialized.  We guarantee to the
 
185
        // Transport that this has been initialized, though.
 
186
        if req.Header == nil {
 
187
                req.Header = make(Header)
 
188
        }
 
189
 
 
190
        if u := req.URL.User; u != nil {
 
191
                username := u.Username()
 
192
                password, _ := u.Password()
 
193
                req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
 
194
        }
 
195
        resp, err = t.RoundTrip(req)
 
196
        if err != nil {
 
197
                if resp != nil {
 
198
                        log.Printf("RoundTripper returned a response & error; ignoring response")
 
199
                }
 
200
                return nil, err
 
201
        }
 
202
        return resp, nil
 
203
}
 
204
 
 
205
// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
 
206
// "To receive authorization, the client sends the userid and password,
 
207
// separated by a single colon (":") character, within a base64
 
208
// encoded string in the credentials."
 
209
// It is not meant to be urlencoded.
 
210
func basicAuth(username, password string) string {
 
211
        auth := username + ":" + password
 
212
        return base64.StdEncoding.EncodeToString([]byte(auth))
 
213
}
 
214
 
 
215
// True if the specified HTTP status code is one for which the Get utility should
 
216
// automatically redirect.
 
217
func shouldRedirectGet(statusCode int) bool {
 
218
        switch statusCode {
 
219
        case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
 
220
                return true
 
221
        }
 
222
        return false
 
223
}
 
224
 
 
225
// True if the specified HTTP status code is one for which the Post utility should
 
226
// automatically redirect.
 
227
func shouldRedirectPost(statusCode int) bool {
 
228
        switch statusCode {
 
229
        case StatusFound, StatusSeeOther:
 
230
                return true
 
231
        }
 
232
        return false
 
233
}
 
234
 
 
235
// Get issues a GET to the specified URL.  If the response is one of the following
 
236
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
 
237
//
 
238
//    301 (Moved Permanently)
 
239
//    302 (Found)
 
240
//    303 (See Other)
 
241
//    307 (Temporary Redirect)
 
242
//
 
243
// An error is returned if there were too many redirects or if there
 
244
// was an HTTP protocol error. A non-2xx response doesn't cause an
 
245
// error.
 
246
//
 
247
// When err is nil, resp always contains a non-nil resp.Body.
 
248
// Caller should close resp.Body when done reading from it.
 
249
//
 
250
// Get is a wrapper around DefaultClient.Get.
 
251
func Get(url string) (resp *Response, err error) {
 
252
        return DefaultClient.Get(url)
 
253
}
 
254
 
 
255
// Get issues a GET to the specified URL.  If the response is one of the
 
256
// following redirect codes, Get follows the redirect after calling the
 
257
// Client's CheckRedirect function.
 
258
//
 
259
//    301 (Moved Permanently)
 
260
//    302 (Found)
 
261
//    303 (See Other)
 
262
//    307 (Temporary Redirect)
 
263
//
 
264
// An error is returned if the Client's CheckRedirect function fails
 
265
// or if there was an HTTP protocol error. A non-2xx response doesn't
 
266
// cause an error.
 
267
//
 
268
// When err is nil, resp always contains a non-nil resp.Body.
 
269
// Caller should close resp.Body when done reading from it.
 
270
func (c *Client) Get(url string) (resp *Response, err error) {
 
271
        req, err := NewRequest("GET", url, nil)
 
272
        if err != nil {
 
273
                return nil, err
 
274
        }
 
275
        return c.doFollowingRedirects(req, shouldRedirectGet)
 
276
}
 
277
 
 
278
func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
 
279
        var base *url.URL
 
280
        redirectChecker := c.CheckRedirect
 
281
        if redirectChecker == nil {
 
282
                redirectChecker = defaultCheckRedirect
 
283
        }
 
284
        var via []*Request
 
285
 
 
286
        if ireq.URL == nil {
 
287
                ireq.closeBody()
 
288
                return nil, errors.New("http: nil Request.URL")
 
289
        }
 
290
 
 
291
        var reqmu sync.Mutex // guards req
 
292
        req := ireq
 
293
 
 
294
        var timer *time.Timer
 
295
        if c.Timeout > 0 {
 
296
                type canceler interface {
 
297
                        CancelRequest(*Request)
 
298
                }
 
299
                tr, ok := c.transport().(canceler)
 
300
                if !ok {
 
301
                        return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
 
302
                }
 
303
                timer = time.AfterFunc(c.Timeout, func() {
 
304
                        reqmu.Lock()
 
305
                        defer reqmu.Unlock()
 
306
                        tr.CancelRequest(req)
 
307
                })
 
308
        }
 
309
 
 
310
        urlStr := "" // next relative or absolute URL to fetch (after first request)
 
311
        redirectFailed := false
 
312
        for redirect := 0; ; redirect++ {
 
313
                if redirect != 0 {
 
314
                        nreq := new(Request)
 
315
                        nreq.Method = ireq.Method
 
316
                        if ireq.Method == "POST" || ireq.Method == "PUT" {
 
317
                                nreq.Method = "GET"
 
318
                        }
 
319
                        nreq.Header = make(Header)
 
320
                        nreq.URL, err = base.Parse(urlStr)
 
321
                        if err != nil {
 
322
                                break
 
323
                        }
 
324
                        if len(via) > 0 {
 
325
                                // Add the Referer header.
 
326
                                lastReq := via[len(via)-1]
 
327
                                if lastReq.URL.Scheme != "https" {
 
328
                                        nreq.Header.Set("Referer", lastReq.URL.String())
 
329
                                }
 
330
 
 
331
                                err = redirectChecker(nreq, via)
 
332
                                if err != nil {
 
333
                                        redirectFailed = true
 
334
                                        break
 
335
                                }
 
336
                        }
 
337
                        reqmu.Lock()
 
338
                        req = nreq
 
339
                        reqmu.Unlock()
 
340
                }
 
341
 
 
342
                urlStr = req.URL.String()
 
343
                if resp, err = c.send(req); err != nil {
 
344
                        break
 
345
                }
 
346
 
 
347
                if shouldRedirect(resp.StatusCode) {
 
348
                        // Read the body if small so underlying TCP connection will be re-used.
 
349
                        // No need to check for errors: if it fails, Transport won't reuse it anyway.
 
350
                        const maxBodySlurpSize = 2 << 10
 
351
                        if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {
 
352
                                io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
 
353
                        }
 
354
                        resp.Body.Close()
 
355
                        if urlStr = resp.Header.Get("Location"); urlStr == "" {
 
356
                                err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
 
357
                                break
 
358
                        }
 
359
                        base = req.URL
 
360
                        via = append(via, req)
 
361
                        continue
 
362
                }
 
363
                if timer != nil {
 
364
                        resp.Body = &cancelTimerBody{timer, resp.Body}
 
365
                }
 
366
                return resp, nil
 
367
        }
 
368
 
 
369
        method := ireq.Method
 
370
        urlErr := &url.Error{
 
371
                Op:  method[0:1] + strings.ToLower(method[1:]),
 
372
                URL: urlStr,
 
373
                Err: err,
 
374
        }
 
375
 
 
376
        if redirectFailed {
 
377
                // Special case for Go 1 compatibility: return both the response
 
378
                // and an error if the CheckRedirect function failed.
 
379
                // See http://golang.org/issue/3795
 
380
                return resp, urlErr
 
381
        }
 
382
 
 
383
        if resp != nil {
 
384
                resp.Body.Close()
 
385
        }
 
386
        return nil, urlErr
 
387
}
 
388
 
 
389
func defaultCheckRedirect(req *Request, via []*Request) error {
 
390
        if len(via) >= 10 {
 
391
                return errors.New("stopped after 10 redirects")
 
392
        }
 
393
        return nil
 
394
}
 
395
 
 
396
// Post issues a POST to the specified URL.
 
397
//
 
398
// Caller should close resp.Body when done reading from it.
 
399
//
 
400
// Post is a wrapper around DefaultClient.Post
 
401
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
 
402
        return DefaultClient.Post(url, bodyType, body)
 
403
}
 
404
 
 
405
// Post issues a POST to the specified URL.
 
406
//
 
407
// Caller should close resp.Body when done reading from it.
 
408
//
 
409
// If the provided body is also an io.Closer, it is closed after the
 
410
// request.
 
411
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
 
412
        req, err := NewRequest("POST", url, body)
 
413
        if err != nil {
 
414
                return nil, err
 
415
        }
 
416
        req.Header.Set("Content-Type", bodyType)
 
417
        return c.doFollowingRedirects(req, shouldRedirectPost)
 
418
}
 
419
 
 
420
// PostForm issues a POST to the specified URL, with data's keys and
 
421
// values URL-encoded as the request body.
 
422
//
 
423
// When err is nil, resp always contains a non-nil resp.Body.
 
424
// Caller should close resp.Body when done reading from it.
 
425
//
 
426
// PostForm is a wrapper around DefaultClient.PostForm
 
427
func PostForm(url string, data url.Values) (resp *Response, err error) {
 
428
        return DefaultClient.PostForm(url, data)
 
429
}
 
430
 
 
431
// PostForm issues a POST to the specified URL,
 
432
// with data's keys and values urlencoded as the request body.
 
433
//
 
434
// When err is nil, resp always contains a non-nil resp.Body.
 
435
// Caller should close resp.Body when done reading from it.
 
436
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
 
437
        return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
 
438
}
 
439
 
 
440
// Head issues a HEAD to the specified URL.  If the response is one of the
 
441
// following redirect codes, Head follows the redirect after calling the
 
442
// Client's CheckRedirect function.
 
443
//
 
444
//    301 (Moved Permanently)
 
445
//    302 (Found)
 
446
//    303 (See Other)
 
447
//    307 (Temporary Redirect)
 
448
//
 
449
// Head is a wrapper around DefaultClient.Head
 
450
func Head(url string) (resp *Response, err error) {
 
451
        return DefaultClient.Head(url)
 
452
}
 
453
 
 
454
// Head issues a HEAD to the specified URL.  If the response is one of the
 
455
// following redirect codes, Head follows the redirect after calling the
 
456
// Client's CheckRedirect function.
 
457
//
 
458
//    301 (Moved Permanently)
 
459
//    302 (Found)
 
460
//    303 (See Other)
 
461
//    307 (Temporary Redirect)
 
462
func (c *Client) Head(url string) (resp *Response, err error) {
 
463
        req, err := NewRequest("HEAD", url, nil)
 
464
        if err != nil {
 
465
                return nil, err
 
466
        }
 
467
        return c.doFollowingRedirects(req, shouldRedirectGet)
 
468
}
 
469
 
 
470
type cancelTimerBody struct {
 
471
        t  *time.Timer
 
472
        rc io.ReadCloser
 
473
}
 
474
 
 
475
func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
 
476
        n, err = b.rc.Read(p)
 
477
        if err == io.EOF {
 
478
                b.t.Stop()
 
479
        }
 
480
        return
 
481
}
 
482
 
 
483
func (b *cancelTimerBody) Close() error {
 
484
        err := b.rc.Close()
 
485
        b.t.Stop()
 
486
        return err
 
487
}