@@ -430,6 +430,8 @@ describe('WebSocket', () => {
430430 describe ( 'Events' , ( ) => {
431431 it ( "emits an 'error' event if an error occurs" , ( done ) => {
432432 let clientCloseEventEmitted = false ;
433+ let serverClientCloseEventEmitted = false ;
434+
433435 const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
434436 const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
435437
@@ -442,19 +444,22 @@ describe('WebSocket', () => {
442444 ) ;
443445
444446 ws . on ( 'close' , ( code , reason ) => {
445- clientCloseEventEmitted = true ;
446447 assert . strictEqual ( code , 1006 ) ;
447448 assert . strictEqual ( reason , '' ) ;
449+
450+ clientCloseEventEmitted = true ;
451+ if ( serverClientCloseEventEmitted ) wss . close ( done ) ;
448452 } ) ;
449453 } ) ;
450454 } ) ;
451455
452456 wss . on ( 'connection' , ( ws ) => {
453457 ws . on ( 'close' , ( code , reason ) => {
454- assert . ok ( clientCloseEventEmitted ) ;
455458 assert . strictEqual ( code , 1002 ) ;
456459 assert . strictEqual ( reason , '' ) ;
457- wss . close ( done ) ;
460+
461+ serverClientCloseEventEmitted = true ;
462+ if ( clientCloseEventEmitted ) wss . close ( done ) ;
458463 } ) ;
459464
460465 ws . _socket . write ( Buffer . from ( [ 0x85 , 0x00 ] ) ) ;
@@ -1419,16 +1424,19 @@ describe('WebSocket', () => {
14191424 } ) ;
14201425
14211426 it ( 'honors the `mask` option' , ( done ) => {
1427+ let clientCloseEventEmitted = false ;
14221428 let serverClientCloseEventEmitted = false ;
1429+
14231430 const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
14241431 const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
14251432
14261433 ws . on ( 'open' , ( ) => ws . send ( 'hi' , { mask : false } ) ) ;
14271434 ws . on ( 'close' , ( code , reason ) => {
1428- assert . ok ( serverClientCloseEventEmitted ) ;
14291435 assert . strictEqual ( code , 1002 ) ;
14301436 assert . strictEqual ( reason , '' ) ;
1431- wss . close ( done ) ;
1437+
1438+ clientCloseEventEmitted = true ;
1439+ if ( serverClientCloseEventEmitted ) wss . close ( done ) ;
14321440 } ) ;
14331441 } ) ;
14341442
@@ -1450,9 +1458,11 @@ describe('WebSocket', () => {
14501458 ) ;
14511459
14521460 ws . on ( 'close' , ( code , reason ) => {
1453- serverClientCloseEventEmitted = true ;
14541461 assert . strictEqual ( code , 1006 ) ;
14551462 assert . strictEqual ( reason , '' ) ;
1463+
1464+ serverClientCloseEventEmitted = true ;
1465+ if ( clientCloseEventEmitted ) wss . close ( done ) ;
14561466 } ) ;
14571467 } ) ;
14581468 } ) ;
@@ -2760,4 +2770,118 @@ describe('WebSocket', () => {
27602770 } ) ;
27612771 } ) ;
27622772 } ) ;
2773+
2774+ describe ( 'Connection close edge cases' , ( ) => {
2775+ it ( 'closes cleanly after simultaneous errors (1/2)' , ( done ) => {
2776+ let clientCloseEventEmitted = false ;
2777+ let serverClientCloseEventEmitted = false ;
2778+
2779+ const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
2780+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
2781+
2782+ ws . on ( 'error' , ( err ) => {
2783+ assert . ok ( err instanceof RangeError ) ;
2784+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2785+ assert . strictEqual (
2786+ err . message ,
2787+ 'Invalid WebSocket frame: invalid opcode 5'
2788+ ) ;
2789+
2790+ ws . on ( 'close' , ( code , reason ) => {
2791+ assert . strictEqual ( code , 1006 ) ;
2792+ assert . strictEqual ( reason , '' ) ;
2793+
2794+ clientCloseEventEmitted = true ;
2795+ if ( serverClientCloseEventEmitted ) wss . close ( done ) ;
2796+ } ) ;
2797+ } ) ;
2798+
2799+ ws . on ( 'open' , ( ) => {
2800+ // Write an invalid frame in both directions to trigger simultaneous
2801+ // failure.
2802+ const chunk = Buffer . from ( [ 0x85 , 0x00 ] ) ;
2803+
2804+ wss . clients . values ( ) . next ( ) . value . _socket . write ( chunk ) ;
2805+ ws . _socket . write ( chunk ) ;
2806+ } ) ;
2807+ } ) ;
2808+
2809+ wss . on ( 'connection' , ( ws ) => {
2810+ ws . on ( 'error' , ( err ) => {
2811+ assert . ok ( err instanceof RangeError ) ;
2812+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2813+ assert . strictEqual (
2814+ err . message ,
2815+ 'Invalid WebSocket frame: invalid opcode 5'
2816+ ) ;
2817+
2818+ ws . on ( 'close' , ( code , reason ) => {
2819+ assert . strictEqual ( code , 1006 ) ;
2820+ assert . strictEqual ( reason , '' ) ;
2821+
2822+ serverClientCloseEventEmitted = true ;
2823+ if ( clientCloseEventEmitted ) wss . close ( done ) ;
2824+ } ) ;
2825+ } ) ;
2826+ } ) ;
2827+ } ) ;
2828+
2829+ it ( 'closes cleanly after simultaneous errors (2/2)' , ( done ) => {
2830+ let clientCloseEventEmitted = false ;
2831+ let serverClientCloseEventEmitted = false ;
2832+
2833+ const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
2834+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
2835+
2836+ ws . on ( 'error' , ( err ) => {
2837+ assert . ok ( err instanceof RangeError ) ;
2838+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2839+ assert . strictEqual (
2840+ err . message ,
2841+ 'Invalid WebSocket frame: invalid opcode 5'
2842+ ) ;
2843+
2844+ ws . on ( 'close' , ( code , reason ) => {
2845+ assert . strictEqual ( code , 1006 ) ;
2846+ assert . strictEqual ( reason , '' ) ;
2847+
2848+ clientCloseEventEmitted = true ;
2849+ if ( serverClientCloseEventEmitted ) wss . close ( done ) ;
2850+ } ) ;
2851+ } ) ;
2852+
2853+ ws . on ( 'open' , ( ) => {
2854+ // Write an invalid frame in both directions and change the
2855+ // `readyState` to `WebSocket.CLOSING`.
2856+ const chunk = Buffer . from ( [ 0x85 , 0x00 ] ) ;
2857+ const serverWs = wss . clients . values ( ) . next ( ) . value ;
2858+
2859+ serverWs . _socket . write ( chunk ) ;
2860+ serverWs . close ( ) ;
2861+
2862+ ws . _socket . write ( chunk ) ;
2863+ ws . close ( ) ;
2864+ } ) ;
2865+ } ) ;
2866+
2867+ wss . on ( 'connection' , ( ws ) => {
2868+ ws . on ( 'error' , ( err ) => {
2869+ assert . ok ( err instanceof RangeError ) ;
2870+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2871+ assert . strictEqual (
2872+ err . message ,
2873+ 'Invalid WebSocket frame: invalid opcode 5'
2874+ ) ;
2875+
2876+ ws . on ( 'close' , ( code , reason ) => {
2877+ assert . strictEqual ( code , 1006 ) ;
2878+ assert . strictEqual ( reason , '' ) ;
2879+
2880+ serverClientCloseEventEmitted = true ;
2881+ if ( clientCloseEventEmitted ) wss . close ( done ) ;
2882+ } ) ;
2883+ } ) ;
2884+ } ) ;
2885+ } ) ;
2886+ } ) ;
27632887} ) ;
0 commit comments