http2: ignore read errors after closing the request body

We close the request body after receiving an error status code.
This is supposed to cause cs.writeRequestBody to return
errStopReqBodyWrite. Ensure that it does so if it gets an error
reading from the post-close body, rather than returning an
error which causes the entire request to be aborted.

Change-Id: I7c51928cb678f5baf37148f0df6ab196518d39d4
Reviewed-on: https://go-review.googlesource.com/c/net/+/356969
Trust: Damien Neil <dneil@google.com>
Run-TryBot: Damien Neil <dneil@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/http2/transport.go b/http2/transport.go
index faeea16..86b734a 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -1539,11 +1539,19 @@
 				return err
 			}
 		}
-		if err == io.EOF {
-			sawEOF = true
-			err = nil
-		} else if err != nil {
-			return err
+		if err != nil {
+			cc.mu.Lock()
+			bodyClosed := cs.reqBodyClosed
+			cc.mu.Unlock()
+			switch {
+			case bodyClosed:
+				return errStopReqBodyWrite
+			case err == io.EOF:
+				sawEOF = true
+				err = nil
+			default:
+				return err
+			}
 		}
 
 		remain := buf[:n]
diff --git a/http2/transport_test.go b/http2/transport_test.go
index 85290a1..f0868d6 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -5701,3 +5701,38 @@
 	res.Body.Close()
 	pw.Close()
 }
+
+func TestTransport300ResponseBody(t *testing.T) {
+	reqc := make(chan struct{})
+	body := []byte("response body")
+	st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(300)
+		w.(http.Flusher).Flush()
+		<-reqc
+		w.Write(body)
+	}, optOnlyServer)
+	defer st.Close()
+
+	tr := &Transport{TLSClientConfig: tlsConfigInsecure}
+	defer tr.CloseIdleConnections()
+
+	pr, pw := net.Pipe()
+	req, err := http.NewRequest("GET", st.ts.URL, pr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res, err := tr.RoundTrip(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	close(reqc)
+	got, err := io.ReadAll(res.Body)
+	if err != nil {
+		t.Fatalf("error reading response body: %v", err)
+	}
+	if !bytes.Equal(got, body) {
+		t.Errorf("got response body %q, want %q", string(got), string(body))
+	}
+	res.Body.Close()
+	pw.Close()
+}