@fails-components/webtransport 1.1.3 → 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/echoserver.js +1 -1
- package/old_test/test.js +1 -1
- package/old_test/testsuite.js +6 -2
- package/package.json +7 -28
- package/readme.md +5 -3
- package/test/bidirectional-streams.spec.js +0 -150
- package/test/datagrams.spec.js +0 -104
- package/test/fixtures/certificate.js +0 -456
- package/test/fixtures/chai.js +0 -8
- package/test/fixtures/known-bytes.js +0 -44
- package/test/fixtures/p-timeout.js +0 -33
- package/test/fixtures/quiche.browser.js +0 -2
- package/test/fixtures/quiche.js +0 -1
- package/test/fixtures/read-cert-hash.js +0 -7
- package/test/fixtures/read-stream.js +0 -57
- package/test/fixtures/reader-value.js +0 -51
- package/test/fixtures/server.js +0 -467
- package/test/fixtures/webtransport.browser.js +0 -19
- package/test/fixtures/webtransport.js +0 -3
- package/test/fixtures/write-stream.js +0 -24
- package/test/index.js +0 -124
- package/test/pw-no-https-errors.json +0 -5
- package/test/session.spec.js +0 -199
- package/test/stream-limits.spec.js +0 -259
- package/test/unidirectional-streams.spec.js +0 -133
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'],
|
|
@@ -103,46 +114,71 @@ export class Http2WebTransportSession {
|
|
|
103
114
|
this.streamIdMngrUni.sendMaxStreamsFrameInitial()
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
drainWrites() {
|
|
118
|
+
while (!this.capsParser.blocked && this.datagramsWaiting_.length > 0) {
|
|
119
|
+
const outChunk = this.datagramsWaiting_.shift()
|
|
120
|
+
this.capsParser.writeCapsule({
|
|
121
|
+
type: ParserBase.DATAGRAM,
|
|
122
|
+
headerVints: [],
|
|
123
|
+
payload: outChunk
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
if (this.datagramsWaiting_.length > 0) {
|
|
127
|
+
this.capsParser.scheduleDrainWrites()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
106
131
|
/**
|
|
107
132
|
* @param {Uint8Array} chunk
|
|
133
|
+
* @return {{ code: "success" | "blocked" | "internalError" | "tooBig", message?: string | undefined; }}
|
|
108
134
|
*/
|
|
109
135
|
writeDatagram(chunk) {
|
|
136
|
+
if (chunk.byteLength > this.getMaxDatagramSize()) return { code: 'tooBig' }
|
|
137
|
+
if (this.capsParser.blocked) {
|
|
138
|
+
this.datagramsWaiting_.push(chunk)
|
|
139
|
+
this.capsParser.scheduleDrainWrites()
|
|
140
|
+
return { code: 'blocked' }
|
|
141
|
+
}
|
|
110
142
|
this.capsParser.writeCapsule({
|
|
111
143
|
type: ParserBase.DATAGRAM,
|
|
112
144
|
headerVints: [],
|
|
113
145
|
payload: chunk
|
|
114
146
|
})
|
|
115
|
-
|
|
116
|
-
this.jsobj.onDatagramSend({})
|
|
117
|
-
})
|
|
147
|
+
return { code: 'success' }
|
|
118
148
|
}
|
|
119
149
|
|
|
120
150
|
trySendingUnidirectionalStreams() {
|
|
121
151
|
while (
|
|
122
|
-
this.orderUniStreams > 0 &&
|
|
152
|
+
this.orderUniStreams.length > 0 &&
|
|
123
153
|
this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
124
154
|
) {
|
|
125
155
|
const streamid = this.streamIdMngrUni.getNextOutgoingStreamId()
|
|
156
|
+
const priority = this.orderUniStreams.pop()
|
|
126
157
|
this.capsParser.writeCapsule({
|
|
127
158
|
type: ParserBase.WT_STREAM_WOFIN,
|
|
128
159
|
headerVints: [streamid],
|
|
129
160
|
payload: undefined
|
|
130
161
|
})
|
|
131
|
-
this.capsParser.newStream(
|
|
132
|
-
|
|
162
|
+
this.capsParser.newStream(
|
|
163
|
+
streamid,
|
|
164
|
+
priority || { sendGroupId: 0n, sendOrder: 0n }
|
|
165
|
+
)
|
|
133
166
|
}
|
|
134
167
|
}
|
|
135
168
|
|
|
136
169
|
/**
|
|
137
|
-
* @param {
|
|
170
|
+
* @param {WebTransportSendStreamOptions} opts
|
|
138
171
|
*/
|
|
139
|
-
// eslint-disable-next-line no-unused-vars
|
|
140
172
|
orderUnidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
141
173
|
const canopen = this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
142
174
|
const maxset = this.streamIdMngrUni.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
143
175
|
|
|
144
176
|
if (canopen || waitUntilAvailable || !maxset) {
|
|
145
|
-
this.orderUniStreams
|
|
177
|
+
this.orderUniStreams.push({
|
|
178
|
+
// @ts-ignore
|
|
179
|
+
sendGroupId: sendGroup?._sendGroupId || 0n,
|
|
180
|
+
sendOrder: sendOrder ?? 0n
|
|
181
|
+
})
|
|
146
182
|
this.trySendingUnidirectionalStreams()
|
|
147
183
|
return true
|
|
148
184
|
}
|
|
@@ -151,30 +187,36 @@ export class Http2WebTransportSession {
|
|
|
151
187
|
|
|
152
188
|
trySendingBidirectionalStreams() {
|
|
153
189
|
while (
|
|
154
|
-
this.orderBiStreams > 0 &&
|
|
190
|
+
this.orderBiStreams.length > 0 &&
|
|
155
191
|
this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
156
192
|
) {
|
|
157
193
|
const streamid = this.streamIdMngrBi.getNextOutgoingStreamId()
|
|
194
|
+
const priority = this.orderBiStreams.pop()
|
|
158
195
|
this.capsParser.writeCapsule({
|
|
159
196
|
type: ParserBase.WT_STREAM_WOFIN,
|
|
160
197
|
headerVints: [streamid],
|
|
161
198
|
payload: undefined
|
|
162
199
|
})
|
|
163
|
-
this.capsParser.newStream(
|
|
164
|
-
|
|
200
|
+
this.capsParser.newStream(
|
|
201
|
+
streamid,
|
|
202
|
+
priority || { sendGroupId: 0n, sendOrder: 0n }
|
|
203
|
+
)
|
|
165
204
|
}
|
|
166
205
|
}
|
|
167
206
|
|
|
168
207
|
/**
|
|
169
|
-
* @param {
|
|
208
|
+
* @param {WebTransportSendStreamOptions} opts
|
|
170
209
|
*/
|
|
171
|
-
// eslint-disable-next-line no-unused-vars
|
|
172
210
|
orderBidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
173
211
|
const canopen = this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
174
212
|
const maxset = this.streamIdMngrBi.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
175
213
|
|
|
176
214
|
if (canopen || waitUntilAvailable || !maxset) {
|
|
177
|
-
this.orderBiStreams
|
|
215
|
+
this.orderBiStreams.push({
|
|
216
|
+
// @ts-ignore
|
|
217
|
+
sendGroupId: sendGroup?._sendGroupId || 0n,
|
|
218
|
+
sendOrder: sendOrder ?? 0n
|
|
219
|
+
})
|
|
178
220
|
this.trySendingBidirectionalStreams()
|
|
179
221
|
return true
|
|
180
222
|
}
|
|
@@ -202,6 +244,10 @@ export class Http2WebTransportSession {
|
|
|
202
244
|
})
|
|
203
245
|
}
|
|
204
246
|
|
|
247
|
+
getMaxDatagramSize() {
|
|
248
|
+
return 16384 // this completly arbitry, we do not have a real restriction, but we choose more than quiche, to make things interesting
|
|
249
|
+
}
|
|
250
|
+
|
|
205
251
|
/*
|
|
206
252
|
* @returns {void}
|
|
207
253
|
*/
|
|
@@ -260,16 +306,19 @@ export class Http2WebTransportSession {
|
|
|
260
306
|
}
|
|
261
307
|
|
|
262
308
|
smoothedRtt() {
|
|
309
|
+
let toret
|
|
263
310
|
if (this.stream) {
|
|
264
311
|
// we are on node
|
|
265
312
|
// @ts-ignore
|
|
266
|
-
|
|
313
|
+
toret = this.stream.session?.WTrtt || 25
|
|
267
314
|
} else if (this.ws) {
|
|
268
315
|
// we are at the Browser, so we use the connection rtt?
|
|
269
316
|
// @ts-ignore
|
|
270
317
|
// eslint-disable-next-line no-undef
|
|
271
|
-
|
|
318
|
+
toret = navigator?.connection?.rtt || 25
|
|
272
319
|
}
|
|
320
|
+
toret = Math.ceil(toret / 25) * 25 // to do be to accurate!
|
|
321
|
+
return toret
|
|
273
322
|
}
|
|
274
323
|
|
|
275
324
|
/**
|
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
|
|
@@ -135,32 +131,85 @@ export class HttpWTSession {
|
|
|
135
131
|
/** @type {ReadableStream<Uint8Array>} */
|
|
136
132
|
// @ts-ignore
|
|
137
133
|
readable: new ReadableStream(readableopts),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (this.
|
|
154
|
-
|
|
134
|
+
/**
|
|
135
|
+
* @param {WebTransportSendOptions|undefined} options
|
|
136
|
+
* @return {import('./dom').WebTransportDatagramsWritable}
|
|
137
|
+
*/
|
|
138
|
+
createWritable: (options) => {
|
|
139
|
+
let sendOrder = options?.sendOrder ?? 0n
|
|
140
|
+
let sendGroup = options?.sendGroup
|
|
141
|
+
/** @type {WebTransportSendStream} */
|
|
142
|
+
// @ts-expect-error some props are initially missing
|
|
143
|
+
const retWritable = new WritableStream({
|
|
144
|
+
start: (controller) => {
|
|
145
|
+
this.outgoDatagramController = controller
|
|
146
|
+
},
|
|
147
|
+
// eslint-disable-next-line no-unused-vars
|
|
148
|
+
write: (chunk, controller) => {
|
|
149
|
+
if (this.state === 'closed') throw new Error('Session is closed')
|
|
150
|
+
if (chunk instanceof Uint8Array) {
|
|
151
|
+
/** @type {Promise<void>} */
|
|
152
|
+
if (this.objint == null) {
|
|
153
|
+
throw new Error('this.objint is not set')
|
|
154
|
+
}
|
|
155
|
+
const { code, message } = this.objint.writeDatagram(chunk)
|
|
156
|
+
if (code !== 'success') {
|
|
157
|
+
throw new WebTransportError(code + ':' + message)
|
|
158
|
+
}
|
|
159
|
+
} else throw new Error('chunk is not of type Uint8Array')
|
|
160
|
+
},
|
|
161
|
+
close: () => {
|
|
162
|
+
// do nothing
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
Object.defineProperties(retWritable, {
|
|
166
|
+
sendOrder: {
|
|
167
|
+
get: () => {
|
|
168
|
+
return sendOrder
|
|
169
|
+
},
|
|
170
|
+
/**
|
|
171
|
+
* @param {bigint} value
|
|
172
|
+
*/
|
|
173
|
+
set: (value) => {
|
|
174
|
+
sendOrder = value
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
sendGroup: {
|
|
178
|
+
get: () => {
|
|
179
|
+
return sendGroup
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* @param {WebTransportSendGroup} value
|
|
183
|
+
*/
|
|
184
|
+
set: (value) => {
|
|
185
|
+
if (value !== sendGroup) {
|
|
186
|
+
sendGroup = value
|
|
187
|
+
}
|
|
155
188
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
return retWritable
|
|
192
|
+
},
|
|
193
|
+
// @ts-ignore
|
|
194
|
+
get writable() {
|
|
195
|
+
// @ts-ignore
|
|
196
|
+
if (!this.datagramwritablepolyfilled_) {
|
|
197
|
+
console.warn('datagrams.writable is deprecated')
|
|
162
198
|
}
|
|
163
|
-
|
|
199
|
+
// @ts-ignore
|
|
200
|
+
return (this.datagramwritablepolyfilled_ ||= this.createWritable())
|
|
201
|
+
},
|
|
202
|
+
get maxDatagramSize() {
|
|
203
|
+
// @ts-ignore
|
|
204
|
+
return this._getMaxDatagramSize()
|
|
205
|
+
},
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
_getMaxDatagramSize: () => {
|
|
208
|
+
if (this.objint == null) {
|
|
209
|
+
throw new Error('this.objint is not set')
|
|
210
|
+
}
|
|
211
|
+
return this.objint.getMaxDatagramSize()
|
|
212
|
+
}
|
|
164
213
|
}
|
|
165
214
|
|
|
166
215
|
/** @type {Array<(stream: WebTransportBidirectionalStream) => void>} */
|
|
@@ -193,6 +242,13 @@ export class HttpWTSession {
|
|
|
193
242
|
this.sendStreamsController = new Set()
|
|
194
243
|
/** @type {Set<ReadableStreamDefaultController>} */
|
|
195
244
|
this.receiveStreamsController = new Set()
|
|
245
|
+
|
|
246
|
+
this._sendGroupNum = 1n // 0n is reserved for no sendgroup
|
|
247
|
+
/** @type {Map<bigint,WebTransportSendGroup>} */
|
|
248
|
+
this._sendGroupIndex = new Map()
|
|
249
|
+
|
|
250
|
+
/** @type {undefined|string} */
|
|
251
|
+
this._selectedProtocol = undefined
|
|
196
252
|
}
|
|
197
253
|
|
|
198
254
|
/**
|
|
@@ -210,6 +266,10 @@ export class HttpWTSession {
|
|
|
210
266
|
}
|
|
211
267
|
}
|
|
212
268
|
|
|
269
|
+
get protocol() {
|
|
270
|
+
return this._selectedProtocol
|
|
271
|
+
}
|
|
272
|
+
|
|
213
273
|
getStats() {
|
|
214
274
|
if (this.objint == null) {
|
|
215
275
|
throw new Error('this.objint not set')
|
|
@@ -279,16 +339,6 @@ export class HttpWTSession {
|
|
|
279
339
|
})
|
|
280
340
|
}
|
|
281
341
|
|
|
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
342
|
notifySessionDraining() {
|
|
293
343
|
if (this.objint == null) {
|
|
294
344
|
throw new Error('this.objint not set')
|
|
@@ -361,7 +411,7 @@ export class HttpWTSession {
|
|
|
361
411
|
})
|
|
362
412
|
const notblocked = this.objint.orderBidiStream({
|
|
363
413
|
sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
|
|
364
|
-
sendOrder: opts?.sendOrder ||
|
|
414
|
+
sendOrder: BigInt(opts?.sendOrder || 0n),
|
|
365
415
|
waitUntilAvailable: opts?.waitUntilAvailable || false
|
|
366
416
|
})
|
|
367
417
|
if (!notblocked) {
|
|
@@ -388,7 +438,7 @@ export class HttpWTSession {
|
|
|
388
438
|
})
|
|
389
439
|
const notblocked = this.objint.orderUnidiStream({
|
|
390
440
|
sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
|
|
391
|
-
sendOrder: opts?.sendOrder ||
|
|
441
|
+
sendOrder: opts?.sendOrder || 0n,
|
|
392
442
|
waitUntilAvailable: opts?.waitUntilAvailable || false
|
|
393
443
|
})
|
|
394
444
|
if (!notblocked) {
|
|
@@ -417,7 +467,33 @@ export class HttpWTSession {
|
|
|
417
467
|
}
|
|
418
468
|
}
|
|
419
469
|
|
|
420
|
-
|
|
470
|
+
/**
|
|
471
|
+
* @returns {WebTransportSendGroup}
|
|
472
|
+
*/
|
|
473
|
+
createSendGroup() {
|
|
474
|
+
if (this.state === 'closed' || this.state === 'failed')
|
|
475
|
+
throw new Error('InvalidState')
|
|
476
|
+
const _sendGroupId = this._sendGroupNum++
|
|
477
|
+
const sendGroup = {
|
|
478
|
+
// @ts-ignore
|
|
479
|
+
_sendGroupId,
|
|
480
|
+
getStats: async () => {
|
|
481
|
+
// TODO implement
|
|
482
|
+
return {
|
|
483
|
+
bytesWritten: 0n,
|
|
484
|
+
bytesSent: 0n,
|
|
485
|
+
bytesAcknowledged: 0n
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this._sendGroupIndex.set(_sendGroupId, sendGroup)
|
|
490
|
+
return sendGroup
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* @param {{protocol?: string}} arg
|
|
494
|
+
**/
|
|
495
|
+
onReady({ protocol }) {
|
|
496
|
+
if (protocol) this._selectedProtocol = protocol
|
|
421
497
|
this.state = 'connected'
|
|
422
498
|
if (!this.reliable) this.reliability = 'supports-unreliable'
|
|
423
499
|
else this.reliability = 'reliable-only'
|
|
@@ -458,13 +534,9 @@ export class HttpWTSession {
|
|
|
458
534
|
|
|
459
535
|
for (const rej of this.rejectBiDi) rej(error)
|
|
460
536
|
for (const rej of this.rejectUniDi) rej(error)
|
|
461
|
-
for (const rej of this.writeDatagramRej) rej(error)
|
|
462
537
|
for (const rej of this.rejectSessionStats) rej(error)
|
|
463
538
|
for (const rej of this.rejectDatagramStats) rej(error)
|
|
464
539
|
|
|
465
|
-
this.writeDatagramRej = []
|
|
466
|
-
this.writeDatagramRes = []
|
|
467
|
-
this.writeDatagramProm = []
|
|
468
540
|
this.resolveBiDi = []
|
|
469
541
|
this.resolveUniDi = []
|
|
470
542
|
this.rejectBiDi = []
|
|
@@ -519,7 +591,9 @@ export class HttpWTSession {
|
|
|
519
591
|
parentobj: this,
|
|
520
592
|
transport: this.parentobj,
|
|
521
593
|
bidirectional: args.bidirectional,
|
|
522
|
-
incoming: args.incoming
|
|
594
|
+
incoming: args.incoming,
|
|
595
|
+
sendGroup: this._sendGroupIndex.get(args.sendGroupId || 0n),
|
|
596
|
+
sendOrder: args.sendOrder
|
|
523
597
|
})
|
|
524
598
|
this.addStreamObj(strobj)
|
|
525
599
|
if (args.incoming) {
|
|
@@ -563,6 +637,11 @@ export class HttpWTSession {
|
|
|
563
637
|
*/
|
|
564
638
|
onDatagramReceived(args) {
|
|
565
639
|
log.trace('datagram received', args.datagram)
|
|
640
|
+
// streams spec says zero length chunk on byob stream is illegal
|
|
641
|
+
if (args.datagram.byteLength === 0) {
|
|
642
|
+
log.trace('zerolength datagram dropped')
|
|
643
|
+
return
|
|
644
|
+
}
|
|
566
645
|
// console.log('datagram received', args.datagram, Date.now())
|
|
567
646
|
if (this.incomDatagramController.byobRequest) {
|
|
568
647
|
/** @type {ReadableStreamBYOBRequest} */
|
|
@@ -588,21 +667,6 @@ export class HttpWTSession {
|
|
|
588
667
|
}
|
|
589
668
|
}
|
|
590
669
|
|
|
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
670
|
/**
|
|
607
671
|
* @param {GoawayReceivedEvent} args
|
|
608
672
|
*/
|