mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net/http: fix hang in probing for a zero-length request body
Fix a hang that occurs when making a request and 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. In this case, we give up on probing for a zero-length body and send the request while the probe completes in the background. Fix a bug in the io.Reader wrapping the in-flight probe: It should return io.EOF after the probe completes, but does not. Fixes #47568. Change-Id: I7f9188c96e1210055df68424081af927006e4816 Reviewed-on: https://go-review.googlesource.com/c/go/+/340256 Trust: Damien Neil <dneil@google.com> Run-TryBot: Damien Neil <dneil@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ingo Oeser <nightlyone@googlemail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
2a463a22ce
commit
acc2957bc9
2 changed files with 48 additions and 0 deletions
|
|
@ -2082,3 +2082,47 @@ func (b *issue40382Body) Close() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProbeZeroLengthBody(t *testing.T) {
|
||||||
|
setParallel(t)
|
||||||
|
defer afterTest(t)
|
||||||
|
reqc := make(chan struct{})
|
||||||
|
cst := newClientServerTest(t, false, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||||
|
close(reqc)
|
||||||
|
if _, err := io.Copy(w, r.Body); err != nil {
|
||||||
|
t.Errorf("error copying request body: %v", err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer cst.close()
|
||||||
|
|
||||||
|
bodyr, bodyw := io.Pipe()
|
||||||
|
var gotBody string
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
req, _ := NewRequest("GET", cst.ts.URL, bodyr)
|
||||||
|
res, err := cst.c.Do(req)
|
||||||
|
b, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
gotBody = string(b)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-reqc:
|
||||||
|
// Request should be sent after trying to probe the request body for 200ms.
|
||||||
|
case <-time.After(60 * time.Second):
|
||||||
|
t.Errorf("request not sent after 60s")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the request body and wait for the request to complete.
|
||||||
|
const content = "body"
|
||||||
|
bodyw.Write([]byte(content))
|
||||||
|
bodyw.Close()
|
||||||
|
wg.Wait()
|
||||||
|
if gotBody != content {
|
||||||
|
t.Fatalf("server got body %q, want %q", gotBody, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ func (t *transferWriter) probeRequestBody() {
|
||||||
rres.b = buf[0]
|
rres.b = buf[0]
|
||||||
}
|
}
|
||||||
t.ByteReadCh <- rres
|
t.ByteReadCh <- rres
|
||||||
|
close(t.ByteReadCh)
|
||||||
}(t.Body)
|
}(t.Body)
|
||||||
timer := time.NewTimer(200 * time.Millisecond)
|
timer := time.NewTimer(200 * time.Millisecond)
|
||||||
select {
|
select {
|
||||||
|
|
@ -1072,6 +1073,9 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) {
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
p[0] = rres.b
|
p[0] = rres.b
|
||||||
}
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue