@@ -5,7 +5,7 @@ const assert = require('node:assert')
5
5
const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require ( './constants' )
6
6
const { kReadyState, kSentClose, kResponse, kReceivedClose } = require ( './symbols' )
7
7
const { channels } = require ( '../../core/diagnostics' )
8
- const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived, utf8Decode, isControlFrame } = require ( './util' )
8
+ const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived, utf8Decode, isControlFrame, isContinuationFrame } = require ( './util' )
9
9
const { WebsocketFrameSend } = require ( './frame' )
10
10
const { CloseEvent } = require ( './events' )
11
11
@@ -80,6 +80,18 @@ class ByteParser extends Writable {
80
80
payloadLength
81
81
} )
82
82
83
+ if ( loop ) {
84
+ continue
85
+ } else {
86
+ return
87
+ }
88
+ } else if ( isContinuationFrame ( opcode ) ) {
89
+ const loop = this . parseContinuationFrame ( callback , {
90
+ fin,
91
+ fragmented,
92
+ payloadLength
93
+ } )
94
+
83
95
if ( loop ) {
84
96
continue
85
97
} else {
@@ -96,9 +108,6 @@ class ByteParser extends Writable {
96
108
this . #state = parserStates . PAYLOADLENGTH_64
97
109
}
98
110
99
- // TODO(@KhafraDev): handle continuation frames separately as their
100
- // semantics are different from TEXT/BINARY frames.
101
- this . #info. originalOpcode ??= opcode
102
111
this . #info. opcode = opcode
103
112
this . #info. masked = masked
104
113
this . #info. fin = fin
@@ -146,19 +155,16 @@ class ByteParser extends Writable {
146
155
// If there is still more data in this chunk that needs to be read
147
156
return callback ( )
148
157
} else if ( this . #byteOffset >= this . #info. payloadLength ) {
149
- // If the server sent multiple frames in a single chunk
150
-
151
158
const body = this . consume ( this . #info. payloadLength )
152
-
153
159
this . #fragments. push ( body )
154
160
155
- // If the frame is unfragmented, or a fragmented frame was terminated,
156
- // a message was received
157
- if ( ! this . #info. fragmented || ( this . #info. fin && this . #info. opcode === opcodes . CONTINUATION ) ) {
161
+ // If the frame is not fragmented, a message has been received.
162
+ // If the frame is fragmented, it will terminate with a fin bit set
163
+ // and an opcode of 0 (continuation), therefore we handle that when
164
+ // parsing continuation frames, not here.
165
+ if ( ! this . #info. fragmented ) {
158
166
const fullMessage = Buffer . concat ( this . #fragments)
159
-
160
- websocketMessageReceived ( this . ws , this . #info. originalOpcode , fullMessage )
161
-
167
+ websocketMessageReceived ( this . ws , this . #info. opcode , fullMessage )
162
168
this . #info = { }
163
169
this . #fragments. length = 0
164
170
}
@@ -265,6 +271,9 @@ class ByteParser extends Writable {
265
271
// Control frames can have a payload length of 125 bytes MAX
266
272
callback ( new Error ( 'Payload length for control frame exceeded 125 bytes.' ) )
267
273
return false
274
+ } else if ( this . #byteOffset < info . payloadLength ) {
275
+ callback ( )
276
+ return false
268
277
}
269
278
270
279
const body = this . consume ( info . payloadLength )
@@ -357,6 +366,39 @@ class ByteParser extends Writable {
357
366
return true
358
367
}
359
368
369
+ /**
370
+ * Parses continuation frames.
371
+ * @param {Buffer } data
372
+ * @param {(err?: Error) => void } callback
373
+ * @param {{ fin: boolean, fragmented: boolean, payloadLength: number } } info
374
+ */
375
+ parseContinuationFrame ( callback , info ) {
376
+ // If we received a continuation frame before we started parsing another frame.
377
+ if ( this . #info. opcode === undefined ) {
378
+ callback ( new Error ( 'Received unexpected continuation frame.' ) )
379
+ return false
380
+ } else if ( this . #byteOffset < info . payloadLength ) {
381
+ callback ( )
382
+ return false
383
+ }
384
+
385
+ const body = this . consume ( info . payloadLength )
386
+ this . #fragments. push ( body )
387
+
388
+ // A fragmented message consists of a single frame with the FIN bit
389
+ // clear and an opcode other than 0, followed by zero or more frames
390
+ // with the FIN bit clear and the opcode set to 0, and terminated by
391
+ // a single frame with the FIN bit set and an opcode of 0.
392
+ if ( info . fin ) {
393
+ const message = Buffer . concat ( this . #fragments)
394
+ websocketMessageReceived ( this . ws , this . #info. opcode , message )
395
+ this . #fragments. length = 0
396
+ this . #info = { }
397
+ }
398
+
399
+ return true
400
+ }
401
+
360
402
get closingInfo ( ) {
361
403
return this . #info. closeInfo
362
404
}
0 commit comments