@@ -41,9 +41,10 @@ type Conn struct {
4141 closer io.Closer
4242 client bool
4343
44- closeOnce sync.Once
45- closeErr error
46- closed chan struct {}
44+ closeOnce sync.Once
45+ closeErrOnce sync.Once
46+ closeErr error
47+ closed chan struct {}
4748
4849 // writeMsgLock is acquired to write a data message.
4950 writeMsgLock chan struct {}
@@ -115,11 +116,17 @@ func (c *Conn) Subprotocol() string {
115116 return c .subprotocol
116117}
117118
119+ func (c * Conn ) setCloseErr (err error ) {
120+ c .closeErrOnce .Do (func () {
121+ c .closeErr = xerrors .Errorf ("websocket closed: %w" , err )
122+ })
123+ }
124+
118125func (c * Conn ) close (err error ) {
119126 c .closeOnce .Do (func () {
120127 runtime .SetFinalizer (c , nil )
121128
122- c .closeErr = xerrors . Errorf ( "websocket closed: %w" , err )
129+ c .setCloseErr ( err )
123130 close (c .closed )
124131
125132 // Have to close after c.closed is closed to ensure any goroutine that wakes up
@@ -304,7 +311,11 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
304311 c .Close (StatusProtocolError , "received invalid close payload" )
305312 return xerrors .Errorf ("received invalid close payload: %w" , err )
306313 }
307- c .writeClose (b , xerrors .Errorf ("received close frame: %w" , ce ))
314+ // This ensures the closeErr of the Conn is always the received CloseError
315+ // in case the echo close frame write fails.
316+ // See https://github.com/nhooyr/websocket/issues/109
317+ c .setCloseErr (xerrors .Errorf ("received close frame: %w" , ce ))
318+ c .writeClose (b , nil )
308319 return c .closeErr
309320 default :
310321 panic (fmt .Sprintf ("websocket: unexpected control opcode: %#v" , h ))
0 commit comments