@@ -86,9 +86,9 @@ type http2Client struct {
8686 writerDone chan struct {} // sync point to enable testing.
8787 // goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor)
8888 // that the server sent GoAway on this transport.
89- goAway chan struct {}
90-
91- framer * framer
89+ goAway chan struct {}
90+ keepaliveDone chan struct {} // Closed when the keepalive goroutine exits.
91+ framer * framer
9292 // controlBuf delivers all the control related tasks (e.g., window
9393 // updates, reset streams, and various settings) to the controller.
9494 // Do not access controlBuf with mu held.
@@ -335,6 +335,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
335335 readerDone : make (chan struct {}),
336336 writerDone : make (chan struct {}),
337337 goAway : make (chan struct {}),
338+ keepaliveDone : make (chan struct {}),
338339 framer : newFramer (conn , writeBufSize , readBufSize , opts .SharedWriteBuffer , maxHeaderListSize ),
339340 fc : & trInFlow {limit : uint32 (icwz )},
340341 scheme : scheme ,
@@ -1029,6 +1030,12 @@ func (t *http2Client) Close(err error) {
10291030 }
10301031 t .cancel ()
10311032 t .conn .Close ()
1033+ // Waits for the reader and keepalive goroutines to exit before returning to
1034+ // ensure all resources are cleaned up before Close can return.
1035+ <- t .readerDone
1036+ if t .keepaliveEnabled {
1037+ <- t .keepaliveDone
1038+ }
10321039 channelz .RemoveEntry (t .channelz .ID )
10331040 var st * status.Status
10341041 if len (goAwayDebugMessage ) > 0 {
@@ -1316,11 +1323,11 @@ func (t *http2Client) handlePing(f *http2.PingFrame) {
13161323 t .controlBuf .put (pingAck )
13171324}
13181325
1319- func (t * http2Client ) handleGoAway (f * http2.GoAwayFrame ) {
1326+ func (t * http2Client ) handleGoAway (f * http2.GoAwayFrame ) error {
13201327 t .mu .Lock ()
13211328 if t .state == closing {
13221329 t .mu .Unlock ()
1323- return
1330+ return nil
13241331 }
13251332 if f .ErrCode == http2 .ErrCodeEnhanceYourCalm && string (f .DebugData ()) == "too_many_pings" {
13261333 // When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug
@@ -1332,8 +1339,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
13321339 id := f .LastStreamID
13331340 if id > 0 && id % 2 == 0 {
13341341 t .mu .Unlock ()
1335- t .Close (connectionErrorf (true , nil , "received goaway with non-zero even-numbered stream id: %v" , id ))
1336- return
1342+ return connectionErrorf (true , nil , "received goaway with non-zero even-numbered stream id: %v" , id )
13371343 }
13381344 // A client can receive multiple GoAways from the server (see
13391345 // https://github.com/grpc/grpc-go/issues/1387). The idea is that the first
@@ -1350,8 +1356,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
13501356 // If there are multiple GoAways the first one should always have an ID greater than the following ones.
13511357 if id > t .prevGoAwayID {
13521358 t .mu .Unlock ()
1353- t .Close (connectionErrorf (true , nil , "received goaway with stream id: %v, which exceeds stream id of previous goaway: %v" , id , t .prevGoAwayID ))
1354- return
1359+ return connectionErrorf (true , nil , "received goaway with stream id: %v, which exceeds stream id of previous goaway: %v" , id , t .prevGoAwayID )
13551360 }
13561361 default :
13571362 t .setGoAwayReason (f )
@@ -1375,8 +1380,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
13751380 t .prevGoAwayID = id
13761381 if len (t .activeStreams ) == 0 {
13771382 t .mu .Unlock ()
1378- t .Close (connectionErrorf (true , nil , "received goaway and there are no active streams" ))
1379- return
1383+ return connectionErrorf (true , nil , "received goaway and there are no active streams" )
13801384 }
13811385
13821386 streamsToClose := make ([]* Stream , 0 )
@@ -1393,6 +1397,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
13931397 for _ , stream := range streamsToClose {
13941398 t .closeStream (stream , errStreamDrain , false , http2 .ErrCodeNo , statusGoAway , nil , false )
13951399 }
1400+ return nil
13961401}
13971402
13981403// setGoAwayReason sets the value of t.goAwayReason based
@@ -1628,7 +1633,13 @@ func (t *http2Client) readServerPreface() error {
16281633// network connection. If the server preface is not read successfully, an
16291634// error is pushed to errCh; otherwise errCh is closed with no error.
16301635func (t * http2Client ) reader (errCh chan <- error ) {
1631- defer close (t .readerDone )
1636+ var errClose error
1637+ defer func () {
1638+ close (t .readerDone )
1639+ if errClose != nil {
1640+ t .Close (errClose )
1641+ }
1642+ }()
16321643
16331644 if err := t .readServerPreface (); err != nil {
16341645 errCh <- err
@@ -1669,7 +1680,7 @@ func (t *http2Client) reader(errCh chan<- error) {
16691680 continue
16701681 }
16711682 // Transport error.
1672- t . Close ( connectionErrorf (true , err , "error reading from server: %v" , err ) )
1683+ errClose = connectionErrorf (true , err , "error reading from server: %v" , err )
16731684 return
16741685 }
16751686 switch frame := frame .(type ) {
@@ -1684,7 +1695,7 @@ func (t *http2Client) reader(errCh chan<- error) {
16841695 case * http2.PingFrame :
16851696 t .handlePing (frame )
16861697 case * http2.GoAwayFrame :
1687- t .handleGoAway (frame )
1698+ errClose = t .handleGoAway (frame )
16881699 case * http2.WindowUpdateFrame :
16891700 t .handleWindowUpdate (frame )
16901701 default :
@@ -1697,6 +1708,13 @@ func (t *http2Client) reader(errCh chan<- error) {
16971708
16981709// keepalive running in a separate goroutine makes sure the connection is alive by sending pings.
16991710func (t * http2Client ) keepalive () {
1711+ var err error
1712+ defer func () {
1713+ close (t .keepaliveDone )
1714+ if err != nil {
1715+ t .Close (err )
1716+ }
1717+ }()
17001718 p := & ping {data : [8 ]byte {}}
17011719 // True iff a ping has been sent, and no data has been received since then.
17021720 outstandingPing := false
@@ -1720,7 +1738,7 @@ func (t *http2Client) keepalive() {
17201738 continue
17211739 }
17221740 if outstandingPing && timeoutLeft <= 0 {
1723- t . Close ( connectionErrorf (true , nil , "keepalive ping failed to receive ACK within timeout" ) )
1741+ err = connectionErrorf (true , nil , "keepalive ping failed to receive ACK within timeout" )
17241742 return
17251743 }
17261744 t .mu .Lock ()
0 commit comments