@fails-components/webtransport 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +3 -0
- package/dist/lib/client.d.ts +7 -3
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/dom.d.ts +15 -4
- package/dist/lib/dom.d.ts.map +1 -1
- package/dist/lib/error.d.ts +8 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/lib/event-loop.d.ts +4 -2
- package/dist/lib/event-loop.d.ts.map +1 -1
- package/dist/lib/native.d.ts.map +1 -1
- package/dist/lib/server.d.ts +5 -2
- package/dist/lib/server.d.ts.map +1 -1
- package/dist/lib/session.d.ts +36 -23
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/stream.d.ts +15 -4
- package/dist/lib/stream.d.ts.map +1 -1
- package/dist/lib/transport.d.ts +12 -3
- package/dist/lib/transport.d.ts.map +1 -1
- package/dist/lib/types.d.ts +43 -4
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/webtransport.d.ts +11 -21
- package/dist/lib/webtransport.d.ts.map +1 -1
- package/dist/test/datagrams.spec.d.ts.map +1 -1
- package/dist/test/event-loop.node.d.ts +2 -0
- package/dist/test/event-loop.node.d.ts.map +1 -0
- package/dist/test/fixtures/chai.d.ts.map +1 -1
- package/dist/test/fixtures/known-bytes.d.ts +6 -0
- package/dist/test/fixtures/known-bytes.d.ts.map +1 -0
- package/dist/test/fixtures/read-cert-hash.d.ts +6 -0
- package/dist/test/fixtures/read-cert-hash.d.ts.map +1 -0
- package/dist/test/fixtures/read-stream.d.ts +10 -1
- package/dist/test/fixtures/read-stream.d.ts.map +1 -1
- package/dist/test/fixtures/reader-value.d.ts +16 -1
- package/dist/test/fixtures/reader-value.d.ts.map +1 -1
- package/dist/test/fixtures/server.d.ts +0 -5
- package/dist/test/fixtures/server.d.ts.map +1 -1
- package/dist/test/fixtures/webtransport.browser.d.ts +3 -0
- package/dist/test/fixtures/webtransport.browser.d.ts.map +1 -0
- package/dist/test/fixtures/webtransport.d.ts +3 -0
- package/dist/test/fixtures/webtransport.d.ts.map +1 -0
- package/dist/test/fixtures/write-stream.d.ts.map +1 -1
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/session.spec.d.ts +5 -0
- package/dist/test/session.spec.d.ts.map +1 -0
- package/dist/test/unidirectional-streams.spec.d.ts +1 -4
- package/dist/test/unidirectional-streams.spec.d.ts.map +1 -1
- package/lib/client.js +57 -43
- package/lib/dom.ts +18 -4
- package/lib/error.js +10 -0
- package/lib/event-loop.js +8 -5
- package/lib/native.js +4 -1
- package/lib/server.js +18 -10
- package/lib/session.js +145 -32
- package/lib/stream.js +51 -22
- package/lib/transport.js +48 -10
- package/lib/types.ts +53 -5
- package/lib/utils.js +13 -0
- package/lib/webtransport.js +10 -18
- package/{test → old_test}/echoserver.js +1 -2
- package/{test → old_test}/test.js +5 -3
- package/package.json +16 -5
- package/readme.md +64 -0
- package/src/http3client.cc +2 -2
- package/src/http3dispatcher.cc +3 -2
- package/src/http3dispatcher.h +2 -1
- package/src/http3eventloop.cc +106 -2
- package/src/http3eventloop.h +13 -0
- package/src/http3wtsessionvisitor.cc +2 -0
- package/src/http3wtsessionvisitor.h +41 -1
- package/test/bidirectional-streams.spec.js +41 -97
- package/test/datagrams.spec.js +50 -99
- package/test/fixtures/chai.js +2 -0
- package/test/fixtures/known-bytes.js +15 -0
- package/test/fixtures/read-cert-hash.js +7 -0
- package/test/fixtures/read-stream.js +5 -0
- package/test/fixtures/reader-value.js +32 -0
- package/test/fixtures/server.js +237 -21
- package/test/fixtures/webtransport.browser.js +2 -0
- package/test/fixtures/webtransport.js +3 -0
- package/test/fixtures/write-stream.js +8 -1
- package/test/index.js +87 -0
- package/test/session.spec.js +159 -0
- package/test/unidirectional-streams.spec.js +67 -138
- package/tsconfig.json +0 -1
- package/dist/test/echoclient.d.ts +0 -2
- package/dist/test/echoclient.d.ts.map +0 -1
- package/dist/test/echoserver.d.ts +0 -2
- package/dist/test/echoserver.d.ts.map +0 -1
- package/dist/test/event-loop.spec.d.ts +0 -2
- package/dist/test/event-loop.spec.d.ts.map +0 -1
- package/dist/test/test.d.ts +0 -2
- package/dist/test/test.d.ts.map +0 -1
- package/dist/test/testsuite.d.ts +0 -25
- package/dist/test/testsuite.d.ts.map +0 -1
- package/platform/base/strings/utf_ostream_operators.h +0 -5
- /package/{test → old_test}/echoclient.js +0 -0
- /package/{test → old_test}/testsuite.js +0 -0
- /package/test/{event-loop.spec.js → event-loop.node.js} +0 -0
package/test/fixtures/server.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { generateWebTransportCertificate } from './certificate.js'
|
|
2
2
|
import { Http3Server } from '../../lib/index.js'
|
|
3
|
+
import { pTimeout } from './p-timeout.js'
|
|
4
|
+
import { getReaderStream, getReaderValue } from './reader-value.js'
|
|
5
|
+
import { writeStream } from './write-stream.js'
|
|
6
|
+
import { readStream } from './read-stream.js'
|
|
7
|
+
import * as ui8 from 'uint8arrays'
|
|
8
|
+
import { KNOWN_BYTES, KNOWN_BYTES_LENGTH } from './known-bytes.js'
|
|
3
9
|
|
|
4
10
|
export async function createServer() {
|
|
5
11
|
const attrs = [
|
|
@@ -19,6 +25,8 @@ export async function createServer() {
|
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
const server = new Http3Server({
|
|
28
|
+
/* port: 8080,
|
|
29
|
+
host: '0.0.0.0', */
|
|
22
30
|
port: 0,
|
|
23
31
|
host: '127.0.0.1',
|
|
24
32
|
secret: 'mysecret',
|
|
@@ -26,33 +34,241 @@ export async function createServer() {
|
|
|
26
34
|
privKey: certificate.private
|
|
27
35
|
})
|
|
28
36
|
|
|
37
|
+
server.ready
|
|
38
|
+
.then(async () => {
|
|
39
|
+
// set up listeners for the different server paths used by the tests
|
|
40
|
+
|
|
41
|
+
await Promise.all(
|
|
42
|
+
[
|
|
43
|
+
// echo server, initiated by remote
|
|
44
|
+
async () => {
|
|
45
|
+
for await (const session of getReaderStream(
|
|
46
|
+
server.sessionStream('/bidirectional_client_initiated_echo')
|
|
47
|
+
)) {
|
|
48
|
+
try {
|
|
49
|
+
const bidiStream = await getReaderValue(
|
|
50
|
+
session.incomingBidirectionalStreams
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// redirect input to output
|
|
54
|
+
await bidiStream.readable.pipeTo(bidiStream.writable)
|
|
55
|
+
} catch {
|
|
56
|
+
// in some tests the client closes the stream
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// echo server, initiated by local
|
|
62
|
+
async () => {
|
|
63
|
+
for await (const session of getReaderStream(
|
|
64
|
+
server.sessionStream('/bidirectional_server_initiated_echo')
|
|
65
|
+
)) {
|
|
66
|
+
try {
|
|
67
|
+
const stream = await session.createBidirectionalStream()
|
|
68
|
+
|
|
69
|
+
await writeStream(stream.writable, KNOWN_BYTES)
|
|
70
|
+
|
|
71
|
+
const received = await readStream(
|
|
72
|
+
stream.readable,
|
|
73
|
+
KNOWN_BYTES_LENGTH
|
|
74
|
+
)
|
|
75
|
+
// await stream.readable.cancel() // cancel so that the client can progress
|
|
76
|
+
|
|
77
|
+
// if we did not get the data we sent, close the session with a reason
|
|
78
|
+
if (
|
|
79
|
+
!ui8.equals(ui8.concat(KNOWN_BYTES), ui8.concat(received))
|
|
80
|
+
) {
|
|
81
|
+
session.close({
|
|
82
|
+
closeCode: 500,
|
|
83
|
+
reason: 'data did not match'
|
|
84
|
+
})
|
|
85
|
+
} else {
|
|
86
|
+
session.close()
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// in some tests the client closes the stream
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// echo datagrams, initiated by remote
|
|
95
|
+
async () => {
|
|
96
|
+
for await (const session of getReaderStream(
|
|
97
|
+
server.sessionStream('/datagrams_client_send')
|
|
98
|
+
)) {
|
|
99
|
+
// datagram transport is unreliable, at least one message should make it through
|
|
100
|
+
const expected = 1
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const received = await pTimeout(
|
|
104
|
+
readStream(session.datagrams.readable, expected),
|
|
105
|
+
1000
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
// if we did not get the data we sent, close the session with a reason
|
|
109
|
+
if (received.length !== expected) {
|
|
110
|
+
throw new Error('Did not receive enough bytes')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
session.close()
|
|
114
|
+
} catch (/** @type {any} */ err) {
|
|
115
|
+
session.close({
|
|
116
|
+
closeCode: 500,
|
|
117
|
+
reason: err.message
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// echo datagrams, initiated by local
|
|
124
|
+
async () => {
|
|
125
|
+
for await (const session of getReaderStream(
|
|
126
|
+
server.sessionStream('/datagrams_server_send')
|
|
127
|
+
)) {
|
|
128
|
+
const writer = session.datagrams.writable.getWriter()
|
|
129
|
+
let closed = false
|
|
130
|
+
|
|
131
|
+
// write datagrams until the client receives one and closes the connection
|
|
132
|
+
// eslint-disable-next-line promise/catch-or-return
|
|
133
|
+
Promise.resolve().then(async () => {
|
|
134
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
|
135
|
+
while (!closed) {
|
|
136
|
+
try {
|
|
137
|
+
await writer.ready
|
|
138
|
+
await writer.write(Uint8Array.from([0, 1, 2, 3, 4]))
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 1)) // do not flood everything
|
|
140
|
+
} catch {
|
|
141
|
+
// the session can be closed while we are writing
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
await session.closed
|
|
147
|
+
closed = true
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// cleanly close remote sessions
|
|
152
|
+
async () => {
|
|
153
|
+
for await (const session of getReaderStream(
|
|
154
|
+
server.sessionStream('/session_close')
|
|
155
|
+
)) {
|
|
156
|
+
session.close()
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// cleanly close remote sessions
|
|
161
|
+
async () => {
|
|
162
|
+
for await (const session of getReaderStream(
|
|
163
|
+
server.sessionStream('/session_close_with_reason')
|
|
164
|
+
)) {
|
|
165
|
+
session.close({
|
|
166
|
+
closeCode: 7,
|
|
167
|
+
reason: 'this is the reason'
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// send data over unidirectional stream, initiated by remote
|
|
173
|
+
async () => {
|
|
174
|
+
for await (const session of getReaderStream(
|
|
175
|
+
server.sessionStream('/unidirectional_client_send')
|
|
176
|
+
)) {
|
|
177
|
+
try {
|
|
178
|
+
const stream = await getReaderValue(
|
|
179
|
+
session.incomingUnidirectionalStreams
|
|
180
|
+
)
|
|
181
|
+
const received = await readStream(stream, KNOWN_BYTES_LENGTH)
|
|
182
|
+
// await stream.cancel() // cancel so that the client can progress
|
|
183
|
+
// if we did not get the data we sent, close the session with a reason
|
|
184
|
+
if (
|
|
185
|
+
!ui8.equals(ui8.concat(KNOWN_BYTES), ui8.concat(received))
|
|
186
|
+
) {
|
|
187
|
+
session.close({
|
|
188
|
+
closeCode: 500,
|
|
189
|
+
reason: 'data did not match'
|
|
190
|
+
})
|
|
191
|
+
} else {
|
|
192
|
+
session.close()
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// in some tests the client closes the stream
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// send data over unidirectional stream, initiated by local
|
|
201
|
+
async () => {
|
|
202
|
+
for await (const session of getReaderStream(
|
|
203
|
+
server.sessionStream('/unidirectional_server_send')
|
|
204
|
+
)) {
|
|
205
|
+
const stream = await session.createUnidirectionalStream()
|
|
206
|
+
|
|
207
|
+
await writeStream(stream, KNOWN_BYTES)
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// delays reading from stream after client writes
|
|
212
|
+
async () => {
|
|
213
|
+
for await (const session of getReaderStream(
|
|
214
|
+
server.sessionStream('/unidirectional_server_delay_before_read')
|
|
215
|
+
)) {
|
|
216
|
+
const stream = await getReaderValue(
|
|
217
|
+
session.incomingUnidirectionalStreams
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
// wait before we read from the stream, should trigger backpressure
|
|
221
|
+
// on the client
|
|
222
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
223
|
+
|
|
224
|
+
const received = await readStream(stream, KNOWN_BYTES_LENGTH)
|
|
225
|
+
// await stream.cancel() // cancel so that the client can progress
|
|
226
|
+
|
|
227
|
+
// if we did not get expected data, close the session with a reason
|
|
228
|
+
if (!ui8.equals(ui8.concat(KNOWN_BYTES), ui8.concat(received))) {
|
|
229
|
+
console.log('ERROR', KNOWN_BYTES, received)
|
|
230
|
+
session.close({
|
|
231
|
+
closeCode: 500,
|
|
232
|
+
reason: 'data did not match'
|
|
233
|
+
})
|
|
234
|
+
} else {
|
|
235
|
+
await new Promise((resolve) => setTimeout(resolve, 2000)) // time out is needed, since it can be received before the read is complete
|
|
236
|
+
// and we want the client to close the session after it has processeed the read
|
|
237
|
+
session.close()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
].map((fn) => fn())
|
|
242
|
+
)
|
|
243
|
+
})
|
|
244
|
+
.catch((/** @type {any} */ err) => {
|
|
245
|
+
console.error('server crashed', err)
|
|
246
|
+
})
|
|
247
|
+
|
|
29
248
|
return {
|
|
30
249
|
server,
|
|
31
250
|
certificate
|
|
32
251
|
}
|
|
33
252
|
}
|
|
34
253
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*/
|
|
39
|
-
export async function getServerSession(server, path) {
|
|
40
|
-
const sessionStream = await server.sessionStream(path)
|
|
41
|
-
const sessionReader = sessionStream.getReader()
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const { done, value } = await sessionReader.read()
|
|
45
|
-
|
|
46
|
-
if (done) {
|
|
47
|
-
throw new Error('Server is gone')
|
|
48
|
-
}
|
|
254
|
+
const { server, certificate } = await createServer()
|
|
255
|
+
server.startServer()
|
|
256
|
+
await server.ready
|
|
49
257
|
|
|
50
|
-
|
|
51
|
-
throw new Error('Session was undefined')
|
|
52
|
-
}
|
|
258
|
+
const address = server.address()
|
|
53
259
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
sessionReader.releaseLock()
|
|
57
|
-
}
|
|
260
|
+
if (address == null) {
|
|
261
|
+
throw new Error('Could not determine server address')
|
|
58
262
|
}
|
|
263
|
+
|
|
264
|
+
// tell the calling process how to contact us
|
|
265
|
+
if (process.send)
|
|
266
|
+
process.send({
|
|
267
|
+
address: `https://${address.host}:${address.port}`,
|
|
268
|
+
certificate: certificate.fingerprint
|
|
269
|
+
})
|
|
270
|
+
else console.error('No IPC channel')
|
|
271
|
+
/* console.log({
|
|
272
|
+
address: `https://${address.host}:${address.port}`,
|
|
273
|
+
certificate: certificate.fingerprint
|
|
274
|
+
}) */
|
|
@@ -10,8 +10,15 @@ export async function writeStream(writable, input) {
|
|
|
10
10
|
const writer = writable.getWriter()
|
|
11
11
|
|
|
12
12
|
for (const buf of input) {
|
|
13
|
+
await writer.ready
|
|
13
14
|
await writer.write(buf)
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
await writer.
|
|
17
|
+
await writer.ready
|
|
18
|
+
await writer.releaseLock()
|
|
19
|
+
try {
|
|
20
|
+
await writable.close()
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.log('Did we get a STOP_SENDING? ignore', error)
|
|
23
|
+
}
|
|
17
24
|
}
|
package/test/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run server as separate process so we can kill it without
|
|
5
|
+
* getting stuck waiting for the custom event loop to end.
|
|
6
|
+
*/
|
|
7
|
+
async function startServer() {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
let foundAddress = false
|
|
10
|
+
|
|
11
|
+
const server = execa('node', ['./test/fixtures/server.js'], {
|
|
12
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
|
|
13
|
+
})
|
|
14
|
+
server.on('message', (data) => {
|
|
15
|
+
if (!foundAddress) {
|
|
16
|
+
foundAddress = true
|
|
17
|
+
|
|
18
|
+
resolve(data)
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} certificate
|
|
26
|
+
* @param {string} serverAddress
|
|
27
|
+
*/
|
|
28
|
+
async function runTests(certificate, serverAddress) {
|
|
29
|
+
const env = process.argv[2]
|
|
30
|
+
/** @type {string} */
|
|
31
|
+
let command = ''
|
|
32
|
+
/** @type {string[]} */
|
|
33
|
+
let args = []
|
|
34
|
+
|
|
35
|
+
if (env === 'node') {
|
|
36
|
+
command = 'mocha'
|
|
37
|
+
args = [
|
|
38
|
+
process.env.CI ? '--no-colors' : '--colors',
|
|
39
|
+
'./test/*.spec.js',
|
|
40
|
+
'./test/*.node.js',
|
|
41
|
+
...process.argv.slice(3)
|
|
42
|
+
]
|
|
43
|
+
const tests = execa(command, args, {
|
|
44
|
+
env: {
|
|
45
|
+
DEBUG_COLORS: process.env.CI ? '' : 'true',
|
|
46
|
+
CERT_HASH: certificate,
|
|
47
|
+
SERVER_URL: serverAddress
|
|
48
|
+
},
|
|
49
|
+
stdio: ['inherit', 'inherit', 'inherit']
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
await tests
|
|
53
|
+
} else if (env === 'chromium') {
|
|
54
|
+
command = 'playwright-test'
|
|
55
|
+
args = ['./test/*.spec.js', ...process.argv.slice(3)]
|
|
56
|
+
const tests = execa(command, args, {
|
|
57
|
+
env: {
|
|
58
|
+
DEBUG_COLORS: process.env.CI ? '' : 'true',
|
|
59
|
+
CERT_HASH: certificate,
|
|
60
|
+
SERVER_URL: serverAddress
|
|
61
|
+
},
|
|
62
|
+
stdio: ['inherit', 'inherit', 'inherit']
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
await tests
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let success = true
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const { address, certificate } = await startServer()
|
|
73
|
+
|
|
74
|
+
await runTests(certificate, address)
|
|
75
|
+
} catch (/** @type {any} */ err) {
|
|
76
|
+
if (err.command == null) {
|
|
77
|
+
// was not an execa error
|
|
78
|
+
throw err
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (err.failed || err.timedOut || err.isCancelled || err.isKilled) {
|
|
82
|
+
success = false
|
|
83
|
+
}
|
|
84
|
+
} finally {
|
|
85
|
+
// this will cause the server process to exit too as it is a child of this process
|
|
86
|
+
process.exit(success ? 0 : 1)
|
|
87
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
|
|
12
|
+
describe('session', function () {
|
|
13
|
+
// FIXME: sometimes there are seemingly arbitrary 5s delays in
|
|
14
|
+
// communicating with the server under node.js
|
|
15
|
+
this.timeout(30000)
|
|
16
|
+
|
|
17
|
+
/** @type {import('../lib/dom').WebTransport | undefined} */
|
|
18
|
+
let client
|
|
19
|
+
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
if (client != null) {
|
|
23
|
+
client.close()
|
|
24
|
+
client = undefined
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should detect session closure', async () => {
|
|
29
|
+
client = new WebTransport(`${process.env.SERVER_URL}/session_close`, {
|
|
30
|
+
serverCertificateHashes: [
|
|
31
|
+
{
|
|
32
|
+
algorithm: 'sha-256',
|
|
33
|
+
value: readCertHash(process.env.CERT_HASH)
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
await client.ready
|
|
38
|
+
|
|
39
|
+
const result = await client.closed
|
|
40
|
+
expect(result).to.have.property('closeCode', 0)
|
|
41
|
+
expect(result).to.have.property('reason', '')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should detect session closure with close info', async () => {
|
|
45
|
+
// client context - connects to the server, sends some datagrams and reads the response
|
|
46
|
+
client = new WebTransport(
|
|
47
|
+
`${process.env.SERVER_URL}/session_close_with_reason`,
|
|
48
|
+
{
|
|
49
|
+
serverCertificateHashes: [
|
|
50
|
+
{
|
|
51
|
+
algorithm: 'sha-256',
|
|
52
|
+
value: readCertHash(process.env.CERT_HASH)
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
await client.ready
|
|
58
|
+
|
|
59
|
+
const result = await client.closed
|
|
60
|
+
expect(result).to.have.property('closeCode', 7)
|
|
61
|
+
expect(result).to.have.property('reason', 'this is the reason')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should error when connecting to a server that does not exist', async () => {
|
|
65
|
+
client = new WebTransport(`https://127.0.0.1:39821`, {
|
|
66
|
+
serverCertificateHashes: [
|
|
67
|
+
{
|
|
68
|
+
algorithm: 'sha-256',
|
|
69
|
+
value: readCertHash(process.env.CERT_HASH)
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
quicConnectTimeout: 100,
|
|
73
|
+
webTransportConnectTimeout: 100
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const [closedResult, readyResult] = await Promise.all([
|
|
77
|
+
client.closed.catch((err) => err),
|
|
78
|
+
client.ready.catch((err) => err)
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
expect(closedResult)
|
|
82
|
+
.to.be.a('WebTransportError')
|
|
83
|
+
.with.property('message', 'Opening handshake failed.')
|
|
84
|
+
expect(readyResult)
|
|
85
|
+
.to.be.a('WebTransportError')
|
|
86
|
+
.with.property('message', 'Opening handshake failed.')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should error when connecting to a path that does not exist', async () => {
|
|
90
|
+
client = new WebTransport(`${process.env.SERVER_URL}/non_existant`, {
|
|
91
|
+
serverCertificateHashes: [
|
|
92
|
+
{
|
|
93
|
+
algorithm: 'sha-256',
|
|
94
|
+
value: readCertHash(process.env.CERT_HASH)
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const [closedResult, readyResult] = await Promise.all([
|
|
100
|
+
client.closed.catch((err) => err),
|
|
101
|
+
client.ready.catch((err) => err)
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
expect(closedResult)
|
|
105
|
+
.to.be.a('WebTransportError')
|
|
106
|
+
.with.property('message', 'Opening handshake failed.')
|
|
107
|
+
expect(readyResult)
|
|
108
|
+
.to.be.a('WebTransportError')
|
|
109
|
+
.with.property('message', 'Opening handshake failed.')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should error when connecting with a bad certificate', async () => {
|
|
113
|
+
client = new WebTransport(`${process.env.SERVER_URL}/session_close`, {
|
|
114
|
+
serverCertificateHashes: [
|
|
115
|
+
{
|
|
116
|
+
algorithm: 'sha-256',
|
|
117
|
+
value: readCertHash(process.env.CERT_HASH + ':DE:AD:BE:EF')
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const [closedResult, readyResult] = await Promise.all([
|
|
123
|
+
client.closed.catch((err) => err),
|
|
124
|
+
client.ready.catch((err) => err)
|
|
125
|
+
])
|
|
126
|
+
|
|
127
|
+
expect(closedResult)
|
|
128
|
+
.to.be.a('WebTransportError')
|
|
129
|
+
.with.property('message', 'Opening handshake failed.')
|
|
130
|
+
expect(readyResult)
|
|
131
|
+
.to.be.a('WebTransportError')
|
|
132
|
+
.with.property('message', 'Opening handshake failed.')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should error when connecting with the wrong certificate', async () => {
|
|
136
|
+
client = new WebTransport(`${process.env.SERVER_URL}/session_close`, {
|
|
137
|
+
serverCertificateHashes: [
|
|
138
|
+
{
|
|
139
|
+
algorithm: 'sha-256',
|
|
140
|
+
value: readCertHash(
|
|
141
|
+
'DE:AD:BE:EF:' + process.env.CERT_HASH?.substring(12)
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const [closedResult, readyResult] = await Promise.all([
|
|
148
|
+
client.closed.catch((err) => err),
|
|
149
|
+
client.ready.catch((err) => err)
|
|
150
|
+
])
|
|
151
|
+
|
|
152
|
+
expect(closedResult)
|
|
153
|
+
.to.be.a('WebTransportError')
|
|
154
|
+
.with.property('message', 'Opening handshake failed.')
|
|
155
|
+
expect(readyResult)
|
|
156
|
+
.to.be.a('WebTransportError')
|
|
157
|
+
.with.property('message', 'Opening handshake failed.')
|
|
158
|
+
})
|
|
159
|
+
})
|