// read to EOF and closed, the [Client]'s underlying [RoundTripper]
// (typically [Transport]) may not be able to re-use a persistent TCP
// connection to the server for a subsequent "keep-alive" request.
+// Note, however, that [Transport] will automatically try to read a
+// [Response] Body to EOF asynchronously up to a conservative limit
+// when a Body is closed.
//
// The request Body, if non-nil, will be closed by the underlying
// Transport, even on errors. The Body may be closed asynchronously after
"sync/atomic"
"testing"
"testing/synctest"
+ "time"
)
func TestTransportNewClientConnRoundTrip(t *testing.T) { run(t, testTransportNewClientConnRoundTrip) }
}
test.consume(t, cc, mode)
+ if mode == http1Mode || mode == https1Mode {
+ time.Sleep(http.MaxPostCloseReadTime)
+ }
synctest.Wait()
// State hook should be called, either to report the
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
- // not read to completion and closed.
+ // not read to completion and closed; however, manually reading
+ // the body to completion should not be needed in most cases,
+ // as closing the body will also cause the body to be read to
+ // completion asynchronously, up to a conservative limit.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
// reading the response body. (or for cancellation or death)
select {
case bodyEOF := <-waitForBodyRead:
- if !bodyEOF && resp.ContentLength <= maxPostCloseReadBytes {
+ tryDrain := !bodyEOF && resp.ContentLength <= maxPostCloseReadBytes
+ if tryDrain {
+ eofc <- struct{}{}
bodyEOF = maybeDrainBody(body.body)
}
alive = alive &&
!pc.sawEOF &&
pc.wroteRequest() &&
tryPutIdleConn(rc.treq)
- if bodyEOF {
+ if !tryDrain && bodyEOF {
eofc <- struct{}{}
}
case <-rc.treq.ctx.Done():