http2: avoid panic on h2c upgrade failure
When performing an h2c upgrade, we would panic if an error occurred
reading the client preface. *serverConn.closeStream assumes that
a conn with an IdleTimeout > 0 will have an idleTimer, but in the
h2c upgrade case the stream may have been created before the timer.
Fixes golang/go#67168
Change-Id: I30b5a701c10753ddc344079b9498285f099155cf
Reviewed-on: https://go-review.googlesource.com/c/net/+/584255
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/http2/server.go b/http2/server.go
index c5d0810..d2f14b1 100644
--- a/http2/server.go
+++ b/http2/server.go
@@ -1639,7 +1639,7 @@
delete(sc.streams, st.id)
if len(sc.streams) == 0 {
sc.setConnState(http.StateIdle)
- if sc.srv.IdleTimeout > 0 {
+ if sc.srv.IdleTimeout > 0 && sc.idleTimer != nil {
sc.idleTimer.Reset(sc.srv.IdleTimeout)
}
if h1ServerKeepAlivesDisabled(sc.hs) {
diff --git a/http2/server_test.go b/http2/server_test.go
index 61c773a..f5aec44 100644
--- a/http2/server_test.go
+++ b/http2/server_test.go
@@ -4880,3 +4880,23 @@
t.Errorf("connection closed with no GOAWAY frame; want one")
}
}
+
+func TestServerUpgradeRequestPrefaceFailure(t *testing.T) {
+ // An h2c upgrade request fails when the client preface is not as expected.
+ s2 := &Server{
+ // Setting IdleTimeout triggers #67168.
+ IdleTimeout: 60 * time.Minute,
+ }
+ c1, c2 := net.Pipe()
+ donec := make(chan struct{})
+ go func() {
+ defer close(donec)
+ s2.ServeConn(c1, &ServeConnOpts{
+ UpgradeRequest: httptest.NewRequest("GET", "/", nil),
+ })
+ }()
+ // The server expects to see the HTTP/2 preface,
+ // but we close the connection instead.
+ c2.Close()
+ <-donec
+}