Description
When making a request which usually lacks a response body (GET, HEAD, DELETE, etc.), if the Request.Body
is non-nil, the content length is not set, and Transfer-Encoding: chunked
is not set, we attempt to read one byte from the body before sending the request. If the read succeeds within 200ms and indicates a zero-length body, we set Request.Body = nil
.
If the read takes more than 200ms, we set Transfer-Encoding: chunked
and proceed.
See: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/transfer.go#191
The latter path (when the probe does not complete within 200ms) is broken when the body is not zero-length.
In this path, we replace Request.Body
with io.MultiReader(finishAsyncByteRead{t}, t.Body)
. finishAsyncByteRead.Read
does not return io.EOF
after reading the initial byte, so the io.MultiReader
retries the read, which never completes.
https://go.googlesource.com/go/+/refs/heads/master/src/net/http/transfer.go#1066
The impact is that sending a request can hang when all of the following apply:
- The request method is one of GET, HEAD, DELETE, OPTIONS, PROPFIND, or SEARCH.
- The
Request.Body
is non-nil. - The content length is not set, or is set to -1.
Transfer-Encoding: chunked
is not set.- The request body does not respond to a read within 200ms.
Thanks to @EdSchouten for identifying this problem in CL 328711.