@@ -47,6 +47,9 @@ type Client struct {
47
47
// method returns both the previous Response (with its Body
48
48
// closed) and CheckRedirect's error (wrapped in a url.Error)
49
49
// instead of issuing the Request req.
50
+ // As a special case, if CheckRedirect returns ErrUseLastResponse,
51
+ // then the most recent response is returned with its body
52
+ // unclosed, along with a nil error.
50
53
//
51
54
// If CheckRedirect is nil, the Client uses its default policy,
52
55
// which is to stop after 10 consecutive requests.
@@ -417,6 +420,12 @@ func (c *Client) Get(url string) (resp *Response, err error) {
417
420
418
421
func alwaysFalse () bool { return false }
419
422
423
+ // ErrUseLastResponse can be returned by Client.CheckRedirect hooks to
424
+ // control how redirects are processed. If returned, the next request
425
+ // is not sent and the most recent response is returned with its body
426
+ // unclosed.
427
+ var ErrUseLastResponse = errors .New ("net/http: use last response" )
428
+
420
429
// checkRedirect calls either the user's configured CheckRedirect
421
430
// function, or the default.
422
431
func (c * Client ) checkRedirect (req * Request , via []* Request ) error {
@@ -467,11 +476,12 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
467
476
}
468
477
ireq := reqs [0 ]
469
478
req = & Request {
470
- Method : ireq .Method ,
471
- URL : u ,
472
- Header : make (Header ),
473
- Cancel : ireq .Cancel ,
474
- ctx : ireq .ctx ,
479
+ Method : ireq .Method ,
480
+ Response : resp ,
481
+ URL : u ,
482
+ Header : make (Header ),
483
+ Cancel : ireq .Cancel ,
484
+ ctx : ireq .ctx ,
475
485
}
476
486
if ireq .Method == "POST" || ireq .Method == "PUT" {
477
487
req .Method = "GET"
@@ -481,7 +491,27 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
481
491
if ref := refererForURL (reqs [len (reqs )- 1 ].URL , req .URL ); ref != "" {
482
492
req .Header .Set ("Referer" , ref )
483
493
}
484
- if err := c .checkRedirect (req , reqs ); err != nil {
494
+ err = c .checkRedirect (req , reqs )
495
+
496
+ // Sentinel error to let users select the
497
+ // previous response, without closing its
498
+ // body. See Issue 10069.
499
+ if err == ErrUseLastResponse {
500
+ return resp , nil
501
+ }
502
+
503
+ // Close the previous response's body. But
504
+ // read at least some of the body so if it's
505
+ // small the underlying TCP connection will be
506
+ // re-used. No need to check for errors: if it
507
+ // fails, the Transport won't reuse it anyway.
508
+ const maxBodySlurpSize = 2 << 10
509
+ if resp .ContentLength == - 1 || resp .ContentLength <= maxBodySlurpSize {
510
+ io .CopyN (ioutil .Discard , resp .Body , maxBodySlurpSize )
511
+ }
512
+ resp .Body .Close ()
513
+
514
+ if err != nil {
485
515
// Special case for Go 1 compatibility: return both the response
486
516
// and an error if the CheckRedirect function failed.
487
517
// See https://golang.org/issue/3795
@@ -508,14 +538,6 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
508
538
if ! shouldRedirect (resp .StatusCode ) {
509
539
return resp , nil
510
540
}
511
-
512
- // Read the body if small so underlying TCP connection will be re-used.
513
- // No need to check for errors: if it fails, Transport won't reuse it anyway.
514
- const maxBodySlurpSize = 2 << 10
515
- if resp .ContentLength == - 1 || resp .ContentLength <= maxBodySlurpSize {
516
- io .CopyN (ioutil .Discard , resp .Body , maxBodySlurpSize )
517
- }
518
- resp .Body .Close ()
519
541
}
520
542
}
521
543
0 commit comments