Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // HTTP client. See RFC 2616. | 5 // HTTP client. See RFC 2616. |
6 // | 6 // |
7 // This is the high-level Client interface. | 7 // This is the high-level Client interface. |
8 // The low-level implementation is in transport.go. | 8 // The low-level implementation is in transport.go. |
9 | 9 |
10 package http | 10 package http |
11 | 11 |
12 import ( | 12 import ( |
13 "encoding/base64" | 13 "encoding/base64" |
14 "errors" | 14 "errors" |
15 "fmt" | 15 "fmt" |
16 "io" | 16 "io" |
(...skipping 20 matching lines...) Expand all Loading... | |
37 // the upcoming request and the requests made already, oldest | 37 // the upcoming request and the requests made already, oldest |
38 // first. If CheckRedirect returns an error, the Client's Get | 38 // first. If CheckRedirect returns an error, the Client's Get |
39 // method returns both the previous Response and | 39 // method returns both the previous Response and |
40 // CheckRedirect's error (wrapped in a url.Error) instead of | 40 // CheckRedirect's error (wrapped in a url.Error) instead of |
41 // issuing the Request req. | 41 // issuing the Request req. |
42 // | 42 // |
43 // If CheckRedirect is nil, the Client uses its default policy, | 43 // If CheckRedirect is nil, the Client uses its default policy, |
44 // which is to stop after 10 consecutive requests. | 44 // which is to stop after 10 consecutive requests. |
45 CheckRedirect func(req *Request, via []*Request) error | 45 CheckRedirect func(req *Request, via []*Request) error |
46 | 46 |
47 » // Jar specifies the cookie jar. | 47 » // Jar specifies the cookie jar. |
48 » // If Jar is nil, cookies are not sent in requests and ignored | 48 » // If Jar is nil, cookies are not sent in requests and ignored |
49 // in responses. | 49 // in responses. |
50 Jar CookieJar | 50 Jar CookieJar |
51 } | 51 } |
52 | 52 |
53 // DefaultClient is the default Client and is used by Get, Head, and Post. | 53 // DefaultClient is the default Client and is used by Get, Head, and Post. |
54 var DefaultClient = &Client{} | 54 var DefaultClient = &Client{} |
55 | 55 |
56 // RoundTripper is an interface representing the ability to execute a | 56 // RoundTripper is an interface representing the ability to execute a |
57 // single HTTP transaction, obtaining the Response for a given Request. | 57 // single HTTP transaction, obtaining the Response for a given Request. |
58 // | 58 // |
(...skipping 21 matching lines...) Expand all Loading... | |
80 func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastInd ex(s, "]") } | 80 func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastInd ex(s, "]") } |
81 | 81 |
82 // Used in Send to implement io.ReadCloser by bundling together the | 82 // Used in Send to implement io.ReadCloser by bundling together the |
83 // bufio.Reader through which we read the response, and the underlying | 83 // bufio.Reader through which we read the response, and the underlying |
84 // network connection. | 84 // network connection. |
85 type readClose struct { | 85 type readClose struct { |
86 io.Reader | 86 io.Reader |
87 io.Closer | 87 io.Closer |
88 } | 88 } |
89 | 89 |
90 func (c *Client) send(req *Request) (*Response, error) { | |
91 if c.Jar != nil { | |
92 for _, cookie := range c.Jar.Cookies(req.URL) { | |
93 req.AddCookie(cookie) | |
94 } | |
95 } | |
96 resp, err := send(req, c.Transport) | |
97 if err != nil { | |
98 return nil, err | |
99 } | |
100 if c.Jar != nil { | |
101 c.Jar.SetCookies(req.URL, resp.Cookies()) | |
serbaut
2012/12/19 16:45:19
SetCookies is always called here, even if there ar
bradfitz
2012/12/19 17:04:02
That would also be fine. Feel free to send a CL.
| |
102 } | |
103 return resp, err | |
104 } | |
105 | |
90 // Do sends an HTTP request and returns an HTTP response, following | 106 // Do sends an HTTP request and returns an HTTP response, following |
91 // policy (e.g. redirects, cookies, auth) as configured on the client. | 107 // policy (e.g. redirects, cookies, auth) as configured on the client. |
92 // | 108 // |
93 // An error is returned if caused by client policy (such as | 109 // An error is returned if caused by client policy (such as |
94 // CheckRedirect), or if there was an HTTP protocol error. | 110 // CheckRedirect), or if there was an HTTP protocol error. |
95 // A non-2xx response doesn't cause an error. | 111 // A non-2xx response doesn't cause an error. |
96 // | 112 // |
97 // When err is nil, resp always contains a non-nil resp.Body. | 113 // When err is nil, resp always contains a non-nil resp.Body. |
98 // | 114 // |
99 // Callers should close resp.Body when done reading from it. If | 115 // Callers should close resp.Body when done reading from it. If |
100 // resp.Body is not closed, the Client's underlying RoundTripper | 116 // resp.Body is not closed, the Client's underlying RoundTripper |
101 // (typically Transport) may not be able to re-use a persistent TCP | 117 // (typically Transport) may not be able to re-use a persistent TCP |
102 // connection to the server for a subsequent "keep-alive" request. | 118 // connection to the server for a subsequent "keep-alive" request. |
103 // | 119 // |
104 // Generally Get, Post, or PostForm will be used instead of Do. | 120 // Generally Get, Post, or PostForm will be used instead of Do. |
105 func (c *Client) Do(req *Request) (resp *Response, err error) { | 121 func (c *Client) Do(req *Request) (resp *Response, err error) { |
106 if req.Method == "GET" || req.Method == "HEAD" { | 122 if req.Method == "GET" || req.Method == "HEAD" { |
107 return c.doFollowingRedirects(req) | 123 return c.doFollowingRedirects(req) |
108 } | 124 } |
109 » return send(req, c.Transport) | 125 » return c.send(req) |
110 } | 126 } |
111 | 127 |
112 // send issues an HTTP request. | 128 // send issues an HTTP request. |
113 // Caller should close resp.Body when done reading from it. | 129 // Caller should close resp.Body when done reading from it. |
114 func send(req *Request, t RoundTripper) (resp *Response, err error) { | 130 func send(req *Request, t RoundTripper) (resp *Response, err error) { |
115 if t == nil { | 131 if t == nil { |
116 t = DefaultTransport | 132 t = DefaultTransport |
117 if t == nil { | 133 if t == nil { |
118 err = errors.New("http: no Client.Transport or DefaultTr ansport") | 134 err = errors.New("http: no Client.Transport or DefaultTr ansport") |
119 return | 135 return |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 redirectChecker := c.CheckRedirect | 224 redirectChecker := c.CheckRedirect |
209 if redirectChecker == nil { | 225 if redirectChecker == nil { |
210 redirectChecker = defaultCheckRedirect | 226 redirectChecker = defaultCheckRedirect |
211 } | 227 } |
212 var via []*Request | 228 var via []*Request |
213 | 229 |
214 if ireq.URL == nil { | 230 if ireq.URL == nil { |
215 return nil, errors.New("http: nil Request.URL") | 231 return nil, errors.New("http: nil Request.URL") |
216 } | 232 } |
217 | 233 |
218 jar := c.Jar | |
219 if jar == nil { | |
220 jar = blackHoleJar{} | |
221 } | |
222 | |
223 req := ireq | 234 req := ireq |
224 urlStr := "" // next relative or absolute URL to fetch (after first requ est) | 235 urlStr := "" // next relative or absolute URL to fetch (after first requ est) |
225 redirectFailed := false | 236 redirectFailed := false |
226 for redirect := 0; ; redirect++ { | 237 for redirect := 0; ; redirect++ { |
227 if redirect != 0 { | 238 if redirect != 0 { |
228 req = new(Request) | 239 req = new(Request) |
229 req.Method = ireq.Method | 240 req.Method = ireq.Method |
230 req.Header = make(Header) | 241 req.Header = make(Header) |
231 req.URL, err = base.Parse(urlStr) | 242 req.URL, err = base.Parse(urlStr) |
232 if err != nil { | 243 if err != nil { |
233 break | 244 break |
234 } | 245 } |
235 if len(via) > 0 { | 246 if len(via) > 0 { |
236 // Add the Referer header. | 247 // Add the Referer header. |
237 lastReq := via[len(via)-1] | 248 lastReq := via[len(via)-1] |
238 if lastReq.URL.Scheme != "https" { | 249 if lastReq.URL.Scheme != "https" { |
239 req.Header.Set("Referer", lastReq.URL.St ring()) | 250 req.Header.Set("Referer", lastReq.URL.St ring()) |
240 } | 251 } |
241 | 252 |
242 err = redirectChecker(req, via) | 253 err = redirectChecker(req, via) |
243 if err != nil { | 254 if err != nil { |
244 redirectFailed = true | 255 redirectFailed = true |
245 break | 256 break |
246 } | 257 } |
247 } | 258 } |
248 } | 259 } |
249 | 260 |
250 for _, cookie := range jar.Cookies(req.URL) { | |
251 req.AddCookie(cookie) | |
252 } | |
253 urlStr = req.URL.String() | 261 urlStr = req.URL.String() |
254 » » if resp, err = send(req, c.Transport); err != nil { | 262 » » if resp, err = c.send(req); err != nil { |
255 break | 263 break |
256 } | 264 } |
257 if c := resp.Cookies(); len(c) > 0 { | |
258 jar.SetCookies(req.URL, c) | |
259 } | |
260 | 265 |
261 if shouldRedirect(resp.StatusCode) { | 266 if shouldRedirect(resp.StatusCode) { |
262 resp.Body.Close() | 267 resp.Body.Close() |
263 if urlStr = resp.Header.Get("Location"); urlStr == "" { | 268 if urlStr = resp.Header.Get("Location"); urlStr == "" { |
264 err = errors.New(fmt.Sprintf("%d response missin g Location header", resp.StatusCode)) | 269 err = errors.New(fmt.Sprintf("%d response missin g Location header", resp.StatusCode)) |
265 break | 270 break |
266 } | 271 } |
267 base = req.URL | 272 base = req.URL |
268 via = append(via, req) | 273 via = append(via, req) |
269 continue | 274 continue |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
309 | 314 |
310 // Post issues a POST to the specified URL. | 315 // Post issues a POST to the specified URL. |
311 // | 316 // |
312 // Caller should close resp.Body when done reading from it. | 317 // Caller should close resp.Body when done reading from it. |
313 func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon se, err error) { | 318 func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon se, err error) { |
314 req, err := NewRequest("POST", url, body) | 319 req, err := NewRequest("POST", url, body) |
315 if err != nil { | 320 if err != nil { |
316 return nil, err | 321 return nil, err |
317 } | 322 } |
318 req.Header.Set("Content-Type", bodyType) | 323 req.Header.Set("Content-Type", bodyType) |
319 » if c.Jar != nil { | 324 » return c.send(req) |
320 » » for _, cookie := range c.Jar.Cookies(req.URL) { | |
321 » » » req.AddCookie(cookie) | |
322 » » } | |
323 » } | |
324 » resp, err = send(req, c.Transport) | |
325 » if err == nil && c.Jar != nil { | |
326 » » c.Jar.SetCookies(req.URL, resp.Cookies()) | |
327 » } | |
328 » return | |
329 } | 325 } |
330 | 326 |
331 // PostForm issues a POST to the specified URL, with data's keys and | 327 // PostForm issues a POST to the specified URL, with data's keys and |
332 // values URL-encoded as the request body. | 328 // values URL-encoded as the request body. |
333 // | 329 // |
334 // When err is nil, resp always contains a non-nil resp.Body. | 330 // When err is nil, resp always contains a non-nil resp.Body. |
335 // Caller should close resp.Body when done reading from it. | 331 // Caller should close resp.Body when done reading from it. |
336 // | 332 // |
337 // PostForm is a wrapper around DefaultClient.PostForm | 333 // PostForm is a wrapper around DefaultClient.PostForm |
338 func PostForm(url string, data url.Values) (resp *Response, err error) { | 334 func PostForm(url string, data url.Values) (resp *Response, err error) { |
339 return DefaultClient.PostForm(url, data) | 335 return DefaultClient.PostForm(url, data) |
340 } | 336 } |
341 | 337 |
342 // PostForm issues a POST to the specified URL, | 338 // PostForm issues a POST to the specified URL, |
343 // with data's keys and values urlencoded as the request body. | 339 // with data's keys and values urlencoded as the request body. |
344 // | 340 // |
345 // When err is nil, resp always contains a non-nil resp.Body. | 341 // When err is nil, resp always contains a non-nil resp.Body. |
346 // Caller should close resp.Body when done reading from it. | 342 // Caller should close resp.Body when done reading from it. |
347 func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro r) { | 343 func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro r) { |
348 return c.Post(url, "application/x-www-form-urlencoded", strings.NewReade r(data.Encode())) | 344 return c.Post(url, "application/x-www-form-urlencoded", strings.NewReade r(data.Encode())) |
349 } | 345 } |
350 | 346 |
351 // Head issues a HEAD to the specified URL. If the response is one of the | 347 // Head issues a HEAD to the specified URL. If the response is one of the |
352 // following redirect codes, Head follows the redirect after calling the | 348 // following redirect codes, Head follows the redirect after calling the |
(...skipping 17 matching lines...) Expand all Loading... | |
370 // 302 (Found) | 366 // 302 (Found) |
371 // 303 (See Other) | 367 // 303 (See Other) |
372 // 307 (Temporary Redirect) | 368 // 307 (Temporary Redirect) |
373 func (c *Client) Head(url string) (resp *Response, err error) { | 369 func (c *Client) Head(url string) (resp *Response, err error) { |
374 req, err := NewRequest("HEAD", url, nil) | 370 req, err := NewRequest("HEAD", url, nil) |
375 if err != nil { | 371 if err != nil { |
376 return nil, err | 372 return nil, err |
377 } | 373 } |
378 return c.doFollowingRedirects(req) | 374 return c.doFollowingRedirects(req) |
379 } | 375 } |
OLD | NEW |