@@ -17,6 +17,7 @@ const {
17
17
} = require ( './util' )
18
18
const { WebsocketFrameSend } = require ( './frame' )
19
19
const { closeWebSocketConnection } = require ( './connection' )
20
+ const { PerMessageDeflate } = require ( './permessage-deflate' )
20
21
21
22
// This code was influenced by ws released under the MIT license.
22
23
// Copyright (c) 2011 Einar Otto Stangvik <[email protected] >
@@ -33,10 +34,18 @@ class ByteParser extends Writable {
33
34
#info = { }
34
35
#fragments = [ ]
35
36
36
- constructor ( ws ) {
37
+ /** @type {Map<string, PerMessageDeflate> } */
38
+ #extensions
39
+
40
+ constructor ( ws , extensions ) {
37
41
super ( )
38
42
39
43
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
+ }
40
49
}
41
50
42
51
/**
@@ -91,7 +100,16 @@ class ByteParser extends Writable {
91
100
// the negotiated extensions defines the meaning of such a nonzero
92
101
// value, the receiving endpoint MUST _Fail the WebSocket
93
102
// 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 ) {
95
113
failWebsocketConnection ( this . ws , 'RSV1, RSV2, RSV3 must be clear' )
96
114
return
97
115
}
@@ -122,7 +140,7 @@ class ByteParser extends Writable {
122
140
return
123
141
}
124
142
125
- if ( isContinuationFrame ( opcode ) && this . #fragments. length === 0 ) {
143
+ if ( isContinuationFrame ( opcode ) && this . #fragments. length === 0 && ! this . #info . compressed ) {
126
144
failWebsocketConnection ( this . ws , 'Unexpected continuation frame' )
127
145
return
128
146
}
@@ -138,6 +156,7 @@ class ByteParser extends Writable {
138
156
139
157
if ( isTextBinaryFrame ( opcode ) ) {
140
158
this . #info. binaryType = opcode
159
+ this . #info. compressed = rsv1 !== 0
141
160
}
142
161
143
162
this . #info. opcode = opcode
@@ -185,21 +204,50 @@ class ByteParser extends Writable {
185
204
186
205
if ( isControlFrame ( this . #info. opcode ) ) {
187
206
this . #loop = this . parseControlFrame ( body )
207
+ this . #state = parserStates . INFO
188
208
} 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
199
249
}
200
250
}
201
-
202
- this . #state = parserStates . INFO
203
251
}
204
252
}
205
253
}
@@ -333,7 +381,6 @@ class ByteParser extends Writable {
333
381
this . ws [ kReadyState ] = states . CLOSING
334
382
this . ws [ kReceivedClose ] = true
335
383
336
- this . end ( )
337
384
return false
338
385
} else if ( opcode === opcodes . PING ) {
339
386
// Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
0 commit comments