diff options
| author | Damien Neil <dneil@google.com> | 2021-08-05 19:26:21 -0700 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2021-09-02 17:00:30 +0000 |
| commit | acc2957bc9873ab7e65942045830a3dc0592eda2 (patch) | |
| tree | 59e58c2ac80f487b9ef4cca1d622d7a2649bbe2e /src/net/http/client_test.go | |
| parent | 2a463a22cee8ddbd4801acd2ef34eefa551a718a (diff) | |
| download | go-acc2957bc9873ab7e65942045830a3dc0592eda2.tar.xz | |
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>
Diffstat (limited to 'src/net/http/client_test.go')
| -rw-r--r-- | src/net/http/client_test.go | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 01d605c351..05ed2268b5 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -2082,3 +2082,47 @@ func (b *issue40382Body) Close() error { } 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) + } +} |
