http2: add tests to verify the type of peer stream resets

Make sure that both the server and client can see typed errors when
the peer's body is interrupted by a stream reset. grpc will need this.

Turns out things were okay, but the behavior wasn't locked in. Now it is.

Change-Id: I449a52e368efe9a3d44c59cc9a4fc15365bc4f12
Reviewed-on: https://go-review.googlesource.com/18502
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/http2/server_test.go b/http2/server_test.go
index e2f42ff..a03dbeb 100644
--- a/http2/server_test.go
+++ b/http2/server_test.go
@@ -75,6 +75,7 @@
 type serverTesterOpt string
 
 var optOnlyServer = serverTesterOpt("only_server")
+var optQuiet = serverTesterOpt("quiet_logging")
 
 func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *serverTester {
 	resetHooks()
@@ -89,7 +90,7 @@
 		NextProtos: []string{NextProtoTLS, "h2-14"},
 	}
 
-	onlyServer := false
+	var onlyServer, quiet bool
 	for _, opt := range opts {
 		switch v := opt.(type) {
 		case func(*tls.Config):
@@ -97,7 +98,12 @@
 		case func(*httptest.Server):
 			v(ts)
 		case serverTesterOpt:
-			onlyServer = (v == optOnlyServer)
+			switch v {
+			case optOnlyServer:
+				onlyServer = true
+			case optQuiet:
+				quiet = true
+			}
 		default:
 			t.Fatalf("unknown newServerTester option type %T", v)
 		}
@@ -116,7 +122,11 @@
 	st.hpackDec = hpack.NewDecoder(initialHeaderTableSize, st.onHeaderField)
 
 	ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
-	ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, logBuf), "", log.LstdFlags)
+	if quiet {
+		ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
+	} else {
+		ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, logBuf), "", log.LstdFlags)
+	}
 	ts.StartTLS()
 
 	if VerboseLogs {
@@ -1120,8 +1130,9 @@
 			}
 		},
 		func(err error) {
-			if err == nil {
-				t.Error("unexpected nil error from Request.Body.Read")
+			want := StreamError{StreamID: 0x1, Code: 0x8}
+			if !reflect.DeepEqual(err, want) {
+				t.Errorf("Read error = %v; want %v", err, want)
 			}
 		},
 	)
diff --git a/http2/transport_test.go b/http2/transport_test.go
index 597f0a2..7183ccb 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -1232,3 +1232,34 @@
 	ct.run()
 
 }
+
+// Test that the the Transport returns a typed error from Response.Body.Read calls
+// when the server sends an error. (here we use a panic, since that should generate
+// a stream error, but others like cancel should be similar)
+func TestTransportBodyReadErrorType(t *testing.T) {
+	st := newServerTester(t,
+		func(w http.ResponseWriter, r *http.Request) {
+			w.(http.Flusher).Flush() // force headers out
+			panic("boom")
+		},
+		optOnlyServer,
+		optQuiet,
+	)
+	defer st.Close()
+
+	tr := &Transport{TLSClientConfig: tlsConfigInsecure}
+	defer tr.CloseIdleConnections()
+	c := &http.Client{Transport: tr}
+
+	res, err := c.Get(st.ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	buf := make([]byte, 100)
+	n, err := res.Body.Read(buf)
+	want := StreamError{StreamID: 0x1, Code: 0x2}
+	if !reflect.DeepEqual(want, err) {
+		t.Errorf("Read = %v, %#v; want error %#v", n, err, want)
+	}
+}