Skip to content

Commit 198b78c

Browse files
neilddmitshur
authored andcommitted
[internal-branch.go1.16-vendor] http2: detect write-blocked PING frames
We start the PingTimeout timer before writing a PING frame. However, writing the frame can block indefinitely (either acquiring cc.wmu or writing to the conn), and the timer is not checked until after the frame is written. Move PING writes into a separate goroutine, so we can detect write-blocked connections. Updates golang/go#49076 Change-Id: Ifd67fa23799e750d02754e1fe5d32719f60faed4 Reviewed-on: https://go-review.googlesource.com/c/net/+/354389 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/net/+/357092 Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent 20ed279 commit 198b78c

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

http2/transport.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,19 +2594,24 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
25942594
}
25952595
cc.mu.Unlock()
25962596
}
2597-
cc.wmu.Lock()
2598-
if err := cc.fr.WritePing(false, p); err != nil {
2599-
cc.wmu.Unlock()
2600-
return err
2601-
}
2602-
if err := cc.bw.Flush(); err != nil {
2603-
cc.wmu.Unlock()
2604-
return err
2605-
}
2606-
cc.wmu.Unlock()
2597+
errc := make(chan error, 1)
2598+
go func() {
2599+
cc.wmu.Lock()
2600+
defer cc.wmu.Unlock()
2601+
if err := cc.fr.WritePing(false, p); err != nil {
2602+
errc <- err
2603+
return
2604+
}
2605+
if err := cc.bw.Flush(); err != nil {
2606+
errc <- err
2607+
return
2608+
}
2609+
}()
26072610
select {
26082611
case <-c:
26092612
return nil
2613+
case err := <-errc:
2614+
return err
26102615
case <-ctx.Done():
26112616
return ctx.Err()
26122617
case <-cc.readerDone:

http2/transport_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3494,6 +3494,36 @@ func TestTransportCloseAfterLostPing(t *testing.T) {
34943494
ct.run()
34953495
}
34963496

3497+
func TestTransportPingWriteBlocks(t *testing.T) {
3498+
st := newServerTester(t,
3499+
func(w http.ResponseWriter, r *http.Request) {},
3500+
optOnlyServer,
3501+
)
3502+
defer st.Close()
3503+
tr := &Transport{
3504+
TLSClientConfig: tlsConfigInsecure,
3505+
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
3506+
s, c := net.Pipe() // unbuffered, unlike a TCP conn
3507+
go func() {
3508+
// Read initial handshake frames.
3509+
// Without this, we block indefinitely in newClientConn,
3510+
// and never get to the point of sending a PING.
3511+
var buf [1024]byte
3512+
s.Read(buf[:])
3513+
}()
3514+
return c, nil
3515+
},
3516+
PingTimeout: 1 * time.Millisecond,
3517+
ReadIdleTimeout: 1 * time.Millisecond,
3518+
}
3519+
defer tr.CloseIdleConnections()
3520+
c := &http.Client{Transport: tr}
3521+
_, err := c.Get(st.ts.URL)
3522+
if err == nil {
3523+
t.Fatalf("Get = nil, want error")
3524+
}
3525+
}
3526+
34973527
func TestTransportPingWhenReading(t *testing.T) {
34983528
testCases := []struct {
34993529
name string

0 commit comments

Comments
 (0)