]> Cypherpunks repositories - gostls13.git/commitdiff
http: make HEAD client request follow redirects
authorEivind Uggedal <eivind@uggedal.com>
Fri, 13 May 2011 15:17:59 +0000 (08:17 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Fri, 13 May 2011 15:17:59 +0000 (08:17 -0700)
HEAD requests should in my opinion have the ability to follow redirects
like the implementation of GET requests does. My use case is polling
several thousand severs to check if they respond with 200 status codes.
Using GET requests is neither efficient in running time of the task nor
for bandwidth consumption.

This suggested patch changes the return signature of http.Head() to match
that of http.Get(), providing the final URL in a redirect chain.

`curl -IL http://google.com` follows redirects with HEAD requests just fine.

Fixes #1806.

R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/4517058

src/pkg/http/client.go
src/pkg/http/client_test.go

index 469d49dbe98e959f1440f9acc92b8a904b888ae5..8b5266964211aeaf0b5527773ef89626d6fcfd33 100644 (file)
@@ -144,6 +144,10 @@ func Get(url string) (r *Response, err os.Error) {
 //
 // Caller should close r.Body when done reading from it.
 func (c *Client) Get(url string) (r *Response, err os.Error) {
+       return c.sendFollowingRedirects("GET", url)
+}
+
+func (c *Client) sendFollowingRedirects(method, url string) (r *Response, err os.Error) {
        // TODO: if/when we add cookie support, the redirected request shouldn't
        // necessarily supply the same cookies as the original.
        var base *URL
@@ -155,7 +159,7 @@ func (c *Client) Get(url string) (r *Response, err os.Error) {
 
        for redirect := 0; ; redirect++ {
                var req Request
-               req.Method = "GET"
+               req.Method = method
                req.Header = make(Header)
                if base == nil {
                        req.URL, err = ParseURL(url)
@@ -195,7 +199,7 @@ func (c *Client) Get(url string) (r *Response, err os.Error) {
                return
        }
 
-       err = &URLError{"Get", url, err}
+       err = &URLError{method[0:1] + strings.ToLower(method[1:]), url, err}
        return
 }
 
@@ -283,19 +287,28 @@ func urlencode(data map[string]string) (b *bytes.Buffer) {
        return bytes.NewBuffer([]byte(EncodeQuery(m)))
 }
 
-// Head issues a HEAD to the specified URL.
+// Head issues a HEAD to the specified URL.  If the response is one of the
+// following redirect codes, Head follows the redirect after calling the
+// Client's CheckRedirect function.
+//
+//    301 (Moved Permanently)
+//    302 (Found)
+//    303 (See Other)
+//    307 (Temporary Redirect)
 //
 // Head is a wrapper around DefaultClient.Head
 func Head(url string) (r *Response, err os.Error) {
        return DefaultClient.Head(url)
 }
 
-// Head issues a HEAD to the specified URL.
+// Head issues a HEAD to the specified URL.  If the response is one of the
+// following redirect codes, Head follows the redirect after calling the
+// Client's CheckRedirect function.
+//
+//    301 (Moved Permanently)
+//    302 (Found)
+//    303 (See Other)
+//    307 (Temporary Redirect)
 func (c *Client) Head(url string) (r *Response, err os.Error) {
-       var req Request
-       req.Method = "HEAD"
-       if req.URL, err = ParseURL(url); err != nil {
-               return
-       }
-       return send(&req, c.Transport)
+       return c.sendFollowingRedirects("HEAD", url)
 }
index 31654d0be3377a68e560034fab64433b89ef2456..6adc9a883634be4137d18dc41b1a9eb6b60d5609 100644 (file)
@@ -101,6 +101,12 @@ func TestRedirects(t *testing.T) {
                t.Errorf("with default client, expected error %q, got %q", e, g)
        }
 
+       // HEAD request should also have the ability to follow redirects.
+       _, err = c.Head(ts.URL)
+       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)
+       }
+
        var checkErr os.Error
        var lastVia []*Request
        c = &Client{CheckRedirect: func(_ *Request, via []*Request) os.Error {