@@ -304,7 +304,8 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
304304 c .Close (StatusProtocolError , "received invalid close payload" )
305305 return xerrors .Errorf ("received invalid close payload: %w" , err )
306306 }
307- c .writeClose (b , xerrors .Errorf ("received close frame: %w" , ce ))
307+ c .writeClose (b )
308+ c .close (xerrors .Errorf ("received close frame: %w" , ce ))
308309 return c .closeErr
309310 default :
310311 panic (fmt .Sprintf ("websocket: unexpected control opcode: %#v" , h ))
@@ -786,9 +787,19 @@ func (c *Conn) writePong(p []byte) error {
786787 return err
787788}
788789
789- // Close closes the WebSocket connection with the given status code and reason.
790+ // Close performs the WebSocket close handshake on the connection with the given
791+ // status code and reason.
792+ //
793+ // First, it will write a WebSocket close frame with a timeout of 5 seconds.
794+ // Next, it will wait a maximum of 10 seconds for a Close frame from the peer.
795+ // You must be reading from the connection in another goroutine for the Close
796+ // frame to be read.
797+ // If the peer does not send a Close frame in the next 10 seconds, the connection
798+ // will be forcefully closed.
799+ //
800+ // The returned error will be a CloseError accessible with xerrors.As if a close frame
801+ // was received.
790802//
791- // It will write a WebSocket close frame with a timeout of 5 seconds.
792803// The connection can only be closed once. Additional calls to Close
793804// are no-ops.
794805//
@@ -823,31 +834,32 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error {
823834 p , _ = ce .bytes ()
824835 }
825836
826- err = c .writeClose (p , xerrors . Errorf ( "sent close frame: %w" , ce ) )
837+ err = c .writeClose (p )
827838 if err != nil {
828839 return err
829840 }
830841
831- if ! xerrors .Is (c .closeErr , ce ) {
832- return c .closeErr
833- }
842+ const to = time .Second * 10
834843
835- return nil
844+ t := time .NewTimer (to )
845+ defer t .Stop ()
846+
847+ // Wait for the close frame to be read.
848+ select {
849+ case <- c .closed :
850+ case <- t .C :
851+ c .close (xerrors .Errorf ("sent close frame (%v) but did not receive close frame from peer in %v" , ce , to ))
852+ }
853+ return c .closeErr
836854}
837855
838- func (c * Conn ) writeClose (p []byte , cerr error ) error {
856+ func (c * Conn ) writeClose (p []byte ) error {
839857 ctx , cancel := context .WithTimeout (context .Background (), time .Second * 5 )
840858 defer cancel ()
841859
842860 // If this fails, the connection had to have died.
843861 err := c .writeControl (ctx , opClose , p )
844- if err != nil {
845- return err
846- }
847-
848- c .close (cerr )
849-
850- return nil
862+ return err
851863}
852864
853865func init () {
0 commit comments