@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
package/lib/http2/session.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('http2').Http2Stream} Http2Stream
|
|
3
3
|
* @typedef {import('../dom.js').WebTransportSendGroup} WebTransportSendGroup
|
|
4
|
+
* @typedef {import('../dom.js').WebTransportSendStreamOptions} WebTransportSendStreamOptions
|
|
4
5
|
*/
|
|
5
6
|
import { ParserBase } from './parserbase.js'
|
|
6
7
|
import { FlowController } from './flowcontroller.js'
|
|
@@ -72,15 +73,25 @@ export class Http2WebTransportSession {
|
|
|
72
73
|
maxAllowedIncomingStreams: initialBidirectionalReceiveStreams,
|
|
73
74
|
maxAllowedOutgoingStreams: initialBidirectionalSendStreams
|
|
74
75
|
})
|
|
75
|
-
|
|
76
|
-
this.
|
|
76
|
+
/** @type {Array<Uint8Array>} */
|
|
77
|
+
this.datagramsWaiting_ = []
|
|
78
|
+
/** @type {Array<{sendOrder: bigint, sendGroupId: bigint}>} */
|
|
79
|
+
this.orderUniStreams = []
|
|
80
|
+
/** @type {Array<{sendOrder: bigint, sendGroupId: bigint}>} */
|
|
81
|
+
this.orderBiStreams = []
|
|
77
82
|
if (stream) {
|
|
78
83
|
if (isclient) {
|
|
79
84
|
stream.on('response', (headers) => {
|
|
80
85
|
processnextTick(() => {
|
|
81
86
|
if (headers[':status'] === 200) {
|
|
87
|
+
const beReady = {}
|
|
88
|
+
if (stream && headers['wt-protocol']) {
|
|
89
|
+
// http/2 case
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
beReady.protocol = headers['wt-protocol']
|
|
92
|
+
}
|
|
82
93
|
// on ready
|
|
83
|
-
this.jsobj.onReady(
|
|
94
|
+
this.jsobj.onReady(beReady)
|
|
84
95
|
} else {
|
|
85
96
|
this.jsobj.onClose({
|
|
86
97
|
errorcode: headers[':status'],
|
|
@@ -98,51 +109,87 @@ export class Http2WebTransportSession {
|
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
sendInitialParameters() {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
let skip = false //skips the initial parameters at environment with settings
|
|
113
|
+
if (process) {
|
|
114
|
+
if (process.version) {
|
|
115
|
+
const majorVersion = parseInt(
|
|
116
|
+
process.version.split('.')[0].substring(1)
|
|
117
|
+
)
|
|
118
|
+
if (majorVersion >= 20) skip = true
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!skip || this.capsParser.initialParametersMandatory()) {
|
|
122
|
+
this.flowController.sendWindowUpdate()
|
|
123
|
+
this.streamIdMngrBi.sendMaxStreamsFrameInitial()
|
|
124
|
+
this.streamIdMngrUni.sendMaxStreamsFrameInitial()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
drainWrites() {
|
|
129
|
+
while (!this.capsParser.blocked && this.datagramsWaiting_.length > 0) {
|
|
130
|
+
const outChunk = this.datagramsWaiting_.shift()
|
|
131
|
+
this.capsParser.writeCapsule({
|
|
132
|
+
type: ParserBase.DATAGRAM,
|
|
133
|
+
headerVints: [],
|
|
134
|
+
payload: outChunk
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
if (this.datagramsWaiting_.length > 0) {
|
|
138
|
+
this.capsParser.scheduleDrainWrites()
|
|
139
|
+
}
|
|
104
140
|
}
|
|
105
141
|
|
|
106
142
|
/**
|
|
107
143
|
* @param {Uint8Array} chunk
|
|
144
|
+
* @return {{ code: "success" | "blocked" | "internalError" | "tooBig", message?: string | undefined; }}
|
|
108
145
|
*/
|
|
109
146
|
writeDatagram(chunk) {
|
|
147
|
+
if (chunk.byteLength > this.getMaxDatagramSize()) return { code: 'tooBig' }
|
|
148
|
+
if (this.capsParser.blocked) {
|
|
149
|
+
this.datagramsWaiting_.push(chunk)
|
|
150
|
+
this.capsParser.scheduleDrainWrites()
|
|
151
|
+
return { code: 'blocked' }
|
|
152
|
+
}
|
|
110
153
|
this.capsParser.writeCapsule({
|
|
111
154
|
type: ParserBase.DATAGRAM,
|
|
112
155
|
headerVints: [],
|
|
113
156
|
payload: chunk
|
|
114
157
|
})
|
|
115
|
-
|
|
116
|
-
this.jsobj.onDatagramSend({})
|
|
117
|
-
})
|
|
158
|
+
return { code: 'success' }
|
|
118
159
|
}
|
|
119
160
|
|
|
120
161
|
trySendingUnidirectionalStreams() {
|
|
121
162
|
while (
|
|
122
|
-
this.orderUniStreams > 0 &&
|
|
163
|
+
this.orderUniStreams.length > 0 &&
|
|
123
164
|
this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
124
165
|
) {
|
|
125
166
|
const streamid = this.streamIdMngrUni.getNextOutgoingStreamId()
|
|
167
|
+
const priority = this.orderUniStreams.pop()
|
|
126
168
|
this.capsParser.writeCapsule({
|
|
127
169
|
type: ParserBase.WT_STREAM_WOFIN,
|
|
128
170
|
headerVints: [streamid],
|
|
129
171
|
payload: undefined
|
|
130
172
|
})
|
|
131
|
-
this.capsParser.newStream(
|
|
132
|
-
|
|
173
|
+
this.capsParser.newStream(
|
|
174
|
+
streamid,
|
|
175
|
+
priority || { sendGroupId: 0n, sendOrder: 0n }
|
|
176
|
+
)
|
|
133
177
|
}
|
|
134
178
|
}
|
|
135
179
|
|
|
136
180
|
/**
|
|
137
|
-
* @param {
|
|
181
|
+
* @param {WebTransportSendStreamOptions} opts
|
|
138
182
|
*/
|
|
139
|
-
// eslint-disable-next-line no-unused-vars
|
|
140
183
|
orderUnidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
141
184
|
const canopen = this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
142
185
|
const maxset = this.streamIdMngrUni.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
143
186
|
|
|
144
187
|
if (canopen || waitUntilAvailable || !maxset) {
|
|
145
|
-
this.orderUniStreams
|
|
188
|
+
this.orderUniStreams.push({
|
|
189
|
+
// @ts-ignore
|
|
190
|
+
sendGroupId: sendGroup?._sendGroupId || 0n,
|
|
191
|
+
sendOrder: sendOrder ?? 0n
|
|
192
|
+
})
|
|
146
193
|
this.trySendingUnidirectionalStreams()
|
|
147
194
|
return true
|
|
148
195
|
}
|
|
@@ -151,30 +198,36 @@ export class Http2WebTransportSession {
|
|
|
151
198
|
|
|
152
199
|
trySendingBidirectionalStreams() {
|
|
153
200
|
while (
|
|
154
|
-
this.orderBiStreams > 0 &&
|
|
201
|
+
this.orderBiStreams.length > 0 &&
|
|
155
202
|
this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
156
203
|
) {
|
|
157
204
|
const streamid = this.streamIdMngrBi.getNextOutgoingStreamId()
|
|
205
|
+
const priority = this.orderBiStreams.pop()
|
|
158
206
|
this.capsParser.writeCapsule({
|
|
159
207
|
type: ParserBase.WT_STREAM_WOFIN,
|
|
160
208
|
headerVints: [streamid],
|
|
161
209
|
payload: undefined
|
|
162
210
|
})
|
|
163
|
-
this.capsParser.newStream(
|
|
164
|
-
|
|
211
|
+
this.capsParser.newStream(
|
|
212
|
+
streamid,
|
|
213
|
+
priority || { sendGroupId: 0n, sendOrder: 0n }
|
|
214
|
+
)
|
|
165
215
|
}
|
|
166
216
|
}
|
|
167
217
|
|
|
168
218
|
/**
|
|
169
|
-
* @param {
|
|
219
|
+
* @param {WebTransportSendStreamOptions} opts
|
|
170
220
|
*/
|
|
171
|
-
// eslint-disable-next-line no-unused-vars
|
|
172
221
|
orderBidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
173
222
|
const canopen = this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
174
223
|
const maxset = this.streamIdMngrBi.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
175
224
|
|
|
176
225
|
if (canopen || waitUntilAvailable || !maxset) {
|
|
177
|
-
this.orderBiStreams
|
|
226
|
+
this.orderBiStreams.push({
|
|
227
|
+
// @ts-ignore
|
|
228
|
+
sendGroupId: sendGroup?._sendGroupId || 0n,
|
|
229
|
+
sendOrder: sendOrder ?? 0n
|
|
230
|
+
})
|
|
178
231
|
this.trySendingBidirectionalStreams()
|
|
179
232
|
return true
|
|
180
233
|
}
|
|
@@ -202,6 +255,10 @@ export class Http2WebTransportSession {
|
|
|
202
255
|
})
|
|
203
256
|
}
|
|
204
257
|
|
|
258
|
+
getMaxDatagramSize() {
|
|
259
|
+
return 16384 // this completly arbitry, we do not have a real restriction, but we choose more than quiche, to make things interesting
|
|
260
|
+
}
|
|
261
|
+
|
|
205
262
|
/*
|
|
206
263
|
* @returns {void}
|
|
207
264
|
*/
|
|
@@ -260,16 +317,19 @@ export class Http2WebTransportSession {
|
|
|
260
317
|
}
|
|
261
318
|
|
|
262
319
|
smoothedRtt() {
|
|
320
|
+
let toret
|
|
263
321
|
if (this.stream) {
|
|
264
322
|
// we are on node
|
|
265
323
|
// @ts-ignore
|
|
266
|
-
|
|
324
|
+
toret = this.stream.session?.WTrtt || 25
|
|
267
325
|
} else if (this.ws) {
|
|
268
326
|
// we are at the Browser, so we use the connection rtt?
|
|
269
327
|
// @ts-ignore
|
|
270
328
|
// eslint-disable-next-line no-undef
|
|
271
|
-
|
|
329
|
+
toret = navigator?.connection?.rtt || 25
|
|
272
330
|
}
|
|
331
|
+
toret = Math.ceil(toret / 25) * 25 // to do be to accurate!
|
|
332
|
+
return toret
|
|
273
333
|
}
|
|
274
334
|
|
|
275
335
|
/**
|
package/lib/http2/stream.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FlowController } from './flowcontroller.js'
|
|
2
2
|
import { ParserBase } from './parserbase.js'
|
|
3
3
|
import { logger } from '../utils.js'
|
|
4
|
+
import { WebTransportError } from '../error.js'
|
|
4
5
|
|
|
5
6
|
const pid = typeof process !== 'undefined' ? process.pid : 0
|
|
6
7
|
const log = logger(`webtransport:http2webtransportstream(${pid})`)
|
|
@@ -69,6 +70,7 @@ export class Http2WebTransportStream {
|
|
|
69
70
|
this.streamIdManager = streamIdManager
|
|
70
71
|
|
|
71
72
|
this.final = false
|
|
73
|
+
this.finalmessagesend = false
|
|
72
74
|
this.stopReading_ = true
|
|
73
75
|
this.drainReads_ = true
|
|
74
76
|
this.recvBytes = 0
|
|
@@ -285,20 +287,34 @@ export class Http2WebTransportStream {
|
|
|
285
287
|
*/
|
|
286
288
|
writeChunk(buf) {
|
|
287
289
|
this.outgochunks.push({ buf, fin: false })
|
|
288
|
-
this.
|
|
290
|
+
this.capsuleParser.scheduler.Schedule(this.streamid)
|
|
291
|
+
this.capsuleParser.scheduleDrainWrites()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
hasPendingData() {
|
|
295
|
+
return this.outgochunks.length > 0
|
|
289
296
|
}
|
|
290
297
|
|
|
291
298
|
drainWrites() {
|
|
299
|
+
let finsend = false
|
|
292
300
|
while (
|
|
293
301
|
this.outgochunks.length > 0 &&
|
|
294
|
-
(!this.capsuleParser.blocked ||
|
|
302
|
+
(!this.capsuleParser.blocked ||
|
|
303
|
+
this.final ||
|
|
304
|
+
!this.capsuleParser.shouldYieldStream(this.streamid)) &&
|
|
295
305
|
this.flowController.sendWindowSize() > 0n &&
|
|
296
306
|
this.sessionFlowController.sendWindowSize() > 0n
|
|
297
307
|
) {
|
|
298
308
|
const cur = this.outgochunks.shift()
|
|
309
|
+
let outgoChunkSend = true
|
|
299
310
|
if (cur) {
|
|
300
311
|
let payload = cur.buf
|
|
301
312
|
if (payload) {
|
|
313
|
+
if (payload.byteLength === 0 && !cur.fin) {
|
|
314
|
+
throw new WebTransportError(
|
|
315
|
+
'Trying to send zero length capsule without a fin'
|
|
316
|
+
)
|
|
317
|
+
}
|
|
302
318
|
const sessWindow = this.sessionFlowController.sendWindowSize()
|
|
303
319
|
const streamWindow = this.flowController.sendWindowSize()
|
|
304
320
|
if (
|
|
@@ -319,6 +335,7 @@ export class Http2WebTransportStream {
|
|
|
319
335
|
const dest = new Uint8Array(payload.byteLength - len)
|
|
320
336
|
dest.set(src)
|
|
321
337
|
this.outgochunks.unshift({ fin: cur.fin, buf: dest })
|
|
338
|
+
outgoChunkSend = false // remaing part is send later
|
|
322
339
|
}
|
|
323
340
|
cur.fin = false
|
|
324
341
|
payload = cur.buf = new Uint8Array(
|
|
@@ -335,27 +352,60 @@ export class Http2WebTransportStream {
|
|
|
335
352
|
headerVints: [this.streamid],
|
|
336
353
|
payload
|
|
337
354
|
})
|
|
355
|
+
finsend ||= !!cur?.fin
|
|
338
356
|
|
|
339
357
|
if (payload) {
|
|
340
358
|
this.flowController.addBytesSent(payload?.byteLength)
|
|
341
359
|
this.sessionFlowController.addBytesSent(payload?.byteLength)
|
|
342
360
|
}
|
|
343
361
|
}
|
|
344
|
-
|
|
345
|
-
|
|
362
|
+
if (outgoChunkSend) {
|
|
363
|
+
this.jsobj.onStreamWrite({
|
|
364
|
+
success: true
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (finsend) {
|
|
369
|
+
this.capsuleParser.removeStream(this.streamid)
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (
|
|
374
|
+
!(
|
|
375
|
+
!this.capsuleParser.blocked ||
|
|
376
|
+
this.final ||
|
|
377
|
+
!this.capsuleParser.shouldYieldStream(this.streamid)
|
|
378
|
+
) &&
|
|
379
|
+
this.outgochunks.length > 0
|
|
380
|
+
) {
|
|
381
|
+
this.capsuleParser.scheduleDrainWriteStream(this.streamid)
|
|
382
|
+
}
|
|
383
|
+
if (this.final && this.outgochunks.length === 0 && !this.finalmessagesend) {
|
|
384
|
+
processnextTick(() => {
|
|
385
|
+
this.jsobj.onStreamNetworkFinish({
|
|
386
|
+
nettask: 'streamFinal'
|
|
387
|
+
})
|
|
346
388
|
})
|
|
389
|
+
this.finalmessagesend = true
|
|
347
390
|
}
|
|
348
391
|
}
|
|
349
392
|
|
|
350
393
|
streamFinal() {
|
|
351
394
|
this.final = true
|
|
352
395
|
this.outgochunks.push({ fin: true })
|
|
353
|
-
this.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
396
|
+
this.capsuleParser.scheduleDrainWriteStream(this.streamid)
|
|
397
|
+
this.capsuleParser.scheduleDrainWrites()
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
*
|
|
402
|
+
* @param {{sendOrder: bigint, sendGroupId: bigint}} args
|
|
403
|
+
*/
|
|
404
|
+
updateSendOrderAndGroup({ sendOrder, sendGroupId }) {
|
|
405
|
+
this.capsuleParser.streamUpdateSendOrderAndGroup(this.streamid, {
|
|
406
|
+
sendOrder,
|
|
407
|
+
sendGroupId
|
|
408
|
+
})
|
|
359
409
|
}
|
|
360
410
|
|
|
361
411
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const supportedVersions = ['kDraft1']
|
|
1
|
+
export const supportedVersions = ['kDraft1', 'kDraft2']
|
package/lib/server.js
CHANGED
|
@@ -214,7 +214,6 @@ export class HttpServer {
|
|
|
214
214
|
setRequestCallback(callback) {
|
|
215
215
|
this.requestHandler = callback
|
|
216
216
|
|
|
217
|
-
this.transportInt.setJSRequestHandler(!!callback)
|
|
218
217
|
if (this.transportInt) this.transportInt.setJSRequestHandler(!!callback)
|
|
219
218
|
else this._pendingRequestCallback = !!callback
|
|
220
219
|
}
|
package/lib/session.js
CHANGED
|
@@ -13,7 +13,6 @@ const log = logger(`webtransport:httpwtsession(${pid})`)
|
|
|
13
13
|
* @typedef {import('./types').SessionReadyEvent} SessionReadyEvent
|
|
14
14
|
* @typedef {import('./types').SessionCloseEvent} SessionCloseEvent
|
|
15
15
|
* @typedef {import('./types').DatagramReceivedEvent} DatagramReceivedEvent
|
|
16
|
-
* @typedef {import('./types').DatagramSendEvent} DatagramSendEvent
|
|
17
16
|
* @typedef {import('./types').GoawayReceivedEvent} GoawayReceivedEvent
|
|
18
17
|
* @typedef {import('./types').DatagramStatsEvent} DatagramStatsEvent
|
|
19
18
|
* @typedef {import('./types').SessionStatsEvent} SessionStatsEvent
|
|
@@ -27,8 +26,11 @@ const log = logger(`webtransport:httpwtsession(${pid})`)
|
|
|
27
26
|
* @typedef {import('./dom').WebTransportDatagramDuplexStream} WebTransportDatagramDuplexStream
|
|
28
27
|
* @typedef {import('./dom').WebTransportReliabilityMode} WebTransportReliabilityMode
|
|
29
28
|
* @typedef {import('./dom').WebTransportCongestionControl} WebTransportCongestionControl
|
|
29
|
+
* @typedef {import('./dom').WebTransportSendGroup} WebTransportSendGroup
|
|
30
30
|
* @typedef {import('./dom').WebTransportStats} WebTransportStats
|
|
31
31
|
* @typedef {import('./dom').WebTransportDatagramStats} WebTransportDatagramStats
|
|
32
|
+
* @typedef {import('./dom').WebTransportSendOptions} WebTransportSendOptions
|
|
33
|
+
* @typedef {import('./dom').WebTransportDatagramsWritable} WebTransportDatagramsWritable
|
|
32
34
|
*
|
|
33
35
|
* @typedef {import('./types').NativeHttpWTSession} NativeHttpWTSession
|
|
34
36
|
*
|
|
@@ -112,12 +114,6 @@ export class HttpWTSession {
|
|
|
112
114
|
}
|
|
113
115
|
})
|
|
114
116
|
|
|
115
|
-
/** @type {Array<() => void>} */
|
|
116
|
-
this.writeDatagramRes = []
|
|
117
|
-
/** @type {Array<(err?: Error) => void>} */
|
|
118
|
-
this.writeDatagramRej = []
|
|
119
|
-
/** @type {Array<Promise<void>>} */
|
|
120
|
-
this.writeDatagramProm = []
|
|
121
117
|
const readableopts = {
|
|
122
118
|
start: (
|
|
123
119
|
/** @type {import("stream/web").ReadableByteStreamController} */ controller
|
|
@@ -130,37 +126,95 @@ export class HttpWTSession {
|
|
|
130
126
|
// @ts-ignore
|
|
131
127
|
delete readableopts.type
|
|
132
128
|
}
|
|
129
|
+
this._lastGetMaxDatagramSize = 0
|
|
133
130
|
/** @type {WebTransportDatagramDuplexStream} */
|
|
134
131
|
this.datagrams = {
|
|
135
132
|
/** @type {ReadableStream<Uint8Array>} */
|
|
136
133
|
// @ts-ignore
|
|
137
134
|
readable: new ReadableStream(readableopts),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (this.
|
|
154
|
-
|
|
135
|
+
/**
|
|
136
|
+
* @param {WebTransportSendOptions|undefined} options
|
|
137
|
+
* @return {import('./dom').WebTransportDatagramsWritable}
|
|
138
|
+
*/
|
|
139
|
+
createWritable: (options) => {
|
|
140
|
+
let sendOrder = options?.sendOrder ?? 0n
|
|
141
|
+
let sendGroup = options?.sendGroup
|
|
142
|
+
/** @type {WebTransportSendStream} */
|
|
143
|
+
// @ts-expect-error some props are initially missing
|
|
144
|
+
const retWritable = new WritableStream({
|
|
145
|
+
start: (controller) => {
|
|
146
|
+
this.outgoDatagramController = controller
|
|
147
|
+
},
|
|
148
|
+
// eslint-disable-next-line no-unused-vars
|
|
149
|
+
write: (chunk, controller) => {
|
|
150
|
+
if (this.state === 'closed') throw new Error('Session is closed')
|
|
151
|
+
if (chunk instanceof Uint8Array) {
|
|
152
|
+
/** @type {Promise<void>} */
|
|
153
|
+
if (this.objint == null) {
|
|
154
|
+
throw new Error('this.objint is not set')
|
|
155
|
+
}
|
|
156
|
+
const { code, message } = this.objint.writeDatagram(chunk)
|
|
157
|
+
if (
|
|
158
|
+
code !== 'success' &&
|
|
159
|
+
code !== 'blocked' &&
|
|
160
|
+
code !== 'tooBig'
|
|
161
|
+
) {
|
|
162
|
+
throw new WebTransportError(code + ':' + message)
|
|
163
|
+
}
|
|
164
|
+
} else throw new Error('chunk is not of type Uint8Array')
|
|
165
|
+
},
|
|
166
|
+
close: () => {
|
|
167
|
+
// do nothing
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
Object.defineProperties(retWritable, {
|
|
171
|
+
sendOrder: {
|
|
172
|
+
get: () => {
|
|
173
|
+
return sendOrder
|
|
174
|
+
},
|
|
175
|
+
/**
|
|
176
|
+
* @param {bigint} value
|
|
177
|
+
*/
|
|
178
|
+
set: (value) => {
|
|
179
|
+
sendOrder = value
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
sendGroup: {
|
|
183
|
+
get: () => {
|
|
184
|
+
return sendGroup
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* @param {WebTransportSendGroup} value
|
|
188
|
+
*/
|
|
189
|
+
set: (value) => {
|
|
190
|
+
if (value !== sendGroup) {
|
|
191
|
+
sendGroup = value
|
|
192
|
+
}
|
|
155
193
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
return retWritable
|
|
197
|
+
},
|
|
198
|
+
// @ts-ignore
|
|
199
|
+
get writable() {
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
if (!this.datagramwritablepolyfilled_) {
|
|
202
|
+
console.warn('datagrams.writable is deprecated')
|
|
162
203
|
}
|
|
163
|
-
|
|
204
|
+
// @ts-ignore
|
|
205
|
+
return (this.datagramwritablepolyfilled_ ||= this.createWritable())
|
|
206
|
+
},
|
|
207
|
+
get maxDatagramSize() {
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
return this._getMaxDatagramSize()
|
|
210
|
+
},
|
|
211
|
+
// @ts-ignore
|
|
212
|
+
_getMaxDatagramSize: () => {
|
|
213
|
+
if (this.objint) {
|
|
214
|
+
this._lastGetMaxDatagramSize = this.objint.getMaxDatagramSize()
|
|
215
|
+
}
|
|
216
|
+
return this._lastGetMaxDatagramSize
|
|
217
|
+
}
|
|
164
218
|
}
|
|
165
219
|
|
|
166
220
|
/** @type {Array<(stream: WebTransportBidirectionalStream) => void>} */
|
|
@@ -193,6 +247,13 @@ export class HttpWTSession {
|
|
|
193
247
|
this.sendStreamsController = new Set()
|
|
194
248
|
/** @type {Set<ReadableStreamDefaultController>} */
|
|
195
249
|
this.receiveStreamsController = new Set()
|
|
250
|
+
|
|
251
|
+
this._sendGroupNum = 1n // 0n is reserved for no sendgroup
|
|
252
|
+
/** @type {Map<bigint,WebTransportSendGroup>} */
|
|
253
|
+
this._sendGroupIndex = new Map()
|
|
254
|
+
|
|
255
|
+
/** @type {undefined|string} */
|
|
256
|
+
this._selectedProtocol = undefined
|
|
196
257
|
}
|
|
197
258
|
|
|
198
259
|
/**
|
|
@@ -210,6 +271,10 @@ export class HttpWTSession {
|
|
|
210
271
|
}
|
|
211
272
|
}
|
|
212
273
|
|
|
274
|
+
get protocol() {
|
|
275
|
+
return this._selectedProtocol
|
|
276
|
+
}
|
|
277
|
+
|
|
213
278
|
getStats() {
|
|
214
279
|
if (this.objint == null) {
|
|
215
280
|
throw new Error('this.objint not set')
|
|
@@ -279,16 +344,6 @@ export class HttpWTSession {
|
|
|
279
344
|
})
|
|
280
345
|
}
|
|
281
346
|
|
|
282
|
-
async waitForDatagramsSend() {
|
|
283
|
-
while (this.writeDatagramProm.length > 0) {
|
|
284
|
-
try {
|
|
285
|
-
await Promise.allSettled(this.writeDatagramProm)
|
|
286
|
-
} catch (error) {
|
|
287
|
-
log.error('datagram promise failed ', error)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
347
|
notifySessionDraining() {
|
|
293
348
|
if (this.objint == null) {
|
|
294
349
|
throw new Error('this.objint not set')
|
|
@@ -361,7 +416,7 @@ export class HttpWTSession {
|
|
|
361
416
|
})
|
|
362
417
|
const notblocked = this.objint.orderBidiStream({
|
|
363
418
|
sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
|
|
364
|
-
sendOrder: opts?.sendOrder ||
|
|
419
|
+
sendOrder: BigInt(opts?.sendOrder || 0n),
|
|
365
420
|
waitUntilAvailable: opts?.waitUntilAvailable || false
|
|
366
421
|
})
|
|
367
422
|
if (!notblocked) {
|
|
@@ -388,7 +443,7 @@ export class HttpWTSession {
|
|
|
388
443
|
})
|
|
389
444
|
const notblocked = this.objint.orderUnidiStream({
|
|
390
445
|
sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
|
|
391
|
-
sendOrder: opts?.sendOrder ||
|
|
446
|
+
sendOrder: opts?.sendOrder || 0n,
|
|
392
447
|
waitUntilAvailable: opts?.waitUntilAvailable || false
|
|
393
448
|
})
|
|
394
449
|
if (!notblocked) {
|
|
@@ -417,7 +472,33 @@ export class HttpWTSession {
|
|
|
417
472
|
}
|
|
418
473
|
}
|
|
419
474
|
|
|
420
|
-
|
|
475
|
+
/**
|
|
476
|
+
* @returns {WebTransportSendGroup}
|
|
477
|
+
*/
|
|
478
|
+
createSendGroup() {
|
|
479
|
+
if (this.state === 'closed' || this.state === 'failed')
|
|
480
|
+
throw new Error('InvalidState')
|
|
481
|
+
const _sendGroupId = this._sendGroupNum++
|
|
482
|
+
const sendGroup = {
|
|
483
|
+
// @ts-ignore
|
|
484
|
+
_sendGroupId,
|
|
485
|
+
getStats: async () => {
|
|
486
|
+
// TODO implement
|
|
487
|
+
return {
|
|
488
|
+
bytesWritten: 0n,
|
|
489
|
+
bytesSent: 0n,
|
|
490
|
+
bytesAcknowledged: 0n
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
this._sendGroupIndex.set(_sendGroupId, sendGroup)
|
|
495
|
+
return sendGroup
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* @param {{protocol?: string}} arg
|
|
499
|
+
**/
|
|
500
|
+
onReady({ protocol }) {
|
|
501
|
+
if (protocol) this._selectedProtocol = protocol
|
|
421
502
|
this.state = 'connected'
|
|
422
503
|
if (!this.reliable) this.reliability = 'supports-unreliable'
|
|
423
504
|
else this.reliability = 'reliable-only'
|
|
@@ -458,13 +539,9 @@ export class HttpWTSession {
|
|
|
458
539
|
|
|
459
540
|
for (const rej of this.rejectBiDi) rej(error)
|
|
460
541
|
for (const rej of this.rejectUniDi) rej(error)
|
|
461
|
-
for (const rej of this.writeDatagramRej) rej(error)
|
|
462
542
|
for (const rej of this.rejectSessionStats) rej(error)
|
|
463
543
|
for (const rej of this.rejectDatagramStats) rej(error)
|
|
464
544
|
|
|
465
|
-
this.writeDatagramRej = []
|
|
466
|
-
this.writeDatagramRes = []
|
|
467
|
-
this.writeDatagramProm = []
|
|
468
545
|
this.resolveBiDi = []
|
|
469
546
|
this.resolveUniDi = []
|
|
470
547
|
this.rejectBiDi = []
|
|
@@ -519,7 +596,9 @@ export class HttpWTSession {
|
|
|
519
596
|
parentobj: this,
|
|
520
597
|
transport: this.parentobj,
|
|
521
598
|
bidirectional: args.bidirectional,
|
|
522
|
-
incoming: args.incoming
|
|
599
|
+
incoming: args.incoming,
|
|
600
|
+
sendGroup: this._sendGroupIndex.get(args.sendGroupId || 0n),
|
|
601
|
+
sendOrder: args.sendOrder
|
|
523
602
|
})
|
|
524
603
|
this.addStreamObj(strobj)
|
|
525
604
|
if (args.incoming) {
|
|
@@ -563,6 +642,11 @@ export class HttpWTSession {
|
|
|
563
642
|
*/
|
|
564
643
|
onDatagramReceived(args) {
|
|
565
644
|
log.trace('datagram received', args.datagram)
|
|
645
|
+
// streams spec says zero length chunk on byob stream is illegal
|
|
646
|
+
if (args.datagram.byteLength === 0) {
|
|
647
|
+
log.trace('zerolength datagram dropped')
|
|
648
|
+
return
|
|
649
|
+
}
|
|
566
650
|
// console.log('datagram received', args.datagram, Date.now())
|
|
567
651
|
if (this.incomDatagramController.byobRequest) {
|
|
568
652
|
/** @type {ReadableStreamBYOBRequest} */
|
|
@@ -588,21 +672,6 @@ export class HttpWTSession {
|
|
|
588
672
|
}
|
|
589
673
|
}
|
|
590
674
|
|
|
591
|
-
/**
|
|
592
|
-
* @param {DatagramSendEvent} args
|
|
593
|
-
*/
|
|
594
|
-
// eslint-disable-next-line no-unused-vars
|
|
595
|
-
onDatagramSend(args) {
|
|
596
|
-
if (this.state === 'closed') return
|
|
597
|
-
this.writeDatagramRej.shift()
|
|
598
|
-
this.writeDatagramProm.shift()
|
|
599
|
-
const res = this.writeDatagramRes.shift()
|
|
600
|
-
|
|
601
|
-
if (res != null) {
|
|
602
|
-
res()
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
675
|
/**
|
|
607
676
|
* @param {GoawayReceivedEvent} args
|
|
608
677
|
*/
|