@fails-components/webtransport 1.6.8 → 1.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,25 @@
1
1
  import { randomBytes } from 'node:crypto'
2
- import { ParserBase, lengthVarInt } from '../parserbase.js'
2
+ import { ParserBase } from '../parserbase.js'
3
3
  import {
4
- ParserBaseHttp2,
4
+ advanceBufferToOffset,
5
+ lengthVarInt,
5
6
  readVarInt,
6
7
  writeVarInt,
7
- readUint32
8
- } from '../parserbasehttp2.js'
8
+ writeUInt8,
9
+ writeUint16BE,
10
+ writeUint32BE,
11
+ writeBigInt64BE,
12
+ readUInt8,
13
+ readUint32,
14
+ advanceBuffer,
15
+ getUint8ArrayfromBuffer,
16
+ getUint8ArraysfromBuffer,
17
+ advanceBufferBy,
18
+ readUInt16BE,
19
+ readString,
20
+ detachReadBuffers
21
+ } from '../bufferHelper.js'
22
+ import { ParserBaseHttp2 } from '../parserbasehttp2.js'
9
23
  import { logger } from '../../utils.js'
10
24
 
11
25
  const log = logger(`webtransport:http2:node:websocketparser(${process?.pid})`)
@@ -14,32 +28,32 @@ const log = logger(`webtransport:http2:node:websocketparser(${process?.pid})`)
14
28
  */
15
29
 
16
30
  /**
17
- * @param{{offset: Number, buffer: Buffer, size: Number}} bs
31
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
18
32
  */
19
33
  function readByte(bs) {
20
- const val = bs.buffer.readUInt8(bs.offset)
21
- bs.offset++
34
+ const val = readUInt8(bs)
35
+ advanceBuffer(bs)
22
36
  return val
23
37
  }
24
38
 
25
39
  /**
26
- * @param{{offset: Number, buffer: Buffer, size: Number}} bs
40
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
27
41
  */
28
42
  function readWord(bs) {
29
- let val = bs.buffer.readUInt8(bs.offset)
30
- bs.offset++
31
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
32
- bs.offset++
43
+ let val = readUInt8(bs)
44
+ advanceBuffer(bs)
45
+ val = (val << 8) | readUInt8(bs)
46
+ advanceBuffer(bs)
33
47
  return val
34
48
  }
35
49
 
36
50
  /**
37
- * @param{{offset: Number, buffer: Buffer, size: Number}} bs
51
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
38
52
  * @param{{offset: Number, mask: Uint8Array}} ms
39
53
  */
40
54
  export function readVarIntMasked(bs, ms) {
41
- let val = bs.buffer.readUInt8(bs.offset) ^ ms.mask[ms.offset % 4]
42
- bs.offset++
55
+ let val = readUInt8(bs) ^ ms.mask[ms.offset % 4]
56
+ advanceBuffer(bs)
43
57
  ms.offset++
44
58
  const prefix = val >>> 6
45
59
  const intlength = 1 << prefix
@@ -49,49 +63,48 @@ export function readVarIntMasked(bs, ms) {
49
63
  }
50
64
  val = val & 0x3f
51
65
  for (let i = 0; i < intlength - 1; i++) {
52
- val = (val << 8) | (bs.buffer.readUInt8(bs.offset) ^ ms.mask[ms.offset % 4])
53
- bs.offset++
66
+ val = (val << 8) | (readUInt8(bs) ^ ms.mask[ms.offset % 4])
67
+ advanceBuffer(bs)
54
68
  ms.offset++
55
69
  }
56
70
  return val
57
71
  }
58
72
 
59
73
  /**
60
- * @param{{offset: Number, buffer: Buffer, size: Number}} bs
74
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
61
75
  */
62
76
  function readQWord(bs) {
63
- let val = bs.buffer.readUInt8(bs.offset)
64
- bs.offset++
65
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
66
- bs.offset++
67
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
68
- bs.offset++
69
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
70
- bs.offset++
71
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
72
- bs.offset++
73
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
74
- bs.offset++
75
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
76
- bs.offset++
77
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
78
- bs.offset++
77
+ let val = BigInt(readUInt8(bs))
78
+ advanceBuffer(bs)
79
+ val = (val << 8n) | BigInt(readUInt8(bs))
80
+ advanceBuffer(bs)
81
+ val = (val << 8n) | BigInt(readUInt8(bs))
82
+ advanceBuffer(bs)
83
+ val = (val << 8n) | BigInt(readUInt8(bs))
84
+ advanceBuffer(bs)
85
+ val = (val << 8n) | BigInt(readUInt8(bs))
86
+ advanceBuffer(bs)
87
+ val = (val << 8n) | BigInt(readUInt8(bs))
88
+ advanceBuffer(bs)
89
+ val = (val << 8n) | BigInt(readUInt8(bs))
90
+ advanceBuffer(bs)
91
+ val = (val << 8n) | BigInt(readUInt8(bs))
92
+ advanceBuffer(bs)
79
93
  return val
80
94
  }
81
95
 
82
96
  /**
83
97
  * @param{{offset: Number, mask: Uint8Array}} ms
84
- * @param{Buffer} buffer
98
+ * @param{Uint8Array} data
85
99
  * @param{number} offset
86
100
  * @param{number} length
87
101
  */
88
- function applyMask(ms, buffer, offset, length) {
102
+ function applyMask(ms, data, offset, length) {
89
103
  // Note offset: includes the byteOffset into the buffer
90
104
  if (length > 24) {
91
105
  let run = 0
92
106
  // alignment preamble, inspired from ws bufferutil
93
- const data = new Uint8Array(buffer.buffer)
94
- while (run < length && (offset + run) % 8) {
107
+ while (run < length && (offset + run + data.byteOffset) % 8) {
95
108
  data[run + offset] ^= ms.mask[(run + ms.offset) % 4]
96
109
  run++
97
110
  }
@@ -106,8 +119,8 @@ function applyMask(ms, buffer, offset, length) {
106
119
  workmask[6] = ms.mask[(6 + run + ms.offset) % 4]
107
120
  workmask[7] = ms.mask[(7 + run + ms.offset) % 4]
108
121
  const data64 = new BigUint64Array(
109
- buffer.buffer,
110
- run + offset,
122
+ data.buffer,
123
+ run + offset + data.byteOffset,
111
124
  Math.floor((length - run) / 8)
112
125
  )
113
126
  const workmask64 = new BigUint64Array(workmask.buffer)
@@ -128,40 +141,45 @@ function applyMask(ms, buffer, offset, length) {
128
141
  workmask[1] = ms.mask[(1 + ms.offset) % 4]
129
142
  workmask[2] = ms.mask[(2 + ms.offset) % 4]
130
143
  workmask[3] = ms.mask[(3 + ms.offset) % 4]
131
- const data = new Uint8Array(buffer.buffer, offset, length)
132
144
  for (let run = 0 /* Math.round(length / 4) * 4 */; run < length; run++) {
133
- data[run] ^= workmask[run % 4]
145
+ data[run + offset] ^= workmask[run % 4]
134
146
  }
135
147
  }
136
148
  ms.offset += length
137
149
  }
138
- /*
139
- function readDWord(bs) {
140
- let val = bs.buffer.readUInt8(bs.offset)
141
- bs.offset++
142
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
143
- bs.offset++
144
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
145
- bs.offset++
146
- val = (val << 8) | bs.buffer.readUInt8(bs.offset)
147
- bs.offset++
148
- return val
150
+ /**
151
+ * @param{{offset: Number, mask: Uint8Array}} ms
152
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
153
+ * @param{number} length
154
+ */
155
+ function applyMaskBuffer(ms, bs, length) {
156
+ let curBufOffset = bs.curBufOffset
157
+ let curBuf = bs.curBuf
158
+ let remainLen = length
159
+
160
+ while (remainLen > 0) {
161
+ let buf = bs.buffer[curBuf]
162
+ let curbufsize = Math.min(buf.byteLength - curBufOffset, remainLen)
163
+ applyMask(ms, buf, curBufOffset, curbufsize)
164
+ curBufOffset = 0
165
+ curBuf++
166
+ remainLen -= curbufsize
167
+ }
149
168
  }
150
- */
151
169
 
152
170
  /**
153
- * @param{{offset: Number, buffer: Buffer, size: Number}} bs
171
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
154
172
  */
155
173
  function readMask(bs) {
156
174
  const val = new Uint8Array(4)
157
- val[0] = bs.buffer.readUInt8(bs.offset)
158
- bs.offset++
159
- val[1] = bs.buffer.readUInt8(bs.offset)
160
- bs.offset++
161
- val[2] = bs.buffer.readUInt8(bs.offset)
162
- bs.offset++
163
- val[3] = bs.buffer.readUInt8(bs.offset)
164
- bs.offset++
175
+ val[0] = readUInt8(bs)
176
+ advanceBuffer(bs)
177
+ val[1] = readUInt8(bs)
178
+ advanceBuffer(bs)
179
+ val[2] = readUInt8(bs)
180
+ advanceBuffer(bs)
181
+ val[3] = readUInt8(bs)
182
+ advanceBuffer(bs)
165
183
  return val
166
184
  }
167
185
 
@@ -196,8 +214,18 @@ export class WebSocketParser extends ParserBaseHttp2 {
196
214
  streamReceiveWindowSizeLimit
197
215
  })
198
216
  this.mode = 's' // frame start
199
- /** @type {Buffer|undefined} */
200
- this.saveddata = undefined
217
+ this.bufferstate = {
218
+ /** @type {number} */
219
+ offset: 0,
220
+ /** @type {number} */
221
+ size: 0,
222
+ /** @type {Uint8Array[]} */
223
+ buffer: [],
224
+ /** @type {number} */
225
+ curBuf: 0,
226
+ /** @type {number} */
227
+ curBufOffset: 0
228
+ }
201
229
  /** @type {Number|undefined} */
202
230
  this.rtype = undefined
203
231
 
@@ -206,30 +234,25 @@ export class WebSocketParser extends ParserBaseHttp2 {
206
234
  }
207
235
 
208
236
  /**
209
- * @param {Buffer} data
237
+ * @param {Uint8Array[]} data
210
238
  */
211
239
  parseData(data) {
212
- let cdata = data
213
- if (this.saveddata) {
214
- cdata = Buffer.concat([this.saveddata, cdata])
215
- delete this.saveddata
216
- }
217
- const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
240
+ const bufferstate = this.bufferstate
241
+ bufferstate.buffer.push(...data)
242
+ bufferstate.size += data.reduce((sum, chunk) => sum + chunk.byteLength, 0)
218
243
  while (bufferstate.size - bufferstate.offset > 0) {
219
244
  switch (this.mode) {
220
245
  case 's':
221
246
  {
222
- const framestart =
223
- bufferstate.offset + bufferstate.buffer.byteOffset
224
- const framemaxlength =
225
- bufferstate.buffer.byteLength - bufferstate.offset
247
+ const framestart = bufferstate.offset
248
+ const frameCurBuf = bufferstate.curBuf
249
+ const frameCurBufOffset = bufferstate.curBufOffset
226
250
  // we are at frame start
227
251
  if (bufferstate.size < 2 + bufferstate.offset) {
228
- this.saveddata = Buffer.from(
229
- bufferstate.buffer.buffer,
230
- framestart,
231
- framemaxlength
232
- )
252
+ bufferstate.offset = framestart
253
+ bufferstate.curBuf = frameCurBuf
254
+ bufferstate.curBufOffset = frameCurBufOffset
255
+ detachReadBuffers(bufferstate)
233
256
  return
234
257
  }
235
258
  let curbyte = readByte(bufferstate)
@@ -242,34 +265,58 @@ export class WebSocketParser extends ParserBaseHttp2 {
242
265
  curbyte = readByte(bufferstate)
243
266
  const mask = (curbyte & 0x80) >>> 7
244
267
  const mlength = mask ? 4 : 0
268
+ /** @type {Number|undefined} */
245
269
  let plength = curbyte & 0x7f
246
270
  if (plength === 126) {
247
271
  if (bufferstate.size < 2 + bufferstate.offset) {
248
- this.saveddata = Buffer.from(
249
- bufferstate.buffer.buffer,
250
- framestart,
251
- framemaxlength
252
- )
272
+ bufferstate.offset = framestart
273
+ bufferstate.curBuf = frameCurBuf
274
+ bufferstate.curBufOffset = frameCurBufOffset
275
+ detachReadBuffers(bufferstate)
253
276
  return
254
277
  }
255
278
  plength = readWord(bufferstate)
256
279
  } else if (plength === 127) {
257
280
  if (bufferstate.size < 8 + bufferstate.offset) {
258
- this.saveddata = Buffer.from(
259
- bufferstate.buffer.buffer,
260
- framestart,
261
- framemaxlength
262
- )
281
+ bufferstate.offset = framestart
282
+ bufferstate.curBuf = frameCurBuf
283
+ bufferstate.curBufOffset = frameCurBufOffset
284
+ detachReadBuffers(bufferstate)
263
285
  return
264
286
  }
265
- plength = readQWord(bufferstate)
287
+ let longlength = readQWord(bufferstate)
288
+
289
+ if (
290
+ typeof longlength !== 'undefined' &&
291
+ longlength <= BigInt(Number.MAX_SAFE_INTEGER)
292
+ ) {
293
+ plength = Number(longlength)
294
+ } else {
295
+ plength = undefined
296
+ }
297
+ }
298
+ if (
299
+ typeof plength === 'undefined' ||
300
+ plength >
301
+ Math.max(
302
+ 16 * Number(this.session.flowController.receiveWindowSize),
303
+ 1000000
304
+ )
305
+ ) {
306
+ // too long abort, could be an attack vector
307
+ this.session.closeConnection({
308
+ code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, // probably the right one...
309
+ reason: plength
310
+ ? 'Frame length too big :' + plength
311
+ : 'Frame length overflow'
312
+ })
313
+ return
266
314
  }
267
315
  if (bufferstate.size < mlength + bufferstate.offset) {
268
- this.saveddata = Buffer.from(
269
- bufferstate.buffer.buffer,
270
- framestart,
271
- framemaxlength
272
- )
316
+ bufferstate.offset = framestart
317
+ bufferstate.curBuf = frameCurBuf
318
+ bufferstate.curBufOffset = frameCurBufOffset
319
+ detachReadBuffers(bufferstate)
273
320
  return
274
321
  }
275
322
  if (mask) {
@@ -292,22 +339,24 @@ export class WebSocketParser extends ParserBaseHttp2 {
292
339
  const length = plength
293
340
 
294
341
  if (
342
+ typeof length === 'undefined' ||
295
343
  length >
296
- 4 * Number(this.session.flowController.receiveWindowSize)
344
+ 4 * Number(this.session.flowController.receiveWindowSize)
297
345
  ) {
298
346
  // too long abort, could be an attack vector
299
347
  this.session.closeConnection({
300
348
  code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, // probably the right one...
301
- reason: 'Frame length too big :' + length
349
+ reason: length
350
+ ? 'Frame length too big :' + length
351
+ : 'Frame length overflow'
302
352
  })
303
353
  return
304
354
  } else {
305
355
  if (bufferstate.size < length + bufferstate.offset) {
306
- this.saveddata = Buffer.from(
307
- bufferstate.buffer.buffer,
308
- framestart,
309
- framemaxlength
310
- )
356
+ bufferstate.offset = framestart
357
+ bufferstate.curBuf = frameCurBuf
358
+ bufferstate.curBufOffset = frameCurBufOffset
359
+ detachReadBuffers(bufferstate)
311
360
  return
312
361
  }
313
362
  }
@@ -315,29 +364,16 @@ export class WebSocketParser extends ParserBaseHttp2 {
315
364
  case WebSocketParser.WS_CLOSE:
316
365
  if (!this.closesend) {
317
366
  if (this.maskcontext)
318
- applyMask(
319
- this.maskcontext,
320
- bufferstate.buffer,
321
- bufferstate.buffer.byteOffset + bufferstate.offset,
322
- length
323
- )
367
+ applyMaskBuffer(this.maskcontext, bufferstate, length)
324
368
  this.sendCloseInt(
325
- new Uint8Array(
326
- bufferstate.buffer.buffer,
327
- bufferstate.buffer.byteOffset + bufferstate.offset,
328
- length
329
- )
369
+ getUint8ArrayfromBuffer(bufferstate, length)
330
370
  )
331
371
  let code = 0
332
372
  let error = 'Session websocket closed'
333
373
  if (length > 2) {
334
- const bufhelp = Buffer.from(
335
- bufferstate.buffer.buffer,
336
- bufferstate.buffer.byteOffset + bufferstate.offset,
337
- length
338
- )
339
- code = bufhelp.readUint16BE(0)
340
- const terror = bufhelp.toString('utf8', 2)
374
+ const wbufferstate = { ...bufferstate } // working copy
375
+ code = readUInt16BE(wbufferstate)
376
+ const terror = readString(wbufferstate, length - 2)
341
377
  let tokens = terror.split(':')
342
378
  if (tokens.length > 1) {
343
379
  code = parseInt(tokens[0])
@@ -345,33 +381,32 @@ export class WebSocketParser extends ParserBaseHttp2 {
345
381
  }
346
382
  error = tokens.join(':')
347
383
  }
348
- this.session.jsobj.onClose({
349
- errorcode: code,
350
- error
351
- })
384
+ if (
385
+ !(
386
+ this.session.jsobj.state === 'failed' ||
387
+ this.session.jsobj.state === 'closed'
388
+ )
389
+ ) {
390
+ this.session.jsobj.onClose({
391
+ errorcode: code,
392
+ error
393
+ })
394
+ }
352
395
  } else {
353
396
  // just the answer
354
397
  }
355
398
  break
356
399
  case WebSocketParser.WS_PING:
357
400
  if (this.maskcontext)
358
- applyMask(
359
- this.maskcontext,
360
- bufferstate.buffer,
361
- bufferstate.buffer.byteOffset + bufferstate.offset,
362
- length
363
- )
401
+ applyMaskBuffer(this.maskcontext, bufferstate, length)
364
402
  this.sendPong(
365
- new Uint8Array(
366
- bufferstate.buffer.buffer,
367
- bufferstate.buffer.byteOffset + bufferstate.offset,
368
- length
369
- )
403
+ getUint8ArrayfromBuffer(bufferstate, length) ||
404
+ new Uint8Array()
370
405
  )
371
406
  break
372
407
  default: // aka pong
373
408
  }
374
- bufferstate.offset += length
409
+ advanceBufferBy(bufferstate, length)
375
410
  } else if (
376
411
  opcode === WebSocketParser.WS_BINARY ||
377
412
  (opcode === WebSocketParser.WS_CONTINUE &&
@@ -384,17 +419,16 @@ export class WebSocketParser extends ParserBaseHttp2 {
384
419
  if (plength === 0) {
385
420
  log('warning empty data frame')
386
421
  // empty frame ?
387
- bufferstate.offset += plength
422
+ advanceBufferBy(bufferstate, plength)
388
423
  continue
389
424
  }
390
425
 
391
426
  if (opcode === WebSocketParser.WS_BINARY) {
392
427
  if (bufferstate.size < 2 + bufferstate.offset) {
393
- this.saveddata = Buffer.from(
394
- bufferstate.buffer.buffer,
395
- framestart,
396
- framemaxlength
397
- )
428
+ bufferstate.offset = framestart
429
+ bufferstate.curBuf = frameCurBuf
430
+ bufferstate.curBufOffset = frameCurBufOffset
431
+ detachReadBuffers(bufferstate)
398
432
  return
399
433
  }
400
434
  continuep = false
@@ -408,11 +442,10 @@ export class WebSocketParser extends ParserBaseHttp2 {
408
442
  typeof type === 'undefined' ||
409
443
  bufferstate.size < 1 + bufferstate.offset
410
444
  ) {
411
- this.saveddata = Buffer.from(
412
- bufferstate.buffer.buffer,
413
- framestart,
414
- framemaxlength
415
- )
445
+ bufferstate.offset = framestart
446
+ bufferstate.curBuf = frameCurBuf
447
+ bufferstate.curBufOffset = frameCurBufOffset
448
+ detachReadBuffers(bufferstate)
416
449
  return
417
450
  }
418
451
  this.curtype = type
@@ -439,19 +472,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
439
472
  checklength = Math.min(length, 64) // stream id + some Data
440
473
  }
441
474
  if (bufferstate.size < checklength + bufferstate.offset) {
442
- this.saveddata = Buffer.from(
443
- bufferstate.buffer.buffer,
444
- framestart,
445
- framemaxlength
446
- )
475
+ bufferstate.offset = framestart
476
+ bufferstate.curBuf = frameCurBuf
477
+ bufferstate.curBufOffset = frameCurBufOffset
478
+ detachReadBuffers(bufferstate)
447
479
  return
448
480
  }
449
481
  // all safeguards passed now apply the mask
450
482
  if (this.maskcontext)
451
- applyMask(
483
+ applyMaskBuffer(
452
484
  this.maskcontext,
453
- bufferstate.buffer,
454
- bufferstate.buffer.byteOffset + bufferstate.offset,
485
+ bufferstate,
455
486
  offsetend - bufferstate.offset
456
487
  )
457
488
  let streamid
@@ -461,30 +492,25 @@ export class WebSocketParser extends ParserBaseHttp2 {
461
492
  type !== ParserBase.WT_STREAM_WFIN
462
493
  ) {
463
494
  if (fin) {
464
- wbufferstate = bufferstate
495
+ wbufferstate = { ...bufferstate }
465
496
  if (this.contframes) {
466
- this.contframes.push(
467
- new Uint8Array(
468
- bufferstate.buffer.buffer,
469
- bufferstate.buffer.byteOffset + bufferstate.offset,
470
- offsetend - bufferstate.offset
471
- )
497
+ const newframes = getUint8ArraysfromBuffer(
498
+ bufferstate,
499
+ offsetend - bufferstate.offset
472
500
  )
501
+ if (newframes) this.contframes.push(...newframes)
473
502
  // we need to concat all buffers super
474
503
  const nsize = this.contframes.reduce(
475
504
  (length, val) => length + val.byteLength,
476
505
  0
477
506
  )
478
- const jbuffer = Buffer.allocUnsafe(nsize)
479
- this.contframes.reduce((offset, val) => {
480
- Buffer.from(
481
- val.buffer,
482
- val.byteOffset,
483
- val.byteLength
484
- ).copy(jbuffer, offset)
485
- return offset + val.byteLength
486
- }, 0)
487
- wbufferstate = { offset: 0, size: nsize, buffer: jbuffer }
507
+ wbufferstate = {
508
+ offset: 0,
509
+ size: nsize,
510
+ buffer: this.contframes, // Fix me
511
+ curBuf: 0,
512
+ curBufOffset: 0
513
+ }
488
514
  }
489
515
  } else {
490
516
  if (!this.contframes)
@@ -492,13 +518,11 @@ export class WebSocketParser extends ParserBaseHttp2 {
492
518
  * @type {Uint8Array[]}
493
519
  */
494
520
  this.contframes = []
495
- this.contframes.push(
496
- new Uint8Array(
497
- bufferstate.buffer.buffer,
498
- bufferstate.buffer.byteOffset + bufferstate.offset,
499
- offsetend - bufferstate.offset
500
- )
521
+ const newframes = getUint8ArraysfromBuffer(
522
+ bufferstate,
523
+ offsetend - bufferstate.offset
501
524
  )
525
+ if (newframes) this.contframes.push(...newframes)
502
526
  }
503
527
  }
504
528
 
@@ -556,10 +580,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
556
580
  object.recvData({
557
581
  data:
558
582
  offsetend - bufferstate.offset > 0
559
- ? new Uint8Array(
560
- bufferstate.buffer.buffer,
561
- bufferstate.buffer.byteOffset +
562
- bufferstate.offset,
583
+ ? getUint8ArraysfromBuffer(
584
+ bufferstate,
563
585
  offsetend - bufferstate.offset
564
586
  )
565
587
  : undefined,
@@ -612,13 +634,15 @@ export class WebSocketParser extends ParserBaseHttp2 {
612
634
  {
613
635
  const code = readUint32(bufferstate) || 0
614
636
  const decoder = new TextDecoder()
615
- const reason = decoder.decode(
616
- new Uint8Array(
617
- bufferstate.buffer.buffer,
618
- bufferstate.buffer.byteOffset + bufferstate.offset,
619
- offsetend - bufferstate.offset
620
- )
621
- )
637
+ const reason =
638
+ offsetend - bufferstate.offset > 0
639
+ ? decoder.decode(
640
+ getUint8ArrayfromBuffer(
641
+ bufferstate,
642
+ offsetend - bufferstate.offset
643
+ )
644
+ )
645
+ : ''
622
646
  this.onCloseWebTransportSession({ code, reason })
623
647
  }
624
648
  break
@@ -628,11 +652,11 @@ export class WebSocketParser extends ParserBaseHttp2 {
628
652
  case ParserBase.DATAGRAM:
629
653
  if (wbufferstate) {
630
654
  this.session.jsobj.onDatagramReceived({
631
- datagram: new Uint8Array(
632
- wbufferstate.buffer.buffer,
633
- wbufferstate.buffer.byteOffset + wbufferstate.offset,
634
- offsetend - wbufferstate.offset
635
- )
655
+ datagram:
656
+ getUint8ArrayfromBuffer(
657
+ wbufferstate,
658
+ offsetend - wbufferstate.offset
659
+ ) || new Uint8Array(0)
636
660
  })
637
661
  }
638
662
  break
@@ -643,12 +667,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
643
667
  if (bufferstate.size < length + offsetbegin) {
644
668
  this.remainlength = length + offsetbegin - bufferstate.size
645
669
  this.mode = 'c'
646
- if (streamid) {
670
+ if (typeof streamid !== 'undefined') {
647
671
  this.rstreamid = streamid
648
672
  this.rfin = type === ParserBase.WT_STREAM_WFIN
649
673
  }
650
674
  }
651
- bufferstate.offset = offsetend
675
+ advanceBufferToOffset(bufferstate, offsetend)
652
676
  } else {
653
677
  const length = plength
654
678
  if (bufferstate.offset + length > bufferstate.size) {
@@ -656,9 +680,9 @@ export class WebSocketParser extends ParserBaseHttp2 {
656
680
  this.rstreamid = undefined
657
681
  this.remainlength =
658
682
  bufferstate.offset + length - bufferstate.size
659
- bufferstate.offset = bufferstate.size
683
+ advanceBufferToOffset(bufferstate, bufferstate.size)
660
684
  } else {
661
- bufferstate.offset += length
685
+ advanceBufferBy(bufferstate, length)
662
686
  }
663
687
  }
664
688
  }
@@ -669,7 +693,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
669
693
  bufferstate.size - bufferstate.offset,
670
694
  this.remainlength
671
695
  )
672
- if (this.rstreamid) {
696
+ if (typeof this.rstreamid !== 'undefined') {
673
697
  // not in j mode
674
698
  // TODO submitData
675
699
  const object = this.wtstreams.get(this.rstreamid)
@@ -679,26 +703,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
679
703
  if (object) {
680
704
  if (fin) object.onFin()
681
705
  if (this.maskcontext)
682
- applyMask(
683
- this.maskcontext,
684
- bufferstate.buffer,
685
- bufferstate.buffer.byteOffset + bufferstate.offset,
686
- clength
687
- )
706
+ applyMaskBuffer(this.maskcontext, bufferstate, clength)
688
707
  // TODO submit data
689
708
  object.recvData({
690
- data: new Uint8Array(
691
- bufferstate.buffer.buffer,
692
- bufferstate.buffer.byteOffset + bufferstate.offset,
693
- clength
694
- ),
709
+ data: getUint8ArraysfromBuffer(bufferstate, clength),
695
710
  fin
696
711
  })
697
712
  }
698
713
  }
699
714
 
700
715
  this.remainlength = this.remainlength - clength
701
- bufferstate.offset += clength
716
+ advanceBufferBy(bufferstate, clength)
702
717
  if (this.remainlength === 0) {
703
718
  this.maskcontext = undefined
704
719
  this.mode = 's'
@@ -709,6 +724,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
709
724
  break
710
725
  }
711
726
  }
727
+ detachReadBuffers(bufferstate)
712
728
  }
713
729
 
714
730
  /**
@@ -754,8 +770,14 @@ export class WebSocketParser extends ParserBaseHttp2 {
754
770
  headlength += 2
755
771
  }
756
772
 
757
- const cdata = Buffer.alloc(headlength)
758
- const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
773
+ const cdata = new Uint8Array(headlength)
774
+ const bufferstate = {
775
+ offset: 0,
776
+ size: cdata.length,
777
+ buffer: [cdata],
778
+ curBuf: 0,
779
+ curBufOffset: 0
780
+ }
759
781
  const maskstate = this.writeHeader(bufferstate, {
760
782
  opcode: WebSocketParser.WS_BINARY,
761
783
  plength,
@@ -768,19 +790,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
768
790
  for (const ind in headerVints) writeVarInt(bufferstate, headerVints[ind])
769
791
  const endmask = bufferstate.offset
770
792
  if (maskstate) {
771
- applyMask(
772
- maskstate,
773
- bufferstate.buffer,
774
- bufferstate.offset + bufferstate.buffer.byteOffset,
775
- endmask - beginmask
776
- )
777
- if (payload)
778
- applyMask(
779
- maskstate,
780
- Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength),
781
- payload.byteOffset,
782
- payload.byteLength
783
- )
793
+ applyMaskBuffer(maskstate, bufferstate, endmask - beginmask)
794
+ if (payload) applyMask(maskstate, payload, 0, payload.byteLength)
784
795
  }
785
796
  let blocked = false
786
797
  if (payload) {
@@ -795,12 +806,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
795
806
  }
796
807
 
797
808
  /**
798
- * @param {{ offset: number; size?: number; buffer: Buffer; }} bs
809
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
799
810
  * @param {{ opcode: number; plength: number; mask: number; }} args
800
811
  */
801
812
  writeHeader(bs, { opcode, plength, mask }) {
802
- bs.buffer.writeUint8(0x80 | (opcode & 0x0f), bs.offset)
803
- bs.offset++
813
+ writeUInt8(bs, 0x80 | (opcode & 0x0f))
814
+ advanceBuffer(bs)
804
815
  let splength = plength
805
816
  if (plength > 0xffff) {
806
817
  splength = 127
@@ -808,20 +819,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
808
819
  splength = 126
809
820
  }
810
821
 
811
- bs.buffer.writeUint8((mask && 0x80) | (splength & 0x7f), bs.offset)
812
- bs.offset++
822
+ writeUInt8(bs, (mask && 0x80) | (splength & 0x7f))
823
+ advanceBuffer(bs)
813
824
  if (plength > 0xffff) {
814
- bs.buffer.writeBigInt64BE(BigInt(plength), bs.offset)
815
- bs.offset += 8
825
+ writeBigInt64BE(bs, BigInt(plength))
816
826
  } else if (plength > 125) {
817
- bs.buffer.writeUint16BE(plength, bs.offset)
818
- bs.offset += 2
827
+ writeUint16BE(bs, plength)
819
828
  }
820
829
  let maskstate
821
830
  if (mask) {
822
831
  maskstate = { offset: 0, mask: randomBytes(4) }
823
- bs.buffer.writeUint32BE(maskstate.mask.readUInt32BE(0), bs.offset)
824
- bs.offset += 4
832
+ writeUint32BE(bs, maskstate.mask.readUInt32BE(0))
825
833
  }
826
834
  return maskstate
827
835
  }
@@ -842,8 +850,14 @@ export class WebSocketParser extends ParserBaseHttp2 {
842
850
  } else if (plength > 125) {
843
851
  headlength += 2
844
852
  }
845
- const cdata = Buffer.alloc(headlength)
846
- const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
853
+ const cdata = new Uint8Array(headlength)
854
+ const bufferstate = {
855
+ offset: 0,
856
+ size: cdata.length,
857
+ buffer: [cdata],
858
+ curBuf: 0,
859
+ curBufOffset: 0
860
+ }
847
861
  const maskstate = this.writeHeader(bufferstate, { opcode, plength, mask })
848
862
 
849
863
  if (bufferstate.offset !== headlength)
@@ -851,12 +865,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
851
865
 
852
866
  let blocked = !this.stream.write(cdata)
853
867
  if (maskstate && payload)
854
- applyMask(
855
- maskstate,
856
- Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength),
857
- 0,
858
- payload.byteLength
859
- )
868
+ applyMask(maskstate, payload, 0, payload.byteLength)
860
869
  if (payload) blocked = !this.stream.write(payload) || blocked
861
870
  // do something if blocked
862
871
  if (blocked) this.blocked = true