diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2016-05-18 15:42:54 +0000 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-05-18 18:01:50 +0000 |
| commit | 8f13080267d0ddbb50da9029339796841224116a (patch) | |
| tree | 6fda6484226584bde777be21e2b1abf264564978 /src/net/http/client.go | |
| parent | 5d92aefc18317578226a3873fb8fc37411cd2184 (diff) | |
| download | go-8f13080267d0ddbb50da9029339796841224116a.tar.xz | |
net/http: allow Client.CheckRedirect to use most recent response
Fixes #10069
Change-Id: I3819ff597d5a0c8e785403bf9d65a054f50655a6
Reviewed-on: https://go-review.googlesource.com/23207
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/net/http/client.go')
| -rw-r--r-- | src/net/http/client.go | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/src/net/http/client.go b/src/net/http/client.go index 1127634bec..993c247eef 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -47,6 +47,9 @@ type Client struct { // method returns both the previous Response (with its Body // closed) and CheckRedirect's error (wrapped in a url.Error) // instead of issuing the Request req. + // As a special case, if CheckRedirect returns ErrUseLastResponse, + // then the most recent response is returned with its body + // unclosed, along with a nil error. // // If CheckRedirect is nil, the Client uses its default policy, // which is to stop after 10 consecutive requests. @@ -417,6 +420,12 @@ func (c *Client) Get(url string) (resp *Response, err error) { func alwaysFalse() bool { return false } +// ErrUseLastResponse can be returned by Client.CheckRedirect hooks to +// control how redirects are processed. If returned, the next request +// is not sent and the most recent response is returned with its body +// unclosed. +var ErrUseLastResponse = errors.New("net/http: use last response") + // checkRedirect calls either the user's configured CheckRedirect // function, or the default. func (c *Client) checkRedirect(req *Request, via []*Request) error { @@ -467,11 +476,12 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo } ireq := reqs[0] req = &Request{ - Method: ireq.Method, - URL: u, - Header: make(Header), - Cancel: ireq.Cancel, - ctx: ireq.ctx, + Method: ireq.Method, + Response: resp, + URL: u, + Header: make(Header), + Cancel: ireq.Cancel, + ctx: ireq.ctx, } if ireq.Method == "POST" || ireq.Method == "PUT" { req.Method = "GET" @@ -481,7 +491,27 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL); ref != "" { req.Header.Set("Referer", ref) } - if err := c.checkRedirect(req, reqs); err != nil { + err = c.checkRedirect(req, reqs) + + // Sentinel error to let users select the + // previous response, without closing its + // body. See Issue 10069. + if err == ErrUseLastResponse { + return resp, nil + } + + // Close the previous response's body. But + // read at least some of the body so if it's + // small the underlying TCP connection will be + // re-used. No need to check for errors: if it + // fails, the Transport won't reuse it anyway. + const maxBodySlurpSize = 2 << 10 + if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize { + io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize) + } + resp.Body.Close() + + if err != nil { // Special case for Go 1 compatibility: return both the response // and an error if the CheckRedirect function failed. // See https://golang.org/issue/3795 @@ -508,14 +538,6 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo if !shouldRedirect(resp.StatusCode) { return resp, nil } - - // Read the body if small so underlying TCP connection will be re-used. - // No need to check for errors: if it fails, Transport won't reuse it anyway. - const maxBodySlurpSize = 2 << 10 - if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize { - io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize) - } - resp.Body.Close() } } |
