http2/h2c: handle errors when reading HTTP/1 request body

When processing an HTTP/1 Upgrade: h2c request, detect errors reading
the request body and fail the request rather than passing off the
partially-read request to the HTTP/2 server.

Correctly handles the case where a MaxBytesHandler has limited the
size of the initial HTTP/1 request body.

Fixes golang/go#56352

Change-Id: I08d60953cea26961cffbab3094cc1b44236f4e37
Reviewed-on: https://go-review.googlesource.com/c/net/+/447396
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: John Howard <howardjohn@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
diff --git a/http2/h2c/h2c.go b/http2/h2c/h2c.go
index 2b77ffd..a72bbed 100644
--- a/http2/h2c/h2c.go
+++ b/http2/h2c/h2c.go
@@ -109,6 +109,7 @@
 			if http2VerboseLogs {
 				log.Printf("h2c: error h2c upgrade: %v", err)
 			}
+			w.WriteHeader(http.StatusInternalServerError)
 			return
 		}
 		defer conn.Close()
@@ -167,7 +168,10 @@
 		return nil, nil, errors.New("h2c: connection does not support Hijack")
 	}
 
-	body, _ := io.ReadAll(r.Body)
+	body, err := io.ReadAll(r.Body)
+	if err != nil {
+		return nil, nil, err
+	}
 	r.Body = io.NopCloser(bytes.NewBuffer(body))
 
 	conn, rw, err := hijacker.Hijack()
diff --git a/http2/h2c/h2c_test.go b/http2/h2c/h2c_test.go
index 558e597..038cbc3 100644
--- a/http2/h2c/h2c_test.go
+++ b/http2/h2c/h2c_test.go
@@ -8,6 +8,7 @@
 	"context"
 	"crypto/tls"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"log"
 	"net"
@@ -134,3 +135,38 @@
 		t.Fatal("expected server err, got nil")
 	}
 }
+
+func TestMaxBytesHandler(t *testing.T) {
+	const bodyLimit = 10
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		t.Errorf("got request, expected to be blocked by body limit")
+	})
+
+	h2s := &http2.Server{}
+	h1s := httptest.NewUnstartedServer(http.MaxBytesHandler(NewHandler(handler, h2s), bodyLimit))
+	h1s.Start()
+	defer h1s.Close()
+
+	// Wrap the body in a struct{io.Reader} to prevent it being rewound and resent.
+	body := "0123456789abcdef"
+	req, err := http.NewRequest("POST", h1s.URL, struct{ io.Reader }{strings.NewReader(body)})
+	if err != nil {
+		t.Fatal(err)
+	}
+	req.Header.Set("Http2-Settings", "")
+	req.Header.Set("Upgrade", "h2c")
+	req.Header.Set("Connection", "Upgrade, HTTP2-Settings")
+
+	resp, err := h1s.Client().Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer resp.Body.Close()
+	_, err = ioutil.ReadAll(resp.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if got, want := resp.StatusCode, http.StatusInternalServerError; got != want {
+		t.Errorf("resp.StatusCode = %v, want %v", got, want)
+	}
+}