@fails-components/webtransport 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/http2/browser/browser.d.ts.map +1 -1
- package/dist/lib/http2/browser/browserparser.d.ts +4 -3
- package/dist/lib/http2/browser/browserparser.d.ts.map +1 -1
- package/dist/lib/http2/node/capsuleparser.d.ts +3 -2
- package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -1
- package/dist/lib/http2/node/client.d.ts.map +1 -1
- package/dist/lib/http2/node/server.d.ts.map +1 -1
- package/dist/lib/http2/node/websocketparser.d.ts +7 -6
- package/dist/lib/http2/node/websocketparser.d.ts.map +1 -1
- package/dist/lib/http2/parserbase.d.ts +16 -4
- package/dist/lib/http2/parserbase.d.ts.map +1 -1
- package/dist/lib/http2/parserbasehttp2.d.ts +9 -1
- package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -1
- package/dist/lib/http2/session.d.ts.map +1 -1
- package/dist/lib/index.node.d.ts +1 -1
- package/dist/lib/index.node.d.ts.map +1 -1
- package/dist/lib/index.types.d.ts +1 -1
- package/dist/lib/index.types.d.ts.map +1 -1
- package/dist/lib/types.d.ts +9 -2
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/webtransport.node.d.ts +1 -0
- package/dist/lib/webtransport.node.d.ts.map +1 -1
- package/lib/http2/browser/browser.js +2 -1
- package/lib/http2/browser/browserparser.js +43 -7
- package/lib/http2/node/capsuleparser.js +41 -10
- package/lib/http2/node/client.js +23 -5
- package/lib/http2/node/server.js +53 -16
- package/lib/http2/node/websocketparser.js +42 -20
- package/lib/http2/parserbase.js +44 -7
- package/lib/http2/parserbasehttp2.js +15 -2
- package/lib/http2/session.js +1 -5
- package/lib/index.node.js +1 -1
- package/lib/index.types.js +1 -1
- package/lib/types.ts +10 -2
- package/lib/webtransport.node.js +10 -1
- package/lib/webtransportbase.js +1 -1
- package/old_test/test.js +13 -1
- package/package.json +3 -2
- package/readme.md +4 -1
- package/test/bidirectional-streams.spec.js +31 -0
- package/test/datagrams.spec.js +12 -0
- package/test/fixtures/quiche.browser.js +2 -0
- package/test/fixtures/quiche.js +1 -0
- package/test/fixtures/server.js +24 -0
- package/test/session.spec.js +13 -3
- package/test/stream-limits.spec.js +12 -0
- package/test/unidirectional-streams.spec.js +12 -0
package/lib/http2/node/server.js
CHANGED
|
@@ -65,7 +65,8 @@ 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
|
-
}
|
|
68
|
+
},
|
|
69
|
+
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
|
|
69
70
|
}
|
|
70
71
|
})
|
|
71
72
|
|
|
@@ -153,7 +154,8 @@ export class Http2WebTransportServer {
|
|
|
153
154
|
stream,
|
|
154
155
|
nativesession,
|
|
155
156
|
isclient: false,
|
|
156
|
-
|
|
157
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
158
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
157
159
|
initialStreamReceiveWindowOffset:
|
|
158
160
|
this.initialStreamFlowControlWindow,
|
|
159
161
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -244,6 +246,14 @@ export class Http2WebTransportServer {
|
|
|
244
246
|
path = path?.slice(1)
|
|
245
247
|
}
|
|
246
248
|
if (this.paths[path]) {
|
|
249
|
+
const {
|
|
250
|
+
0x2b65: remoteBidirectionalStreams = undefined,
|
|
251
|
+
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
252
|
+
0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
|
|
253
|
+
0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
|
|
254
|
+
0x2b61: remoteSessionFlowControlWindow = undefined
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
} = stream?.session?.remoteSettings?.customSettings || {}
|
|
247
257
|
const retObj = {
|
|
248
258
|
session: new Http2WebTransportSession({
|
|
249
259
|
stream,
|
|
@@ -256,8 +266,12 @@ export class Http2WebTransportServer {
|
|
|
256
266
|
stream,
|
|
257
267
|
nativesession,
|
|
258
268
|
isclient: false,
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
initialStreamSendWindowOffsetBidi:
|
|
270
|
+
remoteBidirectionalStreamFlowControlWindow ||
|
|
271
|
+
this.initialStreamFlowControlWindow,
|
|
272
|
+
initialStreamSendWindowOffsetUnidi:
|
|
273
|
+
remoteUnidirectionalStreamFlowControlWindow ||
|
|
274
|
+
this.initialStreamFlowControlWindow,
|
|
261
275
|
initialStreamReceiveWindowOffset:
|
|
262
276
|
this.initialStreamFlowControlWindow,
|
|
263
277
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -270,7 +284,8 @@ export class Http2WebTransportServer {
|
|
|
270
284
|
stream,
|
|
271
285
|
nativesession,
|
|
272
286
|
isclient: false,
|
|
273
|
-
|
|
287
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
288
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
274
289
|
initialStreamReceiveWindowOffset:
|
|
275
290
|
this.initialStreamFlowControlWindow,
|
|
276
291
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -280,18 +295,20 @@ export class Http2WebTransportServer {
|
|
|
280
295
|
}))
|
|
281
296
|
}
|
|
282
297
|
},
|
|
283
|
-
initialBidirectionalSendStreams: websocketProt
|
|
298
|
+
initialBidirectionalSendStreams: websocketProt
|
|
284
299
|
? 0
|
|
285
|
-
: this.initialBidirectionalStreams,
|
|
300
|
+
: remoteBidirectionalStreams || this.initialBidirectionalStreams,
|
|
286
301
|
initialBidirectionalReceiveStreams:
|
|
287
302
|
this.initialBidirectionalStreams,
|
|
288
303
|
initialUnidirectionalSendStreams: websocketProt
|
|
289
304
|
? 0
|
|
290
|
-
:
|
|
305
|
+
: remoteUnidirectionalStreams ||
|
|
306
|
+
this.initialUnidirectionalStreams,
|
|
291
307
|
initialUnidirectionalReceiveStreams:
|
|
292
308
|
this.initialUnidirectionalStreams,
|
|
293
309
|
sendWindowOffset: !websocketProt
|
|
294
|
-
?
|
|
310
|
+
? remoteSessionFlowControlWindow ||
|
|
311
|
+
this.sessionFlowControlWindowSizeLimit
|
|
295
312
|
: 0, // TODO, once supported by node, use initial settings
|
|
296
313
|
receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
|
|
297
314
|
shouldAutoTuneReceiveWindow:
|
|
@@ -443,7 +460,8 @@ export class Http2WebTransportServer {
|
|
|
443
460
|
stream,
|
|
444
461
|
nativesession,
|
|
445
462
|
isclient: false,
|
|
446
|
-
|
|
463
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
464
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
447
465
|
initialStreamReceiveWindowOffset:
|
|
448
466
|
this.initialStreamFlowControlWindow,
|
|
449
467
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -476,6 +494,14 @@ export class Http2WebTransportServer {
|
|
|
476
494
|
log('sendHttp1Headers error', error)
|
|
477
495
|
})
|
|
478
496
|
} else {
|
|
497
|
+
const {
|
|
498
|
+
0x2b65: remoteBidirectionalStreams = undefined,
|
|
499
|
+
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
500
|
+
0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
|
|
501
|
+
0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
|
|
502
|
+
0x2b61: remoteSessionFlowControlWindow = undefined
|
|
503
|
+
// @ts-ignore
|
|
504
|
+
} = stream?.session?.remoteSettings?.customSettings || {}
|
|
479
505
|
const retObj = {
|
|
480
506
|
session: new Http2WebTransportSession({
|
|
481
507
|
stream,
|
|
@@ -488,8 +514,12 @@ export class Http2WebTransportServer {
|
|
|
488
514
|
stream,
|
|
489
515
|
nativesession,
|
|
490
516
|
isclient: false,
|
|
491
|
-
|
|
492
|
-
|
|
517
|
+
initialStreamSendWindowOffsetBidi:
|
|
518
|
+
remoteBidirectionalStreamFlowControlWindow ||
|
|
519
|
+
this.initialStreamFlowControlWindow,
|
|
520
|
+
initialStreamSendWindowOffsetUnidi:
|
|
521
|
+
remoteUnidirectionalStreamFlowControlWindow ||
|
|
522
|
+
this.initialStreamFlowControlWindow,
|
|
493
523
|
initialStreamReceiveWindowOffset:
|
|
494
524
|
this.initialStreamFlowControlWindow,
|
|
495
525
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -502,7 +532,8 @@ export class Http2WebTransportServer {
|
|
|
502
532
|
stream,
|
|
503
533
|
nativesession,
|
|
504
534
|
isclient: false,
|
|
505
|
-
|
|
535
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
536
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
506
537
|
initialStreamReceiveWindowOffset:
|
|
507
538
|
this.initialStreamFlowControlWindow,
|
|
508
539
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -513,16 +544,22 @@ export class Http2WebTransportServer {
|
|
|
513
544
|
}
|
|
514
545
|
},
|
|
515
546
|
initialBidirectionalSendStreams:
|
|
516
|
-
protocol !== 'websocket'
|
|
547
|
+
protocol !== 'websocket'
|
|
548
|
+
? remoteBidirectionalStreams || this.initialBidirectionalStreams
|
|
549
|
+
: 0, // TODO, once supported by node, use initial settings
|
|
517
550
|
initialBidirectionalReceiveStreams:
|
|
518
551
|
this.initialBidirectionalStreams,
|
|
519
552
|
initialUnidirectionalSendStreams:
|
|
520
|
-
protocol !== 'websocket'
|
|
553
|
+
protocol !== 'websocket'
|
|
554
|
+
? remoteUnidirectionalStreams ||
|
|
555
|
+
this.initialUnidirectionalStreams
|
|
556
|
+
: 0, // TODO, once supported by node, use initial settings
|
|
521
557
|
initialUnidirectionalReceiveStreams:
|
|
522
558
|
this.initialUnidirectionalStreams,
|
|
523
559
|
sendWindowOffset:
|
|
524
560
|
protocol !== 'websocket'
|
|
525
|
-
?
|
|
561
|
+
? remoteSessionFlowControlWindow ||
|
|
562
|
+
this.sessionFlowControlWindowSizeLimit
|
|
526
563
|
: 0, // TODO, once supported by node, use initial settings
|
|
527
564
|
receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
|
|
528
565
|
shouldAutoTuneReceiveWindow:
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
2
|
import { ParserBase, lengthVarInt } from '../parserbase.js'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ParserBaseHttp2,
|
|
5
|
+
readVarInt,
|
|
6
|
+
writeVarInt,
|
|
7
|
+
readUint32
|
|
8
|
+
} from '../parserbasehttp2.js'
|
|
4
9
|
import { logger } from '../../utils.js'
|
|
5
10
|
|
|
6
11
|
const log = logger(`webtransport:http2:node:websocketparser(${process?.pid})`)
|
|
@@ -174,7 +179,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
174
179
|
stream,
|
|
175
180
|
nativesession,
|
|
176
181
|
isclient,
|
|
177
|
-
|
|
182
|
+
initialStreamSendWindowOffsetUnidi,
|
|
183
|
+
initialStreamSendWindowOffsetBidi,
|
|
178
184
|
initialStreamReceiveWindowOffset,
|
|
179
185
|
streamShouldAutoTuneReceiveWindow,
|
|
180
186
|
streamReceiveWindowSizeLimit
|
|
@@ -183,7 +189,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
183
189
|
stream,
|
|
184
190
|
nativesession,
|
|
185
191
|
isclient,
|
|
186
|
-
|
|
192
|
+
initialStreamSendWindowOffsetUnidi,
|
|
193
|
+
initialStreamSendWindowOffsetBidi,
|
|
187
194
|
initialStreamReceiveWindowOffset,
|
|
188
195
|
streamShouldAutoTuneReceiveWindow,
|
|
189
196
|
streamReceiveWindowSizeLimit
|
|
@@ -598,6 +605,23 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
598
605
|
case ParserBase.WT_STREAMS_BLOCKED_BIDI:
|
|
599
606
|
this.onStreamsBlockedBidi(readVarInt(bufferstate))
|
|
600
607
|
break
|
|
608
|
+
case ParserBase.CLOSE_WEBTRANSPORT_SESSION:
|
|
609
|
+
{
|
|
610
|
+
const code = readUint32(bufferstate) || 0
|
|
611
|
+
const decoder = new TextDecoder()
|
|
612
|
+
const reason = decoder.decode(
|
|
613
|
+
new Uint8Array(
|
|
614
|
+
bufferstate.buffer.buffer,
|
|
615
|
+
bufferstate.buffer.byteOffset + bufferstate.offset,
|
|
616
|
+
offsetend - bufferstate.offset
|
|
617
|
+
)
|
|
618
|
+
)
|
|
619
|
+
this.onCloseWebTransportSession({ code, reason })
|
|
620
|
+
}
|
|
621
|
+
break
|
|
622
|
+
case ParserBase.DRAIN_WEBTRANSPORT_SESSION:
|
|
623
|
+
this.onDrain()
|
|
624
|
+
break
|
|
601
625
|
case ParserBase.DATAGRAM:
|
|
602
626
|
if (wbufferstate) {
|
|
603
627
|
this.session.jsobj.onDatagramReceived({
|
|
@@ -688,19 +712,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
688
712
|
* @param{{code: Number, reason: string}}arg
|
|
689
713
|
*/
|
|
690
714
|
sendClose({ code, reason }) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
'utf8'
|
|
694
|
-
)
|
|
695
|
-
const wbuf = Buffer.allocUnsafe(2 + reasBuf.byteLength)
|
|
696
|
-
const errorcode = code === 0 ? 1000 : 1003
|
|
697
|
-
wbuf.writeUint16BE(errorcode, 0)
|
|
698
|
-
reasBuf.copy(wbuf, 2)
|
|
699
|
-
this.sendCloseInt(wbuf)
|
|
715
|
+
super.sendClose({ code, reason })
|
|
716
|
+
this.sendCloseInt()
|
|
700
717
|
}
|
|
701
718
|
|
|
702
719
|
/**
|
|
703
|
-
* @param {Uint8Array} payload
|
|
720
|
+
* @param {Uint8Array} [payload]
|
|
704
721
|
*/
|
|
705
722
|
sendCloseInt(payload) {
|
|
706
723
|
this.writeWSFrame({ opcode: WebSocketParser.WS_CLOSE, payload })
|
|
@@ -715,9 +732,9 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
715
732
|
}
|
|
716
733
|
|
|
717
734
|
/**
|
|
718
|
-
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined}} bs
|
|
735
|
+
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined, end?: () => void}} bs
|
|
719
736
|
*/
|
|
720
|
-
writeCapsule({ type, headerVints, payload }) {
|
|
737
|
+
writeCapsule({ type, headerVints, payload, end }) {
|
|
721
738
|
let plength = 0
|
|
722
739
|
for (const ind in headerVints) plength += lengthVarInt(headerVints[ind])
|
|
723
740
|
plength += lengthVarInt(type)
|
|
@@ -762,8 +779,13 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
762
779
|
payload.byteLength
|
|
763
780
|
)
|
|
764
781
|
}
|
|
765
|
-
let blocked =
|
|
766
|
-
if (payload)
|
|
782
|
+
let blocked = false
|
|
783
|
+
if (payload) {
|
|
784
|
+
blocked = !this.stream.write(cdata)
|
|
785
|
+
blocked = !this.stream.write(payload, end) || blocked
|
|
786
|
+
} else {
|
|
787
|
+
blocked = !this.stream.write(cdata, end)
|
|
788
|
+
}
|
|
767
789
|
// do something if blocked
|
|
768
790
|
if (blocked) this.blocked = true
|
|
769
791
|
return blocked
|
|
@@ -802,10 +824,10 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
802
824
|
}
|
|
803
825
|
|
|
804
826
|
/**
|
|
805
|
-
* @param {{ opcode: number; payload: Uint8Array; }} args
|
|
827
|
+
* @param {{ opcode: number; payload: Uint8Array|undefined; }} args
|
|
806
828
|
*/
|
|
807
829
|
writeWSFrame({ opcode, payload }) {
|
|
808
|
-
const plength = payload
|
|
830
|
+
const plength = payload?.byteLength || 0
|
|
809
831
|
let headlength = 2
|
|
810
832
|
let mask = 0
|
|
811
833
|
if (this.isclient) {
|
|
@@ -825,7 +847,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
825
847
|
throw new Error('Headlength does not match pos')
|
|
826
848
|
|
|
827
849
|
let blocked = !this.stream.write(cdata)
|
|
828
|
-
if (maskstate)
|
|
850
|
+
if (maskstate && payload)
|
|
829
851
|
applyMask(
|
|
830
852
|
maskstate,
|
|
831
853
|
Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength),
|
package/lib/http2/parserbase.js
CHANGED
|
@@ -30,6 +30,8 @@ export class ParserBase {
|
|
|
30
30
|
static WT_STREAM_DATA_BLOCKED = 0x190b4d42
|
|
31
31
|
static WT_STREAMS_BLOCKED_UNIDI = 0x190b4d43
|
|
32
32
|
static WT_STREAMS_BLOCKED_BIDI = 0x190b4d44
|
|
33
|
+
static CLOSE_WEBTRANSPORT_SESSION = 0x2843
|
|
34
|
+
static DRAIN_WEBTRANSPORT_SESSION = 0x78ae
|
|
33
35
|
static DATAGRAM = 0x00
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -38,7 +40,8 @@ export class ParserBase {
|
|
|
38
40
|
constructor({
|
|
39
41
|
nativesession,
|
|
40
42
|
isclient,
|
|
41
|
-
|
|
43
|
+
initialStreamSendWindowOffsetBidi,
|
|
44
|
+
initialStreamSendWindowOffsetUnidi,
|
|
42
45
|
initialStreamReceiveWindowOffset,
|
|
43
46
|
streamShouldAutoTuneReceiveWindow,
|
|
44
47
|
streamReceiveWindowSizeLimit
|
|
@@ -48,7 +51,8 @@ export class ParserBase {
|
|
|
48
51
|
/** @type {boolean} */
|
|
49
52
|
this.blocked = false
|
|
50
53
|
|
|
51
|
-
this.
|
|
54
|
+
this.initialStreamSendWindowOffsetUnidi = initialStreamSendWindowOffsetUnidi
|
|
55
|
+
this.initialStreamSendWindowOffsetBidi = initialStreamSendWindowOffsetBidi
|
|
52
56
|
this.initialStreamReceiveWindowOffset = initialStreamReceiveWindowOffset
|
|
53
57
|
this.streamShouldAutoTuneReceiveWindow = streamShouldAutoTuneReceiveWindow
|
|
54
58
|
this.streamReceiveWindowSizeLimit = streamReceiveWindowSizeLimit
|
|
@@ -67,16 +71,31 @@ export class ParserBase {
|
|
|
67
71
|
|
|
68
72
|
/**
|
|
69
73
|
* @abstract
|
|
70
|
-
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined}} bs
|
|
74
|
+
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined, end?: () => void}} bs
|
|
71
75
|
*/
|
|
72
|
-
writeCapsule({ type, headerVints, payload }) {
|
|
76
|
+
writeCapsule({ type, headerVints, payload, end }) {
|
|
73
77
|
throw new Error('Implement writeCapsule in derived Class')
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/**
|
|
77
81
|
* @param{{code: Number, reason: string}}arg
|
|
78
82
|
*/
|
|
79
|
-
sendClose({ code, reason }) {
|
|
83
|
+
sendClose({ code, reason }) {
|
|
84
|
+
const encoder = new TextEncoder()
|
|
85
|
+
const payload = encoder.encode('AAAA' + reason)
|
|
86
|
+
payload[0] = (code >> 24) & 0xff
|
|
87
|
+
payload[1] = (code >> 16) & 0xff
|
|
88
|
+
payload[2] = (code >> 8) & 0xff
|
|
89
|
+
payload[3] = code & 0xff
|
|
90
|
+
this.writeCapsule({
|
|
91
|
+
type: ParserBase.CLOSE_WEBTRANSPORT_SESSION,
|
|
92
|
+
headerVints: [],
|
|
93
|
+
payload,
|
|
94
|
+
end: () => {
|
|
95
|
+
this.closeHttp2Stream(code)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
}
|
|
80
99
|
|
|
81
100
|
/**
|
|
82
101
|
* @param {bigint} streamid
|
|
@@ -101,12 +120,15 @@ export class ParserBase {
|
|
|
101
120
|
return undefined
|
|
102
121
|
}
|
|
103
122
|
}
|
|
123
|
+
const unidirectional = !!(streamid & 0x2n)
|
|
104
124
|
const stream = new Http2WebTransportStream({
|
|
105
125
|
streamid,
|
|
106
|
-
unidirectional
|
|
126
|
+
unidirectional,
|
|
107
127
|
incoming,
|
|
108
128
|
capsuleParser: this,
|
|
109
|
-
sendWindowOffset:
|
|
129
|
+
sendWindowOffset: unidirectional
|
|
130
|
+
? this.initialStreamSendWindowOffsetUnidi
|
|
131
|
+
: this.initialStreamSendWindowOffsetBidi,
|
|
110
132
|
receiveWindowOffset: this.initialStreamReceiveWindowOffset,
|
|
111
133
|
shouldAutoTuneReceiveWindow: this.streamShouldAutoTuneReceiveWindow,
|
|
112
134
|
receiveWindowSizeLimit: this.streamReceiveWindowSizeLimit,
|
|
@@ -212,6 +234,21 @@ export class ParserBase {
|
|
|
212
234
|
}
|
|
213
235
|
}
|
|
214
236
|
|
|
237
|
+
/**
|
|
238
|
+
* @param {{code: number, reason: string}} opts
|
|
239
|
+
*/
|
|
240
|
+
onCloseWebTransportSession({ code, reason }) {
|
|
241
|
+
this.session.jsobj.onClose({
|
|
242
|
+
errorcode: code,
|
|
243
|
+
error: reason
|
|
244
|
+
})
|
|
245
|
+
this.closeHttp2Stream(code) // is this necessary
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
onDrain() {
|
|
249
|
+
this.session.jsobj.onGoAwayReceived()
|
|
250
|
+
}
|
|
251
|
+
|
|
215
252
|
/**
|
|
216
253
|
* @param {number} code
|
|
217
254
|
*/
|
|
@@ -4,6 +4,17 @@ import { ParserBase } from './parserbase.js'
|
|
|
4
4
|
* @typedef {import('node:http2').Http2Stream} Http2Stream
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @param{{offset: Number, buffer: Buffer, size: Number}} bs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export function readUint32(bs) {
|
|
12
|
+
if (bs.offset + 4 > bs.size) return undefined
|
|
13
|
+
const toret = bs.buffer.readUInt32BE(bs.offset)
|
|
14
|
+
bs.offset += 4
|
|
15
|
+
return toret
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
/**
|
|
8
19
|
* @param{{offset: Number, buffer: Buffer, size: Number}} bs
|
|
9
20
|
*/
|
|
@@ -63,7 +74,8 @@ export class ParserBaseHttp2 extends ParserBase {
|
|
|
63
74
|
stream,
|
|
64
75
|
nativesession,
|
|
65
76
|
isclient,
|
|
66
|
-
|
|
77
|
+
initialStreamSendWindowOffsetBidi,
|
|
78
|
+
initialStreamSendWindowOffsetUnidi,
|
|
67
79
|
initialStreamReceiveWindowOffset,
|
|
68
80
|
streamShouldAutoTuneReceiveWindow,
|
|
69
81
|
streamReceiveWindowSizeLimit
|
|
@@ -71,7 +83,8 @@ export class ParserBaseHttp2 extends ParserBase {
|
|
|
71
83
|
super({
|
|
72
84
|
nativesession,
|
|
73
85
|
isclient,
|
|
74
|
-
|
|
86
|
+
initialStreamSendWindowOffsetBidi,
|
|
87
|
+
initialStreamSendWindowOffsetUnidi,
|
|
75
88
|
initialStreamReceiveWindowOffset,
|
|
76
89
|
streamShouldAutoTuneReceiveWindow,
|
|
77
90
|
streamReceiveWindowSizeLimit
|
package/lib/http2/session.js
CHANGED
|
@@ -209,10 +209,7 @@ export class Http2WebTransportSession {
|
|
|
209
209
|
*/
|
|
210
210
|
close({ code, reason }) {
|
|
211
211
|
this.capsParser.sendClose({ code, reason }) // this includes for ws closing the session!
|
|
212
|
-
//
|
|
213
|
-
if (this.stream) {
|
|
214
|
-
this.capsParser.closeHttp2Stream(code)
|
|
215
|
-
}
|
|
212
|
+
// should also close the stream
|
|
216
213
|
}
|
|
217
214
|
|
|
218
215
|
/**
|
|
@@ -252,7 +249,6 @@ export class Http2WebTransportSession {
|
|
|
252
249
|
* @param {{ code: number, reason: string }} arg
|
|
253
250
|
*/
|
|
254
251
|
closeConnection({ code, reason }) {
|
|
255
|
-
console.trace()
|
|
256
252
|
// called in case of failure in parsing or flowcontrol
|
|
257
253
|
this.jsobj.onClose({
|
|
258
254
|
errorcode: code,
|
package/lib/index.node.js
CHANGED
package/lib/index.types.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
// both imports from the browser side and for nodes to generate a joint type file
|
|
27
27
|
export { HttpServer, Http3Server, Http2Server } from './server.js'
|
|
28
|
-
export { WebTransport } from './webtransport.node.js'
|
|
28
|
+
export { WebTransport, quicheLoaded } from './webtransport.node.js'
|
|
29
29
|
|
|
30
30
|
export {
|
|
31
31
|
WebTransportPonyfill,
|
package/lib/types.ts
CHANGED
|
@@ -266,10 +266,17 @@ export interface WebTransportSessionImpl extends WebTransportSession {
|
|
|
266
266
|
state: WebTransportSessionState
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
export type QUICHE_LOG_OFF = -1
|
|
270
|
+
export type QUICHE_LOG_INFO = 0
|
|
271
|
+
export type QUICHE_LOG_WARNING = 1
|
|
272
|
+
export type QUICHE_LOG_ERROR = 2
|
|
273
|
+
export type QUICHE_LOG_FATAL = 3
|
|
274
|
+
export type QUICHE_LOG = QUICHE_LOG_OFF | QUICHE_LOG_INFO | QUICHE_LOG_WARNING | QUICHE_LOG_ERROR | QUICHE_LOG_FATAL
|
|
275
|
+
|
|
269
276
|
export interface HttpWebTransportInit extends WebTransportOptions {
|
|
270
277
|
host: string
|
|
271
278
|
port: string | number
|
|
272
|
-
quicheLogVerbose?:
|
|
279
|
+
quicheLogVerbose?: QUICHE_LOG
|
|
273
280
|
forceIpv6?: boolean
|
|
274
281
|
localPort?: number
|
|
275
282
|
}
|
|
@@ -331,7 +338,8 @@ export type CreateParserFunction = (
|
|
|
331
338
|
export interface ParserInit {
|
|
332
339
|
isclient: boolean
|
|
333
340
|
nativesession: any
|
|
334
|
-
|
|
341
|
+
initialStreamSendWindowOffsetBidi: number
|
|
342
|
+
initialStreamSendWindowOffsetUnidi: number
|
|
335
343
|
initialStreamReceiveWindowOffset: number
|
|
336
344
|
streamShouldAutoTuneReceiveWindow: boolean
|
|
337
345
|
streamReceiveWindowSizeLimit: number
|
package/lib/webtransport.node.js
CHANGED
|
@@ -18,9 +18,13 @@ let Http3WebTransportClientSocket
|
|
|
18
18
|
* @type {new (arg0: import("./types").HttpWebTransportInit) => any}
|
|
19
19
|
*/
|
|
20
20
|
let Http3WebTransportClient
|
|
21
|
+
/**
|
|
22
|
+
* @type {boolean}
|
|
23
|
+
*/
|
|
24
|
+
let quicheLoadedReady
|
|
21
25
|
// @ts-ignore
|
|
22
26
|
// eslint-disable-next-line no-unused-vars
|
|
23
|
-
const quicheLoaded = new Promise((resolve, reject) => {
|
|
27
|
+
export const quicheLoaded = new Promise((resolve, reject) => {
|
|
24
28
|
// @ts-ignore
|
|
25
29
|
import('@fails-components/webtransport-transport-http3-quiche')
|
|
26
30
|
.then(
|
|
@@ -39,6 +43,9 @@ const quicheLoaded = new Promise((resolve, reject) => {
|
|
|
39
43
|
resolve(undefined)
|
|
40
44
|
}
|
|
41
45
|
)
|
|
46
|
+
.finally(() => {
|
|
47
|
+
quicheLoadedReady = true
|
|
48
|
+
})
|
|
42
49
|
.catch((error) => {
|
|
43
50
|
log('Problem loading http3-quiche transport', error)
|
|
44
51
|
reject(error)
|
|
@@ -102,6 +109,8 @@ export class WebTransport extends WebTransportBase {
|
|
|
102
109
|
*/
|
|
103
110
|
createClient(args) {
|
|
104
111
|
let createUnreliableClient
|
|
112
|
+
if (!quicheLoadedReady)
|
|
113
|
+
throw new Error('Lib quiche loading attempt did not end')
|
|
105
114
|
if (checkQuicheInit) {
|
|
106
115
|
createUnreliableClient = function (/** @type {any} */ _client) {
|
|
107
116
|
if (
|
package/lib/webtransportbase.js
CHANGED
package/old_test/test.js
CHANGED
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
// this file runs various tests
|
|
6
6
|
|
|
7
7
|
import { generateWebTransportCertificate } from '../test/fixtures/certificate.js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Http3Server,
|
|
10
|
+
Http2Server,
|
|
11
|
+
WebTransport,
|
|
12
|
+
quicheLoaded
|
|
13
|
+
} from '../lib/index.node.js'
|
|
9
14
|
import { echoTestsConnection, runEchoServer } from './testsuite.js'
|
|
10
15
|
|
|
11
16
|
let http2 = false
|
|
@@ -15,6 +20,13 @@ if (process.argv.some((el) => el === 'http2')) {
|
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
async function run() {
|
|
23
|
+
if (
|
|
24
|
+
process.env.USE_HTTP2 !== 'true' &&
|
|
25
|
+
process.env.USE_PONYFILL !== 'true' &&
|
|
26
|
+
process.env.USE_POLYFILL !== 'true'
|
|
27
|
+
) {
|
|
28
|
+
await quicheLoaded
|
|
29
|
+
}
|
|
18
30
|
console.log('try connecting to server that does not exist')
|
|
19
31
|
const badClient = new WebTransport('https://127.0.0.1:49823/echo', {
|
|
20
32
|
serverCertificateHashes: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fails-components/webtransport",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "A component to add webtransport support (server and client) to node.js using libquiche",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"bugs": {
|
|
44
44
|
"url": "https://github.com/fails-components/webtransport/issues"
|
|
45
45
|
},
|
|
46
|
-
"homepage": "https://github.com/fails-components/webtransport/main
|
|
46
|
+
"homepage": "https://github.com/fails-components/webtransport/tree/master/main#readme",
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@types/debug": "^4.1.7",
|
|
49
49
|
"bindings": "^1.5.0",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
},
|
|
84
84
|
"browser": {
|
|
85
85
|
"./test/fixtures/webtransport.js": "./test/fixtures/webtransport.browser.js",
|
|
86
|
+
"./test/fixtures/quiche.js": "./test/fixtures/quiche.browser.js",
|
|
86
87
|
"./lib/webstreams.js": "./lib/webstreams.browser.js"
|
|
87
88
|
}
|
|
88
89
|
}
|
package/readme.md
CHANGED
|
@@ -35,7 +35,7 @@ Note, that it is only partially implemented and also some http2 features require
|
|
|
35
35
|
Use for the server side either `HttpServer` for http/2 and http/3 support (including WebTransport over WebSocket), Http3Server for http/3 only and Http2Server for http/2 only.
|
|
36
36
|
For the client side pass the options `forceReliable`, `requireUnreliable` etc. to force/select a type of transport.
|
|
37
37
|
|
|
38
|
-
**Note, that the http/2 webtransport protocol (not supported by any browser yet),
|
|
38
|
+
**Note, that the http/2 webtransport protocol (not supported by any browser yet), requires support for sending and receiving initial a node version with support for receiving customSettings. Versions without support can cause a problem with the initial flow control of the WebTransport streams. Note, that for sending and receiving the required initial setting you need a very recent version (>=21.6.1 sending >= 21.7.0 receiving) of node.js.** This limitation does not apply to the polyfill/ponyfill and the webtransport over websocket protocol.
|
|
39
39
|
|
|
40
40
|
The current proposal for the WebTransport over WebSocket protocol can be found [here] (https://datatracker.ietf.org/doc/draft-richter-webtransport-websocket/).
|
|
41
41
|
|
|
@@ -126,6 +126,9 @@ The `WebTransportSession` should behave most like a WebTransport object on the b
|
|
|
126
126
|
### Using a client
|
|
127
127
|
The `WebTransport` object for node or the `WebTransportPonyfill`, `WebTransportPolyfill` objects for the browser, behave like the `WebTransport` client object for the browser with few additions and some missing features.
|
|
128
128
|
|
|
129
|
+
As the http/3 package is loaded dynamically and the WebTransport object is created synchronously, you may want to make sure, that the modules are already loaded. You can do so, by waiting for the promise `quicheLoaded` exported by the package.
|
|
130
|
+
|
|
131
|
+
|
|
129
132
|
## Specification divergence
|
|
130
133
|
|
|
131
134
|
This module implements parts of the [WebTransport spec](https://datatracker.ietf.org/doc/html/draft-vvv-webtransport-quic-00) but not all of it.
|
|
@@ -6,6 +6,7 @@ import { readStream } from './fixtures/read-stream.js'
|
|
|
6
6
|
import { writeStream } from './fixtures/write-stream.js'
|
|
7
7
|
import { readCertHash } from './fixtures/read-cert-hash.js'
|
|
8
8
|
import WebTransport from './fixtures/webtransport.js'
|
|
9
|
+
import { quicheLoaded } from './fixtures/quiche.js'
|
|
9
10
|
import * as ui8 from 'uint8arrays'
|
|
10
11
|
import {
|
|
11
12
|
KNOWN_BYTES,
|
|
@@ -38,6 +39,17 @@ describe('bidirectional streams', function () {
|
|
|
38
39
|
// @ts-ignore
|
|
39
40
|
delete wtOptions.serverCertificateHashes
|
|
40
41
|
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
beforeEach(async () => {
|
|
44
|
+
if (
|
|
45
|
+
process.env.USE_HTTP2 !== 'true' &&
|
|
46
|
+
process.env.USE_PONYFILL !== 'true' &&
|
|
47
|
+
process.env.USE_POLYFILL !== 'true'
|
|
48
|
+
) {
|
|
49
|
+
await quicheLoaded
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
41
53
|
// @ts-ignore
|
|
42
54
|
afterEach(async () => {
|
|
43
55
|
if (client != null) {
|
|
@@ -116,4 +128,23 @@ describe('bidirectional streams', function () {
|
|
|
116
128
|
expect(result).to.have.property('reason', '')
|
|
117
129
|
expect(result).to.have.property('closeCode', 0)
|
|
118
130
|
})
|
|
131
|
+
|
|
132
|
+
it('receives data from server and checks receiving stream fin', async () => {
|
|
133
|
+
// client context - waits for the server to open a bidi stream then pipes it back to them
|
|
134
|
+
client = new WebTransport(
|
|
135
|
+
`${process.env.SERVER_URL}/bidirectional_server_fin_send`,
|
|
136
|
+
wtOptions
|
|
137
|
+
)
|
|
138
|
+
await client.ready
|
|
139
|
+
|
|
140
|
+
const bidiStream = await getReaderValue(client.incomingBidirectionalStreams)
|
|
141
|
+
const reader = bidiStream.readable.getReader()
|
|
142
|
+
|
|
143
|
+
while (true) {
|
|
144
|
+
const res = await reader.read()
|
|
145
|
+
if (res.done) {
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})
|
|
119
150
|
})
|