Skip to content

Commit ede18a5

Browse files
committed
don't block on connection close
1 parent 43cd707 commit ede18a5

File tree

4 files changed

+22
-17
lines changed

4 files changed

+22
-17
lines changed

const.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ func (e *GoAwayError) Temporary() bool {
4545

4646
func (e *GoAwayError) Is(target error) bool {
4747
// to maintain compatibility with errors returned by previous versions
48-
if e.Remote && target == ErrRemoteGoAway {
48+
if e.Remote && target == ErrRemoteGoAwayNormal {
4949
return true
5050
} else if !e.Remote && target == ErrSessionShutdown {
5151
return true
52+
} else if target == ErrStreamReset {
53+
// A GoAway on a connection also resets all the streams.
54+
return true
5255
}
5356

5457
if err, ok := target.(*GoAwayError); ok {
@@ -111,8 +114,8 @@ var (
111114
// ErrUnexpectedFlag is set when we get an unexpected flag
112115
ErrUnexpectedFlag = &Error{msg: "unexpected flag"}
113116

114-
// ErrRemoteGoAway is used when we get a go away from the other side
115-
ErrRemoteGoAway = &GoAwayError{Remote: true, ErrorCode: goAwayNormal}
117+
// ErrRemoteGoAwayNormal is used when we get a go away from the other side
118+
ErrRemoteGoAwayNormal = &GoAwayError{Remote: true, ErrorCode: goAwayNormal}
116119

117120
// ErrStreamReset is sent if a stream is reset. This can happen
118121
// if the backlog is exceeded, or if there was a remote GoAway.

session.go

+12-11
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ var nullMemoryManager = &nullMemoryManagerImpl{}
4646
type Session struct {
4747
rtt int64 // to be accessed atomically, in nanoseconds
4848

49-
// remoteGoAway indicates the remote side does
49+
// remoteGoAwayNormal indicates the remote side does
5050
// not want futher connections. Must be first for alignment.
51-
remoteGoAway int32
51+
remoteGoAwayNormal int32
5252

5353
// localGoAway indicates that we should stop
5454
// accepting futher connections. Must be first for alignment.
@@ -205,8 +205,8 @@ func (s *Session) OpenStream(ctx context.Context) (*Stream, error) {
205205
if s.IsClosed() {
206206
return nil, s.shutdownErr
207207
}
208-
if atomic.LoadInt32(&s.remoteGoAway) == 1 {
209-
return nil, ErrRemoteGoAway
208+
if atomic.LoadInt32(&s.remoteGoAwayNormal) == 1 {
209+
return nil, ErrRemoteGoAwayNormal
210210
}
211211

212212
// Block if we have too many inflight SYNs
@@ -285,15 +285,15 @@ func (s *Session) AcceptStream() (*Stream, error) {
285285
}
286286
}
287287

288-
// Close is used to close the session and all streams.
289-
// Attempts to send a GoAway before closing the connection. The GoAway may not actually be sent depending on the
290-
// semantics of the underlying net.Conn. For TCP connections, it may be dropped depending on LINGER value or
291-
// if there's unread data in the kernel receive buffer.
288+
// Close is used to close the session and all streams. It doesn't send a GoAway before
289+
// closing the connection.
292290
func (s *Session) Close() error {
293291
return s.close(ErrSessionShutdown, false, goAwayNormal)
294292
}
295293

296294
// CloseWithError is used to close the session and all streams after sending a GoAway message with errCode.
295+
// Blocks for ConnectionWriteTimeout to write the GoAway message.
296+
//
297297
// The GoAway may not actually be sent depending on the semantics of the underlying net.Conn.
298298
// For TCP connections, it may be dropped depending on LINGER value or if there's unread data in the kernel
299299
// receive buffer.
@@ -315,7 +315,8 @@ func (s *Session) close(shutdownErr error, sendGoAway bool, errCode uint32) erro
315315
close(s.shutdownCh)
316316
s.stopKeepalive()
317317

318-
if sendGoAway {
318+
// Only send GoAway if we have an error code.
319+
if sendGoAway && errCode != goAwayNormal {
319320
// wait for write loop to exit
320321
// We need to write the current frame completely before sending a goaway.
321322
// This will wait for at most s.config.ConnectionWriteTimeout
@@ -334,7 +335,7 @@ func (s *Session) close(shutdownErr error, sendGoAway bool, errCode uint32) erro
334335
s.streamLock.Lock()
335336
defer s.streamLock.Unlock()
336337
for id, stream := range s.streams {
337-
stream.forceClose(fmt.Errorf("%w: connection closed: %w", ErrStreamReset, s.shutdownErr))
338+
stream.forceClose(s.shutdownErr)
338339
delete(s.streams, id)
339340
stream.memorySpan.Done()
340341
}
@@ -814,7 +815,7 @@ func (s *Session) handleGoAway(hdr header) error {
814815
code := hdr.Length()
815816
switch code {
816817
case goAwayNormal:
817-
atomic.SwapInt32(&s.remoteGoAway, 1)
818+
atomic.SwapInt32(&s.remoteGoAwayNormal, 1)
818819
// Don't close connection on normal go away. Let the existing streams
819820
// complete gracefully.
820821
return nil

session_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ func TestGoAway(t *testing.T) {
651651
switch err {
652652
case nil:
653653
s.Close()
654-
case ErrRemoteGoAway:
654+
case ErrRemoteGoAwayNormal:
655655
return
656656
default:
657657
t.Fatalf("err: %v", err)

stream.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ func (s *Stream) CloseWrite() error {
310310
return nil
311311
case halfReset:
312312
s.stateLock.Unlock()
313-
return ErrStreamReset
313+
return s.writeErr
314314
default:
315315
panic("invalid state")
316316
}
@@ -331,7 +331,8 @@ func (s *Stream) CloseWrite() error {
331331
return err
332332
}
333333

334-
// CloseRead is used to close the stream for writing.
334+
// CloseRead is used to close the stream for reading.
335+
// Note: Remote is not notified.
335336
func (s *Stream) CloseRead() error {
336337
cleanup := false
337338
s.stateLock.Lock()

0 commit comments

Comments
 (0)