@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.
- package/CMakeLists.txt +110 -73
- package/build.js +32 -8
- package/dist/lib/client.d.ts +74 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/dom.d.ts +70 -0
- package/dist/lib/dom.d.ts.map +1 -0
- package/dist/lib/event-loop.d.ts +26 -0
- package/dist/lib/event-loop.d.ts.map +1 -0
- package/dist/lib/index.d.ts +79 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/native.d.ts +2 -0
- package/dist/lib/native.d.ts.map +1 -0
- package/dist/lib/server.d.ts +75 -0
- package/dist/lib/server.d.ts.map +1 -0
- package/dist/lib/session.d.ts +221 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/stream.d.ts +116 -0
- package/dist/lib/stream.d.ts.map +1 -0
- package/dist/lib/transport.d.ts +25 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/lib/types.d.ts +157 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +6 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/webtransport.d.ts +35 -0
- package/dist/lib/webtransport.d.ts.map +1 -0
- package/dist/test/bidirectional-streams.spec.d.ts +5 -0
- package/dist/test/bidirectional-streams.spec.d.ts.map +1 -0
- package/dist/test/datagrams.spec.d.ts +5 -0
- package/dist/test/datagrams.spec.d.ts.map +1 -0
- package/dist/test/echoclient.d.ts +2 -0
- package/dist/test/echoclient.d.ts.map +1 -0
- package/dist/test/echoserver.d.ts +2 -0
- package/dist/test/echoserver.d.ts.map +1 -0
- package/dist/test/event-loop.spec.d.ts +2 -0
- package/dist/test/event-loop.spec.d.ts.map +1 -0
- package/dist/test/fixtures/certificate.d.ts +21 -0
- package/dist/test/fixtures/certificate.d.ts.map +1 -0
- package/dist/test/fixtures/chai.d.ts +2 -0
- package/dist/test/fixtures/chai.d.ts.map +1 -0
- package/dist/test/fixtures/read-stream.d.ts +10 -0
- package/dist/test/fixtures/read-stream.d.ts.map +1 -0
- package/dist/test/fixtures/reader-value.d.ts +7 -0
- package/dist/test/fixtures/reader-value.d.ts.map +1 -0
- package/dist/test/fixtures/server.d.ts +10 -0
- package/dist/test/fixtures/server.d.ts.map +1 -0
- package/dist/test/fixtures/write-stream.d.ts +10 -0
- package/dist/test/fixtures/write-stream.d.ts.map +1 -0
- package/dist/test/test.d.ts +2 -0
- package/dist/test/test.d.ts.map +1 -0
- package/dist/test/testsuite.d.ts +25 -0
- package/dist/test/testsuite.d.ts.map +1 -0
- package/dist/test/unidirectional-streams.spec.d.ts +5 -0
- package/dist/test/unidirectional-streams.spec.d.ts.map +1 -0
- package/lib/client.js +163 -0
- package/lib/dom.ts +95 -0
- package/lib/event-loop.js +79 -0
- package/lib/index.js +33 -0
- package/lib/native.js +25 -0
- package/lib/server.js +165 -0
- package/lib/session.js +422 -0
- package/lib/stream.js +325 -0
- package/lib/transport.js +42 -0
- package/lib/types.ts +181 -0
- package/lib/utils.js +22 -0
- package/lib/webtransport.js +114 -0
- package/package.json +18 -6
- package/readme.md +28 -5
- package/src/http3client.h +0 -1
- package/src/http3eventloop.cc +52 -0
- package/src/http3eventloop.h +22 -1
- package/src/http3server.cc +4 -1
- package/src/http3server.h +8 -0
- package/src/http3serversession.h +1 -1
- package/test/bidirectional-streams.spec.js +138 -0
- package/test/datagrams.spec.js +132 -0
- package/test/echoclient.js +10 -6
- package/test/echoserver.js +6 -8
- package/test/event-loop.spec.js +24 -0
- package/test/{certificate.js → fixtures/certificate.js} +31 -15
- package/test/fixtures/chai.js +6 -0
- package/test/fixtures/read-stream.js +36 -0
- package/test/fixtures/reader-value.js +24 -0
- package/test/fixtures/server.js +58 -0
- package/test/fixtures/write-stream.js +17 -0
- package/test/test.js +29 -3
- package/test/testsuite.js +22 -0
- package/test/unidirectional-streams.spec.js +128 -0
- package/tsconfig.json +40 -0
- package/src/webtransport.js +0 -870
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import { expect } from './fixtures/chai.js'
|
|
4
|
+
import { Http3EventLoop } from '../lib/event-loop.js'
|
|
5
|
+
|
|
6
|
+
describe.skip('event-loop', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
if (Http3EventLoop.globalLoop != null) {
|
|
9
|
+
// shut down loop, otherwise we have to wait for
|
|
10
|
+
// it to time out which takes a long time.
|
|
11
|
+
Http3EventLoop.globalLoop.shutdownEventLoop()
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should start and stop the event loop', () => {
|
|
16
|
+
expect(Http3EventLoop.globalLoop).to.be.null()
|
|
17
|
+
|
|
18
|
+
Http3EventLoop.createGlobalEventLoop()
|
|
19
|
+
expect(Http3EventLoop.globalLoop).to.not.be.null()
|
|
20
|
+
|
|
21
|
+
Http3EventLoop.globalLoop?.shutdownEventLoop()
|
|
22
|
+
expect(Http3EventLoop.globalLoop).to.be.null()
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -53,16 +53,16 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
53
53
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
54
54
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
55
55
|
|
|
56
|
+
// @ts-expect-error node-forge has no types and @types/node-forge do not include oids
|
|
56
57
|
import forge from 'node-forge'
|
|
57
|
-
import { webcrypto as crypto } from 'crypto'
|
|
58
|
-
import { X509Certificate } from 'crypto'
|
|
58
|
+
import { webcrypto as crypto, X509Certificate } from 'crypto'
|
|
59
59
|
|
|
60
60
|
const { pki, asn1, oids } = forge
|
|
61
61
|
// taken from node-forge
|
|
62
62
|
/**
|
|
63
63
|
* Converts an X.509 subject or issuer to an ASN.1 RDNSequence.
|
|
64
64
|
*
|
|
65
|
-
* @param obj the subject or issuer (distinguished name).
|
|
65
|
+
* @param {any} obj the subject or issuer (distinguished name).
|
|
66
66
|
*
|
|
67
67
|
* @return the ASN.1 RDNSequence.
|
|
68
68
|
*/
|
|
@@ -110,19 +110,19 @@ function _dnToAsn1(obj) {
|
|
|
110
110
|
return rval
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
const jan_1_1950 = new Date('1950-01-01T00:00:00Z') // eslint-disable-line camelcase
|
|
114
|
+
const jan_1_2050 = new Date('2050-01-01T00:00:00Z') // eslint-disable-line camelcase
|
|
113
115
|
// taken from node-forge almost not modified
|
|
114
116
|
/**
|
|
115
117
|
* Converts a Date object to ASN.1
|
|
116
118
|
* Handles the different format before and after 1st January 2050
|
|
117
119
|
*
|
|
118
|
-
* @param date date object.
|
|
120
|
+
* @param {Date} date date object.
|
|
119
121
|
*
|
|
120
122
|
* @return the ASN.1 object representing the date.
|
|
121
123
|
*/
|
|
122
|
-
|
|
123
|
-
const jan_1_1950 = new Date('1950-01-01T00:00:00Z')
|
|
124
|
-
const jan_1_2050 = new Date('2050-01-01T00:00:00Z')
|
|
125
124
|
function _dateToAsn1(date) {
|
|
125
|
+
// eslint-disable-next-line camelcase
|
|
126
126
|
if (date >= jan_1_1950 && date < jan_1_2050) {
|
|
127
127
|
return asn1.create(
|
|
128
128
|
asn1.Class.UNIVERSAL,
|
|
@@ -144,15 +144,15 @@ function _dateToAsn1(date) {
|
|
|
144
144
|
/**
|
|
145
145
|
* Convert signature parameters object to ASN.1
|
|
146
146
|
*
|
|
147
|
-
* @param {
|
|
148
|
-
* @param params The signature
|
|
147
|
+
* @param {string} oid Signature algorithm OID
|
|
148
|
+
* @param {any} params The signature parameters object
|
|
149
149
|
* @return ASN.1 object representing signature parameters
|
|
150
150
|
*/
|
|
151
151
|
function _signatureParametersToAsn1(oid, params) {
|
|
152
|
+
const parts = []
|
|
153
|
+
|
|
152
154
|
switch (oid) {
|
|
153
155
|
case oids['RSASSA-PSS']:
|
|
154
|
-
const parts = []
|
|
155
|
-
|
|
156
156
|
if (params.hash.algorithmOid !== undefined) {
|
|
157
157
|
parts.push(
|
|
158
158
|
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
@@ -217,7 +217,7 @@ function _signatureParametersToAsn1(oid, params) {
|
|
|
217
217
|
/**
|
|
218
218
|
* Gets the ASN.1 TBSCertificate part of an X.509v3 certificate.
|
|
219
219
|
*
|
|
220
|
-
* @param cert the certificate.
|
|
220
|
+
* @param {any} cert the certificate.
|
|
221
221
|
*
|
|
222
222
|
* @return the asn1 TBSCertificate.
|
|
223
223
|
*/
|
|
@@ -319,6 +319,10 @@ function getTBSCertificate(cert) {
|
|
|
319
319
|
// because serial numbers use ones' complement notation
|
|
320
320
|
// this RFC in section 4.1.2.2 requires serial numbers to be positive
|
|
321
321
|
// http://www.ietf.org/rfc/rfc5280.txt
|
|
322
|
+
/**
|
|
323
|
+
* @param {string} hexString
|
|
324
|
+
* @returns
|
|
325
|
+
*/
|
|
322
326
|
function toPositiveHex(hexString) {
|
|
323
327
|
let mostSiginficativeHexAsInt = parseInt(hexString[0], 16)
|
|
324
328
|
if (mostSiginficativeHexAsInt < 8) {
|
|
@@ -330,9 +334,21 @@ function toPositiveHex(hexString) {
|
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
// the next is an edit of the selfsigned function reduced to the function necessary for webtransport
|
|
337
|
+
/**
|
|
338
|
+
* @typedef {object} Certificate
|
|
339
|
+
* @property {string} public
|
|
340
|
+
* @property {string} private
|
|
341
|
+
* @property {string} cert
|
|
342
|
+
* @property {Uint8Array} hash
|
|
343
|
+
* @property {string} fingerprint
|
|
344
|
+
*
|
|
345
|
+
* @param {*} attrs
|
|
346
|
+
* @param {*} options
|
|
347
|
+
* @returns {Promise<Certificate | null>}
|
|
348
|
+
*/
|
|
333
349
|
export async function generateWebTransportCertificate(attrs, options) {
|
|
334
350
|
try {
|
|
335
|
-
|
|
351
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
336
352
|
{
|
|
337
353
|
name: 'ECDSA',
|
|
338
354
|
namedCurve: 'P-256'
|
|
@@ -355,7 +371,7 @@ export async function generateWebTransportCertificate(attrs, options) {
|
|
|
355
371
|
cert.setSubject(attrs)
|
|
356
372
|
cert.setIssuer(attrs)
|
|
357
373
|
|
|
358
|
-
|
|
374
|
+
const privateKey = crypto.subtle.exportKey('pkcs8', keyPair.privateKey)
|
|
359
375
|
const publicKey = (cert.publicKey = await crypto.subtle.exportKey(
|
|
360
376
|
'spki',
|
|
361
377
|
keyPair.publicKey
|
|
@@ -392,7 +408,7 @@ export async function generateWebTransportCertificate(attrs, options) {
|
|
|
392
408
|
oids['1.2.840.10045.4.3.2'] = 'ecdsa-with-sha256'
|
|
393
409
|
oids['ecdsa-with-sha256'] = '1.2.840.10045.4.3.2'
|
|
394
410
|
|
|
395
|
-
cert.siginfo.algorithmOid = cert.signatureOid = '1.2.840.10045.4.3.2' //'ecdsa-with-sha256'
|
|
411
|
+
cert.siginfo.algorithmOid = cert.signatureOid = '1.2.840.10045.4.3.2' // 'ecdsa-with-sha256'
|
|
396
412
|
|
|
397
413
|
cert.tbsCertificate = getTBSCertificate(cert)
|
|
398
414
|
const encoded = Buffer.from(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a stream contents to the end and return it
|
|
3
|
+
*
|
|
4
|
+
* @template T
|
|
5
|
+
* @param {ReadableStream<T>} readable
|
|
6
|
+
* @param {number} [expected]
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export async function readStream(readable, expected) {
|
|
10
|
+
const reader = readable.getReader()
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
/** @type {T[]} */
|
|
14
|
+
const output = []
|
|
15
|
+
|
|
16
|
+
while (true) {
|
|
17
|
+
const { done, value } = await reader.read()
|
|
18
|
+
|
|
19
|
+
if (done) {
|
|
20
|
+
break
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (value != null) {
|
|
24
|
+
output.push(value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (expected != null && output.length === expected) {
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return output
|
|
33
|
+
} finally {
|
|
34
|
+
reader.releaseLock()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template T
|
|
3
|
+
* @param {ReadableStream<T>} readableStream
|
|
4
|
+
* @returns {Promise<T>}
|
|
5
|
+
*/
|
|
6
|
+
export async function getReaderValue(readableStream) {
|
|
7
|
+
const reader = readableStream.getReader()
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const { done, value } = await reader.read()
|
|
11
|
+
|
|
12
|
+
if (done) {
|
|
13
|
+
throw new Error('Stream ended')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!value) {
|
|
17
|
+
throw new Error('Stream value was undefined')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return value
|
|
21
|
+
} finally {
|
|
22
|
+
reader.releaseLock()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { generateWebTransportCertificate } from './certificate.js'
|
|
2
|
+
import { Http3Server } from '../../lib/index.js'
|
|
3
|
+
|
|
4
|
+
export async function createServer() {
|
|
5
|
+
const attrs = [
|
|
6
|
+
{ shortName: 'C', value: 'DE' },
|
|
7
|
+
{ shortName: 'ST', value: 'Berlin' },
|
|
8
|
+
{ shortName: 'L', value: 'Berlin' },
|
|
9
|
+
{ shortName: 'O', value: 'WebTransport Test Server' },
|
|
10
|
+
{ shortName: 'CN', value: '127.0.0.1' }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const certificate = await generateWebTransportCertificate(attrs, {
|
|
14
|
+
days: 13
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
if (certificate == null) {
|
|
18
|
+
throw new Error('Certificate generation failed')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const server = new Http3Server({
|
|
22
|
+
port: 0,
|
|
23
|
+
host: '127.0.0.1',
|
|
24
|
+
secret: 'mysecret',
|
|
25
|
+
cert: certificate.cert, // unclear if it is the correct format
|
|
26
|
+
privKey: certificate.private
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
server,
|
|
31
|
+
certificate
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {import('../../lib/server.js').Http3Server} server
|
|
37
|
+
* @param {string} path
|
|
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
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!value) {
|
|
51
|
+
throw new Error('Session was undefined')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return value
|
|
55
|
+
} finally {
|
|
56
|
+
sessionReader.releaseLock()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write contents to a stream and close it
|
|
3
|
+
*
|
|
4
|
+
* @template T
|
|
5
|
+
* @param {WritableStream<T>} writable
|
|
6
|
+
* @param {T[]} input
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export async function writeStream(writable, input) {
|
|
10
|
+
const writer = writable.getWriter()
|
|
11
|
+
|
|
12
|
+
for (const buf of input) {
|
|
13
|
+
await writer.write(buf)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await writer.close()
|
|
17
|
+
}
|
package/test/test.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
// this file runs various tests
|
|
6
6
|
|
|
7
|
-
import { generateWebTransportCertificate } from './certificate.js'
|
|
8
|
-
import { Http3Server, WebTransport, testcheck } from '../
|
|
7
|
+
import { generateWebTransportCertificate } from './fixtures/certificate.js'
|
|
8
|
+
import { Http3Server, WebTransport, testcheck } from '../lib/index.js'
|
|
9
9
|
import { echoTestsConnection, runEchoServer } from './testsuite.js'
|
|
10
10
|
|
|
11
11
|
async function run() {
|
|
@@ -17,7 +17,28 @@ async function run() {
|
|
|
17
17
|
console.log('global event loop gone, everything alright')
|
|
18
18
|
process.exit(0)
|
|
19
19
|
}
|
|
20
|
-
},
|
|
20
|
+
}, 50 * 1000)
|
|
21
|
+
console.log('try connecting to server that does not exist')
|
|
22
|
+
const badClient = new WebTransport('https://127.0.0.1:49823/echo', {
|
|
23
|
+
serverCertificateHashes: [
|
|
24
|
+
{
|
|
25
|
+
algorithm: 'sha-256',
|
|
26
|
+
value: Buffer.from(
|
|
27
|
+
'a589bf4f98a0158aa890328d5d3f519b9e2a5b1e61b09eb10b7a9be0e79bf148',
|
|
28
|
+
'hex'
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
await badClient.ready
|
|
34
|
+
.then(() => {
|
|
35
|
+
console.error('Successfully connected to a non-running server?!')
|
|
36
|
+
process.exit(1)
|
|
37
|
+
})
|
|
38
|
+
.catch(() => {
|
|
39
|
+
console.log('Did not connect to non-running server')
|
|
40
|
+
})
|
|
41
|
+
|
|
21
42
|
console.log('start generating self signed certificate')
|
|
22
43
|
|
|
23
44
|
const attrs = [
|
|
@@ -32,6 +53,10 @@ async function run() {
|
|
|
32
53
|
days: 13
|
|
33
54
|
})
|
|
34
55
|
|
|
56
|
+
if (certificate == null) {
|
|
57
|
+
throw new Error('Certificate generation failed')
|
|
58
|
+
}
|
|
59
|
+
|
|
35
60
|
console.log('start Http3Server and startup echo tests')
|
|
36
61
|
// now ramp up the server
|
|
37
62
|
const http3server = new Http3Server({
|
|
@@ -52,6 +77,7 @@ async function run() {
|
|
|
52
77
|
|
|
53
78
|
const url = 'https://127.0.0.1:8080/echo'
|
|
54
79
|
|
|
80
|
+
/** @type {import('../lib/dom').WebTransport | null} */
|
|
55
81
|
let client = new WebTransport(url, {
|
|
56
82
|
serverCertificateHashes: [{ algorithm: 'sha-256', value: certificate.hash }]
|
|
57
83
|
})
|
package/test/testsuite.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../lib/dom').WebTransport} session
|
|
7
|
+
*/
|
|
5
8
|
export async function incomingBidirectionalEchoTest(session) {
|
|
6
9
|
try {
|
|
7
10
|
const bidiReader = session.incomingBidirectionalStreams.getReader()
|
|
@@ -24,6 +27,9 @@ export async function incomingBidirectionalEchoTest(session) {
|
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @param {import('../lib/dom').WebTransport} session
|
|
32
|
+
*/
|
|
27
33
|
export async function outgoingBidirectionalEchoTest(session) {
|
|
28
34
|
try {
|
|
29
35
|
const mybidistream = await session.createBidirectionalStream()
|
|
@@ -33,6 +39,9 @@ export async function outgoingBidirectionalEchoTest(session) {
|
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @param {import('../lib/dom').WebTransport} session
|
|
44
|
+
*/
|
|
36
45
|
export async function unidirectionalEchoTest(session) {
|
|
37
46
|
try {
|
|
38
47
|
const unidiReader = session.incomingUnidirectionalStreams.getReader()
|
|
@@ -56,6 +65,9 @@ export async function unidirectionalEchoTest(session) {
|
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @param {import('../lib/dom').WebTransport} session
|
|
70
|
+
*/
|
|
59
71
|
export async function datagramEchoTest(session) {
|
|
60
72
|
try {
|
|
61
73
|
session.datagrams.readable.pipeTo(session.datagrams.writable)
|
|
@@ -64,6 +76,9 @@ export async function datagramEchoTest(session) {
|
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
|
|
79
|
+
/**
|
|
80
|
+
* @param {import('../lib').Http3Server} server
|
|
81
|
+
*/
|
|
67
82
|
export async function runEchoServer(server) {
|
|
68
83
|
try {
|
|
69
84
|
const sessionStream = await server.sessionStream('/echo')
|
|
@@ -99,6 +114,10 @@ export async function runEchoServer(server) {
|
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @param {ArrayLike<any>} array1
|
|
119
|
+
* @param {ArrayLike<any>} array2
|
|
120
|
+
*/
|
|
102
121
|
function testArraysEqual(array1, array2) {
|
|
103
122
|
if (array1.length !== array2.length)
|
|
104
123
|
throw new Error('Array not equal in length')
|
|
@@ -107,6 +126,9 @@ function testArraysEqual(array1, array2) {
|
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @param {import('../lib/dom').WebTransport} transport
|
|
131
|
+
*/
|
|
110
132
|
export async function echoTestsConnection(transport) {
|
|
111
133
|
// some echo tests for testing the webtransport library, not for production
|
|
112
134
|
const stream = await transport.createBidirectionalStream()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import { createServer } from './fixtures/server.js'
|
|
4
|
+
import { getReaderValue } from './fixtures/reader-value.js'
|
|
5
|
+
import { WebTransport } from '../lib/index.js'
|
|
6
|
+
import { expect } from 'chai'
|
|
7
|
+
import { readStream } from './fixtures/read-stream.js'
|
|
8
|
+
import { writeStream } from './fixtures/write-stream.js'
|
|
9
|
+
import { defer } from '../lib/utils.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @template T
|
|
13
|
+
* @typedef {import('../lib/types').Deferred<T>} Deferred<T>
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const SERVER_PATH = '/unidirectional-streams'
|
|
17
|
+
|
|
18
|
+
describe('unidirectional streams', function () {
|
|
19
|
+
/** @type {import('../lib/server').Http3Server} */
|
|
20
|
+
let server
|
|
21
|
+
/** @type {import('./fixtures/certificate.js').Certificate} */
|
|
22
|
+
let certificate
|
|
23
|
+
/** @type {import('../lib/dom').WebTransport | undefined} */
|
|
24
|
+
let client
|
|
25
|
+
/** @type {string} */
|
|
26
|
+
let url
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
;({ server, certificate } = await createServer())
|
|
30
|
+
server.startServer()
|
|
31
|
+
await server.ready
|
|
32
|
+
|
|
33
|
+
const address = server.address()
|
|
34
|
+
|
|
35
|
+
if (address == null || address.port == null) {
|
|
36
|
+
throw new Error('No address')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
url = `https://${address.host}:${address.port}`
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
afterEach(async () => {
|
|
43
|
+
if (client != null) {
|
|
44
|
+
client.close()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (server != null) {
|
|
48
|
+
server.stopServer()
|
|
49
|
+
await server.closed
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('sends data over an outgoing unidirectional stream', async () => {
|
|
54
|
+
this.timeout(200)
|
|
55
|
+
/** @type {Deferred<Uint8Array[]>} */
|
|
56
|
+
const serverData = defer()
|
|
57
|
+
|
|
58
|
+
// server context - waits for the client to open a bidi stream and pipes it back to them
|
|
59
|
+
Promise.resolve().then(async () => {
|
|
60
|
+
const session = await getReaderValue(server.sessionStream(SERVER_PATH))
|
|
61
|
+
const stream = await getReaderValue(session.incomingUnidirectionalStreams)
|
|
62
|
+
|
|
63
|
+
const output = await readStream(stream)
|
|
64
|
+
serverData.resolve(output)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// client context - connects to the server, opens a bidi stream, sends some data and reads the response
|
|
68
|
+
client = new WebTransport(`${url}${SERVER_PATH}`, {
|
|
69
|
+
serverCertificateHashes: [
|
|
70
|
+
{
|
|
71
|
+
algorithm: 'sha-256',
|
|
72
|
+
value: certificate.hash
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
})
|
|
76
|
+
await client.ready
|
|
77
|
+
|
|
78
|
+
const input = [
|
|
79
|
+
Uint8Array.from([0, 1, 2, 3, 4]),
|
|
80
|
+
Uint8Array.from([5, 6, 7, 8, 9]),
|
|
81
|
+
Uint8Array.from([10, 11, 12, 13, 14])
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
const stream = await client.createUnidirectionalStream()
|
|
85
|
+
await writeStream(stream, input)
|
|
86
|
+
|
|
87
|
+
const received = await serverData.promise
|
|
88
|
+
expect(received).to.deep.equal(
|
|
89
|
+
input,
|
|
90
|
+
'Server did not receive the same bytes we sent'
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('receives data over an incoming unidirectional stream', async () => {
|
|
95
|
+
this.timeout(200)
|
|
96
|
+
const input = [
|
|
97
|
+
Uint8Array.from([0, 1, 2, 3, 4]),
|
|
98
|
+
Uint8Array.from([5, 6, 7, 8, 9]),
|
|
99
|
+
Uint8Array.from([10, 11, 12, 13, 14])
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
// server context - waits for the client to connect, opens a bidi stream, sends some data and reads the response
|
|
103
|
+
Promise.resolve().then(async () => {
|
|
104
|
+
const session = await getReaderValue(server.sessionStream(SERVER_PATH))
|
|
105
|
+
const stream = await session.createUnidirectionalStream()
|
|
106
|
+
|
|
107
|
+
await writeStream(stream, input)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// client context - waits for the server to open a bidi stream then pipes it back to them
|
|
111
|
+
client = new WebTransport(`${url}${SERVER_PATH}`, {
|
|
112
|
+
serverCertificateHashes: [
|
|
113
|
+
{
|
|
114
|
+
algorithm: 'sha-256',
|
|
115
|
+
value: certificate.hash
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
await client.ready
|
|
120
|
+
|
|
121
|
+
const stream = await getReaderValue(client.incomingUnidirectionalStreams)
|
|
122
|
+
const received = await readStream(stream)
|
|
123
|
+
expect(received).to.deep.equal(
|
|
124
|
+
input,
|
|
125
|
+
'Did not receive the same bytes we sent'
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"strict": true,
|
|
4
|
+
// project options
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"checkJs": true,
|
|
8
|
+
"target": "ES2020",
|
|
9
|
+
"module": "ES2022",
|
|
10
|
+
"lib": ["ES2021", "ES2021.Promise", "ES2021.String", "ES2020.BigInt", "DOM", "DOM.Iterable"],
|
|
11
|
+
"noEmit": false,
|
|
12
|
+
"noEmitOnError": true,
|
|
13
|
+
"emitDeclarationOnly": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"incremental": true,
|
|
17
|
+
"composite": true,
|
|
18
|
+
"isolatedModules": true,
|
|
19
|
+
"removeComments": false,
|
|
20
|
+
"sourceMap": true,
|
|
21
|
+
// module resolution
|
|
22
|
+
"esModuleInterop": true,
|
|
23
|
+
"moduleResolution": "node",
|
|
24
|
+
// linter checks
|
|
25
|
+
"noImplicitReturns": false,
|
|
26
|
+
"noFallthroughCasesInSwitch": true,
|
|
27
|
+
"noUnusedLocals": true,
|
|
28
|
+
"noUnusedParameters": false,
|
|
29
|
+
// advanced
|
|
30
|
+
"importsNotUsedAsValues": "error",
|
|
31
|
+
"forceConsistentCasingInFileNames": true,
|
|
32
|
+
"skipLibCheck": true,
|
|
33
|
+
"stripInternal": true,
|
|
34
|
+
"resolveJsonModule": true
|
|
35
|
+
},
|
|
36
|
+
"include": [
|
|
37
|
+
"lib",
|
|
38
|
+
"test"
|
|
39
|
+
]
|
|
40
|
+
}
|