@fails-components/webtransport 1.1.4 → 1.2.1
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 +38 -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 +32 -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 +11 -2
- package/lib/http2/node/capsuleparser.js +14 -5
- package/lib/http2/node/client.js +38 -27
- package/lib/http2/node/server.js +56 -19
- package/lib/http2/node/websocketparser.js +10 -3
- package/lib/http2/parserbase.js +84 -7
- package/lib/http2/priorityscheduler.js +654 -0
- package/lib/http2/session.js +83 -23
- package/lib/http2/stream.js +60 -10
- package/lib/http2/websocketcommon.js +1 -1
- package/lib/server.js +0 -1
- package/lib/session.js +133 -64
- package/lib/stream.js +50 -8
- package/lib/types.ts +15 -8
- package/lib/webtransport.browser.js +84 -18
- package/lib/webtransportbase.js +7 -0
- package/old_test/testsuite.js +6 -2
- package/package.json +6 -6
- package/readme.md +3 -1
|
@@ -124,7 +124,9 @@ export class BrowserParser extends ParserBase {
|
|
|
124
124
|
|
|
125
125
|
const offsetend = bufferstate.size
|
|
126
126
|
|
|
127
|
-
const
|
|
127
|
+
const rtype = readVarInt(bufferstate)
|
|
128
|
+
if (typeof rtype === 'undefined') return
|
|
129
|
+
const type = Number(rtype)
|
|
128
130
|
|
|
129
131
|
switch (type) {
|
|
130
132
|
case ParserBase.PADDING:
|
|
@@ -162,7 +164,10 @@ export class BrowserParser extends ParserBase {
|
|
|
162
164
|
if (typeof streamid !== 'undefined') {
|
|
163
165
|
let object = this.wtstreams.get(streamid)
|
|
164
166
|
if (!object) {
|
|
165
|
-
object = this.newStream(streamid
|
|
167
|
+
object = this.newStream(streamid, {
|
|
168
|
+
sendOrder: 0n,
|
|
169
|
+
sendGroupId: 0n
|
|
170
|
+
})
|
|
166
171
|
if (!object) return // stream broken
|
|
167
172
|
}
|
|
168
173
|
// TODO submit data
|
|
@@ -282,4 +287,8 @@ export class BrowserParser extends ParserBase {
|
|
|
282
287
|
closeHttp2Stream(code) {
|
|
283
288
|
this.ws.close(1000)
|
|
284
289
|
}
|
|
290
|
+
|
|
291
|
+
initialParametersMandatory() {
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
285
294
|
}
|
|
@@ -64,9 +64,9 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
64
64
|
)
|
|
65
65
|
return
|
|
66
66
|
}
|
|
67
|
-
const
|
|
67
|
+
const rtype = readVarInt(bufferstate)
|
|
68
68
|
if (
|
|
69
|
-
typeof
|
|
69
|
+
typeof rtype === 'undefined' ||
|
|
70
70
|
bufferstate.size < 1 + bufferstate.offset
|
|
71
71
|
) {
|
|
72
72
|
this.saveddata = Buffer.from(
|
|
@@ -76,8 +76,9 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
76
76
|
)
|
|
77
77
|
return
|
|
78
78
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
79
|
+
const type = Number(rtype)
|
|
80
|
+
const rlength = readVarInt(bufferstate)
|
|
81
|
+
if (typeof rlength === 'undefined') {
|
|
81
82
|
this.saveddata = Buffer.from(
|
|
82
83
|
bufferstate.buffer.buffer,
|
|
83
84
|
capsulestart,
|
|
@@ -85,6 +86,7 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
85
86
|
)
|
|
86
87
|
return
|
|
87
88
|
}
|
|
89
|
+
const length = Number(rlength)
|
|
88
90
|
let checklength = length // we want to read most times the full capsule
|
|
89
91
|
const offsetend = Math.min(
|
|
90
92
|
bufferstate.offset + length,
|
|
@@ -154,7 +156,10 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
154
156
|
if (typeof streamid !== 'undefined') {
|
|
155
157
|
let object = this.wtstreams.get(streamid)
|
|
156
158
|
if (!object) {
|
|
157
|
-
object = this.newStream(streamid
|
|
159
|
+
object = this.newStream(streamid, {
|
|
160
|
+
sendOrder: 0n,
|
|
161
|
+
sendGroupId: 0n
|
|
162
|
+
})
|
|
158
163
|
if (!object) return // stream broken
|
|
159
164
|
}
|
|
160
165
|
// TODO submit data
|
|
@@ -334,4 +339,8 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
334
339
|
this.stream.close(code)
|
|
335
340
|
this.stream.destroy()
|
|
336
341
|
}
|
|
342
|
+
|
|
343
|
+
initialParametersMandatory() {
|
|
344
|
+
return false
|
|
345
|
+
}
|
|
337
346
|
}
|
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) {
|
|
@@ -458,7 +458,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
458
458
|
let wbufferstate
|
|
459
459
|
if (
|
|
460
460
|
type !== ParserBase.WT_STREAM_WOFIN ||
|
|
461
|
-
type !== ParserBase.
|
|
461
|
+
type !== ParserBase.WT_STREAM_WFIN
|
|
462
462
|
) {
|
|
463
463
|
if (fin) {
|
|
464
464
|
wbufferstate = bufferstate
|
|
@@ -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
|
|
@@ -873,4 +876,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
873
876
|
} else if (stream.end) stream.end()
|
|
874
877
|
else throw new Error('http2:session not close method')
|
|
875
878
|
}
|
|
879
|
+
|
|
880
|
+
initialParametersMandatory() {
|
|
881
|
+
return true
|
|
882
|
+
}
|
|
876
883
|
}
|
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
|
/**
|
|
@@ -79,6 +82,14 @@ export class ParserBase {
|
|
|
79
82
|
throw new Error('Implement writeCapsule in derived Class')
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
/**
|
|
86
|
+
* @abstract
|
|
87
|
+
* @return{boolean}
|
|
88
|
+
*/
|
|
89
|
+
initialParametersMandatory() {
|
|
90
|
+
throw new Error('Implement initialParametersMandatory in derived Class')
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
/**
|
|
83
94
|
* @param{{code: Number, reason: string}}arg
|
|
84
95
|
*/
|
|
@@ -101,8 +112,9 @@ export class ParserBase {
|
|
|
101
112
|
|
|
102
113
|
/**
|
|
103
114
|
* @param {bigint} streamid
|
|
115
|
+
* @param {{sendOrder: bigint,sendGroupId: bigint}} priority
|
|
104
116
|
*/
|
|
105
|
-
newStream(streamid) {
|
|
117
|
+
newStream(streamid, priority) {
|
|
106
118
|
const incoming = this.isclient ? !(streamid & 0x1n) : !!(streamid & 0x1n)
|
|
107
119
|
const streamIdManager =
|
|
108
120
|
streamid & 0x2n
|
|
@@ -138,16 +150,36 @@ export class ParserBase {
|
|
|
138
150
|
streamIdManager
|
|
139
151
|
})
|
|
140
152
|
this.wtstreams.set(streamid, stream)
|
|
153
|
+
this.scheduler.Register(streamid, priority)
|
|
141
154
|
this.session.jsobj.onStream({
|
|
142
155
|
bidirectional: !(streamid & 0x2n),
|
|
143
156
|
incoming,
|
|
144
|
-
stream
|
|
157
|
+
stream,
|
|
158
|
+
sendGroupId: priority.sendGroupId,
|
|
159
|
+
sendOrder: priority.sendOrder
|
|
145
160
|
})
|
|
146
161
|
return stream
|
|
147
162
|
}
|
|
148
163
|
|
|
164
|
+
scheduleDrainWrites() {
|
|
165
|
+
if (this._scheduledDrainWriteCall) return
|
|
166
|
+
const prom = Promise.resolve()
|
|
167
|
+
this._scheduledDrainWriteCall = prom
|
|
168
|
+
prom
|
|
169
|
+
.then(() => {
|
|
170
|
+
delete this._scheduledDrainWriteCall
|
|
171
|
+
this.drainWrites()
|
|
172
|
+
})
|
|
173
|
+
.catch((error) => log('Error in drainWrites', error))
|
|
174
|
+
}
|
|
175
|
+
|
|
149
176
|
drainWrites() {
|
|
150
|
-
|
|
177
|
+
if (!this.blocked) this.session.drainWrites()
|
|
178
|
+
while (!this.blocked) {
|
|
179
|
+
const frontId = this.scheduler.PopFront()
|
|
180
|
+
if (typeof frontId === 'undefined') break
|
|
181
|
+
const stream = this.wtstreams.get(frontId)
|
|
182
|
+
if (!stream) break
|
|
151
183
|
stream.drainWrites()
|
|
152
184
|
}
|
|
153
185
|
}
|
|
@@ -156,8 +188,16 @@ export class ParserBase {
|
|
|
156
188
|
* @param {bigint|undefined} val
|
|
157
189
|
*/
|
|
158
190
|
onMaxData(val) {
|
|
159
|
-
if (val && this.session.flowController.updateSendWindowOffset(val))
|
|
160
|
-
|
|
191
|
+
if (val && this.session.flowController.updateSendWindowOffset(val)) {
|
|
192
|
+
let pending = false
|
|
193
|
+
this.wtstreams.forEach((stream, streamid) => {
|
|
194
|
+
if (stream.hasPendingData()) {
|
|
195
|
+
pending = true
|
|
196
|
+
this.scheduleDrainWriteStream(streamid)
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
if (pending) this.drainWrites()
|
|
200
|
+
}
|
|
161
201
|
}
|
|
162
202
|
|
|
163
203
|
/**
|
|
@@ -167,8 +207,10 @@ export class ParserBase {
|
|
|
167
207
|
onMaxStreamData(streamid, offset) {
|
|
168
208
|
const object = this.wtstreams.get(streamid)
|
|
169
209
|
if (object && offset) {
|
|
170
|
-
if (object.flowController.updateSendWindowOffset(offset))
|
|
171
|
-
|
|
210
|
+
if (object.flowController.updateSendWindowOffset(offset)) {
|
|
211
|
+
this.scheduleDrainWriteStream(streamid)
|
|
212
|
+
this.drainWrites()
|
|
213
|
+
}
|
|
172
214
|
}
|
|
173
215
|
}
|
|
174
216
|
|
|
@@ -251,6 +293,41 @@ export class ParserBase {
|
|
|
251
293
|
this.session.jsobj.onGoAwayReceived()
|
|
252
294
|
}
|
|
253
295
|
|
|
296
|
+
/**
|
|
297
|
+
*
|
|
298
|
+
* @param {bigint} streamid
|
|
299
|
+
*/
|
|
300
|
+
shouldYieldStream(streamid) {
|
|
301
|
+
return this.scheduler.ShouldYield(streamid)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
*
|
|
306
|
+
* @param {bigint} streamid
|
|
307
|
+
*/
|
|
308
|
+
scheduleDrainWriteStream(streamid) {
|
|
309
|
+
this.scheduler.Schedule(streamid)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param {bigint} streamid
|
|
315
|
+
*/
|
|
316
|
+
removeStream(streamid) {
|
|
317
|
+
this.scheduler.Unregister(streamid)
|
|
318
|
+
//this.wtstreams.delete(streamid) // why does this create troubles
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
*
|
|
323
|
+
* @param {bigint} streamid
|
|
324
|
+
* @param {{sendOrder: bigint, sendGroupId: bigint}} arg2
|
|
325
|
+
*/
|
|
326
|
+
streamUpdateSendOrderAndGroup(streamid, { sendOrder, sendGroupId }) {
|
|
327
|
+
this.scheduler.UpdateSendGroup(streamid, sendGroupId)
|
|
328
|
+
this.scheduler.UpdateSendOrder(streamid, sendOrder)
|
|
329
|
+
}
|
|
330
|
+
|
|
254
331
|
/**
|
|
255
332
|
* @param {number} code
|
|
256
333
|
*/
|