diff --git a/client.go b/client.go index 26e6037..c209794 100644 --- a/client.go +++ b/client.go @@ -1093,9 +1093,10 @@ func clientGetURLDeadline(dst []byte, url string, deadline time.Time, c clientDo go func() { req := AcquireRequest() - statusCodeCopy, bodyCopy, errCopy := doRequestFollowRedirectsBuffer(req, dst, url, c) + statusCodeCopy, bodyCopy, errCopy := doRequestFollowRedirectsBuffer(req, nil, url, c) mu.Lock() if !timedout { + bodyCopy = append(dst[:0], bodyCopy...) ch <- clientURLResponse{ statusCode: statusCodeCopy, body: bodyCopy, diff --git a/client_test.go b/client_test.go index 476698e..0401e25 100644 --- a/client_test.go +++ b/client_test.go @@ -2290,6 +2290,56 @@ func TestClientGetTimeoutError(t *testing.T) { testClientGetTimeoutError(t, c, 100) } +func TestClientGetURLDeadlineDoesNotMutateDstAfterTimeout(t *testing.T) { + t.Parallel() + + d := &delayedBodyDoer{ + unblock: make(chan struct{}), + done: make(chan struct{}), + body: "mutated!", + } + dst := []byte("original") + + statusCode, body, err := clientGetURLDeadline(dst, "http://example.com/", time.Now().Add(time.Millisecond), d) + if err != ErrTimeout { + t.Fatalf("unexpected error: %v. Expecting %v", err, ErrTimeout) + } + if statusCode != 0 { + t.Fatalf("unexpected status code: %d. Expecting 0", statusCode) + } + if string(body) != "original" { + t.Fatalf("unexpected timeout body %q. Expecting %q", body, "original") + } + + close(d.unblock) + select { + case <-d.done: + case <-time.After(time.Second): + t.Fatal("timed out waiting for background request to finish") + } + + if string(dst) != "original" { + t.Fatalf("dst was mutated after timeout: %q. Expecting %q", dst, "original") + } + if string(body) != "original" { + t.Fatalf("returned body was mutated after timeout: %q. Expecting %q", body, "original") + } +} + +type delayedBodyDoer struct { + unblock chan struct{} + done chan struct{} + body string +} + +func (d *delayedBodyDoer) Do(req *Request, resp *Response) error { + <-d.unblock + resp.SetStatusCode(StatusOK) + resp.SetBodyString(d.body) + close(d.done) + return nil +} + func TestClientGetTimeoutErrorConcurrent(t *testing.T) { t.Parallel()