@fails-components/webtransport 1.0.9 → 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.
Files changed (48) hide show
  1. package/dist/lib/http2/browser/browser.d.ts.map +1 -1
  2. package/dist/lib/http2/browser/browserparser.d.ts +4 -3
  3. package/dist/lib/http2/browser/browserparser.d.ts.map +1 -1
  4. package/dist/lib/http2/node/capsuleparser.d.ts +3 -2
  5. package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -1
  6. package/dist/lib/http2/node/client.d.ts.map +1 -1
  7. package/dist/lib/http2/node/server.d.ts.map +1 -1
  8. package/dist/lib/http2/node/websocketparser.d.ts +7 -6
  9. package/dist/lib/http2/node/websocketparser.d.ts.map +1 -1
  10. package/dist/lib/http2/parserbase.d.ts +16 -4
  11. package/dist/lib/http2/parserbase.d.ts.map +1 -1
  12. package/dist/lib/http2/parserbasehttp2.d.ts +9 -1
  13. package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -1
  14. package/dist/lib/http2/session.d.ts.map +1 -1
  15. package/dist/lib/index.node.d.ts +1 -1
  16. package/dist/lib/index.node.d.ts.map +1 -1
  17. package/dist/lib/index.types.d.ts +60 -0
  18. package/dist/lib/index.types.d.ts.map +1 -0
  19. package/dist/lib/types.d.ts +9 -2
  20. package/dist/lib/types.d.ts.map +1 -1
  21. package/dist/lib/webtransport.node.d.ts +1 -0
  22. package/dist/lib/webtransport.node.d.ts.map +1 -1
  23. package/lib/http2/browser/browser.js +2 -1
  24. package/lib/http2/browser/browserparser.js +43 -7
  25. package/lib/http2/node/capsuleparser.js +41 -10
  26. package/lib/http2/node/client.js +23 -5
  27. package/lib/http2/node/server.js +53 -16
  28. package/lib/http2/node/websocketparser.js +42 -20
  29. package/lib/http2/parserbase.js +44 -7
  30. package/lib/http2/parserbasehttp2.js +15 -2
  31. package/lib/http2/session.js +1 -5
  32. package/lib/index.browser.js +1 -0
  33. package/lib/index.node.js +2 -1
  34. package/lib/index.types.js +33 -0
  35. package/lib/types.ts +10 -2
  36. package/lib/webtransport.node.js +10 -1
  37. package/lib/webtransportbase.js +1 -1
  38. package/old_test/test.js +13 -1
  39. package/package.json +4 -3
  40. package/readme.md +4 -1
  41. package/test/bidirectional-streams.spec.js +31 -0
  42. package/test/datagrams.spec.js +12 -0
  43. package/test/fixtures/quiche.browser.js +2 -0
  44. package/test/fixtures/quiche.js +1 -0
  45. package/test/fixtures/server.js +24 -0
  46. package/test/session.spec.js +13 -3
  47. package/test/stream-limits.spec.js +12 -0
  48. package/test/unidirectional-streams.spec.js +12 -0
@@ -91,7 +91,8 @@ export class Http2WebTransportClient {
91
91
  0x2b63: this.initialStreamFlowControlWindow, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAM_DATA_BIDI
92
92
  0x2b64: this.initialUnidirectionalStreams, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_UNI
93
93
  0x2b65: this.initialBidirectionalStreams // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_BIDI
94
- }
94
+ },
95
+ remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
95
96
  },
96
97
  localPort: this.localPort,
97
98
  // TODO: REMOVE BEFORE RELEASE; UNSAFE SETTING
@@ -210,6 +211,14 @@ export class Http2WebTransportClient {
210
211
  authority: this.hostname,
211
212
  origin: this.hostname
212
213
  })
214
+ const {
215
+ 0x2b65: remoteBidirectionalStreams = undefined,
216
+ 0x2b64: remoteUnidirectionalStreams = undefined,
217
+ 0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
218
+ 0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
219
+ 0x2b61: remoteSessionFlowControlWindow = undefined
220
+ // @ts-ignore
221
+ } = this.clientInt.remoteSettings?.customSettings || {}
213
222
 
214
223
  const retObj = {
215
224
  header: stream.sentHeaders,
@@ -221,18 +230,27 @@ export class Http2WebTransportClient {
221
230
  stream,
222
231
  nativesession,
223
232
  isclient: true,
224
- initialStreamSendWindowOffset: this.initialStreamFlowControlWindow, // TODO, once supported by node, use initial settings
233
+ initialStreamSendWindowOffsetBidi:
234
+ remoteBidirectionalStreamFlowControlWindow ||
235
+ this.initialStreamFlowControlWindow,
236
+ initialStreamSendWindowOffsetUnidi:
237
+ remoteUnidirectionalStreamFlowControlWindow ||
238
+ this.initialStreamFlowControlWindow,
225
239
  initialStreamReceiveWindowOffset:
226
240
  this.initialStreamFlowControlWindow,
227
241
  streamShouldAutoTuneReceiveWindow:
228
242
  this.streamShouldAutoTuneReceiveWindow,
229
243
  streamReceiveWindowSizeLimit: this.streamFlowControlWindowSizeLimit
230
244
  }),
231
- initialBidirectionalSendStreams: this.initialBidirectionalStreams, // TODO, once supported by node, use initial settings
245
+ initialBidirectionalSendStreams:
246
+ remoteBidirectionalStreams || this.initialBidirectionalStreams,
232
247
  initialBidirectionalReceiveStreams: this.initialBidirectionalStreams,
233
- initialUnidirectionalSendStreams: this.initialUnidirectionalStreams, // TODO, once supported by node, use initial settings
248
+ initialUnidirectionalSendStreams:
249
+ remoteUnidirectionalStreams || this.initialUnidirectionalStreams,
234
250
  initialUnidirectionalReceiveStreams: this.initialUnidirectionalStreams,
235
- sendWindowOffset: this.sessionFlowControlWindowSizeLimit,
251
+ sendWindowOffset:
252
+ remoteSessionFlowControlWindow ||
253
+ this.sessionFlowControlWindowSizeLimit,
236
254
  receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
237
255
  shouldAutoTuneReceiveWindow: this.sessionShouldAutoTuneReceiveWindow,
238
256
  receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
@@ -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
- initialStreamSendWindowOffset: 0,
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
- initialStreamSendWindowOffset:
260
- this.initialStreamFlowControlWindow, // TODO, once supported by node, use initial settings
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
- initialStreamSendWindowOffset: 0,
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 // TODO, once supported by node, use initial settings
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
- : this.initialUnidirectionalStreams, // TODO, once supported by node, use initial settings
305
+ : remoteUnidirectionalStreams ||
306
+ this.initialUnidirectionalStreams,
291
307
  initialUnidirectionalReceiveStreams:
292
308
  this.initialUnidirectionalStreams,
293
309
  sendWindowOffset: !websocketProt
294
- ? this.sessionFlowControlWindowSizeLimit
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
- initialStreamSendWindowOffset: 0,
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
- initialStreamSendWindowOffset:
492
- this.initialStreamFlowControlWindow, // TODO, once supported by node, use initial settings
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
- initialStreamSendWindowOffset: 0,
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' ? this.initialBidirectionalStreams : 0, // TODO, once supported by node, use initial settings
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' ? this.initialUnidirectionalStreams : 0, // TODO, once supported by node, use initial settings
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
- ? this.sessionFlowControlWindowSizeLimit
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 { ParserBaseHttp2, readVarInt, writeVarInt } from '../parserbasehttp2.js'
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
- initialStreamSendWindowOffset,
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
- initialStreamSendWindowOffset,
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
- const reasBuf = Buffer.from(
692
- (code || 0).toString() + ':' + (reason || ''),
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 = !this.stream.write(cdata)
766
- if (payload) blocked = !this.stream.write(payload) || blocked
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.byteLength
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),
@@ -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
- initialStreamSendWindowOffset,
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.initialStreamSendWindowOffset = initialStreamSendWindowOffset
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: !!(streamid & 0x2n),
126
+ unidirectional,
107
127
  incoming,
108
128
  capsuleParser: this,
109
- sendWindowOffset: this.initialStreamSendWindowOffset,
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
- initialStreamSendWindowOffset,
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
- initialStreamSendWindowOffset,
86
+ initialStreamSendWindowOffsetBidi,
87
+ initialStreamSendWindowOffsetUnidi,
75
88
  initialStreamReceiveWindowOffset,
76
89
  streamShouldAutoTuneReceiveWindow,
77
90
  streamReceiveWindowSizeLimit
@@ -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
- // what to do with the reason
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,
@@ -23,6 +23,7 @@
23
23
  * @typedef {import('./types').HttpServerInit} HttpServerInit
24
24
  */
25
25
 
26
+ // also edit index.types.js
26
27
  export {
27
28
  WebTransportPonyfill,
28
29
  WebTransportPolyfill
package/lib/index.node.js CHANGED
@@ -23,5 +23,6 @@
23
23
  * @typedef {import('./types').HttpServerInit} HttpServerInit
24
24
  */
25
25
 
26
+ // also edit index.types.js
26
27
  export { HttpServer, Http3Server, Http2Server } from './server.js'
27
- export { WebTransport } from './webtransport.node.js'
28
+ export { WebTransport, quicheLoaded } from './webtransport.node.js'
@@ -0,0 +1,33 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ // Copyright (c) 2022 Marten Richter or other contributers (see commit). All rights reserved.
3
+ // Use of this source code is governed by a BSD-style license that can be
4
+ // found in the LICENSE file.
5
+
6
+ /**
7
+ * // Spec
8
+ * @typedef {import('./dom').WebTransportDatagramStats} WebTransportDatagramStats
9
+ * @typedef {import('./dom').WebTransportStats} WebTransportStats
10
+ * @typedef {import('./dom').WebTransportCloseInfo} WebTransportCloseInfo
11
+ * @typedef {import('./dom').WebTransportDatagramDuplexStream} WebTransportDatagramDuplexStream
12
+ * @typedef {import('./dom').WebTransportBidirectionalStream} WebTransportBidirectionalStream
13
+ * @typedef {import('./dom').WebTransportSendStreamStats} WebTransportSendStreamStats
14
+ * @typedef {import('./dom').WebTransportSendStream} WebTransportSendStream
15
+ * @typedef {import('./dom').WebTransportReceiveStreamStats} WebTransportReceiveStreamStats
16
+ * @typedef {import('./dom').WebTransportReceiveStream} WebTransportReceiveStream
17
+ * @typedef {import('./dom').WebTransportHash} WebTransportHash
18
+ * @typedef {import('./dom').WebTransportOptions} WebTransportOptions
19
+ * @typedef {import('./dom').WebTransportReliabilityMode} WebTransportReliabilityMode
20
+ *
21
+ * Public API
22
+ * @typedef {import('./types').WebTransportSessionImpl} WebTransportSession
23
+ * @typedef {import('./types').HttpServerInit} HttpServerInit
24
+ */
25
+
26
+ // both imports from the browser side and for nodes to generate a joint type file
27
+ export { HttpServer, Http3Server, Http2Server } from './server.js'
28
+ export { WebTransport, quicheLoaded } from './webtransport.node.js'
29
+
30
+ export {
31
+ WebTransportPonyfill,
32
+ WebTransportPolyfill
33
+ } from './webtransport.browser.js'
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?: number
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
- initialStreamSendWindowOffset: number
341
+ initialStreamSendWindowOffsetBidi: number
342
+ initialStreamSendWindowOffsetUnidi: number
335
343
  initialStreamReceiveWindowOffset: number
336
344
  streamShouldAutoTuneReceiveWindow: boolean
337
345
  streamReceiveWindowSizeLimit: number
@@ -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 (
@@ -68,7 +68,7 @@ export class WebTransportBase {
68
68
  * @abstract
69
69
  */
70
70
  startUpConnection({ client, sessionint, ourl }) {
71
- throw new Error('Implement createClient')
71
+ throw new Error('Implement startUpConnection')
72
72
  }
73
73
 
74
74
  get reliability() {
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 { Http3Server, Http2Server, WebTransport } from '../lib/index.node.js'
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: [