@@ -284,8 +284,14 @@ func (s *Session) AcceptStream() (*Stream, error) {
284284}
285285
286286// Close is used to close the session and all streams.
287- // Attempts to send a GoAway before closing the connection.
287+ // Attempts to send a GoAway before closing the connection. The GoAway may not actually be sent depending on the
288+ // semantics of the underlying net.Conn. For TCP connections, it may be dropped depending on LINGER value or
289+ // if there's unread data in the kernel receive buffer.
288290func (s * Session ) Close () error {
291+ return s .close (true , goAwayNormal )
292+ }
293+
294+ func (s * Session ) close (sendGoAway bool , errCode uint32 ) error {
289295 s .shutdownLock .Lock ()
290296 defer s .shutdownLock .Unlock ()
291297
@@ -297,10 +303,21 @@ func (s *Session) Close() error {
297303 s .shutdownErr = ErrSessionShutdown
298304 }
299305 close (s .shutdownCh )
300- s .conn .Close ()
301306 s .stopKeepalive ()
302- <- s .recvDoneCh
307+
308+ // wait for write loop to exit
309+ _ = s .conn .SetWriteDeadline (time .Now ().Add (- 1 * time .Hour )) // if SetWriteDeadline errored, any blocked writes will be unblocked
303310 <- s .sendDoneCh
311+ if sendGoAway {
312+ ga := s .goAway (errCode )
313+ if err := s .conn .SetWriteDeadline (time .Now ().Add (goAwayWaitTime )); err == nil {
314+ _ , _ = s .conn .Write (ga [:]) // there's nothing we can do on error here
315+ }
316+ }
317+
318+ s .conn .SetWriteDeadline (time.Time {})
319+ s .conn .Close ()
320+ <- s .recvDoneCh
304321
305322 s .streamLock .Lock ()
306323 defer s .streamLock .Unlock ()
@@ -320,7 +337,7 @@ func (s *Session) exitErr(err error) {
320337 s .shutdownErr = err
321338 }
322339 s .shutdownLock .Unlock ()
323- s .Close ( )
340+ s .close ( false , 0 )
324341}
325342
326343// GoAway can be used to prevent accepting further
0 commit comments