Close kicks off the shutdown process of the transport. This should be called only once on a transport. Once it is called, the transport should not be accessed anymore.
(err error)
| 1008 | // only once on a transport. Once it is called, the transport should not be |
| 1009 | // accessed anymore. |
| 1010 | func (t *http2Client) Close(err error) { |
| 1011 | t.conn.SetWriteDeadline(time.Now().Add(time.Second * 10)) |
| 1012 | // For background on the deadline value chosen here, see |
| 1013 | // https://github.com/grpc/grpc-go/issues/8425#issuecomment-3057938248 . |
| 1014 | t.conn.SetReadDeadline(time.Now().Add(time.Second)) |
| 1015 | t.mu.Lock() |
| 1016 | // Make sure we only close once. |
| 1017 | if t.state == closing { |
| 1018 | t.mu.Unlock() |
| 1019 | return |
| 1020 | } |
| 1021 | if t.logger.V(logLevel) { |
| 1022 | t.logger.Infof("Closing: %v", err) |
| 1023 | } |
| 1024 | // Call t.onClose ASAP to prevent the client from attempting to create new |
| 1025 | // streams. |
| 1026 | if t.state != draining { |
| 1027 | t.onClose(GoAwayInfo{Reason: GoAwayInvalid, GoAwayCode: http2.ErrCodeNo, Err: err}) |
| 1028 | } |
| 1029 | t.state = closing |
| 1030 | streams := t.activeStreams |
| 1031 | t.activeStreams = nil |
| 1032 | if t.kpDormant { |
| 1033 | // If the keepalive goroutine is blocked on this condition variable, we |
| 1034 | // should unblock it so that the goroutine eventually exits. |
| 1035 | t.kpDormancyCond.Signal() |
| 1036 | } |
| 1037 | // Append info about previous goaways if there were any, since this may be important |
| 1038 | // for understanding the root cause for this connection to be closed. |
| 1039 | goAwayDebugMessage := t.goAwayDebugMessage |
| 1040 | t.mu.Unlock() |
| 1041 | |
| 1042 | // Per HTTP/2 spec, a GOAWAY frame must be sent before closing the |
| 1043 | // connection. See https://httpwg.org/specs/rfc7540.html#GOAWAY. It |
| 1044 | // also waits for loopyWriter to be closed with a timer to avoid the |
| 1045 | // long blocking in case the connection is blackholed, i.e. TCP is |
| 1046 | // just stuck. |
| 1047 | t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte("client transport shutdown"), closeConn: err}) |
| 1048 | timer := time.NewTimer(goAwayLoopyWriterTimeout) |
| 1049 | defer timer.Stop() |
| 1050 | select { |
| 1051 | case <-t.writerDone: // success |
| 1052 | case <-timer.C: |
| 1053 | t.logger.Infof("Failed to write a GOAWAY frame as part of connection close after %s. Giving up and closing the transport.", goAwayLoopyWriterTimeout) |
| 1054 | } |
| 1055 | t.cancel() |
| 1056 | t.conn.Close() |
| 1057 | // Waits for the reader and keepalive goroutines to exit before returning to |
| 1058 | // ensure all resources are cleaned up before Close can return. |
| 1059 | <-t.readerDone |
| 1060 | if t.keepaliveEnabled { |
| 1061 | <-t.keepaliveDone |
| 1062 | } |
| 1063 | channelz.RemoveEntry(t.channelz.ID) |
| 1064 | var st *status.Status |
| 1065 | if len(goAwayDebugMessage) > 0 { |
| 1066 | st = status.Newf(codes.Unavailable, "closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage) |
| 1067 | err = st.Err() |
no test coverage detected