@@ -2641,6 +2641,168 @@ describe('WebSocket', () => {
26412641 } ) ;
26422642 } ) ;
26432643
2644+ it ( 'handles a close frame received while compressing data' , ( done ) => {
2645+ const wss = new WebSocket . Server (
2646+ {
2647+ perMessageDeflate : true ,
2648+ port : 0
2649+ } ,
2650+ ( ) => {
2651+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` , {
2652+ perMessageDeflate : { threshold : 0 }
2653+ } ) ;
2654+
2655+ ws . on ( 'open' , ( ) => {
2656+ ws . _receiver . on ( 'conclude' , ( ) => {
2657+ assert . ok ( ws . _sender . _deflating ) ;
2658+ } ) ;
2659+
2660+ ws . send ( 'foo' ) ;
2661+ ws . send ( 'bar' ) ;
2662+ ws . send ( 'baz' ) ;
2663+ ws . send ( 'qux' ) ;
2664+ } ) ;
2665+ }
2666+ ) ;
2667+
2668+ wss . on ( 'connection' , ( ws ) => {
2669+ const messages = [ ] ;
2670+
2671+ ws . on ( 'message' , ( message ) => {
2672+ messages . push ( message ) ;
2673+ } ) ;
2674+
2675+ ws . on ( 'close' , ( code , reason ) => {
2676+ assert . strictEqual ( code , 1000 ) ;
2677+ assert . strictEqual ( reason , '' ) ;
2678+ assert . deepStrictEqual ( messages , [ 'foo' , 'bar' , 'baz' , 'qux' ] ) ;
2679+ wss . close ( done ) ;
2680+ } ) ;
2681+
2682+ ws . close ( 1000 ) ;
2683+ } ) ;
2684+ } ) ;
2685+
2686+ it ( "handles the socket 'end' event while compressing data" , ( done ) => {
2687+ const wss = new WebSocket . Server (
2688+ {
2689+ perMessageDeflate : true ,
2690+ port : 0
2691+ } ,
2692+ ( ) => {
2693+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` , {
2694+ perMessageDeflate : { threshold : 0 }
2695+ } ) ;
2696+
2697+ ws . on ( 'open' , ( ) => {
2698+ ws . _socket . on ( 'end' , ( ) => {
2699+ assert . ok ( ws . _sender . _deflating ) ;
2700+ } ) ;
2701+
2702+ ws . send ( 'foo' ) ;
2703+ ws . send ( 'bar' ) ;
2704+ ws . send ( 'baz' ) ;
2705+ ws . send ( 'qux' ) ;
2706+ } ) ;
2707+ }
2708+ ) ;
2709+
2710+ wss . on ( 'connection' , ( ws ) => {
2711+ const messages = [ ] ;
2712+
2713+ ws . on ( 'message' , ( message ) => {
2714+ messages . push ( message ) ;
2715+ } ) ;
2716+
2717+ ws . on ( 'close' , ( code , reason ) => {
2718+ assert . strictEqual ( code , 1000 ) ;
2719+ assert . strictEqual ( reason , '' ) ;
2720+ assert . deepStrictEqual ( messages , [ 'foo' , 'bar' , 'baz' , 'qux' ] ) ;
2721+ wss . close ( done ) ;
2722+ } ) ;
2723+
2724+ ws . close ( 1000 ) ;
2725+ ws . _socket . end ( ) ; // Ensure the socket is immediately half-closed.
2726+ } ) ;
2727+ } ) ;
2728+
2729+ describe ( '#close' , ( ) => {
2730+ it ( 'can be used while data is being decompressed (1/2)' , ( done ) => {
2731+ const wss = new WebSocket . Server (
2732+ {
2733+ perMessageDeflate : true ,
2734+ port : 0
2735+ } ,
2736+ ( ) => {
2737+ const messages = [ ] ;
2738+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
2739+
2740+ ws . on ( 'open' , ( ) => {
2741+ ws . _socket . on ( 'end' , ( ) => {
2742+ assert . strictEqual ( ws . _receiver . _state , 5 ) ;
2743+ } ) ;
2744+ } ) ;
2745+
2746+ ws . on ( 'message' , ( message ) => {
2747+ if ( messages . push ( message ) > 1 ) return ;
2748+
2749+ ws . close ( 1000 ) ;
2750+ } ) ;
2751+
2752+ ws . on ( 'close' , ( code , reason ) => {
2753+ assert . strictEqual ( code , 1000 ) ;
2754+ assert . strictEqual ( reason , '' ) ;
2755+ assert . deepStrictEqual ( messages , [ '' , '' , '' , '' ] ) ;
2756+ wss . close ( done ) ;
2757+ } ) ;
2758+ }
2759+ ) ;
2760+
2761+ wss . on ( 'connection' , ( ws ) => {
2762+ const buf = Buffer . from ( 'c10100c10100c10100c10100' , 'hex' ) ;
2763+ ws . _socket . write ( buf ) ;
2764+ } ) ;
2765+ } ) ;
2766+
2767+ it ( 'can be used while data is being decompressed (2/2)' , ( done ) => {
2768+ const wss = new WebSocket . Server (
2769+ {
2770+ perMessageDeflate : true ,
2771+ port : 0
2772+ } ,
2773+ ( ) => {
2774+ const messages = [ ] ;
2775+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
2776+
2777+ ws . on ( 'open' , ( ) => {
2778+ ws . _socket . on ( 'end' , ( ) => {
2779+ assert . strictEqual ( ws . _receiver . _state , 5 ) ;
2780+ } ) ;
2781+ } ) ;
2782+
2783+ ws . on ( 'message' , ( message ) => {
2784+ if ( messages . push ( message ) > 1 ) return ;
2785+
2786+ ws . close ( 1000 ) ;
2787+ ws . _socket . end ( ) ; // Ensure the socket is immediately half-closed.
2788+ } ) ;
2789+
2790+ ws . on ( 'close' , ( code , reason ) => {
2791+ assert . strictEqual ( code , 1000 ) ;
2792+ assert . strictEqual ( reason , '' ) ;
2793+ assert . deepStrictEqual ( messages , [ '' , '' , '' , '' ] ) ;
2794+ wss . close ( done ) ;
2795+ } ) ;
2796+ }
2797+ ) ;
2798+
2799+ wss . on ( 'connection' , ( ws ) => {
2800+ const buf = Buffer . from ( 'c10100c10100c10100c10100' , 'hex' ) ;
2801+ ws . _socket . write ( buf ) ;
2802+ } ) ;
2803+ } ) ;
2804+ } ) ;
2805+
26442806 describe ( '#send' , ( ) => {
26452807 it ( 'ignores the `compress` option if the extension is disabled' , ( done ) => {
26462808 const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
@@ -2771,4 +2933,60 @@ describe('WebSocket', () => {
27712933 } ) ;
27722934 } ) ;
27732935 } ) ;
2936+
2937+ describe ( 'Connection close edge cases' , ( ) => {
2938+ it ( 'cleanly shuts down after simultaneous errors' , ( done ) => {
2939+ let clientCloseEventEmitted = false ;
2940+ let serverClientCloseEventEmitted = false ;
2941+
2942+ const wss = new WebSocket . Server ( { port : 0 } , ( ) => {
2943+ const ws = new WebSocket ( `ws://localhost:${ wss . address ( ) . port } ` ) ;
2944+
2945+ ws . on ( 'error' , ( err ) => {
2946+ assert . ok ( err instanceof RangeError ) ;
2947+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2948+ assert . strictEqual (
2949+ err . message ,
2950+ 'Invalid WebSocket frame: invalid opcode 5'
2951+ ) ;
2952+
2953+ ws . on ( 'close' , ( code , reason ) => {
2954+ assert . strictEqual ( code , 1006 ) ;
2955+ assert . strictEqual ( reason , '' ) ;
2956+
2957+ clientCloseEventEmitted = true ;
2958+ if ( serverClientCloseEventEmitted ) wss . close ( done ) ;
2959+ } ) ;
2960+ } ) ;
2961+
2962+ ws . on ( 'open' , ( ) => {
2963+ // Write an invalid frame in both directions to trigger simultaneous
2964+ // failure.
2965+ const chunk = Buffer . from ( [ 0x85 , 0x00 ] ) ;
2966+
2967+ wss . clients . values ( ) . next ( ) . value . _socket . write ( chunk ) ;
2968+ ws . _socket . write ( chunk ) ;
2969+ } ) ;
2970+ } ) ;
2971+
2972+ wss . on ( 'connection' , ( ws ) => {
2973+ ws . on ( 'error' , ( err ) => {
2974+ assert . ok ( err instanceof RangeError ) ;
2975+ assert . strictEqual ( err . code , 'WS_ERR_INVALID_OPCODE' ) ;
2976+ assert . strictEqual (
2977+ err . message ,
2978+ 'Invalid WebSocket frame: invalid opcode 5'
2979+ ) ;
2980+
2981+ ws . on ( 'close' , ( code , reason ) => {
2982+ assert . strictEqual ( code , 1006 ) ;
2983+ assert . strictEqual ( reason , '' ) ;
2984+
2985+ serverClientCloseEventEmitted = true ;
2986+ if ( clientCloseEventEmitted ) wss . close ( done ) ;
2987+ } ) ;
2988+ } ) ;
2989+ } ) ;
2990+ } ) ;
2991+ } ) ;
27742992} ) ;
0 commit comments