@@ -17,6 +17,7 @@ const {
1717} = require ( './util' )
1818const { WebsocketFrameSend } = require ( './frame' )
1919const { closeWebSocketConnection } = require ( './connection' )
20+ const { PerMessageDeflate } = require ( './permessage-deflate' )
2021
2122// This code was influenced by ws released under the MIT license.
2223// Copyright (c) 2011 Einar Otto Stangvik <[email protected] > @@ -33,10 +34,18 @@ class ByteParser extends Writable {
3334 #info = { }
3435 #fragments = [ ]
3536
36- constructor ( ws ) {
37+ /** @type {Map<string, PerMessageDeflate> } */
38+ #extensions
39+
40+ constructor ( ws , extensions ) {
3741 super ( )
3842
3943 this . ws = ws
44+ this . #extensions = extensions == null ? new Map ( ) : extensions
45+
46+ if ( this . #extensions. has ( 'permessage-deflate' ) ) {
47+ this . #extensions. set ( 'permessage-deflate' , new PerMessageDeflate ( extensions ) )
48+ }
4049 }
4150
4251 /**
@@ -91,7 +100,16 @@ class ByteParser extends Writable {
91100 // the negotiated extensions defines the meaning of such a nonzero
92101 // value, the receiving endpoint MUST _Fail the WebSocket
93102 // Connection_.
94- if ( rsv1 !== 0 || rsv2 !== 0 || rsv3 !== 0 ) {
103+ // This document allocates the RSV1 bit of the WebSocket header for
104+ // PMCEs and calls the bit the "Per-Message Compressed" bit. On a
105+ // WebSocket connection where a PMCE is in use, this bit indicates
106+ // whether a message is compressed or not.
107+ if ( rsv1 !== 0 && ! this . #extensions. has ( 'permessage-deflate' ) ) {
108+ failWebsocketConnection ( this . ws , 'Expected RSV1 to be clear.' )
109+ return
110+ }
111+
112+ if ( rsv2 !== 0 || rsv3 !== 0 ) {
95113 failWebsocketConnection ( this . ws , 'RSV1, RSV2, RSV3 must be clear' )
96114 return
97115 }
@@ -122,7 +140,7 @@ class ByteParser extends Writable {
122140 return
123141 }
124142
125- if ( isContinuationFrame ( opcode ) && this . #fragments. length === 0 ) {
143+ if ( isContinuationFrame ( opcode ) && this . #fragments. length === 0 && ! this . #info . compressed ) {
126144 failWebsocketConnection ( this . ws , 'Unexpected continuation frame' )
127145 return
128146 }
@@ -138,6 +156,7 @@ class ByteParser extends Writable {
138156
139157 if ( isTextBinaryFrame ( opcode ) ) {
140158 this . #info. binaryType = opcode
159+ this . #info. compressed = rsv1 !== 0
141160 }
142161
143162 this . #info. opcode = opcode
@@ -185,21 +204,50 @@ class ByteParser extends Writable {
185204
186205 if ( isControlFrame ( this . #info. opcode ) ) {
187206 this . #loop = this . parseControlFrame ( body )
207+ this . #state = parserStates . INFO
188208 } else {
189- this . #fragments. push ( body )
190-
191- // If the frame is not fragmented, a message has been received.
192- // If the frame is fragmented, it will terminate with a fin bit set
193- // and an opcode of 0 (continuation), therefore we handle that when
194- // parsing continuation frames, not here.
195- if ( ! this . #info. fragmented && this . #info. fin ) {
196- const fullMessage = Buffer . concat ( this . #fragments)
197- websocketMessageReceived ( this . ws , this . #info. binaryType , fullMessage )
198- this . #fragments. length = 0
209+ if ( ! this . #info. compressed ) {
210+ this . #fragments. push ( body )
211+
212+ // If the frame is not fragmented, a message has been received.
213+ // If the frame is fragmented, it will terminate with a fin bit set
214+ // and an opcode of 0 (continuation), therefore we handle that when
215+ // parsing continuation frames, not here.
216+ if ( ! this . #info. fragmented && this . #info. fin ) {
217+ const fullMessage = Buffer . concat ( this . #fragments)
218+ websocketMessageReceived ( this . ws , this . #info. binaryType , fullMessage )
219+ this . #fragments. length = 0
220+ }
221+
222+ this . #state = parserStates . INFO
223+ } else {
224+ this . #extensions. get ( 'permessage-deflate' ) . decompress ( body , this . #info. fin , ( error , data ) => {
225+ if ( error ) {
226+ closeWebSocketConnection ( this . ws , 1007 , error . message , error . message . length )
227+ return
228+ }
229+
230+ this . #fragments. push ( data )
231+
232+ if ( ! this . #info. fin ) {
233+ this . #state = parserStates . INFO
234+ this . #loop = true
235+ this . run ( callback )
236+ return
237+ }
238+
239+ websocketMessageReceived ( this . ws , this . #info. binaryType , Buffer . concat ( this . #fragments) )
240+
241+ this . #loop = true
242+ this . #state = parserStates . INFO
243+ this . run ( callback )
244+ this . #fragments. length = 0
245+ } )
246+
247+ this . #loop = false
248+ break
199249 }
200250 }
201-
202- this . #state = parserStates . INFO
203251 }
204252 }
205253 }
@@ -333,7 +381,6 @@ class ByteParser extends Writable {
333381 this . ws [ kReadyState ] = states . CLOSING
334382 this . ws [ kReceivedClose ] = true
335383
336- this . end ( )
337384 return false
338385 } else if ( opcode === opcodes . PING ) {
339386 // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
0 commit comments