Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1349)

Side by Side Diff: src/pkg/net/http/client.go

Issue 6653049: code review 6653049: net/http/client.go: fix cookie handling on (*Client) Do()
Patch Set: diff -r bb4ee132b967 https://code.google.com/p/go Created 11 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/pkg/net/http/client_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | src/pkg/net/http/client_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b