~chipaca/ubuntu-push/gsettings

« back to all changes in this revision

Viewing changes to http13client/client.go

  • Committer: Samuele Pedroni (Canonical Services Ltd.)
  • Date: 2014-03-19 20:20:19 UTC
  • mto: This revision was merged to the branch mainline in revision 82.
  • Revision ID: samuele.pedroni@canonical.com-20140319202019-p0w8krshj1098f82
grab go 1.3 dev net/http and massage it so that the test run on 1.2

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. The request's URL and
 
95
        // Header fields are guaranteed to be initialized.
 
96
        RoundTrip(*Request) (*Response, error)
 
97
}
 
98
 
 
99
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
 
100
// return true if the string includes a port.
 
101
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
 
102
 
 
103
// Used in Send to implement io.ReadCloser by bundling together the
 
104
// bufio.Reader through which we read the response, and the underlying
 
105
// network connection.
 
106
type readClose struct {
 
107
        io.Reader
 
108
        io.Closer
 
109
}
 
110
 
 
111
func (c *Client) send(req *Request) (*Response, error) {
 
112
        if c.Jar != nil {
 
113
                for _, cookie := range c.Jar.Cookies(req.URL) {
 
114
                        req.AddCookie(cookie)
 
115
                }
 
116
        }
 
117
        resp, err := send(req, c.transport())
 
118
        if err != nil {
 
119
                return nil, err
 
120
        }
 
121
        if c.Jar != nil {
 
122
                if rc := resp.Cookies(); len(rc) > 0 {
 
123
                        c.Jar.SetCookies(req.URL, rc)
 
124
                }
 
125
        }
 
126
        return resp, err
 
127
}
 
128
 
 
129
// Do sends an HTTP request and returns an HTTP response, following
 
130
// policy (e.g. redirects, cookies, auth) as configured on the client.
 
131
//
 
132
// An error is returned if caused by client policy (such as
 
133
// CheckRedirect), or if there was an HTTP protocol error.
 
134
// A non-2xx response doesn't cause an error.
 
135
//
 
136
// When err is nil, resp always contains a non-nil resp.Body.
 
137
//
 
138
// Callers should close resp.Body when done reading from it. If
 
139
// resp.Body is not closed, the Client's underlying RoundTripper
 
140
// (typically Transport) may not be able to re-use a persistent TCP
 
141
// connection to the server for a subsequent "keep-alive" request.
 
142
//
 
143
// Generally Get, Post, or PostForm will be used instead of Do.
 
144
func (c *Client) Do(req *Request) (resp *Response, err error) {
 
145
        if req.Method == "GET" || req.Method == "HEAD" {
 
146
                return c.doFollowingRedirects(req, shouldRedirectGet)
 
147
        }
 
148
        if req.Method == "POST" || req.Method == "PUT" {
 
149
                return c.doFollowingRedirects(req, shouldRedirectPost)
 
150
        }
 
151
        return c.send(req)
 
152
}
 
153
 
 
154
func (c *Client) transport() RoundTripper {
 
155
        if c.Transport != nil {
 
156
                return c.Transport
 
157
        }
 
158
        return DefaultTransport
 
159
}
 
160
 
 
161
// send issues an HTTP request.
 
162
// Caller should close resp.Body when done reading from it.
 
163
func send(req *Request, t RoundTripper) (resp *Response, err error) {
 
164
        if t == nil {
 
165
                return nil, errors.New("http: no Client.Transport or DefaultTransport")
 
166
        }
 
167
 
 
168
        if req.URL == nil {
 
169
                return nil, errors.New("http: nil Request.URL")
 
170
        }
 
171
 
 
172
        if req.RequestURI != "" {
 
173
                return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
 
174
        }
 
175
 
 
176
        // Most the callers of send (Get, Post, et al) don't need
 
177
        // Headers, leaving it uninitialized.  We guarantee to the
 
178
        // Transport that this has been initialized, though.
 
179
        if req.Header == nil {
 
180
                req.Header = make(Header)
 
181
        }
 
182
 
 
183
        if u := req.URL.User; u != nil {
 
184
                username := u.Username()
 
185
                password, _ := u.Password()
 
186
                req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
 
187
        }
 
188
        resp, err = t.RoundTrip(req)
 
189
        if err != nil {
 
190
                if resp != nil {
 
191
                        log.Printf("RoundTripper returned a response & error; ignoring response")
 
192
                }
 
193
                return nil, err
 
194
        }
 
195
        return resp, nil
 
196
}
 
197
 
 
198
// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
 
199
// "To receive authorization, the client sends the userid and password,
 
200
// separated by a single colon (":") character, within a base64
 
201
// encoded string in the credentials."
 
202
// It is not meant to be urlencoded.
 
203
func basicAuth(username, password string) string {
 
204
        auth := username + ":" + password
 
205
        return base64.StdEncoding.EncodeToString([]byte(auth))
 
206
}
 
207
 
 
208
// True if the specified HTTP status code is one for which the Get utility should
 
209
// automatically redirect.
 
210
func shouldRedirectGet(statusCode int) bool {
 
211
        switch statusCode {
 
212
        case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
 
213
                return true
 
214
        }
 
215
        return false
 
216
}
 
217
 
 
218
// True if the specified HTTP status code is one for which the Post utility should
 
219
// automatically redirect.
 
220
func shouldRedirectPost(statusCode int) bool {
 
221
        switch statusCode {
 
222
        case StatusFound, StatusSeeOther:
 
223
                return true
 
224
        }
 
225
        return false
 
226
}
 
227
 
 
228
// Get issues a GET to the specified URL.  If the response is one of the following
 
229
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
 
230
//
 
231
//    301 (Moved Permanently)
 
232
//    302 (Found)
 
233
//    303 (See Other)
 
234
//    307 (Temporary Redirect)
 
235
//
 
236
// An error is returned if there were too many redirects or if there
 
237
// was an HTTP protocol error. A non-2xx response doesn't cause an
 
238
// error.
 
239
//
 
240
// When err is nil, resp always contains a non-nil resp.Body.
 
241
// Caller should close resp.Body when done reading from it.
 
242
//
 
243
// Get is a wrapper around DefaultClient.Get.
 
244
func Get(url string) (resp *Response, err error) {
 
245
        return DefaultClient.Get(url)
 
246
}
 
247
 
 
248
// Get issues a GET to the specified URL.  If the response is one of the
 
249
// following redirect codes, Get follows the redirect after calling the
 
250
// Client's CheckRedirect function.
 
251
//
 
252
//    301 (Moved Permanently)
 
253
//    302 (Found)
 
254
//    303 (See Other)
 
255
//    307 (Temporary Redirect)
 
256
//
 
257
// An error is returned if the Client's CheckRedirect function fails
 
258
// or if there was an HTTP protocol error. A non-2xx response doesn't
 
259
// cause an error.
 
260
//
 
261
// When err is nil, resp always contains a non-nil resp.Body.
 
262
// Caller should close resp.Body when done reading from it.
 
263
func (c *Client) Get(url string) (resp *Response, err error) {
 
264
        req, err := NewRequest("GET", url, nil)
 
265
        if err != nil {
 
266
                return nil, err
 
267
        }
 
268
        return c.doFollowingRedirects(req, shouldRedirectGet)
 
269
}
 
270
 
 
271
func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
 
272
        var base *url.URL
 
273
        redirectChecker := c.CheckRedirect
 
274
        if redirectChecker == nil {
 
275
                redirectChecker = defaultCheckRedirect
 
276
        }
 
277
        var via []*Request
 
278
 
 
279
        if ireq.URL == nil {
 
280
                return nil, errors.New("http: nil Request.URL")
 
281
        }
 
282
 
 
283
        var reqmu sync.Mutex // guards req
 
284
        req := ireq
 
285
 
 
286
        var timer *time.Timer
 
287
        if c.Timeout > 0 {
 
288
                type canceler interface {
 
289
                        CancelRequest(*Request)
 
290
                }
 
291
                tr, ok := c.transport().(canceler)
 
292
                if !ok {
 
293
                        return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
 
294
                }
 
295
                timer = time.AfterFunc(c.Timeout, func() {
 
296
                        reqmu.Lock()
 
297
                        defer reqmu.Unlock()
 
298
                        tr.CancelRequest(req)
 
299
                })
 
300
        }
 
301
 
 
302
        urlStr := "" // next relative or absolute URL to fetch (after first request)
 
303
        redirectFailed := false
 
304
        for redirect := 0; ; redirect++ {
 
305
                if redirect != 0 {
 
306
                        nreq := new(Request)
 
307
                        nreq.Method = ireq.Method
 
308
                        if ireq.Method == "POST" || ireq.Method == "PUT" {
 
309
                                nreq.Method = "GET"
 
310
                        }
 
311
                        nreq.Header = make(Header)
 
312
                        nreq.URL, err = base.Parse(urlStr)
 
313
                        if err != nil {
 
314
                                break
 
315
                        }
 
316
                        if len(via) > 0 {
 
317
                                // Add the Referer header.
 
318
                                lastReq := via[len(via)-1]
 
319
                                if lastReq.URL.Scheme != "https" {
 
320
                                        nreq.Header.Set("Referer", lastReq.URL.String())
 
321
                                }
 
322
 
 
323
                                err = redirectChecker(nreq, via)
 
324
                                if err != nil {
 
325
                                        redirectFailed = true
 
326
                                        break
 
327
                                }
 
328
                        }
 
329
                        reqmu.Lock()
 
330
                        req = nreq
 
331
                        reqmu.Unlock()
 
332
                }
 
333
 
 
334
                urlStr = req.URL.String()
 
335
                if resp, err = c.send(req); err != nil {
 
336
                        break
 
337
                }
 
338
 
 
339
                if shouldRedirect(resp.StatusCode) {
 
340
                        // Read the body if small so underlying TCP connection will be re-used.
 
341
                        // No need to check for errors: if it fails, Transport won't reuse it anyway.
 
342
                        const maxBodySlurpSize = 2 << 10
 
343
                        if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {
 
344
                                io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
 
345
                        }
 
346
                        resp.Body.Close()
 
347
                        if urlStr = resp.Header.Get("Location"); urlStr == "" {
 
348
                                err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
 
349
                                break
 
350
                        }
 
351
                        base = req.URL
 
352
                        via = append(via, req)
 
353
                        continue
 
354
                }
 
355
                if timer != nil {
 
356
                        resp.Body = &cancelTimerBody{timer, resp.Body}
 
357
                }
 
358
                return resp, nil
 
359
        }
 
360
 
 
361
        method := ireq.Method
 
362
        urlErr := &url.Error{
 
363
                Op:  method[0:1] + strings.ToLower(method[1:]),
 
364
                URL: urlStr,
 
365
                Err: err,
 
366
        }
 
367
 
 
368
        if redirectFailed {
 
369
                // Special case for Go 1 compatibility: return both the response
 
370
                // and an error if the CheckRedirect function failed.
 
371
                // See http://golang.org/issue/3795
 
372
                return resp, urlErr
 
373
        }
 
374
 
 
375
        if resp != nil {
 
376
                resp.Body.Close()
 
377
        }
 
378
        return nil, urlErr
 
379
}
 
380
 
 
381
func defaultCheckRedirect(req *Request, via []*Request) error {
 
382
        if len(via) >= 10 {
 
383
                return errors.New("stopped after 10 redirects")
 
384
        }
 
385
        return nil
 
386
}
 
387
 
 
388
// Post issues a POST to the specified URL.
 
389
//
 
390
// Caller should close resp.Body when done reading from it.
 
391
//
 
392
// Post is a wrapper around DefaultClient.Post
 
393
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
 
394
        return DefaultClient.Post(url, bodyType, body)
 
395
}
 
396
 
 
397
// Post issues a POST to the specified URL.
 
398
//
 
399
// Caller should close resp.Body when done reading from it.
 
400
//
 
401
// If the provided body is also an io.Closer, it is closed after the
 
402
// body is successfully written to the server.
 
403
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
 
404
        req, err := NewRequest("POST", url, body)
 
405
        if err != nil {
 
406
                return nil, err
 
407
        }
 
408
        req.Header.Set("Content-Type", bodyType)
 
409
        return c.doFollowingRedirects(req, shouldRedirectPost)
 
410
}
 
411
 
 
412
// PostForm issues a POST to the specified URL, with data's keys and
 
413
// values URL-encoded as the request body.
 
414
//
 
415
// When err is nil, resp always contains a non-nil resp.Body.
 
416
// Caller should close resp.Body when done reading from it.
 
417
//
 
418
// PostForm is a wrapper around DefaultClient.PostForm
 
419
func PostForm(url string, data url.Values) (resp *Response, err error) {
 
420
        return DefaultClient.PostForm(url, data)
 
421
}
 
422
 
 
423
// PostForm issues a POST to the specified URL,
 
424
// with data's keys and values urlencoded as the request body.
 
425
//
 
426
// When err is nil, resp always contains a non-nil resp.Body.
 
427
// Caller should close resp.Body when done reading from it.
 
428
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
 
429
        return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
 
430
}
 
431
 
 
432
// Head issues a HEAD to the specified URL.  If the response is one of the
 
433
// following redirect codes, Head follows the redirect after calling the
 
434
// Client's CheckRedirect function.
 
435
//
 
436
//    301 (Moved Permanently)
 
437
//    302 (Found)
 
438
//    303 (See Other)
 
439
//    307 (Temporary Redirect)
 
440
//
 
441
// Head is a wrapper around DefaultClient.Head
 
442
func Head(url string) (resp *Response, err error) {
 
443
        return DefaultClient.Head(url)
 
444
}
 
445
 
 
446
// Head issues a HEAD to the specified URL.  If the response is one of the
 
447
// following redirect codes, Head follows the redirect after calling the
 
448
// Client's CheckRedirect function.
 
449
//
 
450
//    301 (Moved Permanently)
 
451
//    302 (Found)
 
452
//    303 (See Other)
 
453
//    307 (Temporary Redirect)
 
454
func (c *Client) Head(url string) (resp *Response, err error) {
 
455
        req, err := NewRequest("HEAD", url, nil)
 
456
        if err != nil {
 
457
                return nil, err
 
458
        }
 
459
        return c.doFollowingRedirects(req, shouldRedirectGet)
 
460
}
 
461
 
 
462
type cancelTimerBody struct {
 
463
        t  *time.Timer
 
464
        rc io.ReadCloser
 
465
}
 
466
 
 
467
func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
 
468
        n, err = b.rc.Read(p)
 
469
        if err == io.EOF {
 
470
                b.t.Stop()
 
471
        }
 
472
        return
 
473
}
 
474
 
 
475
func (b *cancelTimerBody) Close() error {
 
476
        err := b.rc.Close()
 
477
        b.t.Stop()
 
478
        return err
 
479
}