Commit 4ffff35a authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

http: Client.Do should follow redirects for GET and HEAD

It's documented as such, but it was never wired up
after Transport went in and Head was fixed.

If people don't want redirects, that's what RoundTripper/
Transport are for.  Or a custom redirect policy.

R=golang-dev, kevlar
CC=golang-dev
https://golang.org/cl/4526065
parent 1d550bde
...@@ -74,6 +74,9 @@ type readClose struct { ...@@ -74,6 +74,9 @@ type readClose struct {
// //
// Generally Get, Post, or PostForm will be used instead of Do. // Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err os.Error) { func (c *Client) Do(req *Request) (resp *Response, err os.Error) {
if req.Method == "GET" || req.Method == "HEAD" {
return c.doFollowingRedirects(req)
}
return send(req, c.Transport) return send(req, c.Transport)
} }
...@@ -144,10 +147,14 @@ func Get(url string) (r *Response, err os.Error) { ...@@ -144,10 +147,14 @@ func Get(url string) (r *Response, err os.Error) {
// //
// Caller should close r.Body when done reading from it. // Caller should close r.Body when done reading from it.
func (c *Client) Get(url string) (r *Response, err os.Error) { func (c *Client) Get(url string) (r *Response, err os.Error) {
return c.sendFollowingRedirects("GET", url) req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return c.doFollowingRedirects(req)
} }
func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os.Error) { func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) {
// TODO: if/when we add cookie support, the redirected request shouldn't // TODO: if/when we add cookie support, the redirected request shouldn't
// necessarily supply the same cookies as the original. // necessarily supply the same cookies as the original.
var base *URL var base *URL
...@@ -157,33 +164,33 @@ func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os ...@@ -157,33 +164,33 @@ func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os
} }
var via []*Request var via []*Request
req := ireq
url := "" // next relative or absolute URL to fetch (after first request)
for redirect := 0; ; redirect++ { for redirect := 0; ; redirect++ {
var req Request if redirect != 0 {
req.Method = method req = new(Request)
req.Header = make(Header) req.Method = ireq.Method
if base == nil { req.Header = make(Header)
req.URL, err = ParseURL(url)
} else {
req.URL, err = base.ParseURL(url) req.URL, err = base.ParseURL(url)
}
if err != nil {
break
}
if len(via) > 0 {
// Add the Referer header.
lastReq := via[len(via)-1]
if lastReq.URL.Scheme != "https" {
req.Referer = lastReq.URL.String()
}
err = redirectChecker(&req, via)
if err != nil { if err != nil {
break break
} }
if len(via) > 0 {
// Add the Referer header.
lastReq := via[len(via)-1]
if lastReq.URL.Scheme != "https" {
req.Referer = lastReq.URL.String()
}
err = redirectChecker(req, via)
if err != nil {
break
}
}
} }
url = req.URL.String() url = req.URL.String()
if r, err = send(&req, c.Transport); err != nil { if r, err = send(req, c.Transport); err != nil {
break break
} }
if shouldRedirect(r.StatusCode) { if shouldRedirect(r.StatusCode) {
...@@ -193,12 +200,13 @@ func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os ...@@ -193,12 +200,13 @@ func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os
break break
} }
base = req.URL base = req.URL
via = append(via, &req) via = append(via, req)
continue continue
} }
return return
} }
method := ireq.Method
err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err} err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err}
return return
} }
...@@ -310,5 +318,9 @@ func Head(url string) (r *Response, err os.Error) { ...@@ -310,5 +318,9 @@ func Head(url string) (r *Response, err os.Error) {
// 303 (See Other) // 303 (See Other)
// 307 (Temporary Redirect) // 307 (Temporary Redirect)
func (c *Client) Head(url string) (r *Response, err os.Error) { func (c *Client) Head(url string) (r *Response, err os.Error) {
return c.sendFollowingRedirects("HEAD", url) req, err := NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return c.doFollowingRedirects(req)
} }
...@@ -98,13 +98,20 @@ func TestRedirects(t *testing.T) { ...@@ -98,13 +98,20 @@ func TestRedirects(t *testing.T) {
c := &Client{} c := &Client{}
_, err := c.Get(ts.URL) _, err := c.Get(ts.URL)
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g { if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
t.Errorf("with default client, expected error %q, got %q", e, g) t.Errorf("with default client Get, expected error %q, got %q", e, g)
} }
// HEAD request should also have the ability to follow redirects. // HEAD request should also have the ability to follow redirects.
_, err = c.Head(ts.URL) _, err = c.Head(ts.URL)
if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g { if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
t.Errorf("with default client, expected error %q, got %q", e, g) t.Errorf("with default client Head, expected error %q, got %q", e, g)
}
// Do should also follow redirects.
greq, _ := NewRequest("GET", ts.URL, nil)
_, err = c.Do(greq)
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
t.Errorf("with default client Do, expected error %q, got %q", e, g)
} }
var checkErr os.Error var checkErr os.Error
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment