@@ -304,7 +304,8 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
304
304
c .Close (StatusProtocolError , "received invalid close payload" )
305
305
return xerrors .Errorf ("received invalid close payload: %w" , err )
306
306
}
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 ))
308
309
return c .closeErr
309
310
default :
310
311
panic (fmt .Sprintf ("websocket: unexpected control opcode: %#v" , h ))
@@ -786,9 +787,19 @@ func (c *Conn) writePong(p []byte) error {
786
787
return err
787
788
}
788
789
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.
790
802
//
791
- // It will write a WebSocket close frame with a timeout of 5 seconds.
792
803
// The connection can only be closed once. Additional calls to Close
793
804
// are no-ops.
794
805
//
@@ -823,31 +834,32 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error {
823
834
p , _ = ce .bytes ()
824
835
}
825
836
826
- err = c .writeClose (p , xerrors . Errorf ( "sent close frame: %w" , ce ) )
837
+ err = c .writeClose (p )
827
838
if err != nil {
828
839
return err
829
840
}
830
841
831
- if ! xerrors .Is (c .closeErr , ce ) {
832
- return c .closeErr
833
- }
842
+ const to = time .Second * 10
834
843
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
836
854
}
837
855
838
- func (c * Conn ) writeClose (p []byte , cerr error ) error {
856
+ func (c * Conn ) writeClose (p []byte ) error {
839
857
ctx , cancel := context .WithTimeout (context .Background (), time .Second * 5 )
840
858
defer cancel ()
841
859
842
860
// If this fails, the connection had to have died.
843
861
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
851
863
}
852
864
853
865
func init () {
0 commit comments