@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.
- package/dist/lib/http2/browser/browserparser.d.ts +12 -15
- package/dist/lib/http2/browser/browserparser.d.ts.map +1 -1
- package/dist/lib/http2/bufferHelper.d.ts +178 -0
- package/dist/lib/http2/bufferHelper.d.ts.map +1 -0
- package/dist/lib/http2/node/capsuleparser.d.ts +12 -6
- package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -1
- package/dist/lib/http2/node/websocketparser.d.ts +24 -14
- package/dist/lib/http2/node/websocketparser.d.ts.map +1 -1
- package/dist/lib/http2/parserbase.d.ts +2 -7
- package/dist/lib/http2/parserbase.d.ts.map +1 -1
- package/dist/lib/http2/parserbasehttp2.d.ts +0 -25
- package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -1
- package/dist/lib/http2/stream.d.ts +2 -2
- package/dist/lib/http2/stream.d.ts.map +1 -1
- package/eslint.config.js +1 -1
- package/lib/http2/browser/browserparser.js +53 -89
- package/lib/http2/bufferHelper.js +269 -0
- package/lib/http2/node/capsuleparser.js +69 -61
- package/lib/http2/node/server.js +1 -1
- package/lib/http2/node/websocketparser.js +266 -253
- package/lib/http2/parserbase.js +13 -18
- package/lib/http2/parserbasehttp2.js +12 -67
- package/lib/http2/stream.js +19 -9
- package/lib/session.js +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
|
-
import { ParserBase
|
|
2
|
+
import { ParserBase } from '../parserbase.js'
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
advanceBufferToOffset,
|
|
5
|
+
lengthVarInt,
|
|
5
6
|
readVarInt,
|
|
6
7
|
writeVarInt,
|
|
7
|
-
|
|
8
|
-
|
|
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:
|
|
31
|
+
* @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
|
|
18
32
|
*/
|
|
19
33
|
function readByte(bs) {
|
|
20
|
-
const val =
|
|
21
|
-
bs
|
|
34
|
+
const val = readUInt8(bs)
|
|
35
|
+
advanceBuffer(bs)
|
|
22
36
|
return val
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
/**
|
|
26
|
-
* @param{{offset: Number, buffer:
|
|
40
|
+
* @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
|
|
27
41
|
*/
|
|
28
42
|
function readWord(bs) {
|
|
29
|
-
let val =
|
|
30
|
-
bs
|
|
31
|
-
val = (val << 8) |
|
|
32
|
-
bs
|
|
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:
|
|
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 =
|
|
42
|
-
bs
|
|
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) | (
|
|
53
|
-
bs
|
|
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:
|
|
74
|
+
* @param{{offset: Number, buffer: Uint8Array[], size: Number, curBuf: Number, curBufOffset: Number}} bs
|
|
61
75
|
*/
|
|
62
76
|
function readQWord(bs) {
|
|
63
|
-
let val =
|
|
64
|
-
bs
|
|
65
|
-
val = (val <<
|
|
66
|
-
bs
|
|
67
|
-
val = (val <<
|
|
68
|
-
bs
|
|
69
|
-
val = (val <<
|
|
70
|
-
bs
|
|
71
|
-
val = (val <<
|
|
72
|
-
bs
|
|
73
|
-
val = (val <<
|
|
74
|
-
bs
|
|
75
|
-
val = (val <<
|
|
76
|
-
bs
|
|
77
|
-
val = (val <<
|
|
78
|
-
bs
|
|
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{
|
|
98
|
+
* @param{Uint8Array} data
|
|
85
99
|
* @param{number} offset
|
|
86
100
|
* @param{number} length
|
|
87
101
|
*/
|
|
88
|
-
function applyMask(ms,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
bs.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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:
|
|
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] =
|
|
158
|
-
bs
|
|
159
|
-
val[1] =
|
|
160
|
-
bs
|
|
161
|
-
val[2] =
|
|
162
|
-
bs
|
|
163
|
-
val[3] =
|
|
164
|
-
bs
|
|
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
|
-
|
|
200
|
-
|
|
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 {
|
|
243
|
+
* @param {Uint8Array[]} data
|
|
210
244
|
*/
|
|
211
245
|
parseData(data) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
224
|
-
const
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
287
|
+
bufferstate.offset = framestart
|
|
288
|
+
bufferstate.curBuf = frameCurBuf
|
|
289
|
+
bufferstate.curBufOffset = frameCurBufOffset
|
|
290
|
+
detachReadBuffers(bufferstate)
|
|
263
291
|
return
|
|
264
292
|
}
|
|
265
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
490
|
+
applyMaskBuffer(
|
|
452
491
|
this.maskcontext,
|
|
453
|
-
bufferstate
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
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
|
-
?
|
|
560
|
-
bufferstate
|
|
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 =
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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:
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
|
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
|
|
687
|
+
advanceBufferToOffset(bufferstate, bufferstate.size)
|
|
660
688
|
} else {
|
|
661
|
-
bufferstate
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 =
|
|
758
|
-
const bufferstate = {
|
|
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
|
-
|
|
772
|
-
|
|
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
|
|
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
|
|
803
|
-
bs
|
|
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
|
|
812
|
-
bs
|
|
826
|
+
writeUInt8(bs, (mask && 0x80) | (splength & 0x7f))
|
|
827
|
+
advanceBuffer(bs)
|
|
813
828
|
if (plength > 0xffff) {
|
|
814
|
-
|
|
815
|
-
bs.offset += 8
|
|
829
|
+
writeBigInt64BE(bs, BigInt(plength))
|
|
816
830
|
} else if (plength > 125) {
|
|
817
|
-
|
|
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
|
-
|
|
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 =
|
|
846
|
-
const bufferstate = {
|
|
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
|