@fails-components/webtransport 1.1.4 → 1.2.0
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/dom.d.ts +27 -10
- package/dist/lib/dom.d.ts.map +1 -1
- package/dist/lib/http2/browser/browser.d.ts +5 -2
- package/dist/lib/http2/browser/browser.d.ts.map +1 -1
- package/dist/lib/http2/browser/browserparser.d.ts.map +1 -1
- package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -1
- package/dist/lib/http2/node/client.d.ts +3 -2
- package/dist/lib/http2/node/client.d.ts.map +1 -1
- package/dist/lib/http2/node/server.d.ts +10 -5
- package/dist/lib/http2/node/server.d.ts.map +1 -1
- package/dist/lib/http2/node/websocketparser.d.ts.map +1 -1
- package/dist/lib/http2/parserbase.d.ts +33 -1
- package/dist/lib/http2/parserbase.d.ts.map +1 -1
- package/dist/lib/http2/priorityscheduler.d.ts +167 -0
- package/dist/lib/http2/priorityscheduler.d.ts.map +1 -0
- package/dist/lib/http2/session.d.ts +25 -16
- package/dist/lib/http2/session.d.ts.map +1 -1
- package/dist/lib/http2/stream.d.ts +10 -0
- package/dist/lib/http2/stream.d.ts.map +1 -1
- package/dist/lib/http2/websocketcommon.d.ts.map +1 -1
- package/dist/lib/server.d.ts.map +1 -1
- package/dist/lib/session.d.ts +31 -17
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/stream.d.ts +13 -0
- package/dist/lib/stream.d.ts.map +1 -1
- package/dist/lib/types.d.ts +18 -15
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/webtransport.browser.d.ts +3 -2
- package/dist/lib/webtransport.browser.d.ts.map +1 -1
- package/dist/lib/webtransportbase.d.ts +1 -0
- package/dist/lib/webtransportbase.d.ts.map +1 -1
- package/eslint.config.js +20 -22
- package/lib/dom.ts +31 -13
- package/lib/http2/browser/browser.js +28 -13
- package/lib/http2/browser/browserparser.js +4 -1
- package/lib/http2/node/capsuleparser.js +4 -1
- package/lib/http2/node/client.js +38 -27
- package/lib/http2/node/server.js +56 -19
- package/lib/http2/node/websocketparser.js +5 -2
- package/lib/http2/parserbase.js +76 -7
- package/lib/http2/priorityscheduler.js +654 -0
- package/lib/http2/session.js +69 -20
- package/lib/http2/stream.js +60 -10
- package/lib/http2/websocketcommon.js +1 -1
- package/lib/server.js +0 -1
- package/lib/session.js +128 -64
- package/lib/stream.js +50 -8
- package/lib/types.ts +15 -8
- package/lib/webtransport.browser.js +77 -18
- package/lib/webtransportbase.js +7 -0
- package/old_test/testsuite.js +6 -2
- package/package.json +6 -6
- package/readme.md +3 -1
|
@@ -162,7 +162,10 @@ export class BrowserParser extends ParserBase {
|
|
|
162
162
|
if (typeof streamid !== 'undefined') {
|
|
163
163
|
let object = this.wtstreams.get(streamid)
|
|
164
164
|
if (!object) {
|
|
165
|
-
object = this.newStream(streamid
|
|
165
|
+
object = this.newStream(streamid, {
|
|
166
|
+
sendOrder: 0n,
|
|
167
|
+
sendGroupId: 0n
|
|
168
|
+
})
|
|
166
169
|
if (!object) return // stream broken
|
|
167
170
|
}
|
|
168
171
|
// TODO submit data
|
|
@@ -154,7 +154,10 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
154
154
|
if (typeof streamid !== 'undefined') {
|
|
155
155
|
let object = this.wtstreams.get(streamid)
|
|
156
156
|
if (!object) {
|
|
157
|
-
object = this.newStream(streamid
|
|
157
|
+
object = this.newStream(streamid, {
|
|
158
|
+
sendOrder: 0n,
|
|
159
|
+
sendGroupId: 0n
|
|
160
|
+
})
|
|
158
161
|
if (!object) return // stream broken
|
|
159
162
|
}
|
|
160
163
|
// TODO submit data
|
package/lib/http2/node/client.js
CHANGED
|
@@ -15,6 +15,7 @@ export class Http2WebTransportClient {
|
|
|
15
15
|
this.port = Number(port)
|
|
16
16
|
this.hostname = args?.host || 'localhost'
|
|
17
17
|
this.serverCertificateHashes = args?.serverCertificateHashes || undefined
|
|
18
|
+
this.protocols = args?.protocols || []
|
|
18
19
|
this.localPort = Number(args?.localPort) || undefined
|
|
19
20
|
this.allowPooling = args?.allowPooling || false
|
|
20
21
|
this.forceIpv6 = args?.forceIpv6 || false
|
|
@@ -29,12 +30,12 @@ export class Http2WebTransportClient {
|
|
|
29
30
|
args?.initialUnidirectionalSendStreams || 100
|
|
30
31
|
|
|
31
32
|
this.streamShouldAutoTuneReceiveWindow =
|
|
32
|
-
args.streamShouldAutoTuneReceiveWindow ||
|
|
33
|
+
args.streamShouldAutoTuneReceiveWindow || true
|
|
33
34
|
this.streamFlowControlWindowSizeLimit =
|
|
34
35
|
args?.streamFlowControlWindowSizeLimit || 6 * 1024 * 1024
|
|
35
36
|
|
|
36
37
|
this.sessionShouldAutoTuneReceiveWindow =
|
|
37
|
-
args.sessionShouldAutoTuneReceiveWindow ||
|
|
38
|
+
args.sessionShouldAutoTuneReceiveWindow || true
|
|
38
39
|
this.sessionFlowControlWindowSizeLimit =
|
|
39
40
|
args?.sessionFlowControlWindowSizeLimit || 15 * 1024 * 1024
|
|
40
41
|
/** @type {import('../../session.js').HttpClient} */
|
|
@@ -92,9 +93,9 @@ export class Http2WebTransportClient {
|
|
|
92
93
|
0x2b63: this.initialStreamFlowControlWindow, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAM_DATA_BIDI
|
|
93
94
|
0x2b64: this.initialUnidirectionalStreams, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_UNI
|
|
94
95
|
0x2b65: this.initialBidirectionalStreams // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_BIDI
|
|
95
|
-
}
|
|
96
|
-
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
|
|
96
|
+
}
|
|
97
97
|
},
|
|
98
|
+
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65],
|
|
98
99
|
localPort: this.localPort,
|
|
99
100
|
// TODO: REMOVE BEFORE RELEASE; UNSAFE SETTING
|
|
100
101
|
rejectUnauthorized: !this.serverCertificateHashes
|
|
@@ -109,26 +110,8 @@ export class Http2WebTransportClient {
|
|
|
109
110
|
http2Options
|
|
110
111
|
)
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
let
|
|
114
|
-
// ok we got a session and want to measure RTT
|
|
115
|
-
let pingsender = setInterval(() => {
|
|
116
|
-
if (this.clientInt && !this.clientInt.closed) {
|
|
117
|
-
// eslint-disable-next-line no-unused-vars
|
|
118
|
-
this.clientInt.ping((err, duration, payload) => {
|
|
119
|
-
if (!err) {
|
|
120
|
-
rtt = adjust * duration + (1 - adjust) * rtt
|
|
121
|
-
adjust = 0.2
|
|
122
|
-
// @ts-ignore
|
|
123
|
-
this.clientInt.WTrtt = rtt
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
} else {
|
|
127
|
-
clearInterval(pingsender)
|
|
128
|
-
// @ts-ignore
|
|
129
|
-
pingsender = undefined
|
|
130
|
-
}
|
|
131
|
-
}, 1000)
|
|
113
|
+
/** @type {NodeJS.Timeout|undefined} */
|
|
114
|
+
let pingsender
|
|
132
115
|
|
|
133
116
|
this.clientInt.on('close', () => {
|
|
134
117
|
if (pingsender) clearInterval(pingsender)
|
|
@@ -180,6 +163,28 @@ export class Http2WebTransportClient {
|
|
|
180
163
|
this.jsobj.onClientConnected({
|
|
181
164
|
success: true
|
|
182
165
|
})
|
|
166
|
+
let rtt = 100
|
|
167
|
+
let adjust = 1
|
|
168
|
+
// ok we got a session and want to measure RTT
|
|
169
|
+
const pingupdater = () => {
|
|
170
|
+
if (this.clientInt && !this.clientInt.closed) {
|
|
171
|
+
// eslint-disable-next-line no-unused-vars
|
|
172
|
+
this.clientInt.ping((err, duration, payload) => {
|
|
173
|
+
if (!err) {
|
|
174
|
+
rtt = adjust * duration + (1 - adjust) * rtt
|
|
175
|
+
adjust = 0.2
|
|
176
|
+
// @ts-ignore
|
|
177
|
+
this.clientInt.WTrtt = rtt
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
} else {
|
|
181
|
+
clearInterval(pingsender)
|
|
182
|
+
// @ts-ignore
|
|
183
|
+
pingsender = undefined
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
pingsender = setInterval(pingupdater, 1000)
|
|
187
|
+
pingupdater()
|
|
183
188
|
}
|
|
184
189
|
})
|
|
185
190
|
this.clientInt.on('error', (error) => {
|
|
@@ -207,14 +212,20 @@ export class Http2WebTransportClient {
|
|
|
207
212
|
openWTSession(path) {
|
|
208
213
|
if (!this.clientInt) throw new Error('clientInt not present')
|
|
209
214
|
|
|
210
|
-
const
|
|
215
|
+
const requestOpts = {
|
|
211
216
|
':method': 'CONNECT',
|
|
212
217
|
':protocol': 'webtransport',
|
|
213
218
|
':scheme': 'https',
|
|
214
219
|
':path': path,
|
|
215
220
|
authority: this.hostname,
|
|
216
221
|
origin: this.hostname
|
|
217
|
-
}
|
|
222
|
+
}
|
|
223
|
+
if (this.protocols.length > 0) {
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
requestOpts['wt-available-protocols'] = this.protocols.join(',')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const stream = this.clientInt.request(requestOpts)
|
|
218
229
|
const {
|
|
219
230
|
0x2b65: remoteBidirectionalStreams = undefined,
|
|
220
231
|
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
@@ -255,7 +266,7 @@ export class Http2WebTransportClient {
|
|
|
255
266
|
sendWindowOffset:
|
|
256
267
|
remoteSessionFlowControlWindow ||
|
|
257
268
|
this.sessionFlowControlWindowSizeLimit,
|
|
258
|
-
receiveWindowOffset: this.
|
|
269
|
+
receiveWindowOffset: this.initialSessionFlowControlWindow,
|
|
259
270
|
shouldAutoTuneReceiveWindow: this.sessionShouldAutoTuneReceiveWindow,
|
|
260
271
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
|
261
272
|
}),
|
package/lib/http2/node/server.js
CHANGED
|
@@ -33,12 +33,12 @@ export class Http2WebTransportServer {
|
|
|
33
33
|
args?.initialUnidirectionalStreams || 100
|
|
34
34
|
|
|
35
35
|
this.streamShouldAutoTuneReceiveWindow =
|
|
36
|
-
args.streamShouldAutoTuneReceiveWindow ||
|
|
36
|
+
args.streamShouldAutoTuneReceiveWindow || true
|
|
37
37
|
this.streamFlowControlWindowSizeLimit =
|
|
38
38
|
args?.streamFlowControlWindowSizeLimit || 6 * 1024 * 1024
|
|
39
39
|
|
|
40
40
|
this.sessionShouldAutoTuneReceiveWindow =
|
|
41
|
-
args.sessionShouldAutoTuneReceiveWindow ||
|
|
41
|
+
args.sessionShouldAutoTuneReceiveWindow || true
|
|
42
42
|
this.sessionFlowControlWindowSizeLimit =
|
|
43
43
|
args?.sessionFlowControlWindowSizeLimit || 15 * 1024 * 1024
|
|
44
44
|
|
|
@@ -65,9 +65,9 @@ export class Http2WebTransportServer {
|
|
|
65
65
|
0x2b63: this.initialStreamFlowControlWindow, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAM_DATA_BIDI
|
|
66
66
|
0x2b64: this.initialUnidirectionalStreams, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_UNI
|
|
67
67
|
0x2b65: this.initialBidirectionalStreams // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_BIDI
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
this.serverInt.on('listening', () => {
|
|
@@ -95,7 +95,7 @@ export class Http2WebTransportServer {
|
|
|
95
95
|
let rtt = 100
|
|
96
96
|
let adjust = 1
|
|
97
97
|
// ok we got a session and want to measure RTT
|
|
98
|
-
|
|
98
|
+
const pingupdater = () => {
|
|
99
99
|
if (!session.closed)
|
|
100
100
|
// eslint-disable-next-line no-unused-vars
|
|
101
101
|
session.ping((err, duration, payload) => {
|
|
@@ -111,7 +111,9 @@ export class Http2WebTransportServer {
|
|
|
111
111
|
// @ts-ignore
|
|
112
112
|
pingsender = undefined
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
}
|
|
115
|
+
let pingsender = setInterval(pingupdater, 1000)
|
|
116
|
+
pingupdater()
|
|
115
117
|
|
|
116
118
|
session.on('close', () => {
|
|
117
119
|
if (pingsender) clearInterval(pingsender)
|
|
@@ -136,7 +138,11 @@ export class Http2WebTransportServer {
|
|
|
136
138
|
path = path?.slice(1)
|
|
137
139
|
}
|
|
138
140
|
const header = { ...request.headers, ':path': path }
|
|
139
|
-
const websocketProt =
|
|
141
|
+
const { websocketProt, webtransportProt } =
|
|
142
|
+
this.checkProtocolHeader(header)
|
|
143
|
+
if (webtransportProt) {
|
|
144
|
+
header['wt-available-protocols'] = webtransportProt
|
|
145
|
+
}
|
|
140
146
|
if (!websocketProt) {
|
|
141
147
|
stream.destroy()
|
|
142
148
|
return
|
|
@@ -227,7 +233,11 @@ export class Http2WebTransportServer {
|
|
|
227
233
|
header[':protocol'] === 'websocket' &&
|
|
228
234
|
header['sec-websocket-protocol']
|
|
229
235
|
) {
|
|
230
|
-
|
|
236
|
+
let obj = this.checkProtocolHeader(header)
|
|
237
|
+
websocketProt = obj.websocketProt
|
|
238
|
+
if (obj.webtransportProt) {
|
|
239
|
+
header['wt-available-protocols'] = obj.webtransportProt
|
|
240
|
+
}
|
|
231
241
|
}
|
|
232
242
|
if (header[':protocol'] !== 'webtransport' && !websocketProt) {
|
|
233
243
|
stream.respond({
|
|
@@ -334,8 +344,11 @@ export class Http2WebTransportServer {
|
|
|
334
344
|
const resp = {
|
|
335
345
|
':status': '200'
|
|
336
346
|
}
|
|
337
|
-
|
|
338
|
-
if (websocketProt)
|
|
347
|
+
|
|
348
|
+
if (websocketProt) {
|
|
349
|
+
// @ts-ignore
|
|
350
|
+
resp['sec-websocket-protocol'] = websocketProt
|
|
351
|
+
}
|
|
339
352
|
stream.respond(resp)
|
|
340
353
|
this.jsobj.onHttpWTSessionVisitor(retObj)
|
|
341
354
|
} else {
|
|
@@ -352,9 +365,12 @@ export class Http2WebTransportServer {
|
|
|
352
365
|
|
|
353
366
|
/**
|
|
354
367
|
* @param {import("http2").IncomingHttpHeaders} header
|
|
368
|
+
* @return {{websocketProt: string|undefined, webtransportProt?: string}}
|
|
355
369
|
*/
|
|
356
370
|
checkProtocolHeader(header) {
|
|
357
371
|
const sechead = header['sec-websocket-protocol']
|
|
372
|
+
?.split?.(',')
|
|
373
|
+
?.map?.((el) => el.trim())
|
|
358
374
|
let prots
|
|
359
375
|
if (!Array.isArray(sechead)) {
|
|
360
376
|
prots = [sechead]
|
|
@@ -365,8 +381,19 @@ export class Http2WebTransportServer {
|
|
|
365
381
|
.map((el) => (el ? el.split('_') : [undefined, undefined]))
|
|
366
382
|
.filter((el) => el[0] === 'webtransport')
|
|
367
383
|
.filter((el) => (el[1] ? supportedVersions.includes(el[1]) : false))
|
|
368
|
-
if (prots.length > 0)
|
|
369
|
-
|
|
384
|
+
if (prots.length > 0) {
|
|
385
|
+
const selWtVersion = prots[0][1]
|
|
386
|
+
prots = prots.filter((el) => el[1] === selWtVersion)
|
|
387
|
+
/** @type {{websocketProt: string|undefined, webtransportProt?: string}} */
|
|
388
|
+
const retObj = { websocketProt: prots[0].slice(0, 2).join('_') }
|
|
389
|
+
if (prots[0][2]) {
|
|
390
|
+
// @ts-ignore
|
|
391
|
+
retObj.webtransportProt = [
|
|
392
|
+
...new Set(prots.map((el) => el.slice(2).join('_')))
|
|
393
|
+
].join(',')
|
|
394
|
+
}
|
|
395
|
+
return retObj
|
|
396
|
+
} else return { websocketProt: undefined }
|
|
370
397
|
}
|
|
371
398
|
|
|
372
399
|
startServer() {
|
|
@@ -444,7 +471,8 @@ export class Http2WebTransportServer {
|
|
|
444
471
|
protocol,
|
|
445
472
|
head,
|
|
446
473
|
path,
|
|
447
|
-
transportPrivate
|
|
474
|
+
transportPrivate,
|
|
475
|
+
selectedProtocol
|
|
448
476
|
}) {
|
|
449
477
|
if (status !== 200) {
|
|
450
478
|
if (protocol === 'websocketoverhttp1') {
|
|
@@ -462,8 +490,11 @@ export class Http2WebTransportServer {
|
|
|
462
490
|
// @ts-ignore
|
|
463
491
|
stream,
|
|
464
492
|
header,
|
|
465
|
-
|
|
466
|
-
|
|
493
|
+
protocol: selectedProtocol
|
|
494
|
+
? // @ts-ignore
|
|
495
|
+
transportPrivate.websocketProt + '_' + selectedProtocol
|
|
496
|
+
: // @ts-ignore
|
|
497
|
+
transportPrivate.websocketProt
|
|
467
498
|
})
|
|
468
499
|
.then(() => {
|
|
469
500
|
const retObj = {
|
|
@@ -496,7 +527,7 @@ export class Http2WebTransportServer {
|
|
|
496
527
|
initialUnidirectionalReceiveStreams:
|
|
497
528
|
this.initialUnidirectionalStreams,
|
|
498
529
|
sendWindowOffset: 0,
|
|
499
|
-
receiveWindowOffset: this.
|
|
530
|
+
receiveWindowOffset: this.initialSessionFlowControlWindow,
|
|
500
531
|
shouldAutoTuneReceiveWindow:
|
|
501
532
|
this.sessionShouldAutoTuneReceiveWindow,
|
|
502
533
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
|
@@ -518,7 +549,13 @@ export class Http2WebTransportServer {
|
|
|
518
549
|
// @ts-ignore
|
|
519
550
|
if (transportPrivate?.websocketProt)
|
|
520
551
|
// @ts-ignore
|
|
521
|
-
resp['sec-websocket-protocol'] =
|
|
552
|
+
resp['sec-websocket-protocol'] = selectedProtocol
|
|
553
|
+
? // @ts-ignore
|
|
554
|
+
transportPrivate.websocketProt + '_' + selectedProtocol
|
|
555
|
+
: // @ts-ignore
|
|
556
|
+
transportPrivate.websocketProt
|
|
557
|
+
// @ts-ignore
|
|
558
|
+
else if (selectedProtocol) resp['wt-protocol'] = selectedProtocol
|
|
522
559
|
stream.respond(resp)
|
|
523
560
|
const {
|
|
524
561
|
0x2b65: remoteBidirectionalStreams = undefined,
|
|
@@ -587,7 +624,7 @@ export class Http2WebTransportServer {
|
|
|
587
624
|
? remoteSessionFlowControlWindow ||
|
|
588
625
|
this.sessionFlowControlWindowSizeLimit
|
|
589
626
|
: 0, // TODO, once supported by node, use initial settings
|
|
590
|
-
receiveWindowOffset: this.
|
|
627
|
+
receiveWindowOffset: this.initialSessionFlowControlWindow,
|
|
591
628
|
shouldAutoTuneReceiveWindow:
|
|
592
629
|
this.sessionShouldAutoTuneReceiveWindow,
|
|
593
630
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
|
@@ -132,8 +132,8 @@ function applyMask(ms, buffer, offset, length) {
|
|
|
132
132
|
for (let run = 0 /* Math.round(length / 4) * 4 */; run < length; run++) {
|
|
133
133
|
data[run] ^= workmask[run % 4]
|
|
134
134
|
}
|
|
135
|
-
ms.offset += length
|
|
136
135
|
}
|
|
136
|
+
ms.offset += length
|
|
137
137
|
}
|
|
138
138
|
/*
|
|
139
139
|
function readDWord(bs) {
|
|
@@ -541,7 +541,10 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
541
541
|
if (typeof streamid !== 'undefined') {
|
|
542
542
|
let object = this.wtstreams.get(streamid)
|
|
543
543
|
if (!object) {
|
|
544
|
-
object = this.newStream(streamid
|
|
544
|
+
object = this.newStream(streamid, {
|
|
545
|
+
sendOrder: 0n,
|
|
546
|
+
sendGroupId: 0n
|
|
547
|
+
})
|
|
545
548
|
if (!object) return // stream broken
|
|
546
549
|
}
|
|
547
550
|
// TODO submit data
|
package/lib/http2/parserbase.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Http2WebTransportStream } from './stream.js'
|
|
2
2
|
import { logger } from '../utils.js'
|
|
3
|
+
import { PriorityScheduler } from './priorityscheduler.js'
|
|
3
4
|
|
|
4
5
|
const pid = typeof process !== 'undefined' ? process.pid : 0
|
|
5
6
|
const log = logger(`webtransport:parserbase(${pid})`)
|
|
@@ -59,6 +60,8 @@ export class ParserBase {
|
|
|
59
60
|
|
|
60
61
|
/** @type {Map<bigint,Http2WebTransportStream>} */
|
|
61
62
|
this.wtstreams = new Map()
|
|
63
|
+
|
|
64
|
+
this.scheduler = new PriorityScheduler()
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
@@ -101,8 +104,9 @@ export class ParserBase {
|
|
|
101
104
|
|
|
102
105
|
/**
|
|
103
106
|
* @param {bigint} streamid
|
|
107
|
+
* @param {{sendOrder: bigint,sendGroupId: bigint}} priority
|
|
104
108
|
*/
|
|
105
|
-
newStream(streamid) {
|
|
109
|
+
newStream(streamid, priority) {
|
|
106
110
|
const incoming = this.isclient ? !(streamid & 0x1n) : !!(streamid & 0x1n)
|
|
107
111
|
const streamIdManager =
|
|
108
112
|
streamid & 0x2n
|
|
@@ -138,16 +142,36 @@ export class ParserBase {
|
|
|
138
142
|
streamIdManager
|
|
139
143
|
})
|
|
140
144
|
this.wtstreams.set(streamid, stream)
|
|
145
|
+
this.scheduler.Register(streamid, priority)
|
|
141
146
|
this.session.jsobj.onStream({
|
|
142
147
|
bidirectional: !(streamid & 0x2n),
|
|
143
148
|
incoming,
|
|
144
|
-
stream
|
|
149
|
+
stream,
|
|
150
|
+
sendGroupId: priority.sendGroupId,
|
|
151
|
+
sendOrder: priority.sendOrder
|
|
145
152
|
})
|
|
146
153
|
return stream
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
scheduleDrainWrites() {
|
|
157
|
+
if (this._scheduledDrainWriteCall) return
|
|
158
|
+
const prom = Promise.resolve()
|
|
159
|
+
this._scheduledDrainWriteCall = prom
|
|
160
|
+
prom
|
|
161
|
+
.then(() => {
|
|
162
|
+
delete this._scheduledDrainWriteCall
|
|
163
|
+
this.drainWrites()
|
|
164
|
+
})
|
|
165
|
+
.catch((error) => log('Error in drainWrites', error))
|
|
166
|
+
}
|
|
167
|
+
|
|
149
168
|
drainWrites() {
|
|
150
|
-
|
|
169
|
+
if (!this.blocked) this.session.drainWrites()
|
|
170
|
+
while (!this.blocked) {
|
|
171
|
+
const frontId = this.scheduler.PopFront()
|
|
172
|
+
if (typeof frontId === 'undefined') break
|
|
173
|
+
const stream = this.wtstreams.get(frontId)
|
|
174
|
+
if (!stream) break
|
|
151
175
|
stream.drainWrites()
|
|
152
176
|
}
|
|
153
177
|
}
|
|
@@ -156,8 +180,16 @@ export class ParserBase {
|
|
|
156
180
|
* @param {bigint|undefined} val
|
|
157
181
|
*/
|
|
158
182
|
onMaxData(val) {
|
|
159
|
-
if (val && this.session.flowController.updateSendWindowOffset(val))
|
|
160
|
-
|
|
183
|
+
if (val && this.session.flowController.updateSendWindowOffset(val)) {
|
|
184
|
+
let pending = false
|
|
185
|
+
this.wtstreams.forEach((stream, streamid) => {
|
|
186
|
+
if (stream.hasPendingData()) {
|
|
187
|
+
pending = true
|
|
188
|
+
this.scheduleDrainWriteStream(streamid)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
if (pending) this.drainWrites()
|
|
192
|
+
}
|
|
161
193
|
}
|
|
162
194
|
|
|
163
195
|
/**
|
|
@@ -167,8 +199,10 @@ export class ParserBase {
|
|
|
167
199
|
onMaxStreamData(streamid, offset) {
|
|
168
200
|
const object = this.wtstreams.get(streamid)
|
|
169
201
|
if (object && offset) {
|
|
170
|
-
if (object.flowController.updateSendWindowOffset(offset))
|
|
171
|
-
|
|
202
|
+
if (object.flowController.updateSendWindowOffset(offset)) {
|
|
203
|
+
this.scheduleDrainWriteStream(streamid)
|
|
204
|
+
this.drainWrites()
|
|
205
|
+
}
|
|
172
206
|
}
|
|
173
207
|
}
|
|
174
208
|
|
|
@@ -251,6 +285,41 @@ export class ParserBase {
|
|
|
251
285
|
this.session.jsobj.onGoAwayReceived()
|
|
252
286
|
}
|
|
253
287
|
|
|
288
|
+
/**
|
|
289
|
+
*
|
|
290
|
+
* @param {bigint} streamid
|
|
291
|
+
*/
|
|
292
|
+
shouldYieldStream(streamid) {
|
|
293
|
+
return this.scheduler.ShouldYield(streamid)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
*
|
|
298
|
+
* @param {bigint} streamid
|
|
299
|
+
*/
|
|
300
|
+
scheduleDrainWriteStream(streamid) {
|
|
301
|
+
this.scheduler.Schedule(streamid)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
*
|
|
306
|
+
* @param {bigint} streamid
|
|
307
|
+
*/
|
|
308
|
+
removeStream(streamid) {
|
|
309
|
+
this.scheduler.Unregister(streamid)
|
|
310
|
+
//this.wtstreams.delete(streamid) // why does this create troubles
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
*
|
|
315
|
+
* @param {bigint} streamid
|
|
316
|
+
* @param {{sendOrder: bigint, sendGroupId: bigint}} arg2
|
|
317
|
+
*/
|
|
318
|
+
streamUpdateSendOrderAndGroup(streamid, { sendOrder, sendGroupId }) {
|
|
319
|
+
this.scheduler.UpdateSendGroup(streamid, sendGroupId)
|
|
320
|
+
this.scheduler.UpdateSendOrder(streamid, sendOrder)
|
|
321
|
+
}
|
|
322
|
+
|
|
254
323
|
/**
|
|
255
324
|
* @param {number} code
|
|
256
325
|
*/
|