From 5d6afe98e0b7d884bea12f3ab4e7f8c1a07ce5c8 Mon Sep 17 00:00:00 2001 From: Michael Fraenkel Date: Tue, 10 Nov 2020 21:51:57 -0700 Subject: [PATCH] http2: send a nil error if we cancel a delayed body write Once a request body is scheduled to be written, a result of the write is always expected. If the body writer is cancelled, and the write was never started, send a successful result. The test included is a modified version of the TestNoSniffExpectRequestBody_h2 found in net/http. Updates golang/go#42498 Change-Id: If3f23993170bdf10e9ae4244ec13ae269bd3877a Reviewed-on: https://go-review.googlesource.com/c/net/+/269058 Trust: Dmitri Shuralyov Trust: Brad Fitzpatrick Run-TryBot: Dmitri Shuralyov TryBot-Result: Go Bot Reviewed-by: Brad Fitzpatrick --- http2/transport.go | 4 +++- http2/transport_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/http2/transport.go b/http2/transport.go index 8b129b794..7688d72c3 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -2632,7 +2632,9 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body func (s bodyWriterState) cancel() { if s.timer != nil { - s.timer.Stop() + if s.timer.Stop() { + s.resc <- nil + } } } diff --git a/http2/transport_test.go b/http2/transport_test.go index 13a316035..900fe85d9 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -4882,3 +4882,39 @@ func TestTransportBodyRewindRace(t *testing.T) { wg.Wait() } + +// Issue 42498: A request with a body will never be sent if the stream is +// reset prior to sending any data. +func TestTransportServerResetStreamAtHeaders(t *testing.T) { + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + return + }, optOnlyServer) + defer st.Close() + + tr := &http.Transport{ + TLSClientConfig: tlsConfigInsecure, + MaxConnsPerHost: 1, + ExpectContinueTimeout: 10 * time.Second, + } + + err := ConfigureTransport(tr) + if err != nil { + t.Fatal(err) + } + client := &http.Client{ + Transport: tr, + } + + req, err := http.NewRequest("POST", st.ts.URL, errorReader{io.EOF}) + if err != nil { + t.Fatalf("unexpect new request error: %v", err) + } + req.ContentLength = 0 // so transport is tempted to sniff it + req.Header.Set("Expect", "100-continue") + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + res.Body.Close() +}