@fails-components/webtransport 1.6.8 → 1.6.10

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,40 +214,51 @@ 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
 
204
232
  this.bidirectionalLimitsSet = false
205
233
  this.unidirectionalLimitsSet = false
234
+
235
+ /**
236
+ * @type {Uint8Array[]| undefined}
237
+ */
238
+ this.contframes = undefined
239
+ this.remainlength = 0
206
240
  }
207
241
 
208
242
  /**
209
- * @param {Buffer} data
243
+ * @param {Uint8Array[]} data
210
244
  */
211
245
  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 }
246
+ const bufferstate = this.bufferstate
247
+ bufferstate.buffer.push(...data)
248
+ bufferstate.size += data.reduce((sum, chunk) => sum + chunk.byteLength, 0)
218
249
  while (bufferstate.size - bufferstate.offset > 0) {
219
250
  switch (this.mode) {
220
251
  case 's':
221
252
  {
222
- const framestart =
223
- bufferstate.offset + bufferstate.buffer.byteOffset
224
- const framemaxlength =
225
- bufferstate.buffer.byteLength - bufferstate.offset
253
+ const framestart = bufferstate.offset
254
+ const frameCurBuf = bufferstate.curBuf
255
+ const frameCurBufOffset = bufferstate.curBufOffset
226
256
  // we are at frame start
227
257
  if (bufferstate.size < 2 + bufferstate.offset) {
228
- this.saveddata = Buffer.from(
229
- bufferstate.buffer.buffer,
230
- framestart,
231
- framemaxlength
232
- )
258
+ bufferstate.offset = framestart
259
+ bufferstate.curBuf = frameCurBuf
260
+ bufferstate.curBufOffset = frameCurBufOffset
261
+ detachReadBuffers(bufferstate)
233
262
  return
234
263
  }
235
264
  let curbyte = readByte(bufferstate)
@@ -242,34 +271,58 @@ export class WebSocketParser extends ParserBaseHttp2 {
242
271
  curbyte = readByte(bufferstate)
243
272
  const mask = (curbyte & 0x80) >>> 7
244
273
  const mlength = mask ? 4 : 0
274
+ /** @type {Number|undefined} */
245
275
  let plength = curbyte & 0x7f
246
276
  if (plength === 126) {
247
277
  if (bufferstate.size < 2 + bufferstate.offset) {
248
- this.saveddata = Buffer.from(
249
- bufferstate.buffer.buffer,
250
- framestart,
251
- framemaxlength
252
- )
278
+ bufferstate.offset = framestart
279
+ bufferstate.curBuf = frameCurBuf
280
+ bufferstate.curBufOffset = frameCurBufOffset
281
+ detachReadBuffers(bufferstate)
253
282
  return
254
283
  }
255
284
  plength = readWord(bufferstate)
256
285
  } else if (plength === 127) {
257
286
  if (bufferstate.size < 8 + bufferstate.offset) {
258
- this.saveddata = Buffer.from(
259
- bufferstate.buffer.buffer,
260
- framestart,
261
- framemaxlength
262
- )
287
+ bufferstate.offset = framestart
288
+ bufferstate.curBuf = frameCurBuf
289
+ bufferstate.curBufOffset = frameCurBufOffset
290
+ detachReadBuffers(bufferstate)
263
291
  return
264
292
  }
265
- plength = readQWord(bufferstate)
293
+ let longlength = readQWord(bufferstate)
294
+
295
+ if (
296
+ typeof longlength !== 'undefined' &&
297
+ longlength <= BigInt(Number.MAX_SAFE_INTEGER)
298
+ ) {
299
+ plength = Number(longlength)
300
+ } else {
301
+ plength = undefined
302
+ }
303
+ }
304
+ if (
305
+ typeof plength === 'undefined' ||
306
+ plength >
307
+ Math.max(
308
+ 16 * Number(this.session.flowController.receiveWindowSize),
309
+ 1000000
310
+ )
311
+ ) {
312
+ // too long abort, could be an attack vector
313
+ this.session.closeConnection({
314
+ code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, // probably the right one...
315
+ reason: plength
316
+ ? 'Frame length too big :' + plength
317
+ : 'Frame length overflow'
318
+ })
319
+ return
266
320
  }
267
321
  if (bufferstate.size < mlength + bufferstate.offset) {
268
- this.saveddata = Buffer.from(
269
- bufferstate.buffer.buffer,
270
- framestart,
271
- framemaxlength
272
- )
322
+ bufferstate.offset = framestart
323
+ bufferstate.curBuf = frameCurBuf
324
+ bufferstate.curBufOffset = frameCurBufOffset
325
+ detachReadBuffers(bufferstate)
273
326
  return
274
327
  }
275
328
  if (mask) {
@@ -282,6 +335,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
282
335
  }
283
336
  if (!fin && opcode !== WebSocketParser.WS_CONTINUE) {
284
337
  this.lastopcode = opcode
338
+ this.contframes = undefined // reset
285
339
  }
286
340
  if (fin) this.lastopcode = undefined
287
341
  if (
@@ -292,22 +346,24 @@ export class WebSocketParser extends ParserBaseHttp2 {
292
346
  const length = plength
293
347
 
294
348
  if (
349
+ typeof length === 'undefined' ||
295
350
  length >
296
- 4 * Number(this.session.flowController.receiveWindowSize)
351
+ 4 * Number(this.session.flowController.receiveWindowSize)
297
352
  ) {
298
353
  // too long abort, could be an attack vector
299
354
  this.session.closeConnection({
300
355
  code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, // probably the right one...
301
- reason: 'Frame length too big :' + length
356
+ reason: length
357
+ ? 'Frame length too big :' + length
358
+ : 'Frame length overflow'
302
359
  })
303
360
  return
304
361
  } else {
305
362
  if (bufferstate.size < length + bufferstate.offset) {
306
- this.saveddata = Buffer.from(
307
- bufferstate.buffer.buffer,
308
- framestart,
309
- framemaxlength
310
- )
363
+ bufferstate.offset = framestart
364
+ bufferstate.curBuf = frameCurBuf
365
+ bufferstate.curBufOffset = frameCurBufOffset
366
+ detachReadBuffers(bufferstate)
311
367
  return
312
368
  }
313
369
  }
@@ -315,29 +371,16 @@ export class WebSocketParser extends ParserBaseHttp2 {
315
371
  case WebSocketParser.WS_CLOSE:
316
372
  if (!this.closesend) {
317
373
  if (this.maskcontext)
318
- applyMask(
319
- this.maskcontext,
320
- bufferstate.buffer,
321
- bufferstate.buffer.byteOffset + bufferstate.offset,
322
- length
323
- )
374
+ applyMaskBuffer(this.maskcontext, bufferstate, length)
324
375
  this.sendCloseInt(
325
- new Uint8Array(
326
- bufferstate.buffer.buffer,
327
- bufferstate.buffer.byteOffset + bufferstate.offset,
328
- length
329
- )
376
+ getUint8ArrayfromBuffer(bufferstate, length)
330
377
  )
331
378
  let code = 0
332
379
  let error = 'Session websocket closed'
333
380
  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)
381
+ const wbufferstate = { ...bufferstate } // working copy
382
+ code = readUInt16BE(wbufferstate)
383
+ const terror = readString(wbufferstate, length - 2)
341
384
  let tokens = terror.split(':')
342
385
  if (tokens.length > 1) {
343
386
  code = parseInt(tokens[0])
@@ -345,33 +388,32 @@ export class WebSocketParser extends ParserBaseHttp2 {
345
388
  }
346
389
  error = tokens.join(':')
347
390
  }
348
- this.session.jsobj.onClose({
349
- errorcode: code,
350
- error
351
- })
391
+ if (
392
+ !(
393
+ this.session.jsobj.state === 'failed' ||
394
+ this.session.jsobj.state === 'closed'
395
+ )
396
+ ) {
397
+ this.session.jsobj.onClose({
398
+ errorcode: code,
399
+ error
400
+ })
401
+ }
352
402
  } else {
353
403
  // just the answer
354
404
  }
355
405
  break
356
406
  case WebSocketParser.WS_PING:
357
407
  if (this.maskcontext)
358
- applyMask(
359
- this.maskcontext,
360
- bufferstate.buffer,
361
- bufferstate.buffer.byteOffset + bufferstate.offset,
362
- length
363
- )
408
+ applyMaskBuffer(this.maskcontext, bufferstate, length)
364
409
  this.sendPong(
365
- new Uint8Array(
366
- bufferstate.buffer.buffer,
367
- bufferstate.buffer.byteOffset + bufferstate.offset,
368
- length
369
- )
410
+ getUint8ArrayfromBuffer(bufferstate, length) ||
411
+ new Uint8Array()
370
412
  )
371
413
  break
372
414
  default: // aka pong
373
415
  }
374
- bufferstate.offset += length
416
+ advanceBufferBy(bufferstate, length)
375
417
  } else if (
376
418
  opcode === WebSocketParser.WS_BINARY ||
377
419
  (opcode === WebSocketParser.WS_CONTINUE &&
@@ -384,17 +426,16 @@ export class WebSocketParser extends ParserBaseHttp2 {
384
426
  if (plength === 0) {
385
427
  log('warning empty data frame')
386
428
  // empty frame ?
387
- bufferstate.offset += plength
429
+ advanceBufferBy(bufferstate, plength)
388
430
  continue
389
431
  }
390
432
 
391
433
  if (opcode === WebSocketParser.WS_BINARY) {
392
434
  if (bufferstate.size < 2 + bufferstate.offset) {
393
- this.saveddata = Buffer.from(
394
- bufferstate.buffer.buffer,
395
- framestart,
396
- framemaxlength
397
- )
435
+ bufferstate.offset = framestart
436
+ bufferstate.curBuf = frameCurBuf
437
+ bufferstate.curBufOffset = frameCurBufOffset
438
+ detachReadBuffers(bufferstate)
398
439
  return
399
440
  }
400
441
  continuep = false
@@ -408,11 +449,10 @@ export class WebSocketParser extends ParserBaseHttp2 {
408
449
  typeof type === 'undefined' ||
409
450
  bufferstate.size < 1 + bufferstate.offset
410
451
  ) {
411
- this.saveddata = Buffer.from(
412
- bufferstate.buffer.buffer,
413
- framestart,
414
- framemaxlength
415
- )
452
+ bufferstate.offset = framestart
453
+ bufferstate.curBuf = frameCurBuf
454
+ bufferstate.curBufOffset = frameCurBufOffset
455
+ detachReadBuffers(bufferstate)
416
456
  return
417
457
  }
418
458
  this.curtype = type
@@ -439,19 +479,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
439
479
  checklength = Math.min(length, 64) // stream id + some Data
440
480
  }
441
481
  if (bufferstate.size < checklength + bufferstate.offset) {
442
- this.saveddata = Buffer.from(
443
- bufferstate.buffer.buffer,
444
- framestart,
445
- framemaxlength
446
- )
482
+ bufferstate.offset = framestart
483
+ bufferstate.curBuf = frameCurBuf
484
+ bufferstate.curBufOffset = frameCurBufOffset
485
+ detachReadBuffers(bufferstate)
447
486
  return
448
487
  }
449
488
  // all safeguards passed now apply the mask
450
489
  if (this.maskcontext)
451
- applyMask(
490
+ applyMaskBuffer(
452
491
  this.maskcontext,
453
- bufferstate.buffer,
454
- bufferstate.buffer.byteOffset + bufferstate.offset,
492
+ bufferstate,
455
493
  offsetend - bufferstate.offset
456
494
  )
457
495
  let streamid
@@ -461,44 +499,34 @@ export class WebSocketParser extends ParserBaseHttp2 {
461
499
  type !== ParserBase.WT_STREAM_WFIN
462
500
  ) {
463
501
  if (fin) {
464
- wbufferstate = bufferstate
502
+ wbufferstate = { ...bufferstate }
465
503
  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
- )
504
+ const newframes = getUint8ArraysfromBuffer(
505
+ bufferstate,
506
+ offsetend - bufferstate.offset
472
507
  )
508
+ if (newframes) this.contframes.push(...newframes)
473
509
  // we need to concat all buffers super
474
510
  const nsize = this.contframes.reduce(
475
511
  (length, val) => length + val.byteLength,
476
512
  0
477
513
  )
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 }
514
+ wbufferstate = {
515
+ offset: 0,
516
+ size: nsize,
517
+ buffer: this.contframes, // Fix me
518
+ curBuf: 0,
519
+ curBufOffset: 0
520
+ }
521
+ this.contframes = undefined // reset
488
522
  }
489
523
  } else {
490
- if (!this.contframes)
491
- /**
492
- * @type {Uint8Array[]}
493
- */
494
- this.contframes = []
495
- this.contframes.push(
496
- new Uint8Array(
497
- bufferstate.buffer.buffer,
498
- bufferstate.buffer.byteOffset + bufferstate.offset,
499
- offsetend - bufferstate.offset
500
- )
524
+ if (!this.contframes) this.contframes = []
525
+ const newframes = getUint8ArraysfromBuffer(
526
+ bufferstate,
527
+ offsetend - bufferstate.offset
501
528
  )
529
+ if (newframes) this.contframes.push(...newframes)
502
530
  }
503
531
  }
504
532
 
@@ -552,19 +580,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
552
580
  const fin =
553
581
  type === ParserBase.WT_STREAM_WFIN &&
554
582
  bufferstate.size >= length + offsetbegin
555
- if (fin) object.onFin()
556
583
  object.recvData({
557
584
  data:
558
585
  offsetend - bufferstate.offset > 0
559
- ? new Uint8Array(
560
- bufferstate.buffer.buffer,
561
- bufferstate.buffer.byteOffset +
562
- bufferstate.offset,
586
+ ? getUint8ArraysfromBuffer(
587
+ bufferstate,
563
588
  offsetend - bufferstate.offset
564
589
  )
565
590
  : undefined,
566
591
  fin
567
592
  })
593
+ if (fin) object.onFin()
568
594
  }
569
595
  }
570
596
  break
@@ -612,13 +638,15 @@ export class WebSocketParser extends ParserBaseHttp2 {
612
638
  {
613
639
  const code = readUint32(bufferstate) || 0
614
640
  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
- )
641
+ const reason =
642
+ offsetend - bufferstate.offset > 0
643
+ ? decoder.decode(
644
+ getUint8ArrayfromBuffer(
645
+ bufferstate,
646
+ offsetend - bufferstate.offset
647
+ )
648
+ )
649
+ : ''
622
650
  this.onCloseWebTransportSession({ code, reason })
623
651
  }
624
652
  break
@@ -628,11 +656,11 @@ export class WebSocketParser extends ParserBaseHttp2 {
628
656
  case ParserBase.DATAGRAM:
629
657
  if (wbufferstate) {
630
658
  this.session.jsobj.onDatagramReceived({
631
- datagram: new Uint8Array(
632
- wbufferstate.buffer.buffer,
633
- wbufferstate.buffer.byteOffset + wbufferstate.offset,
634
- offsetend - wbufferstate.offset
635
- )
659
+ datagram:
660
+ getUint8ArrayfromBuffer(
661
+ wbufferstate,
662
+ offsetend - wbufferstate.offset
663
+ ) || new Uint8Array(0)
636
664
  })
637
665
  }
638
666
  break
@@ -643,12 +671,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
643
671
  if (bufferstate.size < length + offsetbegin) {
644
672
  this.remainlength = length + offsetbegin - bufferstate.size
645
673
  this.mode = 'c'
646
- if (streamid) {
674
+ if (typeof streamid !== 'undefined') {
647
675
  this.rstreamid = streamid
648
676
  this.rfin = type === ParserBase.WT_STREAM_WFIN
649
677
  }
650
678
  }
651
- bufferstate.offset = offsetend
679
+ advanceBufferToOffset(bufferstate, offsetend)
652
680
  } else {
653
681
  const length = plength
654
682
  if (bufferstate.offset + length > bufferstate.size) {
@@ -656,9 +684,9 @@ export class WebSocketParser extends ParserBaseHttp2 {
656
684
  this.rstreamid = undefined
657
685
  this.remainlength =
658
686
  bufferstate.offset + length - bufferstate.size
659
- bufferstate.offset = bufferstate.size
687
+ advanceBufferToOffset(bufferstate, bufferstate.size)
660
688
  } else {
661
- bufferstate.offset += length
689
+ advanceBufferBy(bufferstate, length)
662
690
  }
663
691
  }
664
692
  }
@@ -669,7 +697,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
669
697
  bufferstate.size - bufferstate.offset,
670
698
  this.remainlength
671
699
  )
672
- if (this.rstreamid) {
700
+ if (typeof this.rstreamid !== 'undefined') {
673
701
  // not in j mode
674
702
  // TODO submitData
675
703
  const object = this.wtstreams.get(this.rstreamid)
@@ -679,26 +707,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
679
707
  if (object) {
680
708
  if (fin) object.onFin()
681
709
  if (this.maskcontext)
682
- applyMask(
683
- this.maskcontext,
684
- bufferstate.buffer,
685
- bufferstate.buffer.byteOffset + bufferstate.offset,
686
- clength
687
- )
710
+ applyMaskBuffer(this.maskcontext, bufferstate, clength)
688
711
  // TODO submit data
689
712
  object.recvData({
690
- data: new Uint8Array(
691
- bufferstate.buffer.buffer,
692
- bufferstate.buffer.byteOffset + bufferstate.offset,
693
- clength
694
- ),
713
+ data: getUint8ArraysfromBuffer(bufferstate, clength),
695
714
  fin
696
715
  })
697
716
  }
698
717
  }
699
718
 
700
719
  this.remainlength = this.remainlength - clength
701
- bufferstate.offset += clength
720
+ advanceBufferBy(bufferstate, clength)
702
721
  if (this.remainlength === 0) {
703
722
  this.maskcontext = undefined
704
723
  this.mode = 's'
@@ -709,6 +728,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
709
728
  break
710
729
  }
711
730
  }
731
+ detachReadBuffers(bufferstate)
712
732
  }
713
733
 
714
734
  /**
@@ -754,8 +774,14 @@ export class WebSocketParser extends ParserBaseHttp2 {
754
774
  headlength += 2
755
775
  }
756
776
 
757
- const cdata = Buffer.alloc(headlength)
758
- const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
777
+ const cdata = new Uint8Array(headlength)
778
+ const bufferstate = {
779
+ offset: 0,
780
+ size: cdata.length,
781
+ buffer: [cdata],
782
+ curBuf: 0,
783
+ curBufOffset: 0
784
+ }
759
785
  const maskstate = this.writeHeader(bufferstate, {
760
786
  opcode: WebSocketParser.WS_BINARY,
761
787
  plength,
@@ -768,19 +794,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
768
794
  for (const ind in headerVints) writeVarInt(bufferstate, headerVints[ind])
769
795
  const endmask = bufferstate.offset
770
796
  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
- )
797
+ applyMaskBuffer(maskstate, bufferstate, endmask - beginmask)
798
+ if (payload) applyMask(maskstate, payload, 0, payload.byteLength)
784
799
  }
785
800
  let blocked = false
786
801
  if (payload) {
@@ -795,12 +810,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
795
810
  }
796
811
 
797
812
  /**
798
- * @param {{ offset: number; size?: number; buffer: Buffer; }} bs
813
+ * @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
799
814
  * @param {{ opcode: number; plength: number; mask: number; }} args
800
815
  */
801
816
  writeHeader(bs, { opcode, plength, mask }) {
802
- bs.buffer.writeUint8(0x80 | (opcode & 0x0f), bs.offset)
803
- bs.offset++
817
+ writeUInt8(bs, 0x80 | (opcode & 0x0f))
818
+ advanceBuffer(bs)
804
819
  let splength = plength
805
820
  if (plength > 0xffff) {
806
821
  splength = 127
@@ -808,20 +823,17 @@ export class WebSocketParser extends ParserBaseHttp2 {
808
823
  splength = 126
809
824
  }
810
825
 
811
- bs.buffer.writeUint8((mask && 0x80) | (splength & 0x7f), bs.offset)
812
- bs.offset++
826
+ writeUInt8(bs, (mask && 0x80) | (splength & 0x7f))
827
+ advanceBuffer(bs)
813
828
  if (plength > 0xffff) {
814
- bs.buffer.writeBigInt64BE(BigInt(plength), bs.offset)
815
- bs.offset += 8
829
+ writeBigInt64BE(bs, BigInt(plength))
816
830
  } else if (plength > 125) {
817
- bs.buffer.writeUint16BE(plength, bs.offset)
818
- bs.offset += 2
831
+ writeUint16BE(bs, plength)
819
832
  }
820
833
  let maskstate
821
834
  if (mask) {
822
835
  maskstate = { offset: 0, mask: randomBytes(4) }
823
- bs.buffer.writeUint32BE(maskstate.mask.readUInt32BE(0), bs.offset)
824
- bs.offset += 4
836
+ writeUint32BE(bs, maskstate.mask.readUInt32BE(0))
825
837
  }
826
838
  return maskstate
827
839
  }
@@ -842,8 +854,14 @@ export class WebSocketParser extends ParserBaseHttp2 {
842
854
  } else if (plength > 125) {
843
855
  headlength += 2
844
856
  }
845
- const cdata = Buffer.alloc(headlength)
846
- const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
857
+ const cdata = new Uint8Array(headlength)
858
+ const bufferstate = {
859
+ offset: 0,
860
+ size: cdata.length,
861
+ buffer: [cdata],
862
+ curBuf: 0,
863
+ curBufOffset: 0
864
+ }
847
865
  const maskstate = this.writeHeader(bufferstate, { opcode, plength, mask })
848
866
 
849
867
  if (bufferstate.offset !== headlength)
@@ -851,12 +869,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
851
869
 
852
870
  let blocked = !this.stream.write(cdata)
853
871
  if (maskstate && payload)
854
- applyMask(
855
- maskstate,
856
- Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength),
857
- 0,
858
- payload.byteLength
859
- )
872
+ applyMask(maskstate, payload, 0, payload.byteLength)
860
873
  if (payload) blocked = !this.stream.write(payload) || blocked
861
874
  // do something if blocked
862
875
  if (blocked) this.blocked = true