@libp2p/webtransport 5.0.51-8484de8a2 → 5.0.51-87bc8d4fb
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/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +63 -32
- package/dist/src/index.js.map +1 -1
- package/dist/src/listener.d.ts +1 -0
- package/dist/src/listener.d.ts.map +1 -1
- package/dist/src/listener.js.map +1 -1
- package/dist/src/muxer.d.ts +5 -2
- package/dist/src/muxer.d.ts.map +1 -1
- package/dist/src/muxer.js +74 -44
- package/dist/src/muxer.js.map +1 -1
- package/dist/src/stream.d.ts +2 -21
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +47 -75
- package/dist/src/stream.js.map +1 -1
- package/dist/src/utils/inert-duplex.d.ts +3 -0
- package/dist/src/utils/inert-duplex.d.ts.map +1 -0
- package/dist/src/utils/inert-duplex.js +20 -0
- package/dist/src/utils/inert-duplex.js.map +1 -0
- package/dist/src/utils/parse-multiaddr.d.ts +1 -1
- package/dist/src/utils/parse-multiaddr.d.ts.map +1 -1
- package/dist/src/utils/parse-multiaddr.js +11 -17
- package/dist/src/utils/parse-multiaddr.js.map +1 -1
- package/package.json +20 -19
- package/src/constants.ts +1 -0
- package/src/index.ts +74 -36
- package/src/listener.ts +1 -0
- package/src/muxer.ts +84 -60
- package/src/stream.ts +55 -88
- package/src/utils/inert-duplex.ts +21 -0
- package/src/utils/parse-multiaddr.ts +12 -20
- package/dist/src/session-to-conn.d.ts +0 -11
- package/dist/src/session-to-conn.d.ts.map +0 -1
- package/dist/src/session-to-conn.js +0 -35
- package/dist/src/session-to-conn.js.map +0 -1
- package/dist/src/utils/webtransport-message-stream.d.ts +0 -18
- package/dist/src/utils/webtransport-message-stream.d.ts.map +0 -1
- package/dist/src/utils/webtransport-message-stream.js +0 -49
- package/dist/src/utils/webtransport-message-stream.js.map +0 -1
- package/src/session-to-conn.ts +0 -50
- package/src/utils/webtransport-message-stream.ts +0 -69
package/dist/src/stream.js
CHANGED
|
@@ -1,96 +1,67 @@
|
|
|
1
|
-
import { AbstractStream } from '@libp2p/utils';
|
|
1
|
+
import { AbstractStream } from '@libp2p/utils/abstract-stream';
|
|
2
2
|
import { raceSignal } from 'race-signal';
|
|
3
|
-
|
|
3
|
+
import { Uint8ArrayList } from 'uint8arraylist';
|
|
4
|
+
class WebTransportStream extends AbstractStream {
|
|
4
5
|
writer;
|
|
5
6
|
reader;
|
|
6
7
|
constructor(init) {
|
|
7
8
|
super(init);
|
|
8
|
-
this.writer = init.
|
|
9
|
-
this.reader = init.
|
|
10
|
-
void this.writer.closed
|
|
11
|
-
.then(() => {
|
|
12
|
-
this.log('writer closed gracefully');
|
|
13
|
-
})
|
|
14
|
-
.catch((err) => {
|
|
15
|
-
// chrome/ff send different messages
|
|
16
|
-
if (err.message.includes('STOP_SENDING') || err.message.includes('StopSending')) {
|
|
17
|
-
// err.code === 0 so we may be able to use this to detect remote close
|
|
18
|
-
// read instead?
|
|
19
|
-
this.onRemoteCloseRead();
|
|
20
|
-
}
|
|
21
|
-
else if (err.message.includes('RESET_STREAM') || err.message.includes('Reset')) {
|
|
22
|
-
this.onRemoteReset();
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
this.log('writer close promise rejected - %e', err);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
this.readData();
|
|
29
|
-
}
|
|
30
|
-
readData() {
|
|
9
|
+
this.writer = init.bidiStream.writable.getWriter();
|
|
10
|
+
this.reader = init.bidiStream.readable.getReader();
|
|
31
11
|
Promise.resolve()
|
|
32
12
|
.then(async () => {
|
|
33
13
|
while (true) {
|
|
34
14
|
const result = await this.reader.read();
|
|
35
|
-
if (result.value != null) {
|
|
36
|
-
this.onData(result.value);
|
|
37
|
-
}
|
|
38
15
|
if (result.done) {
|
|
39
|
-
|
|
40
|
-
this.onRemoteCloseWrite();
|
|
16
|
+
init.log('remote closed write');
|
|
41
17
|
return;
|
|
42
18
|
}
|
|
43
|
-
if (
|
|
44
|
-
|
|
19
|
+
if (result.value != null) {
|
|
20
|
+
this.sourcePush(new Uint8ArrayList(result.value));
|
|
45
21
|
}
|
|
46
22
|
}
|
|
47
23
|
})
|
|
48
24
|
.catch(err => {
|
|
25
|
+
init.log.error('error reading from stream', err);
|
|
49
26
|
this.abort(err);
|
|
50
27
|
})
|
|
51
28
|
.finally(() => {
|
|
52
|
-
this.
|
|
29
|
+
this.remoteCloseWrite();
|
|
30
|
+
});
|
|
31
|
+
void this.writer.closed
|
|
32
|
+
.then(() => {
|
|
33
|
+
init.log('writer closed');
|
|
34
|
+
})
|
|
35
|
+
.catch((err) => {
|
|
36
|
+
init.log('writer close promise rejected', err);
|
|
37
|
+
})
|
|
38
|
+
.finally(() => {
|
|
39
|
+
this.remoteCloseRead();
|
|
53
40
|
});
|
|
54
41
|
}
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
sendNewStream(options) {
|
|
43
|
+
// this is a no-op
|
|
44
|
+
}
|
|
45
|
+
async sendData(buf, options) {
|
|
46
|
+
for (const chunk of buf) {
|
|
47
|
+
this.log('sendData waiting for writer to be ready');
|
|
48
|
+
await raceSignal(this.writer.ready, options?.signal);
|
|
49
|
+
// the streams spec recommends not waiting for data to be sent
|
|
50
|
+
// https://streams.spec.whatwg.org/#example-manual-write-dont-await
|
|
51
|
+
this.writer.write(chunk)
|
|
60
52
|
.catch(err => {
|
|
61
|
-
this.
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
this.log.trace('desired size after sending %d bytes is %d bytes', data.byteLength, this.writer.desiredSize);
|
|
65
|
-
// null means the stream has errored - https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
|
|
66
|
-
if (this.writer.desiredSize == null) {
|
|
67
|
-
return {
|
|
68
|
-
sentBytes: data.byteLength,
|
|
69
|
-
canSendMore: false
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
const canSendMore = this.writer.desiredSize > 0;
|
|
73
|
-
if (!canSendMore) {
|
|
74
|
-
this.writer.ready.then(() => {
|
|
75
|
-
this.safeDispatchEvent('drain');
|
|
76
|
-
}, (err) => {
|
|
77
|
-
this.abort(err);
|
|
53
|
+
this.log.error('error sending stream data', err);
|
|
78
54
|
});
|
|
79
55
|
}
|
|
80
|
-
return {
|
|
81
|
-
sentBytes: data.byteLength,
|
|
82
|
-
canSendMore
|
|
83
|
-
};
|
|
84
56
|
}
|
|
85
|
-
sendReset(
|
|
86
|
-
this.writer
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
57
|
+
async sendReset(options) {
|
|
58
|
+
this.log('sendReset aborting writer');
|
|
59
|
+
await raceSignal(this.writer.abort(), options?.signal);
|
|
60
|
+
this.log('sendReset aborted writer');
|
|
90
61
|
}
|
|
91
62
|
async sendCloseWrite(options) {
|
|
92
63
|
this.log('sendCloseWrite closing writer');
|
|
93
|
-
await raceSignal(this.writer.close()
|
|
64
|
+
await raceSignal(this.writer.close(), options?.signal);
|
|
94
65
|
this.log('sendCloseWrite closed writer');
|
|
95
66
|
}
|
|
96
67
|
async sendCloseRead(options) {
|
|
@@ -98,20 +69,21 @@ export class WebTransportStream extends AbstractStream {
|
|
|
98
69
|
await raceSignal(this.reader.cancel(), options?.signal);
|
|
99
70
|
this.log('sendCloseRead cancelled reader');
|
|
100
71
|
}
|
|
101
|
-
sendPause() {
|
|
102
|
-
}
|
|
103
|
-
sendResume() {
|
|
104
|
-
this.readData();
|
|
105
|
-
}
|
|
106
72
|
}
|
|
107
|
-
export function webtransportBiDiStreamToStream(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
stream,
|
|
73
|
+
export async function webtransportBiDiStreamToStream(bidiStream, streamId, direction, activeStreams, onStreamEnd, log) {
|
|
74
|
+
const stream = new WebTransportStream({
|
|
75
|
+
bidiStream,
|
|
111
76
|
id: streamId,
|
|
112
77
|
direction,
|
|
113
78
|
log: log.newScope(`${direction}:${streamId}`),
|
|
114
|
-
|
|
79
|
+
onEnd: () => {
|
|
80
|
+
const index = activeStreams.findIndex(s => s === stream);
|
|
81
|
+
if (index !== -1) {
|
|
82
|
+
activeStreams.splice(index, 1);
|
|
83
|
+
}
|
|
84
|
+
onStreamEnd?.(stream);
|
|
85
|
+
}
|
|
115
86
|
});
|
|
87
|
+
return stream;
|
|
116
88
|
}
|
|
117
89
|
//# sourceMappingURL=stream.js.map
|
package/dist/src/stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inert-duplex.d.ts","sourceRoot":"","sources":["../../../src/utils/inert-duplex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,iBAAiB,CAAA;AAGrD,wBAAgB,WAAW,IAAK,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAiBpD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Duplex that does nothing. Needed to fulfill the interface
|
|
2
|
+
export function inertDuplex() {
|
|
3
|
+
return {
|
|
4
|
+
source: {
|
|
5
|
+
[Symbol.asyncIterator]() {
|
|
6
|
+
return {
|
|
7
|
+
async next() {
|
|
8
|
+
// This will never resolve
|
|
9
|
+
return new Promise(() => { });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
sink: async (source) => {
|
|
15
|
+
// This will never resolve
|
|
16
|
+
return new Promise(() => { });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=inert-duplex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inert-duplex.js","sourceRoot":"","sources":["../../../src/utils/inert-duplex.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,MAAM,EAAE;YACN,CAAC,MAAM,CAAC,aAAa,CAAC;gBACpB,OAAO;oBACL,KAAK,CAAC,IAAI;wBACR,0BAA0B;wBAC1B,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;oBAC/B,CAAC;iBACF,CAAA;YACH,CAAC;SACF;QACD,IAAI,EAAE,KAAK,EAAE,MAAmB,EAAE,EAAE;YAClC,0BAA0B;YAC1B,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QAC/B,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -4,7 +4,7 @@ import type { MultihashDigest } from 'multiformats/hashes/interface';
|
|
|
4
4
|
export interface ParsedMultiaddr {
|
|
5
5
|
url: string;
|
|
6
6
|
certhashes: MultihashDigest[];
|
|
7
|
-
remotePeer
|
|
7
|
+
remotePeer?: PeerId;
|
|
8
8
|
}
|
|
9
9
|
export declare function parseMultiaddr(ma: Multiaddr): ParsedMultiaddr;
|
|
10
10
|
//# sourceMappingURL=parse-multiaddr.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-multiaddr.d.ts","sourceRoot":"","sources":["../../../src/utils/parse-multiaddr.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AASpE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"parse-multiaddr.d.ts","sourceRoot":"","sources":["../../../src/utils/parse-multiaddr.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AASpE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,cAAc,CAAE,EAAE,EAAE,SAAS,GAAG,eAAe,CAoC9D"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InvalidMultiaddrError } from '@libp2p/interface';
|
|
2
2
|
import { peerIdFromString } from '@libp2p/peer-id';
|
|
3
|
-
import {
|
|
3
|
+
import { protocols } from '@multiformats/multiaddr';
|
|
4
4
|
import { WebTransport } from '@multiformats/multiaddr-matcher';
|
|
5
5
|
import { bases, digest } from 'multiformats/basics';
|
|
6
6
|
// @ts-expect-error - Not easy to combine these types.
|
|
@@ -12,23 +12,17 @@ export function parseMultiaddr(ma) {
|
|
|
12
12
|
if (!WebTransport.matches(ma)) {
|
|
13
13
|
throw new InvalidMultiaddrError('Invalid multiaddr, was not a WebTransport address');
|
|
14
14
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (remotePeer == null) {
|
|
27
|
-
throw new InvalidMultiaddrError('Remote peer must be present in multiaddr');
|
|
28
|
-
}
|
|
29
|
-
const opts = getNetConfig(ma);
|
|
15
|
+
const parts = ma.stringTuples();
|
|
16
|
+
const certhashes = parts
|
|
17
|
+
.filter(([name, _]) => name === protocols('certhash').code)
|
|
18
|
+
.map(([_, value]) => decodeCerthashStr(value ?? ''));
|
|
19
|
+
// only take the first peer id in the multiaddr as it may be a relay
|
|
20
|
+
const remotePeer = parts
|
|
21
|
+
.filter(([name, _]) => name === protocols('p2p').code)
|
|
22
|
+
.map(([_, value]) => peerIdFromString(value ?? ''))[0];
|
|
23
|
+
const opts = ma.toOptions();
|
|
30
24
|
let host = opts.host;
|
|
31
|
-
if (opts.
|
|
25
|
+
if (opts.family === 6 && host?.includes(':')) {
|
|
32
26
|
/**
|
|
33
27
|
* This resolves cases where `new WebTransport()` fails to construct because of an invalid URL being passed.
|
|
34
28
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-multiaddr.js","sourceRoot":"","sources":["../../../src/utils/parse-multiaddr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"parse-multiaddr.js","sourceRoot":"","sources":["../../../src/utils/parse-multiaddr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAKnD,sDAAsD;AACtD,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;AAE3F,SAAS,iBAAiB,CAAE,CAAS;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAClD,CAAC;AAQD,MAAM,UAAU,cAAc,CAAE,EAAa;IAC3C,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,qBAAqB,CAAC,mDAAmD,CAAC,CAAA;IACtF,CAAC;IAED,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,EAAE,CAAA;IAC/B,MAAM,UAAU,GAAG,KAAK;SACrB,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAA;IAEtD,oEAAoE;IACpE,MAAM,UAAU,GAAG,KAAK;SACrB,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;SACrD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAExD,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,EAAE,CAAA;IAC3B,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IAEpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C;;;;;;;WAOG;QACH,IAAI,GAAG,IAAI,IAAI,GAAG,CAAA;IACpB,CAAC;IAED,OAAO;QACL,kCAAkC;QAClC,GAAG,EAAE,WAAW,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;QACnC,UAAU;QACV,UAAU;KACX,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/webtransport",
|
|
3
|
-
"version": "5.0.51-
|
|
3
|
+
"version": "5.0.51-87bc8d4fb",
|
|
4
4
|
"description": "JavaScript implementation of the WebTransport module that libp2p uses and that implements the interface-transport spec",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/transport-webtransport#readme",
|
|
@@ -40,35 +40,36 @@
|
|
|
40
40
|
"build": "aegir build",
|
|
41
41
|
"test": "aegir test -t browser -t webworker",
|
|
42
42
|
"test:chrome": "aegir test -t browser --cov",
|
|
43
|
-
"test:chrome-webworker": "aegir test -t webworker"
|
|
44
|
-
"test:firefox": "aegir test -t browser -- --browser firefox"
|
|
43
|
+
"test:chrome-webworker": "aegir test -t webworker"
|
|
45
44
|
},
|
|
46
45
|
"dependencies": {
|
|
47
|
-
"@libp2p
|
|
48
|
-
"@libp2p/
|
|
49
|
-
"@libp2p/peer-id": "5.1.9-
|
|
50
|
-
"@libp2p/utils": "6.7.2-
|
|
51
|
-
"@multiformats/multiaddr": "^
|
|
52
|
-
"@multiformats/multiaddr-matcher": "^
|
|
46
|
+
"@chainsafe/libp2p-noise": "^16.1.3",
|
|
47
|
+
"@libp2p/interface": "2.11.0-87bc8d4fb",
|
|
48
|
+
"@libp2p/peer-id": "5.1.9-87bc8d4fb",
|
|
49
|
+
"@libp2p/utils": "6.7.2-87bc8d4fb",
|
|
50
|
+
"@multiformats/multiaddr": "^12.4.4",
|
|
51
|
+
"@multiformats/multiaddr-matcher": "^2.0.0",
|
|
52
|
+
"it-stream-types": "^2.0.2",
|
|
53
53
|
"multiformats": "^13.3.6",
|
|
54
54
|
"progress-events": "^1.0.1",
|
|
55
|
-
"race-signal": "^
|
|
55
|
+
"race-signal": "^1.1.3",
|
|
56
56
|
"uint8arraylist": "^2.4.8",
|
|
57
57
|
"uint8arrays": "^5.1.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@libp2p/crypto": "5.1.8-
|
|
61
|
-
"@libp2p/daemon-client": "9.0.
|
|
62
|
-
"@libp2p/logger": "5.2.0-
|
|
63
|
-
"@libp2p/ping": "2.0.37-
|
|
60
|
+
"@libp2p/crypto": "5.1.8-87bc8d4fb",
|
|
61
|
+
"@libp2p/daemon-client": "^9.0.6",
|
|
62
|
+
"@libp2p/logger": "5.2.0-87bc8d4fb",
|
|
63
|
+
"@libp2p/ping": "2.0.37-87bc8d4fb",
|
|
64
64
|
"@noble/hashes": "^1.8.0",
|
|
65
|
-
"aegir": "^47.0.
|
|
66
|
-
"execa": "^9.
|
|
65
|
+
"aegir": "^47.0.14",
|
|
66
|
+
"execa": "^9.5.3",
|
|
67
67
|
"go-libp2p": "^1.6.0",
|
|
68
|
-
"it-
|
|
69
|
-
"
|
|
68
|
+
"it-map": "^3.1.3",
|
|
69
|
+
"it-to-buffer": "^4.0.9",
|
|
70
|
+
"libp2p": "2.10.0-87bc8d4fb",
|
|
70
71
|
"p-defer": "^4.0.1",
|
|
71
|
-
"p-
|
|
72
|
+
"p-wait-for": "^5.0.2",
|
|
72
73
|
"sinon-ts": "^2.0.0"
|
|
73
74
|
},
|
|
74
75
|
"browser": {
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MAX_INBOUND_STREAMS = 1_000
|
package/src/index.ts
CHANGED
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebTransport](https://www.w3.org/TR/webtransport/).
|
|
5
5
|
*
|
|
6
|
+
* >
|
|
6
7
|
* > ⚠️ **Note**
|
|
7
8
|
* >
|
|
8
9
|
* > This WebTransport implementation currently only allows dialing to other nodes. It does not yet allow listening for incoming dials. This feature requires QUIC support to land in Node JS first.
|
|
9
10
|
* >
|
|
10
11
|
* > QUIC support in Node JS is actively being worked on. You can keep an eye on the progress by watching the [related issues on the Node JS issue tracker](https://github.com/nodejs/node/labels/quic)
|
|
12
|
+
* >
|
|
11
13
|
*
|
|
12
14
|
* @example
|
|
13
15
|
*
|
|
14
16
|
* ```TypeScript
|
|
15
17
|
* import { createLibp2p } from 'libp2p'
|
|
16
18
|
* import { webTransport } from '@libp2p/webtransport'
|
|
17
|
-
* import { noise } from '@libp2p
|
|
19
|
+
* import { noise } from '@chainsafe/libp2p-noise'
|
|
18
20
|
*
|
|
19
21
|
* const node = await createLibp2p({
|
|
20
22
|
* transports: [
|
|
@@ -27,21 +29,24 @@
|
|
|
27
29
|
* ```
|
|
28
30
|
*/
|
|
29
31
|
|
|
32
|
+
import { noise } from '@chainsafe/libp2p-noise'
|
|
30
33
|
import { InvalidCryptoExchangeError, InvalidParametersError, serviceCapabilities, transportSymbol } from '@libp2p/interface'
|
|
31
|
-
import { noise } from '@libp2p/noise'
|
|
32
34
|
import { WebTransport as WebTransportMatcher } from '@multiformats/multiaddr-matcher'
|
|
33
35
|
import { CustomProgressEvent } from 'progress-events'
|
|
36
|
+
import { raceSignal } from 'race-signal'
|
|
37
|
+
import { MAX_INBOUND_STREAMS } from './constants.js'
|
|
34
38
|
import createListener from './listener.js'
|
|
35
39
|
import { webtransportMuxer } from './muxer.js'
|
|
36
|
-
import {
|
|
40
|
+
import { inertDuplex } from './utils/inert-duplex.js'
|
|
37
41
|
import { isSubset } from './utils/is-subset.js'
|
|
38
42
|
import { parseMultiaddr } from './utils/parse-multiaddr.js'
|
|
39
|
-
import { WebTransportMessageStream } from './utils/webtransport-message-stream.ts'
|
|
40
43
|
import WebTransport from './webtransport.js'
|
|
41
44
|
import type { Upgrader, Transport, CreateListenerOptions, DialTransportOptions, Listener, ComponentLogger, Logger, Connection, MultiaddrConnection, CounterGroup, Metrics, PeerId, OutboundConnectionUpgradeEvents, PrivateKey } from '@libp2p/interface'
|
|
42
45
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
46
|
+
import type { Source } from 'it-stream-types'
|
|
43
47
|
import type { MultihashDigest } from 'multiformats/hashes/interface'
|
|
44
48
|
import type { ProgressEvent } from 'progress-events'
|
|
49
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
52
|
* PEM format server certificate and private key
|
|
@@ -58,6 +63,7 @@ interface WebTransportSessionCleanup {
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
export interface WebTransportInit {
|
|
66
|
+
maxInboundStreams?: number
|
|
61
67
|
certificates?: WebTransportCertificate[]
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -82,7 +88,6 @@ export type WebTransportDialEvents =
|
|
|
82
88
|
|
|
83
89
|
interface AuthenticateWebTransportOptions extends DialTransportOptions<WebTransportDialEvents> {
|
|
84
90
|
wt: WebTransport
|
|
85
|
-
maConn: MultiaddrConnection
|
|
86
91
|
remotePeer?: PeerId
|
|
87
92
|
certhashes: Array<MultihashDigest<number>>
|
|
88
93
|
}
|
|
@@ -98,6 +103,7 @@ class WebTransportTransport implements Transport<WebTransportDialEvents> {
|
|
|
98
103
|
this.components = components
|
|
99
104
|
this.config = {
|
|
100
105
|
...init,
|
|
106
|
+
maxInboundStreams: init.maxInboundStreams ?? MAX_INBOUND_STREAMS,
|
|
101
107
|
certificates: init.certificates ?? []
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -196,32 +202,36 @@ class WebTransportTransport implements Transport<WebTransportDialEvents> {
|
|
|
196
202
|
cleanUpWTSession('remote_close')
|
|
197
203
|
})
|
|
198
204
|
|
|
199
|
-
this.
|
|
200
|
-
|
|
201
|
-
maConn = toMultiaddrConnection({
|
|
202
|
-
remoteAddr: ma,
|
|
203
|
-
cleanUpWTSession,
|
|
204
|
-
direction: 'outbound',
|
|
205
|
-
log: this.components.logger.forComponent('libp2p:webtransport:connection')
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
authenticated = await this.authenticateWebTransport({
|
|
209
|
-
wt,
|
|
210
|
-
maConn,
|
|
211
|
-
remotePeer,
|
|
212
|
-
certhashes,
|
|
213
|
-
...options
|
|
214
|
-
})
|
|
205
|
+
authenticated = await raceSignal(this.authenticateWebTransport({ wt, remotePeer, certhashes, ...options }), options.signal)
|
|
215
206
|
|
|
216
207
|
if (!authenticated) {
|
|
217
208
|
throw new InvalidCryptoExchangeError('Failed to authenticate webtransport')
|
|
218
209
|
}
|
|
219
210
|
|
|
211
|
+
this.metrics?.dialerEvents.increment({ open: true })
|
|
212
|
+
|
|
213
|
+
maConn = {
|
|
214
|
+
close: async () => {
|
|
215
|
+
this.log('closing webtransport')
|
|
216
|
+
cleanUpWTSession('close')
|
|
217
|
+
},
|
|
218
|
+
abort: (err: Error) => {
|
|
219
|
+
this.log('aborting webtransport due to passed err', err)
|
|
220
|
+
cleanUpWTSession('abort')
|
|
221
|
+
},
|
|
222
|
+
remoteAddr: ma,
|
|
223
|
+
timeline: {
|
|
224
|
+
open: Date.now()
|
|
225
|
+
},
|
|
226
|
+
log: this.components.logger.forComponent('libp2p:webtransport:maconn'),
|
|
227
|
+
// This connection is never used directly since webtransport supports native streams.
|
|
228
|
+
...inertDuplex()
|
|
229
|
+
}
|
|
230
|
+
|
|
220
231
|
return await options.upgrader.upgradeOutbound(maConn, {
|
|
221
232
|
...options,
|
|
222
233
|
skipEncryption: true,
|
|
223
|
-
|
|
224
|
-
muxerFactory: webtransportMuxer(wt),
|
|
234
|
+
muxerFactory: webtransportMuxer(wt, wt.incomingBidirectionalStreams.getReader(), this.log, this.config),
|
|
225
235
|
skipProtection: true
|
|
226
236
|
})
|
|
227
237
|
} catch (err: any) {
|
|
@@ -243,34 +253,61 @@ class WebTransportTransport implements Transport<WebTransportDialEvents> {
|
|
|
243
253
|
}
|
|
244
254
|
}
|
|
245
255
|
|
|
246
|
-
async authenticateWebTransport ({ wt,
|
|
256
|
+
async authenticateWebTransport ({ wt, remotePeer, certhashes, onProgress, signal }: AuthenticateWebTransportOptions): Promise<boolean> {
|
|
257
|
+
signal?.throwIfAborted()
|
|
258
|
+
|
|
247
259
|
onProgress?.(new CustomProgressEvent('webtransport:open-authentication-stream'))
|
|
248
260
|
const stream = await wt.createBidirectionalStream()
|
|
249
|
-
|
|
261
|
+
const writer = stream.writable.getWriter()
|
|
262
|
+
const reader = stream.readable.getReader()
|
|
250
263
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
264
|
+
const duplex = {
|
|
265
|
+
source: (async function * () {
|
|
266
|
+
while (true) {
|
|
267
|
+
const val = await reader.read()
|
|
268
|
+
|
|
269
|
+
if (val.value != null) {
|
|
270
|
+
yield val.value
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (val.done) {
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
})(),
|
|
278
|
+
sink: async (source: Source<Uint8Array | Uint8ArrayList>) => {
|
|
279
|
+
for await (const chunk of source) {
|
|
280
|
+
await raceSignal(writer.ready, signal)
|
|
281
|
+
|
|
282
|
+
const buf = chunk instanceof Uint8Array ? chunk : chunk.subarray()
|
|
283
|
+
|
|
284
|
+
writer.write(buf).catch(err => {
|
|
285
|
+
this.log.error('could not write chunk during authentication of WebTransport stream', err)
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
255
290
|
|
|
256
291
|
const n = noise()(this.components)
|
|
257
292
|
|
|
258
293
|
onProgress?.(new CustomProgressEvent('webtransport:secure-outbound-connection'))
|
|
259
|
-
const { remoteExtensions } = await n.secureOutbound(
|
|
294
|
+
const { remoteExtensions } = await n.secureOutbound(duplex, {
|
|
260
295
|
signal,
|
|
261
296
|
remotePeer,
|
|
262
297
|
skipStreamMuxerNegotiation: true
|
|
263
298
|
})
|
|
264
299
|
|
|
265
300
|
onProgress?.(new CustomProgressEvent('webtransport:close-authentication-stream'))
|
|
266
|
-
|
|
267
301
|
// We're done with this authentication stream
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
writer.close().catch((err: Error) => {
|
|
303
|
+
this.log.error(`Failed to close authentication stream writer: ${err.message}`)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
reader.cancel().catch((err: Error) => {
|
|
307
|
+
this.log.error(`Failed to close authentication stream reader: ${err.message}`)
|
|
270
308
|
})
|
|
271
309
|
|
|
272
|
-
// Verify the certhashes we used when dialing are a subset of the certhashes
|
|
273
|
-
// relayed by the remote peer
|
|
310
|
+
// Verify the certhashes we used when dialing are a subset of the certhashes relayed by the remote peer
|
|
274
311
|
if (!isSubset(remoteExtensions?.webtransportCerthashes ?? [], certhashes.map(ch => ch.bytes))) {
|
|
275
312
|
throw new InvalidParametersError("Our certhashes are not a subset of the remote's reported certhashes")
|
|
276
313
|
}
|
|
@@ -281,7 +318,8 @@ class WebTransportTransport implements Transport<WebTransportDialEvents> {
|
|
|
281
318
|
createListener (options: CreateListenerOptions): Listener {
|
|
282
319
|
return createListener(this.components, {
|
|
283
320
|
...options,
|
|
284
|
-
certificates: this.config.certificates
|
|
321
|
+
certificates: this.config.certificates,
|
|
322
|
+
maxInboundStreams: this.config.maxInboundStreams
|
|
285
323
|
})
|
|
286
324
|
}
|
|
287
325
|
|
package/src/listener.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface WebTransportListenerInit extends CreateListenerOptions {
|
|
|
11
11
|
handler?(conn: Connection): void
|
|
12
12
|
upgrader: Upgrader
|
|
13
13
|
certificates?: WebTransportCertificate[]
|
|
14
|
+
maxInboundStreams?: number
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export default function createListener (components: WebTransportListenerComponents, options: WebTransportListenerInit): Listener {
|