@fails-components/webtransport 0.0.9 → 0.1.0-macarmbuild.6

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 (90) hide show
  1. package/CMakeLists.txt +110 -73
  2. package/build.js +32 -8
  3. package/dist/lib/client.d.ts +74 -0
  4. package/dist/lib/client.d.ts.map +1 -0
  5. package/dist/lib/dom.d.ts +70 -0
  6. package/dist/lib/dom.d.ts.map +1 -0
  7. package/dist/lib/event-loop.d.ts +26 -0
  8. package/dist/lib/event-loop.d.ts.map +1 -0
  9. package/dist/lib/index.d.ts +79 -0
  10. package/dist/lib/index.d.ts.map +1 -0
  11. package/dist/lib/native.d.ts +2 -0
  12. package/dist/lib/native.d.ts.map +1 -0
  13. package/dist/lib/server.d.ts +75 -0
  14. package/dist/lib/server.d.ts.map +1 -0
  15. package/dist/lib/session.d.ts +221 -0
  16. package/dist/lib/session.d.ts.map +1 -0
  17. package/dist/lib/stream.d.ts +116 -0
  18. package/dist/lib/stream.d.ts.map +1 -0
  19. package/dist/lib/transport.d.ts +25 -0
  20. package/dist/lib/transport.d.ts.map +1 -0
  21. package/dist/lib/types.d.ts +157 -0
  22. package/dist/lib/types.d.ts.map +1 -0
  23. package/dist/lib/utils.d.ts +6 -0
  24. package/dist/lib/utils.d.ts.map +1 -0
  25. package/dist/lib/webtransport.d.ts +35 -0
  26. package/dist/lib/webtransport.d.ts.map +1 -0
  27. package/dist/test/bidirectional-streams.spec.d.ts +5 -0
  28. package/dist/test/bidirectional-streams.spec.d.ts.map +1 -0
  29. package/dist/test/datagrams.spec.d.ts +5 -0
  30. package/dist/test/datagrams.spec.d.ts.map +1 -0
  31. package/dist/test/echoclient.d.ts +2 -0
  32. package/dist/test/echoclient.d.ts.map +1 -0
  33. package/dist/test/echoserver.d.ts +2 -0
  34. package/dist/test/echoserver.d.ts.map +1 -0
  35. package/dist/test/event-loop.spec.d.ts +2 -0
  36. package/dist/test/event-loop.spec.d.ts.map +1 -0
  37. package/dist/test/fixtures/certificate.d.ts +21 -0
  38. package/dist/test/fixtures/certificate.d.ts.map +1 -0
  39. package/dist/test/fixtures/chai.d.ts +2 -0
  40. package/dist/test/fixtures/chai.d.ts.map +1 -0
  41. package/dist/test/fixtures/read-stream.d.ts +10 -0
  42. package/dist/test/fixtures/read-stream.d.ts.map +1 -0
  43. package/dist/test/fixtures/reader-value.d.ts +7 -0
  44. package/dist/test/fixtures/reader-value.d.ts.map +1 -0
  45. package/dist/test/fixtures/server.d.ts +10 -0
  46. package/dist/test/fixtures/server.d.ts.map +1 -0
  47. package/dist/test/fixtures/write-stream.d.ts +10 -0
  48. package/dist/test/fixtures/write-stream.d.ts.map +1 -0
  49. package/dist/test/test.d.ts +2 -0
  50. package/dist/test/test.d.ts.map +1 -0
  51. package/dist/test/testsuite.d.ts +25 -0
  52. package/dist/test/testsuite.d.ts.map +1 -0
  53. package/dist/test/unidirectional-streams.spec.d.ts +5 -0
  54. package/dist/test/unidirectional-streams.spec.d.ts.map +1 -0
  55. package/lib/client.js +163 -0
  56. package/lib/dom.ts +95 -0
  57. package/lib/event-loop.js +79 -0
  58. package/lib/index.js +33 -0
  59. package/lib/native.js +25 -0
  60. package/lib/server.js +165 -0
  61. package/lib/session.js +422 -0
  62. package/lib/stream.js +325 -0
  63. package/lib/transport.js +42 -0
  64. package/lib/types.ts +181 -0
  65. package/lib/utils.js +22 -0
  66. package/lib/webtransport.js +114 -0
  67. package/package.json +18 -6
  68. package/readme.md +28 -5
  69. package/src/http3client.h +0 -1
  70. package/src/http3eventloop.cc +52 -0
  71. package/src/http3eventloop.h +22 -1
  72. package/src/http3server.cc +4 -1
  73. package/src/http3server.h +8 -0
  74. package/src/http3serversession.h +1 -1
  75. package/test/bidirectional-streams.spec.js +138 -0
  76. package/test/datagrams.spec.js +132 -0
  77. package/test/echoclient.js +10 -6
  78. package/test/echoserver.js +6 -8
  79. package/test/event-loop.spec.js +24 -0
  80. package/test/{certificate.js → fixtures/certificate.js} +31 -15
  81. package/test/fixtures/chai.js +6 -0
  82. package/test/fixtures/read-stream.js +36 -0
  83. package/test/fixtures/reader-value.js +24 -0
  84. package/test/fixtures/server.js +58 -0
  85. package/test/fixtures/write-stream.js +17 -0
  86. package/test/test.js +29 -3
  87. package/test/testsuite.js +22 -0
  88. package/test/unidirectional-streams.spec.js +128 -0
  89. package/tsconfig.json +40 -0
  90. package/src/webtransport.js +0 -870
package/lib/stream.js ADDED
@@ -0,0 +1,325 @@
1
+ import { ReadableStream, WritableStream } from 'node:stream/web'
2
+
3
+ /**
4
+ * WebTransport stream events
5
+ * @typedef {import('./types').WebTransportStreamEventHandler} WebTransportStreamEventHandler
6
+ * @typedef {import('./types').StreamRecvSignalEvent} StreamRecvSignalEvent
7
+ * @typedef {import('./types').StreamReadEvent} StreamReadEvent
8
+ * @typedef {import('./types').StreamWriteEvent} StreamWriteEvent
9
+ * @typedef {import('./types').StreamResetEvent} StreamResetEvent
10
+ * @typedef {import('./types').StreamNetworkFinishEvent} StreamNetworkFinishEvent
11
+ *
12
+ * @typedef {import('./types').NativeHttp3WTStream} NativeHttp3WTStream
13
+ *
14
+ * @typedef {import('./session').Http3WTSession} Http3WTSession
15
+ *
16
+ * @typedef {import('stream/web').WritableStreamDefaultController} WritableStreamDefaultController
17
+ */
18
+
19
+ export class Http3WTStream {
20
+ /**
21
+ * @param {object} args
22
+ * @param {NativeHttp3WTStream} args.object
23
+ * @param {Http3WTSession} args.parentobj
24
+ * @param {object} args.transport
25
+ * @param {boolean} args.bidirectional
26
+ * @param {boolean} args.incoming
27
+ */
28
+ constructor(args) {
29
+ this.objint = args.object
30
+ this.objint.jsobj = this
31
+ this.parentobj = args.parentobj
32
+ this.transport = args.transport
33
+ this.bidirectional = args.bidirectional
34
+ this.incoming = args.incoming
35
+ this.closed = false
36
+
37
+ /** @type {Promise<void> | null} */
38
+ this.pendingoperation = null
39
+ this.pendingres = null
40
+
41
+ if (this.bidirectional || this.incoming) {
42
+ this.readable = new ReadableStream(
43
+ {
44
+ start: (controller) => {
45
+ this.readableController = controller
46
+ // @ts-expect-error this.readable could be undefined
47
+ this.parentobj.addReceiveStream(this.readable, controller)
48
+ this.objint.startReading()
49
+ },
50
+ pull: (controller) => {
51
+ if (this.readableclosed) {
52
+ return Promise.resolve()
53
+ }
54
+ this.objint.startReading()
55
+ },
56
+ cancel: (reason) => {
57
+ /** @type {Promise<void>} */
58
+ const promise = new Promise((resolve, reject) => {
59
+ this.cancelres = resolve
60
+ })
61
+ let code = 0
62
+ if (reason && reason.code) {
63
+ if (reason.code < 0) code = 0
64
+ else if (reason.code > 255) code = 255
65
+ else code = reason.code
66
+ }
67
+ this.readableclosed = true
68
+ this.objint.stopSending(code)
69
+ return promise
70
+ }
71
+ },
72
+ { highWaterMark: 4 }
73
+ )
74
+ }
75
+ if (this.bidirectional || !this.incoming) {
76
+ this.writable = new WritableStream(
77
+ {
78
+ start: (controller) => {
79
+ this.writableController = controller
80
+ // @ts-expect-error this.writable could be undefined
81
+ this.parentobj.addSendStream(this.writable, controller)
82
+ },
83
+ write: (chunk, controller) => {
84
+ if (this.writableclosed) {
85
+ return Promise.resolve()
86
+ }
87
+ if (chunk instanceof Uint8Array) {
88
+ this.pendingoperation = new Promise((resolve, reject) => {
89
+ this.pendingres = resolve
90
+ })
91
+ const dataprom = this.parentobj.waitForDatagramsSend()
92
+ dataprom.finally(() => {
93
+ this.objint.writeChunk(chunk)
94
+ })
95
+ return this.pendingoperation
96
+ } else {
97
+ console.log('chunk info:', chunk)
98
+ throw new Error('chunk is not of instanceof Uint8Array ')
99
+ }
100
+ },
101
+ close: () => {
102
+ if (this.writableclosed) {
103
+ return Promise.resolve()
104
+ }
105
+ this.objint.streamFinal()
106
+ this.pendingoperation = new Promise((resolve, reject) => {
107
+ this.pendingres = resolve
108
+ })
109
+ return this.pendingoperation
110
+ },
111
+ abort: (reason) => {
112
+ if (this.writableclosed) {
113
+ return new Promise((resolve, reject) => {
114
+ resolve()
115
+ })
116
+ }
117
+ let code = 0
118
+ if (reason && reason.code) {
119
+ if (reason.code < 0) code = 0
120
+ else if (reason.code > 255) code = 255
121
+ else code = reason.code
122
+ }
123
+ /** @type {Promise<void>} */
124
+ const promise = new Promise((resolve, reject) => {
125
+ this.abortres = resolve
126
+ })
127
+ this.objint.resetStream(code)
128
+ return promise
129
+ }
130
+ },
131
+ { highWaterMark: 4 }
132
+ )
133
+ }
134
+
135
+ /** @type {(() => void) | null} */
136
+ this.cancelres = null
137
+ /** @type {(() => void) | null} */
138
+ this.pendingres = null
139
+ /** @type {(() => void) | null} */
140
+ this.abortres = null
141
+ }
142
+
143
+ /**
144
+ * @param {import('./types').StreamRecvSignalEvent} args
145
+ * @returns {void}
146
+ */
147
+ onStreamRecvSignal(args) {
148
+ // console.log('onStreamRecvSignal', args)
149
+ // check if transport is closed
150
+ const parentstate = this.parentobj.state
151
+ if (parentstate === 'closed' || parentstate === 'failed') return
152
+ switch (args.nettask) {
153
+ case 'resetStream':
154
+ if (this.readable) {
155
+ this.parentobj.removeReceiveStream(
156
+ this.readable,
157
+ this.readableController
158
+ )
159
+ this.readableclosed = true
160
+ this.readableController.error(args.code || 0)
161
+ } else console.log('stopSending wihtout readable')
162
+ break
163
+
164
+ case 'stopSending':
165
+ if (this.writable) {
166
+ this.parentobj.removeSendStream(
167
+ this.writable,
168
+ this.writableController
169
+ )
170
+
171
+ this.writableclosed = true
172
+ this.writableController.error(args.code || 0)
173
+ } else console.log('stopSending wihtout writable')
174
+ break
175
+ default:
176
+ console.log('unhandled onStreamRecvSignal')
177
+ }
178
+
179
+ if (this.pendingoperation) {
180
+ const res = this.pendingres
181
+ this.pendingoperation = null
182
+ this.pendingres = null
183
+ if (res != null) {
184
+ res()
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * @param {StreamReadEvent} args
191
+ * @returns {void}
192
+ */
193
+ onStreamRead(args) {
194
+ if (args.data && args.data.length && !this.readableclosed) {
195
+ // console.log('stream read received', args.data, Date.now())
196
+ this.readableController.enqueue(args.data)
197
+ if (
198
+ this.readableController.desiredSize != null &&
199
+ this.readableController.desiredSize < 0
200
+ )
201
+ this.objint.stopReading()
202
+ }
203
+ if (args.fin) {
204
+ if (this.cancelres) {
205
+ const res = this.cancelres
206
+ this.cancelres = null
207
+ res()
208
+ }
209
+ if (!this.readableclosed) {
210
+ this.readableController.close()
211
+ this.readableclosed = true
212
+ }
213
+ }
214
+ }
215
+
216
+ /**
217
+ * @param {StreamWriteEvent} args
218
+ */
219
+ onStreamWrite(args) {
220
+ // we ignore success
221
+ if (this.pendingoperation) {
222
+ const res = this.pendingres
223
+ this.pendingoperation = null
224
+ this.pendingres = null
225
+ if (res != null) {
226
+ res()
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * @param {StreamResetEvent} args
233
+ */
234
+ onStreamReset(args) {
235
+ if (this.abortres) {
236
+ this.abortres()
237
+ if (this.readable)
238
+ this.parentobj.removeReceiveStream(
239
+ this.readable,
240
+ this.readableController
241
+ )
242
+ if (this.writable)
243
+ this.parentobj.removeSendStream(this.writable, this.writableController)
244
+ this.readableclosed = true
245
+ this.parentobj.removeStreamObj(this)
246
+ }
247
+ }
248
+
249
+ /**
250
+ * @param {StreamNetworkFinishEvent} args
251
+ */
252
+ onStreamNetworkFinish(args) {
253
+ // console.log('networkfinish args', args)
254
+ switch (args.nettask) {
255
+ case 'stopSending':
256
+ if (this.cancelres) {
257
+ const res = this.cancelres
258
+ this.cancelres = null
259
+ res()
260
+ }
261
+ this.stopSendingRecv = true
262
+ break
263
+ case 'resetStream':
264
+ if (this.abortres) {
265
+ const res = this.abortres
266
+ this.abortres = null
267
+ res()
268
+ }
269
+
270
+ break
271
+
272
+ case 'streamFinal':
273
+ if (this.pendingoperation) {
274
+ const res = this.pendingres
275
+ this.pendingoperation = null
276
+ this.pendingres = null
277
+ if (res != null) {
278
+ res()
279
+ }
280
+ }
281
+ break
282
+ default:
283
+ console.log('onStreamNetworkFinish unknown task')
284
+ }
285
+ // we could differentiate....
286
+ }
287
+
288
+ /**
289
+ * @param {StreamRecvSignalEvent | StreamReadEvent | StreamWriteEvent | StreamResetEvent | StreamNetworkFinishEvent} args
290
+ */
291
+ static callback(args) {
292
+ // console.log('Stream callback called', args)
293
+ if (!args || !args.object || !args.object.jsobj)
294
+ throw new Error('Stream callback without jsobj')
295
+ const visitor = args.object.jsobj
296
+ if (args.purpose) {
297
+ switch (args.purpose) {
298
+ case 'StreamRecvSignal':
299
+ visitor.onStreamRecvSignal(args)
300
+ break
301
+ case 'StreamRead':
302
+ if (visitor && Object.prototype.hasOwnProperty.call(args, 'data')) {
303
+ visitor.onStreamRead(args)
304
+ } else {
305
+ console.log('Stream callback called', visitor, args)
306
+ throw new Error('Malformed StreamRead')
307
+ }
308
+ break
309
+ case 'StreamWrite':
310
+ visitor.onStreamWrite(args)
311
+ break
312
+ case 'StreamReset':
313
+ visitor.onStreamReset(args)
314
+ break
315
+ case 'StreamNetworkFinish':
316
+ visitor.onStreamNetworkFinish(args)
317
+
318
+ break
319
+ default: {
320
+ throw new Error('unknown purpose Streamcb')
321
+ }
322
+ }
323
+ } else throw new Error('no purpose Streamcb')
324
+ }
325
+ }
@@ -0,0 +1,42 @@
1
+ import { Http3EventLoop } from './event-loop.js'
2
+ import { wtrouter } from './native.js'
3
+
4
+ export class Http3WebTransport {
5
+ /**
6
+ * @param {*} args
7
+ * @param {'server' | 'client'} purpose
8
+ */
9
+ constructor(args, purpose) {
10
+ const eventloop = Http3EventLoop.getGlobalEventLoop(this).eventloopInt
11
+
12
+ if (purpose === 'server')
13
+ this.transportInt = new wtrouter.Http3WebTransportServer(args, eventloop)
14
+ else if (purpose === 'client')
15
+ this.transportInt = new wtrouter.Http3WebTransportClient(args, eventloop)
16
+ else throw new Error('unknown purpose')
17
+ this.transportInt.jsobj = this
18
+
19
+ this.sessions = {}
20
+ }
21
+
22
+ /**
23
+ * @typedef {object} TransportCallbackEvent
24
+ * @property {{ jsobj: { customCallback: (args: any) => void }}} object
25
+ * @property {string} purpose
26
+ *
27
+ * @param {TransportCallbackEvent} args
28
+ */
29
+ static transportCallback(args) {
30
+ // console.log('incoming callback transport', args)
31
+ if (!args || !args.object || !args.object.jsobj)
32
+ throw new Error('Transport callback without jsobj')
33
+ const visitor = args.object.jsobj
34
+ if (args.purpose) {
35
+ if (visitor.customCallback) {
36
+ visitor.customCallback(args)
37
+ } else {
38
+ throw new Error('unknown purpose')
39
+ }
40
+ }
41
+ }
42
+ }
package/lib/types.ts ADDED
@@ -0,0 +1,181 @@
1
+ import type { WebTransport } from './dom'
2
+
3
+ /**
4
+ * Native Http3WTSession counterpart
5
+ */
6
+ export interface NativeHttp3WTSession {
7
+ jsobj: WebTransportSessionEventHandler
8
+ writeDatagram: (chunk: Uint8Array) => void
9
+ orderUnidiStream: () => void
10
+ orderBidiStream: () => void
11
+ close: (arg: { code: number, reason: string }) => void
12
+ }
13
+
14
+ /**
15
+ * Native Http3WTStream counterpart
16
+ */
17
+ export interface NativeHttp3WTStream {
18
+ jsobj: WebTransportStreamEventHandler
19
+ startReading: () => void
20
+ stopReading: () => void
21
+ stopSending: (code: number) => void
22
+ resetStream: (code: number) => void
23
+ writeChunk: (buf: Uint8Array) => void
24
+ streamFinal: () => void
25
+ }
26
+
27
+ export type Purpose = 'StreamRecvSignal' | 'StreamRead' | 'StreamWrite' | 'StreamReset' | 'StreamNetworkFinish'
28
+ export type NetTask = 'stopSending' | 'resetStream' | 'streamFinal'
29
+
30
+ export interface StreamRecvSignalEvent {
31
+ object: NativeHttp3WTStream
32
+ purpose: 'StreamRecvSignal'
33
+ code: number
34
+ nettask: NetTask
35
+ }
36
+
37
+ export interface StreamReadEvent {
38
+ object: NativeHttp3WTStream
39
+ purpose: 'StreamRead'
40
+ data?: Uint8Array
41
+ fin?: boolean
42
+ }
43
+
44
+ export interface StreamWriteEvent {
45
+ object: NativeHttp3WTStream
46
+ purpose: 'StreamWrite'
47
+ }
48
+
49
+ export interface StreamResetEvent {
50
+ object: NativeHttp3WTStream
51
+ purpose: 'StreamReset'
52
+ }
53
+
54
+ export interface StreamNetworkFinishEvent {
55
+ object: NativeHttp3WTStream
56
+ purpose: 'StreamNetworkFinish'
57
+ nettask: NetTask
58
+ }
59
+
60
+ export interface WebTransportStreamEventHandler {
61
+ onStreamRecvSignal: (evt: StreamRecvSignalEvent) => void
62
+ onStreamRead: (evt: StreamReadEvent) => void
63
+ onStreamWrite: (evt: StreamWriteEvent) => void
64
+ onStreamReset: (evt: StreamResetEvent) => void
65
+ onStreamNetworkFinish: (evt: StreamNetworkFinishEvent) => void
66
+ }
67
+
68
+ export interface SessionReadyEvent {
69
+ object: NativeHttp3WTSession
70
+ purpose: 'SessionReady'
71
+ }
72
+
73
+ export interface SessionCloseEvent {
74
+ object: NativeHttp3WTSession
75
+ purpose: 'SessionClose'
76
+ errorcode: number
77
+ error: string
78
+ }
79
+
80
+ export interface DatagramReceivedEvent {
81
+ object: NativeHttp3WTSession
82
+ purpose: 'DatagramReceived'
83
+ datagram: Uint8Array
84
+ }
85
+
86
+ export interface DatagramSendEvent {
87
+ object: NativeHttp3WTSession
88
+ purpose: 'DatagramSend'
89
+ }
90
+
91
+ export interface NewStreamEvent {
92
+ object: NativeHttp3WTSession
93
+ purpose: 'Http3WTStreamVisitor'
94
+ stream: NativeHttp3WTStream
95
+ transport: object
96
+ bidirectional: boolean
97
+ incoming: boolean
98
+ }
99
+
100
+ export interface WebTransportSessionEventHandler {
101
+ onReady: (evt: SessionReadyEvent) => void
102
+ onClose: (evt: SessionCloseEvent) => void
103
+ onDatagramReceived: (evt: DatagramReceivedEvent) => void
104
+ onDatagramSend: (evt: DatagramSendEvent) => void
105
+ onStream: (evt: NewStreamEvent) => void
106
+ closeHook?: (() => void) | null
107
+ }
108
+
109
+ export interface ClientConnectedEvent {
110
+ purpose: 'ClientConnected'
111
+ success: boolean
112
+ }
113
+
114
+ export interface ClientWebtransportSupportEvent {
115
+ purpose: 'ClientWebtransportSupport'
116
+ }
117
+
118
+ export interface Http3WTSessionVisitorEvent {
119
+ purpose: 'Http3WTSessionVisitor'
120
+ session: NativeHttp3WTSession
121
+ }
122
+
123
+ export interface Http3ClientEventHandler {
124
+ onClientConnected: (evt: ClientConnectedEvent) => void
125
+ onClientWebTransportSupport: (evt: ClientWebtransportSupportEvent) => void
126
+ onHttp3WTSessionVisitor: (evt: Http3WTSessionVisitorEvent) => void
127
+ }
128
+
129
+ export interface Http3WTServerSessionVisitorEvent extends Http3WTSessionVisitorEvent {
130
+ path: string
131
+ object: any
132
+ }
133
+
134
+ /**
135
+ * The Http3 server is listening on the specified port
136
+ */
137
+ export interface Http3ServerListeningEvent {
138
+ purpose: 'Http3ServerListening'
139
+ }
140
+
141
+ /**
142
+ * The Http3 server has stopped listening on the specified port
143
+ */
144
+ export interface ServerStatusEvent {
145
+ port: number | null
146
+ host: string | null
147
+ purpose: 'ServerStatus',
148
+ status: 'error' | 'listening' | 'close'
149
+ }
150
+
151
+
152
+ export interface Http3ServerEventHandler {
153
+ onHttp3WTSessionVisitor: (evt: Http3WTServerSessionVisitorEvent) => void
154
+ onServerError: () => void
155
+ onServerListening: () => void
156
+ onServerClose: () => void
157
+ onServerStatus: (evt: ServerStatusEvent) => void
158
+ }
159
+
160
+ /**
161
+ * A defered promise with the value T
162
+ */
163
+ export interface Deferred<T = unknown> {
164
+ promise: Promise<T>
165
+ resolve: (value?: T) => void
166
+ reject: (reason?: any) => void
167
+ }
168
+
169
+ export type WebTransportSessionState = 'connected' | 'closed' | 'failed'
170
+
171
+ export interface WebTransportSession extends WebTransport {
172
+ state: WebTransportSessionState
173
+ }
174
+
175
+ export interface Http3ServerInit {
176
+ port: number
177
+ host: string
178
+ secret: string
179
+ cert: string
180
+ privKey: string
181
+ }
package/lib/utils.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @template {unknown} T
3
+ * @returns {import('./types').Deferred<T>}
4
+ */
5
+ export function defer() {
6
+ /** @type {(value?: T) => void} */
7
+ let res = () => {}
8
+
9
+ /** @type {(reason?: Error) => void} */
10
+ let rej = () => {}
11
+
12
+ const promise = new Promise((resolve, reject) => {
13
+ res = resolve
14
+ rej = reject
15
+ })
16
+
17
+ return {
18
+ promise,
19
+ resolve: res,
20
+ reject: rej
21
+ }
22
+ }
@@ -0,0 +1,114 @@
1
+ import { Http3Client } from './client.js'
2
+ import { Http3WTSession } from './session.js'
3
+
4
+ /** @type {WeakMap<WebTransport, Http3WTSession>} */
5
+ const sessions = new WeakMap()
6
+
7
+ /**
8
+ * @typedef {import('./dom').WebTransportCloseInfo} WebTransportCloseInfo
9
+ * @typedef {import('./dom').WebTransportBidirectionalStream} WebTransportBidirectionalStream
10
+ * @typedef {import('./dom').WebTransportReceiveStream} WebTransportReceiveStream
11
+ */
12
+
13
+ /**
14
+ * @typedef {import('./dom').WebTransport} WebTransportInterface
15
+ *
16
+ * @implements {WebTransportInterface}
17
+ */
18
+ export class WebTransport {
19
+ /**
20
+ * @param {string} url
21
+ * @param {import('./dom').WebTransportOptions} [args]
22
+ */
23
+ constructor(url, args) {
24
+ if (!url) throw new Error('no URL supplied')
25
+
26
+ const ourl = new URL(url)
27
+
28
+ if (ourl.protocol !== 'https:') {
29
+ throw new Error('URL is not supported for webtransport')
30
+ }
31
+
32
+ const hostname = ourl.hostname
33
+ let port = ourl.port
34
+ if (port === '') port = '443'
35
+
36
+ const client = new Http3Client({ hostname, port, ...args })
37
+
38
+ const sessionint = new Http3WTSession({
39
+ /* object: args.session, */
40
+ parentobj: client
41
+ })
42
+
43
+ this.ready = sessionint.ready
44
+ this.closed = sessionint.closed
45
+
46
+ this.datagrams = sessionint.datagrams
47
+
48
+ /** @type {ReadableStream<WebTransportBidirectionalStream>} */
49
+ this.incomingBidirectionalStreams = sessionint.incomingBidirectionalStreams
50
+
51
+ /** @type {ReadableStream<WebTransportReceiveStream>} */
52
+ this.incomingUnidirectionalStreams =
53
+ sessionint.incomingUnidirectionalStreams
54
+
55
+ sessions.set(this, sessionint)
56
+
57
+ client
58
+ .handleConnection()
59
+ .then(() => {
60
+ client.quicconnected
61
+ ?.then(() => {
62
+ return client.createWTSession(sessionint, ourl.pathname)
63
+ })
64
+ .catch((err) => {
65
+ sessionint.readyReject(
66
+ new Error('Establishing session failed ' + err)
67
+ )
68
+ console.log('Establishing session failed ' + err)
69
+ client.closeHookSession()
70
+ })
71
+ })
72
+ .catch((error) => {
73
+ sessionint.readyReject(new Error('Quic connection failed ' + error))
74
+ console.log('Quic connection failed ' + error)
75
+ client.closeHookSession()
76
+ })
77
+ }
78
+
79
+ /**
80
+ * @param {WebTransportCloseInfo} [closeinfo]
81
+ */
82
+ close(closeinfo) {
83
+ const session = sessions.get(this)
84
+
85
+ if (session == null) {
86
+ // should never happen as session is only removed when this instance is garbage collected
87
+ throw new Error('Http3WTSession was undefined')
88
+ }
89
+
90
+ return session.close(closeinfo)
91
+ }
92
+
93
+ createBidirectionalStream() {
94
+ const session = sessions.get(this)
95
+
96
+ if (session == null) {
97
+ // should never happen as session is only removed when this instance is garbage collected
98
+ throw new Error('Http3WTSession was undefined')
99
+ }
100
+
101
+ return session.createBidirectionalStream()
102
+ }
103
+
104
+ createUnidirectionalStream() {
105
+ const session = sessions.get(this)
106
+
107
+ if (session == null) {
108
+ // should never happen as session is only removed when this instance is garbage collected
109
+ throw new Error('Http3WTSession was undefined')
110
+ }
111
+
112
+ return session.createUnidirectionalStream()
113
+ }
114
+ }