@@ -13,6 +13,7 @@ import (
1313 "net/http"
1414 "net/http/httptest"
1515 "os"
16+ "strings"
1617 "sync"
1718 "sync/atomic"
1819 "testing"
@@ -579,8 +580,17 @@ func TestProtocolErrors(t *testing.T) {
579580 opts = newOpts (t )
580581 }
581582 conn := setupRawConn (t , opts )
583+
584+ // write erronous or invalid frames to the connection, which should
585+ // cause the server to start the closing handshake
582586 mustWriteFrames (t , conn , ! tc .unmasked , tc .frames )
587+
588+ // read expected close frame
583589 mustReadCloseFrame (t , conn , tc .wantCloseCode , tc .wantCloseReason )
590+
591+ // server closes connection immediately on protocol errors,
592+ // without waiting for client to reply
593+ assertConnClosed (t , conn )
584594 })
585595 }
586596}
@@ -664,13 +674,15 @@ func TestCloseHandshake(t *testing.T) {
664674 go func () {
665675 defer wg .Done ()
666676 assert .NilError (t , server .Close ())
677+ assertConnClosed (t , serverConn )
667678 }()
668679 // client finishes closing handshake
669680 wg .Add (1 )
670681 go func () {
671682 defer wg .Done ()
672683 mustReadCloseFrame (t , clientConn , websocket .StatusNormalClosure , nil )
673684 mustWriteFrame (t , clientConn , true , websocket .NewCloseFrame (websocket .StatusNormalClosure , "" ))
685+ assertConnClosed (t , clientConn )
674686 }()
675687 wg .Wait ()
676688 })
@@ -691,6 +703,7 @@ func TestCloseHandshake(t *testing.T) {
691703 defer wg .Done ()
692704 mustWriteFrame (t , clientConn , true , websocket .NewCloseFrame (websocket .StatusNormalClosure , "" ))
693705 mustReadCloseFrame (t , clientConn , websocket .StatusNormalClosure , nil )
706+ assertConnClosed (t , serverConn )
694707 }()
695708 // server replies to close frame automatically during a ReadMessage
696709 // call, which returns io.EOF when the connection is closed.
@@ -700,6 +713,7 @@ func TestCloseHandshake(t *testing.T) {
700713 msg , err := server .ReadMessage (t .Context ())
701714 assert .Equal (t , msg , nil , "expected nil message" )
702715 assert .Error (t , err , io .EOF )
716+ assertConnClosed (t , clientConn )
703717 }()
704718 wg .Wait ()
705719 })
@@ -726,12 +740,14 @@ func TestCloseHandshake(t *testing.T) {
726740 elapsed := time .Since (start )
727741 assert .Error (t , err , errors .New ("websocket: close: read failed while waiting for reply: error reading frame header: read pipe: i/o timeout" ))
728742 assert .Equal (t , elapsed > closeTimeout , true , "close should have waited for timeout" )
743+ assertConnClosed (t , serverConn )
729744 }()
730745 // client gets the closing handshake message but ignores it
731746 wg .Add (1 )
732747 go func () {
733748 defer wg .Done ()
734749 mustReadCloseFrame (t , clientConn , websocket .StatusNormalClosure , nil )
750+ assertConnClosed (t , clientConn )
735751 }()
736752 wg .Wait ()
737753 })
@@ -757,6 +773,7 @@ func TestCloseHandshake(t *testing.T) {
757773 defer wg .Done ()
758774 err := server .Close ()
759775 assert .NilError (t , err )
776+ assertConnClosed (t , serverConn )
760777 }()
761778 // client receives initial closing handshake but sends additional data
762779 // before closing its end.
@@ -769,6 +786,7 @@ func TestCloseHandshake(t *testing.T) {
769786 websocket .NewFrame (websocket .OpcodeText , true , []byte ("ignore me" )),
770787 websocket .NewCloseFrame (websocket .StatusNormalClosure , "" ),
771788 })
789+ assertConnClosed (t , clientConn )
772790 }()
773791 wg .Wait ()
774792 })
@@ -812,6 +830,7 @@ func TestCloseHandshake(t *testing.T) {
812830 // client starts closing handshake
813831 mustWriteFrame (t , conn , true , websocket .NewCloseFrame (websocket .StatusNormalClosure , "" ))
814832 wg .Wait ()
833+ assertConnClosed (t , conn )
815834 })
816835
817836 t .Run ("server fails to initiate close" , func (t * testing.T ) {
@@ -821,7 +840,7 @@ func TestCloseHandshake(t *testing.T) {
821840
822841 var wg sync.WaitGroup
823842 wg .Add (1 )
824- setupRawConnWithHandler (t , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
843+ conn := setupRawConnWithHandler (t , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
825844 defer wg .Done ()
826845
827846 // Manually re-implement websocket.Accept in order to wrap the
@@ -848,6 +867,7 @@ func TestCloseHandshake(t *testing.T) {
848867 assert .Error (t , ws .Close (), writeErr )
849868 }))
850869 wg .Wait ()
870+ assertConnClosed (t , conn )
851871 })
852872}
853873
@@ -1085,6 +1105,30 @@ func mustReadCloseFrame(t *testing.T, r io.Reader, wantCode websocket.StatusCode
10851105func assertConnClosed (t testing.TB , conn net.Conn ) {
10861106 t .Helper ()
10871107 _ , err := conn .Read (make ([]byte , 1 ))
1108+ // testing this is super annoying, because we get at least four kinds of
1109+ // errors at various points in our test suite:
1110+ //
1111+ // - a *net.OpError with a non-deterministic strings containing random IP
1112+ // addresses and the substring "connection reset by peer"
1113+ // - an error string "connection reset by peer" (from net.Pipe conns)
1114+ // - a concrete io.EOF value
1115+ // - a concrete net.ErrClosed value
1116+ //
1117+ // The latter two are easy to test, the first one requires this ugly
1118+ // substring check.
1119+ assert .Equal (t , err != nil , true , "expected non-nil error when reading from closed connection" )
1120+
1121+ // first check ugly substring matches
1122+ for _ , substring := range []string {
1123+ "read/write on closed pipe" ,
1124+ "connection reset by peer" ,
1125+ } {
1126+ if strings .Contains (err .Error (), substring ) {
1127+ return
1128+ }
1129+ }
1130+
1131+ // finally check for concrete errors
10881132 assert .Error (t , err , io .EOF , net .ErrClosed )
10891133}
10901134
0 commit comments