@fails-components/webtransport 1.0.8 → 1.0.9

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 (154) hide show
  1. package/dist/lib/client.d.ts.map +1 -0
  2. package/dist/{main/lib → lib}/dom.d.ts +10 -2
  3. package/dist/lib/dom.d.ts.map +1 -0
  4. package/dist/lib/error.d.ts.map +1 -0
  5. package/dist/{main/lib → lib}/features.d.ts.map +1 -1
  6. package/dist/{main/lib → lib}/http2/browser/browser.d.ts +2 -0
  7. package/dist/lib/http2/browser/browser.d.ts.map +1 -0
  8. package/dist/lib/http2/browser/browserparser.d.ts.map +1 -0
  9. package/dist/{main/lib → lib}/http2/flowcontroller.d.ts +1 -1
  10. package/dist/lib/http2/flowcontroller.d.ts.map +1 -0
  11. package/dist/{main/lib → lib}/http2/node/capsuleparser.d.ts +1 -1
  12. package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -0
  13. package/dist/{main/lib → lib}/http2/node/client.d.ts +2 -0
  14. package/dist/lib/http2/node/client.d.ts.map +1 -0
  15. package/dist/{main/test → lib/http2/node}/index.d.ts.map +1 -1
  16. package/dist/{main/lib → lib}/http2/node/server.d.ts +3 -0
  17. package/dist/lib/http2/node/server.d.ts.map +1 -0
  18. package/dist/{main/lib → lib}/http2/node/websocketparser.d.ts +4 -2
  19. package/dist/lib/http2/node/websocketparser.d.ts.map +1 -0
  20. package/dist/{main/lib → lib}/http2/parserbase.d.ts +26 -9
  21. package/dist/lib/http2/parserbase.d.ts.map +1 -0
  22. package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -0
  23. package/dist/{main/lib → lib}/http2/session.d.ts +43 -6
  24. package/dist/lib/http2/session.d.ts.map +1 -0
  25. package/dist/{main/lib → lib}/http2/stream.d.ts +29 -7
  26. package/dist/lib/http2/stream.d.ts.map +1 -0
  27. package/dist/lib/http2/streamidmanager.d.ts +72 -0
  28. package/dist/lib/http2/streamidmanager.d.ts.map +1 -0
  29. package/dist/lib/http2/websocketcommon.d.ts.map +1 -0
  30. package/dist/lib/index.browser.d.ts.map +1 -0
  31. package/dist/lib/index.node.d.ts.map +1 -0
  32. package/dist/lib/server.d.ts.map +1 -0
  33. package/dist/{main/lib → lib}/session.d.ts +10 -3
  34. package/dist/lib/session.d.ts.map +1 -0
  35. package/dist/lib/stream.d.ts.map +1 -0
  36. package/dist/{main/lib → lib}/types.d.ts +26 -4
  37. package/dist/lib/types.d.ts.map +1 -0
  38. package/dist/lib/utils.d.ts.map +1 -0
  39. package/dist/lib/webstreams.browser.d.ts.map +1 -0
  40. package/dist/lib/webstreams.d.ts.map +1 -0
  41. package/dist/{main/lib → lib}/webtransport.browser.d.ts +12 -2
  42. package/dist/lib/webtransport.browser.d.ts.map +1 -0
  43. package/dist/lib/webtransport.node.d.ts.map +1 -0
  44. package/dist/{main/lib → lib}/webtransportbase.d.ts +14 -2
  45. package/dist/lib/webtransportbase.d.ts.map +1 -0
  46. package/lib/dom.ts +12 -2
  47. package/lib/http2/browser/browser.js +12 -3
  48. package/lib/http2/browser/browserparser.js +36 -36
  49. package/lib/http2/flowcontroller.js +2 -2
  50. package/lib/http2/node/capsuleparser.js +58 -51
  51. package/lib/http2/node/client.js +12 -3
  52. package/lib/http2/node/server.js +53 -16
  53. package/lib/http2/node/websocketparser.js +69 -59
  54. package/lib/http2/parserbase.js +87 -12
  55. package/lib/http2/session.js +109 -34
  56. package/lib/http2/stream.js +65 -10
  57. package/lib/http2/streamidmanager.js +316 -0
  58. package/lib/session.js +31 -5
  59. package/lib/types.ts +20 -4
  60. package/lib/webtransport.browser.js +14 -4
  61. package/lib/webtransportbase.js +14 -4
  62. package/package.json +1 -1
  63. package/readme.md +4 -3
  64. package/test/fixtures/certificate.js +1 -2
  65. package/test/fixtures/server.js +112 -7
  66. package/test/stream-limits.spec.js +247 -0
  67. package/tsconfig.json +2 -7
  68. package/dist/main/lib/client.d.ts.map +0 -1
  69. package/dist/main/lib/dom.d.ts.map +0 -1
  70. package/dist/main/lib/error.d.ts.map +0 -1
  71. package/dist/main/lib/http2/browser/browser.d.ts.map +0 -1
  72. package/dist/main/lib/http2/browser/browserparser.d.ts.map +0 -1
  73. package/dist/main/lib/http2/flowcontroller.d.ts.map +0 -1
  74. package/dist/main/lib/http2/node/capsuleparser.d.ts.map +0 -1
  75. package/dist/main/lib/http2/node/client.d.ts.map +0 -1
  76. package/dist/main/lib/http2/node/index.d.ts.map +0 -1
  77. package/dist/main/lib/http2/node/server.d.ts.map +0 -1
  78. package/dist/main/lib/http2/node/websocketparser.d.ts.map +0 -1
  79. package/dist/main/lib/http2/parserbase.d.ts.map +0 -1
  80. package/dist/main/lib/http2/parserbasehttp2.d.ts.map +0 -1
  81. package/dist/main/lib/http2/session.d.ts.map +0 -1
  82. package/dist/main/lib/http2/stream.d.ts.map +0 -1
  83. package/dist/main/lib/http2/websocketcommon.d.ts.map +0 -1
  84. package/dist/main/lib/index.browser.d.ts.map +0 -1
  85. package/dist/main/lib/index.node.d.ts.map +0 -1
  86. package/dist/main/lib/server.d.ts.map +0 -1
  87. package/dist/main/lib/session.d.ts.map +0 -1
  88. package/dist/main/lib/stream.d.ts.map +0 -1
  89. package/dist/main/lib/types.d.ts.map +0 -1
  90. package/dist/main/lib/utils.d.ts.map +0 -1
  91. package/dist/main/lib/webstreams.browser.d.ts.map +0 -1
  92. package/dist/main/lib/webstreams.d.ts.map +0 -1
  93. package/dist/main/lib/webtransport.browser.d.ts.map +0 -1
  94. package/dist/main/lib/webtransport.node.d.ts.map +0 -1
  95. package/dist/main/lib/webtransportbase.d.ts.map +0 -1
  96. package/dist/main/test/bidirectional-streams.spec.d.ts +0 -5
  97. package/dist/main/test/bidirectional-streams.spec.d.ts.map +0 -1
  98. package/dist/main/test/datagrams.spec.d.ts +0 -5
  99. package/dist/main/test/datagrams.spec.d.ts.map +0 -1
  100. package/dist/main/test/fixtures/certificate.d.ts +0 -21
  101. package/dist/main/test/fixtures/certificate.d.ts.map +0 -1
  102. package/dist/main/test/fixtures/chai.d.ts +0 -2
  103. package/dist/main/test/fixtures/chai.d.ts.map +0 -1
  104. package/dist/main/test/fixtures/known-bytes.d.ts +0 -8
  105. package/dist/main/test/fixtures/known-bytes.d.ts.map +0 -1
  106. package/dist/main/test/fixtures/p-timeout.d.ts +0 -8
  107. package/dist/main/test/fixtures/p-timeout.d.ts.map +0 -1
  108. package/dist/main/test/fixtures/read-cert-hash.d.ts +0 -6
  109. package/dist/main/test/fixtures/read-cert-hash.d.ts.map +0 -1
  110. package/dist/main/test/fixtures/read-stream.d.ts +0 -18
  111. package/dist/main/test/fixtures/read-stream.d.ts.map +0 -1
  112. package/dist/main/test/fixtures/reader-value.d.ts +0 -21
  113. package/dist/main/test/fixtures/reader-value.d.ts.map +0 -1
  114. package/dist/main/test/fixtures/server.d.ts +0 -5
  115. package/dist/main/test/fixtures/server.d.ts.map +0 -1
  116. package/dist/main/test/fixtures/webtransport.browser.d.ts +0 -4
  117. package/dist/main/test/fixtures/webtransport.browser.d.ts.map +0 -1
  118. package/dist/main/test/fixtures/webtransport.d.ts +0 -3
  119. package/dist/main/test/fixtures/webtransport.d.ts.map +0 -1
  120. package/dist/main/test/fixtures/write-stream.d.ts +0 -10
  121. package/dist/main/test/fixtures/write-stream.d.ts.map +0 -1
  122. package/dist/main/test/index.d.ts +0 -2
  123. package/dist/main/test/session.spec.d.ts +0 -5
  124. package/dist/main/test/session.spec.d.ts.map +0 -1
  125. package/dist/main/test/unidirectional-streams.spec.d.ts +0 -2
  126. package/dist/main/test/unidirectional-streams.spec.d.ts.map +0 -1
  127. package/dist/main/tsconfig.tsbuildinfo +0 -1
  128. package/dist/transports/http3-quiche/lib/clientsocket.d.ts +0 -19
  129. package/dist/transports/http3-quiche/lib/clientsocket.d.ts.map +0 -1
  130. package/dist/transports/http3-quiche/lib/index.d.ts +0 -11
  131. package/dist/transports/http3-quiche/lib/index.d.ts.map +0 -1
  132. package/dist/transports/http3-quiche/lib/serversocket.d.ts +0 -12
  133. package/dist/transports/http3-quiche/lib/serversocket.d.ts.map +0 -1
  134. package/dist/transports/http3-quiche/lib/socket.d.ts +0 -19
  135. package/dist/transports/http3-quiche/lib/socket.d.ts.map +0 -1
  136. package/dist/transports/http3-quiche/lib/types.d.ts +0 -21
  137. package/dist/transports/http3-quiche/lib/types.d.ts.map +0 -1
  138. package/dist/transports/http3-quiche/lib/utils.d.ts +0 -6
  139. package/dist/transports/http3-quiche/lib/utils.d.ts.map +0 -1
  140. /package/dist/{main/lib → lib}/client.d.ts +0 -0
  141. /package/dist/{main/lib → lib}/error.d.ts +0 -0
  142. /package/dist/{main/lib → lib}/features.d.ts +0 -0
  143. /package/dist/{main/lib → lib}/http2/browser/browserparser.d.ts +0 -0
  144. /package/dist/{main/lib → lib}/http2/node/index.d.ts +0 -0
  145. /package/dist/{main/lib → lib}/http2/parserbasehttp2.d.ts +0 -0
  146. /package/dist/{main/lib → lib}/http2/websocketcommon.d.ts +0 -0
  147. /package/dist/{main/lib → lib}/index.browser.d.ts +0 -0
  148. /package/dist/{main/lib → lib}/index.node.d.ts +0 -0
  149. /package/dist/{main/lib → lib}/server.d.ts +0 -0
  150. /package/dist/{main/lib → lib}/stream.d.ts +0 -0
  151. /package/dist/{main/lib → lib}/utils.d.ts +0 -0
  152. /package/dist/{main/lib → lib}/webstreams.browser.d.ts +0 -0
  153. /package/dist/{main/lib → lib}/webstreams.d.ts +0 -0
  154. /package/dist/{main/lib → lib}/webtransport.node.d.ts +0 -0
package/lib/session.js CHANGED
@@ -22,6 +22,7 @@ const log = logger(`webtransport:httpwtsession(${pid})`)
22
22
  * @typedef {import('./dom').WebTransportCloseInfo} WebTransportCloseInfo
23
23
  * @typedef {import('./dom').WebTransportBidirectionalStream} WebTransportBidirectionalStream
24
24
  * @typedef {import('./dom').WebTransportSendStream} WebTransportSendStream
25
+ * @typedef {import('./dom').WebTransportSendStreamOptions} WebTransportSendStreamOptions
25
26
  * @typedef {import('./dom').WebTransportReceiveStream} WebTransportReceiveStream
26
27
  * @typedef {import('./dom').WebTransportDatagramDuplexStream} WebTransportDatagramDuplexStream
27
28
  * @typedef {import('./dom').WebTransportReliabilityMode} WebTransportReliabilityMode
@@ -199,6 +200,9 @@ export class HttpWTSession {
199
200
  this.objint = object
200
201
  this.objint.jsobj = this
201
202
  this.reliable = !!reliable
203
+ if (this.objint.sendInitialParameters) {
204
+ this.objint.sendInitialParameters()
205
+ }
202
206
  }
203
207
  }
204
208
 
@@ -339,9 +343,10 @@ export class HttpWTSession {
339
343
  }
340
344
 
341
345
  /**
346
+ * @param {WebTransportSendStreamOptions} [opts]
342
347
  * @returns {Promise<WebTransportBidirectionalStream>}
343
348
  */
344
- createBidirectionalStream() {
349
+ createBidirectionalStream(opts) {
345
350
  if (this.objint == null) {
346
351
  throw new Error('this.objint not set')
347
352
  }
@@ -350,14 +355,25 @@ export class HttpWTSession {
350
355
  this.resolveBiDi.push(resolve)
351
356
  this.rejectBiDi.push(reject)
352
357
  })
353
- this.objint.orderBidiStream()
358
+ const notblocked = this.objint.orderBidiStream({
359
+ sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
360
+ sendOrder: opts?.sendOrder || 0,
361
+ waitUntilAvailable: opts?.waitUntilAvailable || false
362
+ })
363
+ if (!notblocked) {
364
+ const rej = this.rejectBiDi.pop()
365
+ this.resolveBiDi.pop()
366
+ if (rej)
367
+ rej(new DOMException('No streams available', 'QuotaExceededError'))
368
+ }
354
369
  return prom
355
370
  }
356
371
 
357
372
  /**
358
- *@returns {Promise<WebTransportSendStream>}
373
+ * @param {WebTransportSendStreamOptions} [opts]
374
+ * @returns {Promise<WebTransportSendStream>}
359
375
  */
360
- createUnidirectionalStream() {
376
+ createUnidirectionalStream(opts) {
361
377
  if (this.objint == null) {
362
378
  throw new Error('this.objint not set')
363
379
  }
@@ -366,7 +382,17 @@ export class HttpWTSession {
366
382
  this.resolveUniDi.push(resolve)
367
383
  this.rejectUniDi.push(reject)
368
384
  })
369
- this.objint.orderUnidiStream()
385
+ const notblocked = this.objint.orderUnidiStream({
386
+ sendGroup: opts?.sendGroup || null, // maybe replace, when implemented
387
+ sendOrder: opts?.sendOrder || 0,
388
+ waitUntilAvailable: opts?.waitUntilAvailable || false
389
+ })
390
+ if (!notblocked) {
391
+ const rej = this.rejectUniDi.pop()
392
+ this.resolveUniDi.pop()
393
+ if (rej)
394
+ rej(new DOMException('No streams available', 'QuotaExceededError'))
395
+ }
370
396
  return prom
371
397
  }
372
398
 
package/lib/types.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type {
2
2
  WebTransportSession,
3
3
  WebTransportHash,
4
- WebTransportOptions
4
+ WebTransportOptions,
5
+ WebTransportSendGroup
5
6
  } from './dom'
6
7
  import type { IncomingHttpHeaders, Http2Stream } from 'http2'
7
8
  import { ParserBase } from './http2/parserbase'
@@ -15,8 +16,8 @@ export interface NativeHttpWTSession {
15
16
  jsobj: WebTransportSessionEventHandler
16
17
  sendInitialParameters?: () => void
17
18
  writeDatagram: (chunk: Uint8Array) => void
18
- orderUnidiStream: () => void
19
- orderBidiStream: () => void
19
+ orderUnidiStream: (opts:{sendGroup: WebTransportSendGroup|null, sendOrder: number, waitUntilAvailable: boolean}) => boolean
20
+ orderBidiStream: (opts:{sendGroup: WebTransportSendGroup|null, sendOrder: number, waitUntilAvailable: boolean}) => boolean
20
21
  orderSessionStats: () => void
21
22
  orderDatagramStats: () => void
22
23
  notifySessionDraining: () => void
@@ -47,12 +48,18 @@ export interface NativeServerOptions {
47
48
  certhttp2?: string // if http2 has a different cert
48
49
  privKey: string
49
50
  privKeyhttp2?: string // if http2 has a different cert
51
+ initialBidirectionalSendStreams?: number
52
+ initialBidirectionalReceiveStreams?: number
53
+ initialUnidirectionalSendStreams?: number
54
+ initialUnidirectionalReceiveStreams?: number
50
55
  initialStreamFlowControlWindow?: number
51
56
  streamShouldAutoTuneReceiveWindow?: boolean
52
57
  streamFlowControlWindowSizeLimit?: number
53
58
  initialSessionFlowControlWindow?: number
54
59
  sessionShouldAutoTuneReceiveWindow?: boolean
55
60
  sessionFlowControlWindowSizeLimit?: number
61
+ initialBidirectionalStreams?: number
62
+ initialUnidirectionalStreams?: number
56
63
  }
57
64
 
58
65
  export interface NativeClientOptions {
@@ -62,6 +69,10 @@ export interface NativeClientOptions {
62
69
  localPort: number
63
70
  allowPooling: boolean
64
71
  forceIpv6: boolean
72
+ initialBidirectionalSendStreams?: number
73
+ initialBidirectionalReceiveStreams?: number
74
+ initialUnidirectionalSendStreams?: number
75
+ initialUnidirectionalReceiveStreams?: number
65
76
  initialStreamFlowControlWindow?: number
66
77
  streamShouldAutoTuneReceiveWindow?: boolean
67
78
  streamFlowControlWindowSizeLimit?: number
@@ -78,6 +89,11 @@ export interface FlowControlable {
78
89
  smoothedRtt: () => number
79
90
  }
80
91
 
92
+ export interface StreamIdClient {
93
+ canSendMaxStreams: () => boolean
94
+ sendMaxStreams: (maxStreams: bigint, unidirectional: boolean) => void
95
+ }
96
+
81
97
  export interface ReadBuffer {
82
98
  buffer?: Uint8Array
83
99
  readBytes?: number
@@ -330,6 +346,6 @@ export interface ParserWebsocketInit extends ParserInit {
330
346
  }
331
347
 
332
348
  export interface ReadDataInt {
333
- data: Uint8Array
349
+ data: Uint8Array|undefined
334
350
  fin: boolean
335
351
  }
@@ -10,6 +10,8 @@ const log = logger(`webtransport:browser()`)
10
10
  * @typedef {import('./dom').WebTransport} WebTransport
11
11
  * @typedef {import('./dom').WebTransportCloseInfo} WebTransportCloseInfo
12
12
  * @typedef {import('./dom').WebTransportBidirectionalStream} WebTransportBidirectionalStream
13
+ * @typedef {import('./dom').WebTransportSendStream} WebTransportSendStream
14
+ * @typedef {import('./dom').WebTransportSendStreamOptions} WebTransportSendStreamOptions
13
15
  * @typedef {import('./dom').WebTransportReceiveStream} WebTransportReceiveStream
14
16
  * @typedef {import('./error').WebTransportError} WebTransportError
15
17
  */
@@ -307,13 +309,21 @@ export class WebTransportPolyfill {
307
309
  this.curtransport.close(closeinfo)
308
310
  }
309
311
 
310
- async createBidirectionalStream() {
312
+ /**
313
+ * @param {WebTransportSendStreamOptions} [opts]
314
+ * @returns {Promise<WebTransportBidirectionalStream>}
315
+ */
316
+ async createBidirectionalStream(opts) {
311
317
  await this.ready
312
- return await this.curtransport.createBidirectionalStream()
318
+ return await this.curtransport.createBidirectionalStream(opts)
313
319
  }
314
320
 
315
- async createUnidirectionalStream() {
321
+ /**
322
+ * @param {WebTransportSendStreamOptions} [opts]
323
+ * @returns {Promise<WebTransportSendStream>}
324
+ */
325
+ async createUnidirectionalStream(opts) {
316
326
  await this.ready
317
- return await this.curtransport.createUnidirectionalStream()
327
+ return await this.curtransport.createUnidirectionalStream(opts)
318
328
  }
319
329
  }
@@ -4,6 +4,8 @@
4
4
  * @typedef {import('./dom.js').WebTransportCloseInfo} WebTransportCloseInfo
5
5
  * @typedef {import('./dom.js').WebTransportBidirectionalStream} WebTransportBidirectionalStream
6
6
  * @typedef {import('./dom.js').WebTransportReceiveStream} WebTransportReceiveStream
7
+ * @typedef {import('./dom.js').WebTransportSendStream} WebTransportSendStream
8
+ * @typedef {import('./dom.js').WebTransportSendStreamOptions} WebTransportSendStreamOptions
7
9
  * @typedef {import('./session.js').HttpWTSession} HttpWTSession
8
10
  * @typedef { import('./session.js').HttpClient} HttpClient
9
11
  */
@@ -119,7 +121,11 @@ export class WebTransportBase {
119
121
  return session.close(closeinfo)
120
122
  }
121
123
 
122
- createBidirectionalStream() {
124
+ /**
125
+ * @param {WebTransportSendStreamOptions} [opts]
126
+ * @returns {Promise<WebTransportBidirectionalStream>}
127
+ */
128
+ createBidirectionalStream(opts) {
123
129
  const session = this.sessionint
124
130
 
125
131
  if (!session) {
@@ -127,10 +133,14 @@ export class WebTransportBase {
127
133
  throw new Error('Http3WTSession was undefined')
128
134
  }
129
135
 
130
- return session.createBidirectionalStream()
136
+ return session.createBidirectionalStream(opts)
131
137
  }
132
138
 
133
- createUnidirectionalStream() {
139
+ /**
140
+ * @param {WebTransportSendStreamOptions} [opts]
141
+ * @returns {Promise<WebTransportSendStream>}
142
+ */
143
+ createUnidirectionalStream(opts) {
134
144
  const session = this.sessionint
135
145
 
136
146
  if (!session) {
@@ -138,6 +148,6 @@ export class WebTransportBase {
138
148
  throw new Error('Http3WTSession was undefined')
139
149
  }
140
150
 
141
- return session.createUnidirectionalStream()
151
+ return session.createUnidirectionalStream(opts)
142
152
  }
143
153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fails-components/webtransport",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "A component to add webtransport support (server and client) to node.js using libquiche",
5
5
  "exports": {
6
6
  ".": {
package/readme.md CHANGED
@@ -23,8 +23,6 @@ Note, that the client implementation only supports certificate checking via `cer
23
23
  It may be possible in the future to also support normal HTTP/3 with not so much effort, however, there is no intention from the author to implement this since it will not be needed by FAILS. However, PR requests are welcome and will be supported by advice from the author.
24
24
  The package for the HTTP/3 should be considered as a duct tape-style solution until a bulletproof native support of HTTP/3 and WebTransport is provided by node itself.
25
25
 
26
- If you need a ponyfill check out the sister package [https://github.com/fails-components/webtransport-ponyfill-websocket/](https://github.com/fails-components/webtransport-ponyfill-websocket/), which provides a mapping of the Webtransport interfaces to Websocket connections.
27
-
28
26
  ## Changes for Version 1.x.x
29
27
 
30
28
  The interface for version 1.x.x is not changed compared to 0.x.x.
@@ -37,12 +35,15 @@ Note, that it is only partially implemented and also some http2 features require
37
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.
38
36
  For the client side pass the options `forceReliable`, `requireUnreliable` etc. to force/select a type of transport.
39
37
 
40
- **Note, that the http/2 webtransport protocol (not supported by any browser yet), is implemented without the support of receiving initial settings, which can cause a problem with the initial flow control of the WebTransport streams. This is a current limitation in node.js. Note, that for sending the required initial setting you need a very recent (unreleased) version of node.js.** This limitation does not apply to the polyfill/ponyfill and the websocket protocol.
38
+ **Note, that the http/2 webtransport protocol (not supported by any browser yet), is implemented without the support of receiving initial settings, which can cause a problem with the initial flow control of the WebTransport streams. This is a current limitation in node.js, but a patch to add support will soon be part of new node versions. Note, that for sending the required initial setting you need a very recent version (>=21.6.1) of node.js.** This limitation does not apply to the polyfill/ponyfill and the webtransport over websocket protocol.
39
+
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
 
42
42
  ### Ponyfill/Polyfill
43
43
  Together with an experimental http/2 implementation of WebTransport, the underlying http/2 capsule protocol had been mapped to the WebSocket protocol, so that the packages can also be used as a ponyfill/polyfill on the browser side.
44
44
  Please use `WebTransportPonyfill` for a WebSocket only or `WebTransportPolyfill` as a replacement for `WebTransport`, which automatically falls back to the browser's implementation, if the corresponding features are natively supported in the browser.
45
45
 
46
+ The used WebSocket protocol is currently proprietary and described in detail [here](https://martenrichter.github.io/draft-ietf-webtransport-websocket/draft-richter-webtransport-websocket.html).
46
47
 
47
48
  ## Installation and usage
48
49
 
@@ -381,7 +381,7 @@ export async function generateWebTransportCertificate(attrs, options) {
381
381
  options.extensions || [
382
382
  {
383
383
  name: 'basicConstraints',
384
- cA: true
384
+ cA: false
385
385
  },
386
386
  {
387
387
  name: 'keyUsage',
@@ -428,7 +428,6 @@ export async function generateWebTransportCertificate(attrs, options) {
428
428
  cert.signature = await cert.signature
429
429
 
430
430
  const pemcert = pki.certificateToPem(cert)
431
-
432
431
  const x509cert = new X509Certificate(pemcert)
433
432
 
434
433
  const certhash = Buffer.from(
@@ -39,7 +39,7 @@ export async function createServer() {
39
39
  } else {
40
40
  server = new Http3Server({
41
41
  /* port: 8080,
42
- host: '0.0.0.0', */
42
+ host: '0.0.0.0', */
43
43
  port: 0,
44
44
  host: '127.0.0.1',
45
45
  secret: 'mysecret',
@@ -48,10 +48,15 @@ export async function createServer() {
48
48
  })
49
49
  }
50
50
 
51
+ let adjustlimit = 1
52
+ if (process.env.USE_HTTP2 === 'true') {
53
+ adjustlimit = 0
54
+ }
55
+
51
56
  server.ready
52
57
  .then(async () => {
53
58
  // set up listeners for the different server paths used by the tests
54
-
59
+ console.log('server ready')
55
60
  await Promise.all(
56
61
  [
57
62
  // echo server, initiated by remote
@@ -162,6 +167,104 @@ export async function createServer() {
162
167
  }
163
168
  },
164
169
 
170
+ // receive 100+ bidi streams and block
171
+ async () => {
172
+ for await (const session of getReaderStream(
173
+ server.sessionStream('/streamlimits_getbidis_wua')
174
+ )) {
175
+ try {
176
+ await session.ready
177
+ const bidistreams = []
178
+ while (bidistreams.length < 100 - adjustlimit) {
179
+ bidistreams.push(
180
+ await getReaderValue(session.incomingBidirectionalStreams)
181
+ )
182
+ }
183
+ await getReaderValue(session.incomingUnidirectionalStreams)
184
+ await getReaderValue(session.incomingUnidirectionalStreams)
185
+ for (let i = 0; i < 50 + adjustlimit; i++) {
186
+ /* const curstream = */ bidistreams.shift()
187
+ // await curstream.writable.close() // canceled by client
188
+ }
189
+ while (bidistreams.length < 100 - adjustlimit) {
190
+ bidistreams.push(
191
+ await getReaderValue(session.incomingBidirectionalStreams)
192
+ )
193
+ }
194
+ await getReaderValue(session.incomingUnidirectionalStreams)
195
+ await session.close()
196
+ } catch (error) {
197
+ // do not crash server, if a problem occurs...
198
+ }
199
+ }
200
+ },
201
+ // receive 100+ bidi streams and do not block
202
+ async () => {
203
+ for await (const session of getReaderStream(
204
+ server.sessionStream('/streamlimits_getbidis')
205
+ )) {
206
+ try {
207
+ await session.ready
208
+ const bidistreams = []
209
+ while (bidistreams.length < 150) {
210
+ bidistreams.push(
211
+ await getReaderValue(session.incomingBidirectionalStreams)
212
+ )
213
+ }
214
+ await session.close()
215
+ } catch (error) {
216
+ // do not crash server, if a problem occurs...
217
+ }
218
+ }
219
+ },
220
+ async () => {
221
+ for await (const session of getReaderStream(
222
+ server.sessionStream('/streamlimits_getunidis_wua')
223
+ )) {
224
+ try {
225
+ await session.ready
226
+ const unidistreams = []
227
+ while (unidistreams.length < 100 - adjustlimit) {
228
+ unidistreams.push(
229
+ await getReaderValue(session.incomingUnidirectionalStreams)
230
+ )
231
+ }
232
+ await getReaderValue(session.incomingBidirectionalStreams)
233
+ await getReaderValue(session.incomingBidirectionalStreams)
234
+ for (let i = 0; i < 50 + adjustlimit; i++) {
235
+ unidistreams.shift()
236
+ }
237
+ while (unidistreams.length < 100 - adjustlimit) {
238
+ unidistreams.push(
239
+ await getReaderValue(session.incomingUnidirectionalStreams)
240
+ )
241
+ }
242
+ await getReaderValue(session.incomingBidirectionalStreams)
243
+ await session.close()
244
+ } catch (error) {
245
+ // do not crash server, if a problem occurs...
246
+ }
247
+ }
248
+ },
249
+ // receive 100+ unidi streams and do not block
250
+ async () => {
251
+ for await (const session of getReaderStream(
252
+ server.sessionStream('/streamlimits_getunidis')
253
+ )) {
254
+ try {
255
+ await session.ready
256
+ const unidistreams = []
257
+ while (unidistreams.length < 150) {
258
+ unidistreams.push(
259
+ await getReaderValue(session.incomingUnidirectionalStreams)
260
+ )
261
+ }
262
+ await session.close()
263
+ } catch (error) {
264
+ // do not crash server, if a problem occurs...
265
+ }
266
+ }
267
+ },
165
268
  // cleanly close remote sessions
166
269
  async () => {
167
270
  for await (const session of getReaderStream(
@@ -281,8 +384,10 @@ if (process.send)
281
384
  address: `https://${address.host}:${address.port}`,
282
385
  certificate: certificate.fingerprint
283
386
  })
284
- else console.error('No IPC channel')
285
- /* console.log({
286
- address: `https://${address.host}:${address.port}`,
287
- certificate: certificate.fingerprint
288
- }) */
387
+ else {
388
+ console.error('No IPC channel')
389
+ console.log({
390
+ address: `https://${address.host}:${address.port}`,
391
+ certificate: certificate.fingerprint
392
+ })
393
+ }
@@ -0,0 +1,247 @@
1
+ /* eslint-env mocha */
2
+
3
+ import { readCertHash } from './fixtures/read-cert-hash.js'
4
+ import WebTransport from './fixtures/webtransport.js'
5
+ import { expect } from './fixtures/chai.js'
6
+
7
+ /**
8
+ * @template T
9
+ * @typedef {import('../lib/types').Deferred<T>} Deferred<T>
10
+ */
11
+ describe('streamlimits', function () {
12
+ this.timeout(6000) // for debugging remove before commit
13
+ let forceReliable = false
14
+ let adjustlimit = 1
15
+ if (process.env.USE_HTTP2 === 'true') {
16
+ forceReliable = true
17
+ adjustlimit = 0
18
+ }
19
+ const browser = process.env.BROWSER
20
+ /* const handshakemess =
21
+ browser !== 'firefox' ||
22
+ process.env.USE_POLYFILL === 'true' ||
23
+ process.env.USE_PONYFILL === 'true'
24
+ ? 'Opening handshake failed.'
25
+ : 'WebTransport connection rejected' */
26
+
27
+ const dowaitUntilAvailable = browser !== 'chromium' // remove after implementation
28
+
29
+ /** @type {import('../lib/dom').WebTransport | undefined} */
30
+ let client
31
+
32
+ const wtOptions = {
33
+ serverCertificateHashes: [
34
+ {
35
+ algorithm: 'sha-256',
36
+ value: readCertHash(process.env.CERT_HASH)
37
+ }
38
+ ],
39
+ // @ts-ignore
40
+ forceReliable
41
+ }
42
+
43
+ if (process.env.NO_CERT_HASHES === 'true')
44
+ // @ts-ignore
45
+ delete wtOptions.serverCertificateHashes
46
+
47
+ // @ts-ignore
48
+ afterEach(async () => {
49
+ if (client != null) {
50
+ client.close()
51
+ client = undefined
52
+ }
53
+ })
54
+
55
+ if (dowaitUntilAvailable) {
56
+ it('should detect stream limit bidi outgoing with waitUntilAvailable = true', async () => {
57
+ client = new WebTransport(
58
+ `${process.env.SERVER_URL}/streamlimits_getbidis_wua`,
59
+ { ...wtOptions }
60
+ )
61
+ await client.ready
62
+ const bidistreams = []
63
+ let numbidi = 0
64
+ for (let i = 0; i < 150; i++) {
65
+ const curstream = client.createBidirectionalStream({
66
+ waitUntilAvailable: true
67
+ })
68
+ bidistreams.push(curstream)
69
+ curstream
70
+ .then(() => {
71
+ numbidi++
72
+ })
73
+ .catch(() => {})
74
+ }
75
+ await client.createUnidirectionalStream({
76
+ waitUntilAvailable: true
77
+ })
78
+ expect(numbidi).to.equal(100 - adjustlimit)
79
+ await client.createUnidirectionalStream({
80
+ waitUntilAvailable: true
81
+ })
82
+ for (let i = 0; i < 50 + adjustlimit; i++) {
83
+ const curstream = await bidistreams.shift()
84
+ await curstream.readable.cancel()
85
+ await curstream.writable.close()
86
+ }
87
+ await Promise.allSettled(bidistreams)
88
+ await client.createUnidirectionalStream({
89
+ waitUntilAvailable: true
90
+ })
91
+ expect(numbidi).to.equal(150)
92
+
93
+ const result = await client.closed
94
+ expect(result).to.have.property('closeCode', 0)
95
+ expect(result).to.have.property('reason', '')
96
+ })
97
+ }
98
+
99
+ it('should detect stream limit bidi outgoing', async () => {
100
+ client = new WebTransport(
101
+ `${process.env.SERVER_URL}/streamlimits_getbidis`,
102
+ { ...wtOptions }
103
+ )
104
+ await client.ready
105
+ const bidistreams = []
106
+ let numbidi = 0
107
+ let numfailed = 0
108
+ for (let i = 0; i < 150; i++) {
109
+ const curstream = client.createBidirectionalStream()
110
+ bidistreams.push(curstream)
111
+ curstream
112
+ .then(() => {
113
+ numbidi++
114
+ })
115
+ .catch(() => {
116
+ numfailed++
117
+ })
118
+ }
119
+ await Promise.allSettled(bidistreams)
120
+ expect(numbidi).to.equal(100 - adjustlimit)
121
+ expect(numfailed).to.equal(50 + adjustlimit)
122
+ numfailed = 0
123
+ for (let i = 0; i < 50 + adjustlimit; i++) {
124
+ const curstream = await bidistreams.shift()
125
+ await curstream.readable.cancel()
126
+ await curstream.writable.close()
127
+ }
128
+ // as close is not a save measure, that the limit is updated
129
+ // actually no save measure exist, waiting for a typical rtt could be a way
130
+ // to ensure that the update of maxstreams arrives
131
+ await new Promise((resolve) => setTimeout(resolve, 200))
132
+
133
+ for (let i = 0; i < 50 + adjustlimit; i++) {
134
+ const curstream = client.createBidirectionalStream()
135
+ bidistreams.push(curstream)
136
+ curstream
137
+ .then(() => {
138
+ numbidi++
139
+ })
140
+ .catch(() => {
141
+ numfailed++
142
+ })
143
+ }
144
+ await Promise.allSettled(bidistreams)
145
+ expect(numbidi).to.equal(150)
146
+ expect(numfailed).to.equal(0)
147
+
148
+ const result = await client.closed
149
+ expect(result).to.have.property('closeCode', 0)
150
+ expect(result).to.have.property('reason', '')
151
+ })
152
+
153
+ if (dowaitUntilAvailable) {
154
+ it('should detect stream limit unidi outgoing with waitUntilAvailable = true', async () => {
155
+ client = new WebTransport(
156
+ `${process.env.SERVER_URL}/streamlimits_getunidis_wua`,
157
+ { ...wtOptions }
158
+ )
159
+ await client.ready
160
+ const unidistreams = []
161
+ let numunidi = 0
162
+ for (let i = 0; i < 150; i++) {
163
+ const curstream = client.createUnidirectionalStream({
164
+ waitUntilAvailable: true
165
+ })
166
+ unidistreams.push(curstream)
167
+ curstream
168
+ .then(() => {
169
+ numunidi++
170
+ })
171
+ .catch(() => {})
172
+ }
173
+ await client.createBidirectionalStream({
174
+ waitUntilAvailable: true
175
+ })
176
+ expect(numunidi).to.equal(100)
177
+ await client.createBidirectionalStream({
178
+ waitUntilAvailable: true
179
+ })
180
+ for (let i = 0; i < 50 + adjustlimit; i++) {
181
+ const curstream = await unidistreams.shift()
182
+ await curstream.close()
183
+ }
184
+ await Promise.allSettled(unidistreams)
185
+ await client.createBidirectionalStream({
186
+ waitUntilAvailable: true
187
+ })
188
+ expect(numunidi).to.equal(150)
189
+
190
+ const result = await client.closed
191
+ expect(result).to.have.property('closeCode', 0)
192
+ expect(result).to.have.property('reason', '')
193
+ })
194
+ }
195
+
196
+ it('should detect stream limit unidi outgoing', async () => {
197
+ client = new WebTransport(
198
+ `${process.env.SERVER_URL}/streamlimits_getunidis`,
199
+ { ...wtOptions }
200
+ )
201
+ await client.ready
202
+ const unidistreams = []
203
+ let numunidi = 0
204
+ let numfailed = 0
205
+ for (let i = 0; i < 150; i++) {
206
+ const curstream = client.createUnidirectionalStream()
207
+ unidistreams.push(curstream)
208
+ curstream
209
+ .then(() => {
210
+ numunidi++
211
+ })
212
+ .catch(() => {
213
+ numfailed++
214
+ })
215
+ }
216
+ await Promise.allSettled(unidistreams)
217
+ expect(numunidi).to.equal(100)
218
+ expect(numfailed).to.equal(50)
219
+ numfailed = 0
220
+ for (let i = 0; i < 50 + adjustlimit; i++) {
221
+ const curstream = await unidistreams.shift()
222
+ await curstream.close()
223
+ }
224
+ // as close is not a save measure, that the limit is updated
225
+ // actually no save measure exist, waiting for a typical rtt could be a way
226
+ // to ensure that the update of maxstreams arrives
227
+ await new Promise((resolve) => setTimeout(resolve, 200))
228
+ for (let i = 0; i < 50; i++) {
229
+ const curstream = client.createUnidirectionalStream()
230
+ unidistreams.push(curstream)
231
+ curstream
232
+ .then(() => {
233
+ numunidi++
234
+ })
235
+ .catch(() => {
236
+ numfailed++
237
+ })
238
+ }
239
+ await Promise.allSettled(unidistreams)
240
+ expect(numunidi).to.equal(150)
241
+ expect(numfailed).to.equal(0)
242
+
243
+ const result = await client.closed
244
+ expect(result).to.have.property('closeCode', 0)
245
+ expect(result).to.have.property('reason', '')
246
+ })
247
+ })