http2: set up the timer of closing idle connection after the initialization
Fixes golang/go#66763
Change-Id: I046028b6072a6da77e7f1b4d1f2e6b14f8edb042
Reviewed-on: https://go-review.googlesource.com/c/net/+/578115
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Damien Neil <dneil@google.com>
diff --git a/http2/transport.go b/http2/transport.go
index 2fa4949..714ef18 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -826,10 +826,6 @@
hooks.newclientconn(cc)
c = cc.tconn
}
- if d := t.idleConnTimeout(); d != 0 {
- cc.idleTimeout = d
- cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
- }
if VerboseLogs {
t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
}
@@ -893,6 +889,12 @@
return nil, cc.werr
}
+ // Start the idle timer after the connection is fully initialized.
+ if d := t.idleConnTimeout(); d != 0 {
+ cc.idleTimeout = d
+ cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
+ }
+
cc.goRun(cc.readLoop)
return cc, nil
}
diff --git a/http2/transport_test.go b/http2/transport_test.go
index 3e4297f..2aa0c3e 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -5426,3 +5426,28 @@
}
tc.wantFrameType(FrameRSTStream)
}
+
+func TestIssue66763Race(t *testing.T) {
+ tr := &Transport{
+ IdleConnTimeout: 1 * time.Nanosecond,
+ AllowHTTP: true, // issue 66763 only occurs when AllowHTTP is true
+ }
+ defer tr.CloseIdleConnections()
+
+ cli, srv := net.Pipe()
+ donec := make(chan struct{})
+ go func() {
+ // Creating the client conn may succeed or fail,
+ // depending on when the idle timeout happens.
+ // Either way, the idle timeout will close the net.Conn.
+ tr.NewClientConn(cli)
+ close(donec)
+ }()
+
+ // The client sends its preface and SETTINGS frame,
+ // and then closes its conn after the idle timeout.
+ io.ReadAll(srv)
+ srv.Close()
+
+ <-donec
+}