@@ -12,11 +12,11 @@ const {
12
12
websocketMessageReceived,
13
13
utf8Decode,
14
14
isControlFrame,
15
- isContinuationFrame ,
16
- isTextBinaryFrame
15
+ isTextBinaryFrame ,
16
+ isContinuationFrame
17
17
} = require ( './util' )
18
18
const { WebsocketFrameSend } = require ( './frame' )
19
- const { CloseEvent } = require ( './events ' )
19
+ const { closeWebSocketConnection } = require ( './connection ' )
20
20
21
21
// This code was influenced by ws released under the MIT license.
22
22
// Copyright (c) 2011 Einar Otto Stangvik <[email protected] >
@@ -26,6 +26,7 @@ const { CloseEvent } = require('./events')
26
26
class ByteParser extends Writable {
27
27
#buffers = [ ]
28
28
#byteOffset = 0
29
+ #loop = false
29
30
30
31
#state = parserStates . INFO
31
32
@@ -45,6 +46,7 @@ class ByteParser extends Writable {
45
46
_write ( chunk , _ , callback ) {
46
47
this . #buffers. push ( chunk )
47
48
this . #byteOffset += chunk . length
49
+ this . #loop = true
48
50
49
51
this . run ( callback )
50
52
}
@@ -55,7 +57,7 @@ class ByteParser extends Writable {
55
57
* or not enough bytes are buffered to parse.
56
58
*/
57
59
run ( callback ) {
58
- while ( true ) {
60
+ while ( this . #loop ) {
59
61
if ( this . #state === parserStates . INFO ) {
60
62
// If there aren't enough bytes to parse the payload length, etc.
61
63
if ( this . #byteOffset < 2 ) {
@@ -67,6 +69,13 @@ class ByteParser extends Writable {
67
69
const opcode = buffer [ 0 ] & 0x0F
68
70
const masked = ( buffer [ 1 ] & 0x80 ) === 0x80
69
71
72
+ const fragmented = ! fin && opcode !== opcodes . CONTINUATION
73
+ const payloadLength = buffer [ 1 ] & 0x7F
74
+
75
+ const rsv1 = buffer [ 0 ] & 0x40
76
+ const rsv2 = buffer [ 0 ] & 0x20
77
+ const rsv3 = buffer [ 0 ] & 0x10
78
+
70
79
if ( ! isValidOpcode ( opcode ) ) {
71
80
failWebsocketConnection ( this . ws , 'Invalid opcode received' )
72
81
return callback ( )
@@ -77,22 +86,16 @@ class ByteParser extends Writable {
77
86
return callback ( )
78
87
}
79
88
80
- const rsv1 = ( buffer [ 0 ] & 0x40 ) !== 0
81
- const rsv2 = ( buffer [ 0 ] & 0x20 ) !== 0
82
- const rsv3 = ( buffer [ 0 ] & 0x10 ) !== 0
83
-
84
89
// MUST be 0 unless an extension is negotiated that defines meanings
85
90
// for non-zero values. If a nonzero value is received and none of
86
91
// the negotiated extensions defines the meaning of such a nonzero
87
92
// value, the receiving endpoint MUST _Fail the WebSocket
88
93
// Connection_.
89
- if ( rsv1 || rsv2 || rsv3 ) {
94
+ if ( rsv1 !== 0 || rsv2 !== 0 || rsv3 !== 0 ) {
90
95
failWebsocketConnection ( this . ws , 'RSV1, RSV2, RSV3 must be clear' )
91
96
return
92
97
}
93
98
94
- const fragmented = ! fin && opcode !== opcodes . CONTINUATION
95
-
96
99
if ( fragmented && ! isTextBinaryFrame ( opcode ) ) {
97
100
// Only text and binary frames can be fragmented
98
101
failWebsocketConnection ( this . ws , 'Invalid frame type was fragmented.' )
@@ -101,39 +104,27 @@ class ByteParser extends Writable {
101
104
102
105
// If we are already parsing a text/binary frame and do not receive either
103
106
// a continuation frame or close frame, fail the connection.
104
- if ( isTextBinaryFrame ( opcode ) && this . #info . opcode !== undefined ) {
107
+ if ( isTextBinaryFrame ( opcode ) && this . #fragments . length > 0 ) {
105
108
failWebsocketConnection ( this . ws , 'Expected continuation frame' )
106
109
return
107
110
}
108
111
109
- const payloadLength = buffer [ 1 ] & 0x7F
110
-
111
- if ( isControlFrame ( opcode ) ) {
112
- const loop = this . parseControlFrame ( callback , {
113
- header : buffer ,
114
- opcode,
115
- fragmented,
116
- payloadLength
117
- } )
112
+ if ( this . #info. fragmented && fragmented ) {
113
+ // A fragmented frame can't be fragmented itself
114
+ failWebsocketConnection ( this . ws , 'Fragmented frame exceeded 125 bytes.' )
115
+ return
116
+ }
118
117
119
- if ( loop ) {
120
- continue
121
- } else {
122
- return
123
- }
124
- } else if ( isContinuationFrame ( opcode ) ) {
125
- const loop = this . parseContinuationFrame ( callback , {
126
- header : buffer ,
127
- fin,
128
- fragmented,
129
- payloadLength
130
- } )
118
+ // "All control frames MUST have a payload length of 125 bytes or less
119
+ // and MUST NOT be fragmented."
120
+ if ( ( payloadLength > 125 || fragmented ) && isControlFrame ( opcode ) ) {
121
+ failWebsocketConnection ( this . ws , 'Control frame either too large or fragmented' )
122
+ return
123
+ }
131
124
132
- if ( loop ) {
133
- continue
134
- } else {
135
- return
136
- }
125
+ if ( isContinuationFrame ( opcode ) && this . #fragments. length === 0 ) {
126
+ failWebsocketConnection ( this . ws , 'Unexpected continuation frame' )
127
+ return
137
128
}
138
129
139
130
if ( payloadLength <= 125 ) {
@@ -145,16 +136,14 @@ class ByteParser extends Writable {
145
136
this . #state = parserStates . PAYLOADLENGTH_64
146
137
}
147
138
139
+ if ( isTextBinaryFrame ( opcode ) ) {
140
+ this . #info. binaryType = opcode
141
+ }
142
+
148
143
this . #info. opcode = opcode
149
144
this . #info. masked = masked
150
145
this . #info. fin = fin
151
146
this . #info. fragmented = fragmented
152
-
153
- if ( this . #info. fragmented && payloadLength > 125 ) {
154
- // A fragmented frame can't be fragmented itself
155
- failWebsocketConnection ( this . ws , 'Fragmented frame exceeded 125 bytes.' )
156
- return
157
- }
158
147
} else if ( this . #state === parserStates . PAYLOADLENGTH_16 ) {
159
148
if ( this . #byteOffset < 2 ) {
160
149
return callback ( )
@@ -189,42 +178,40 @@ class ByteParser extends Writable {
189
178
this . #state = parserStates . READ_DATA
190
179
} else if ( this . #state === parserStates . READ_DATA ) {
191
180
if ( this . #byteOffset < this . #info. payloadLength ) {
192
- // If there is still more data in this chunk that needs to be read
193
181
return callback ( )
194
- } else if ( this . #byteOffset >= this . #info. payloadLength ) {
195
- const body = this . consume ( this . #info. payloadLength )
182
+ }
183
+
184
+ const body = this . consume ( this . #info. payloadLength )
185
+
186
+ if ( isControlFrame ( this . #info. opcode ) ) {
187
+ this . #loop = this . parseControlFrame ( body )
188
+ } else {
196
189
this . #fragments. push ( body )
197
190
198
191
// If the frame is not fragmented, a message has been received.
199
192
// If the frame is fragmented, it will terminate with a fin bit set
200
193
// and an opcode of 0 (continuation), therefore we handle that when
201
194
// parsing continuation frames, not here.
202
- if ( ! this . #info. fragmented ) {
195
+ if ( ! this . #info. fragmented && this . #info . fin ) {
203
196
const fullMessage = Buffer . concat ( this . #fragments)
204
- websocketMessageReceived ( this . ws , this . #info. opcode , fullMessage )
205
- this . #info = { }
197
+ websocketMessageReceived ( this . ws , this . #info. binaryType , fullMessage )
206
198
this . #fragments. length = 0
207
199
}
208
-
209
- this . #state = parserStates . INFO
210
200
}
211
- }
212
201
213
- if ( this . #byteOffset === 0 && this . #info. payloadLength !== 0 ) {
214
- callback ( )
215
- break
202
+ this . #state = parserStates . INFO
216
203
}
217
204
}
218
205
}
219
206
220
207
/**
221
208
* Take n bytes from the buffered Buffers
222
209
* @param {number } n
223
- * @returns {Buffer|null }
210
+ * @returns {Buffer }
224
211
*/
225
212
consume ( n ) {
226
213
if ( n > this . #byteOffset) {
227
- return null
214
+ throw new Error ( 'Called consume() before buffers satiated.' )
228
215
} else if ( n === 0 ) {
229
216
return emptyBuffer
230
217
}
@@ -297,40 +284,25 @@ class ByteParser extends Writable {
297
284
298
285
/**
299
286
* Parses control frames.
300
- * @param {Buffer } data
301
- * @param {(err?: Error) => void } callback
302
- * @param {{ opcode: number, fragmented: boolean, payloadLength: number, header: Buffer } } info
287
+ * @param {Buffer } body
303
288
*/
304
- parseControlFrame ( callback , info ) {
305
- assert ( ! info . fragmented )
306
-
307
- if ( info . payloadLength > 125 ) {
308
- // Control frames can have a payload length of 125 bytes MAX
309
- failWebsocketConnection ( this . ws , 'Payload length for control frame exceeded 125 bytes.' )
310
- return false
311
- } else if ( this . #byteOffset < info . payloadLength ) {
312
- this . #buffers. unshift ( info . header )
313
- this . #byteOffset += 2
314
-
315
- callback ( )
316
- return false
317
- }
318
-
319
- const body = this . consume ( info . payloadLength )
289
+ parseControlFrame ( body ) {
290
+ const { opcode, payloadLength } = this . #info
320
291
321
- if ( info . opcode === opcodes . CLOSE ) {
322
- if ( info . payloadLength === 1 ) {
292
+ if ( opcode === opcodes . CLOSE ) {
293
+ if ( payloadLength === 1 ) {
323
294
failWebsocketConnection ( this . ws , 'Received close frame with a 1-byte body.' )
324
- return
295
+ return false
325
296
}
326
297
327
298
this . #info. closeInfo = this . parseCloseBody ( body )
328
299
329
300
if ( this . #info. closeInfo . error ) {
330
301
const { code, reason } = this . #info. closeInfo
331
302
332
- callback ( new CloseEvent ( 'close' , { wasClean : false , reason, code } ) )
333
- return
303
+ closeWebSocketConnection ( this . ws , code , reason , reason . length )
304
+ failWebsocketConnection ( this . ws , reason )
305
+ return false
334
306
}
335
307
336
308
if ( this . ws [ kSentClose ] !== sentCloseFrameState . SENT ) {
@@ -362,9 +334,8 @@ class ByteParser extends Writable {
362
334
this . ws [ kReceivedClose ] = true
363
335
364
336
this . end ( )
365
-
366
- return
367
- } else if ( info . opcode === opcodes . PING ) {
337
+ return false
338
+ } else if ( opcode === opcodes . PING ) {
368
339
// Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
369
340
// response, unless it already received a Close frame.
370
341
// A Pong frame sent in response to a Ping frame must have identical
@@ -381,12 +352,7 @@ class ByteParser extends Writable {
381
352
} )
382
353
}
383
354
}
384
-
385
- if ( this . #byteOffset <= 0 ) {
386
- callback ( )
387
- return false
388
- }
389
- } else if ( info . opcode === opcodes . PONG ) {
355
+ } else if ( opcode === opcodes . PONG ) {
390
356
// A Pong frame MAY be sent unsolicited. This serves as a
391
357
// unidirectional heartbeat. A response to an unsolicited Pong frame is
392
358
// not expected.
@@ -396,47 +362,6 @@ class ByteParser extends Writable {
396
362
payload : body
397
363
} )
398
364
}
399
-
400
- if ( this . #byteOffset <= 0 ) {
401
- callback ( )
402
- return false
403
- }
404
- }
405
-
406
- return true
407
- }
408
-
409
- /**
410
- * Parses continuation frames.
411
- * @param {Buffer } data
412
- * @param {(err?: Error) => void } callback
413
- * @param {{ fin: boolean, fragmented: boolean, payloadLength: number, header: Buffer } } info
414
- */
415
- parseContinuationFrame ( callback , info ) {
416
- // If we received a continuation frame before we started parsing another frame.
417
- if ( this . #info. opcode === undefined ) {
418
- failWebsocketConnection ( this . ws , 'Received unexpected continuation frame.' )
419
- return false
420
- } else if ( this . #byteOffset < info . payloadLength ) {
421
- this . #buffers. unshift ( info . header )
422
- this . #byteOffset += 2
423
-
424
- callback ( )
425
- return false
426
- }
427
-
428
- const body = this . consume ( info . payloadLength )
429
- this . #fragments. push ( body )
430
-
431
- // A fragmented message consists of a single frame with the FIN bit
432
- // clear and an opcode other than 0, followed by zero or more frames
433
- // with the FIN bit clear and the opcode set to 0, and terminated by
434
- // a single frame with the FIN bit set and an opcode of 0.
435
- if ( info . fin ) {
436
- const message = Buffer . concat ( this . #fragments)
437
- websocketMessageReceived ( this . ws , this . #info. opcode , message )
438
- this . #fragments. length = 0
439
- this . #info = { }
440
365
}
441
366
442
367
return true
0 commit comments