@dxos/network-manager 0.6.12-staging.e11e696 → 0.6.12
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/lib/browser/{chunk-YOKKEU6T.mjs → chunk-XYSYUN63.mjs} +998 -1192
- package/dist/lib/browser/chunk-XYSYUN63.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +19 -10
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +27 -18
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-7ZWQLO5T.cjs → chunk-4YAYC7WN.cjs} +1166 -1233
- package/dist/lib/node/chunk-4YAYC7WN.cjs.map +7 -0
- package/dist/lib/node/index.cjs +37 -27
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +29 -20
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/types/src/signal/integration.test.d.ts +2 -0
- package/dist/types/src/signal/integration.test.d.ts.map +1 -0
- package/dist/types/src/signal/swarm-messenger.test.d.ts +2 -0
- package/dist/types/src/signal/swarm-messenger.test.d.ts.map +1 -0
- package/dist/types/src/swarm/connection.d.ts.map +1 -1
- package/dist/types/src/swarm/swarm.d.ts +1 -1
- package/dist/types/src/testing/test-builder.d.ts +2 -2
- package/dist/types/src/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
- package/dist/types/src/tests/property-test-suite.d.ts.map +1 -1
- package/dist/types/src/tests/tcp-transport.test.d.ts +2 -0
- package/dist/types/src/tests/tcp-transport.test.d.ts.map +1 -0
- package/dist/types/src/tests/utils.d.ts.map +1 -1
- package/dist/types/src/transport/index.d.ts +5 -1
- package/dist/types/src/transport/index.d.ts.map +1 -1
- package/dist/types/src/transport/libdatachannel-transport.d.ts +42 -0
- package/dist/types/src/transport/libdatachannel-transport.d.ts.map +1 -0
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts +2 -0
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts.map +1 -0
- package/dist/types/src/transport/memory-transport.d.ts +2 -2
- package/dist/types/src/transport/memory-transport.d.ts.map +1 -1
- package/dist/types/src/transport/memory-transport.test.d.ts +2 -0
- package/dist/types/src/transport/memory-transport.test.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts +2 -0
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts +2 -0
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts.map +1 -0
- package/dist/types/src/transport/{webrtc/rtc-transport-proxy.d.ts → simplepeer-transport-proxy.d.ts} +12 -10
- package/dist/types/src/transport/simplepeer-transport-proxy.d.ts.map +1 -0
- package/dist/types/src/transport/{webrtc/rtc-transport-service.d.ts → simplepeer-transport-service.d.ts} +7 -9
- package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-transport.d.ts +36 -0
- package/dist/types/src/transport/simplepeer-transport.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-transport.test.d.ts +2 -0
- package/dist/types/src/transport/simplepeer-transport.test.d.ts.map +1 -0
- package/dist/types/src/transport/{tcp/tcp-transport.browser.d.ts → tcp-transport.browser.d.ts} +3 -3
- package/dist/types/src/transport/tcp-transport.browser.d.ts.map +1 -0
- package/dist/types/src/transport/{tcp/tcp-transport.d.ts → tcp-transport.d.ts} +3 -3
- package/dist/types/src/transport/tcp-transport.d.ts.map +1 -0
- package/dist/types/src/transport/transport.d.ts +6 -7
- package/dist/types/src/transport/transport.d.ts.map +1 -1
- package/dist/types/src/transport/webrtc.d.ts +6 -0
- package/dist/types/src/transport/webrtc.d.ts.map +1 -0
- package/package.json +30 -53
- package/src/globals.d.ts +7 -0
- package/src/signal/ice.test.ts +3 -1
- package/src/signal/{integration.node.test.ts → integration.test.ts} +15 -9
- package/src/signal/{swarm-messenger.node.test.ts → swarm-messenger.test.ts} +23 -13
- package/src/swarm/connection-limiter.test.ts +6 -3
- package/src/swarm/connection.test.ts +38 -63
- package/src/swarm/connection.ts +5 -5
- package/src/swarm/swarm.test.ts +11 -9
- package/src/swarm/swarm.ts +1 -1
- package/src/testing/test-builder.ts +28 -12
- package/src/tests/basic-test-suite.ts +33 -34
- package/src/tests/memory-transport.test.ts +42 -40
- package/src/tests/property-test-suite.ts +22 -21
- package/src/tests/tcp-transport.test.ts +67 -0
- package/src/tests/utils.ts +2 -3
- package/src/tests/webrtc-transport.test.ts +9 -9
- package/src/transport/index.ts +5 -1
- package/src/transport/libdatachannel-transport.test.ts +100 -0
- package/src/transport/libdatachannel-transport.ts +376 -0
- package/src/transport/memory-transport.test.ts +74 -0
- package/src/transport/memory-transport.ts +0 -2
- package/src/transport/simplepeer-simple-peer.ts +26 -0
- package/src/transport/simplepeer-transport-proxy-test.ts +181 -0
- package/src/transport/simplepeer-transport-proxy.ts +246 -0
- package/src/transport/simplepeer-transport-service.ts +160 -0
- package/src/transport/simplepeer-transport.test.ts +61 -0
- package/src/transport/simplepeer-transport.ts +250 -0
- package/src/transport/{tcp/tcp-transport.browser.ts → tcp-transport.browser.ts} +3 -7
- package/src/transport/{tcp/tcp-transport.ts → tcp-transport.ts} +1 -3
- package/src/transport/transport.ts +7 -8
- package/src/transport/webrtc.ts +15 -0
- package/src/typings.d.ts +2 -8
- package/dist/lib/browser/chunk-GW3YM55A.mjs +0 -14
- package/dist/lib/browser/chunk-GW3YM55A.mjs.map +0 -7
- package/dist/lib/browser/chunk-YOKKEU6T.mjs.map +0 -7
- package/dist/lib/browser/transport/tcp/index.mjs +0 -39
- package/dist/lib/browser/transport/tcp/index.mjs.map +0 -7
- package/dist/lib/node/chunk-7ZWQLO5T.cjs.map +0 -7
- package/dist/lib/node/transport/tcp/index.cjs +0 -191
- package/dist/lib/node/transport/tcp/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-4VO725JT.mjs +0 -4383
- package/dist/lib/node-esm/chunk-4VO725JT.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -50
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -280
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/transport/tcp/index.mjs +0 -159
- package/dist/lib/node-esm/transport/tcp/index.mjs.map +0 -7
- package/dist/types/src/signal/integration.node.test.d.ts +0 -2
- package/dist/types/src/signal/integration.node.test.d.ts.map +0 -1
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts +0 -2
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts.map +0 -1
- package/dist/types/src/tests/tcp-transport.node.test.d.ts +0 -2
- package/dist/types/src/tests/tcp-transport.node.test.d.ts.map +0 -1
- package/dist/types/src/transport/tcp/index.d.ts +0 -2
- package/dist/types/src/transport/tcp/index.d.ts.map +0 -1
- package/dist/types/src/transport/tcp/tcp-transport.browser.d.ts.map +0 -1
- package/dist/types/src/transport/tcp/tcp-transport.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/index.d.ts +0 -4
- package/dist/types/src/transport/webrtc/index.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts +0 -14
- package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts +0 -68
- package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts +0 -33
- package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts +0 -2
- package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts +0 -4
- package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts +0 -2
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-service.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts +0 -4
- package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts +0 -2
- package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/test-utils.d.ts +0 -5
- package/dist/types/src/transport/webrtc/test-utils.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc/utils.d.ts +0 -3
- package/dist/types/src/transport/webrtc/utils.d.ts.map +0 -1
- package/src/tests/tcp-transport.node.test.ts +0 -65
- package/src/transport/tcp/index.ts +0 -5
- package/src/transport/webrtc/index.ts +0 -7
- package/src/transport/webrtc/rtc-connection-factory.ts +0 -82
- package/src/transport/webrtc/rtc-peer-connection.ts +0 -472
- package/src/transport/webrtc/rtc-transport-channel.test.ts +0 -176
- package/src/transport/webrtc/rtc-transport-channel.ts +0 -195
- package/src/transport/webrtc/rtc-transport-factory.ts +0 -28
- package/src/transport/webrtc/rtc-transport-proxy.test.ts +0 -413
- package/src/transport/webrtc/rtc-transport-proxy.ts +0 -264
- package/src/transport/webrtc/rtc-transport-service.ts +0 -192
- package/src/transport/webrtc/rtc-transport-stats.ts +0 -67
- package/src/transport/webrtc/rtc-transport.test.ts +0 -198
- package/src/transport/webrtc/test-utils.ts +0 -22
- package/src/transport/webrtc/utils.ts +0 -36
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Duplex } from 'node:stream';
|
|
6
|
-
|
|
7
|
-
import { Event as AsyncEvent } from '@dxos/async';
|
|
8
|
-
import { Resource } from '@dxos/context';
|
|
9
|
-
import { ErrorStream } from '@dxos/debug';
|
|
10
|
-
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import { log } from '@dxos/log';
|
|
12
|
-
import { ConnectivityError } from '@dxos/protocols';
|
|
13
|
-
import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
14
|
-
|
|
15
|
-
import { type RtcPeerConnection } from './rtc-peer-connection';
|
|
16
|
-
import { createRtcTransportStats, describeSelectedRemoteCandidate } from './rtc-transport-stats';
|
|
17
|
-
import { type Transport, type TransportOptions, type TransportStats } from '../transport';
|
|
18
|
-
|
|
19
|
-
// https://viblast.com/blog/2015/2/5/webrtc-data-channel-message-size
|
|
20
|
-
const MAX_MESSAGE_SIZE = 64 * 1024;
|
|
21
|
-
// The default Readable stream buffer size: https://nodejs.org/api/stream.html#implementing-a-readable-stream
|
|
22
|
-
const MAX_BUFFERED_AMOUNT = 64 * 1024;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* A WebRTC connection data channel.
|
|
26
|
-
* Manages a WebRTC connection to a remote peer using an abstract signalling mechanism.
|
|
27
|
-
*/
|
|
28
|
-
export class RtcTransportChannel extends Resource implements Transport {
|
|
29
|
-
public readonly closed = new AsyncEvent();
|
|
30
|
-
public readonly connected = new AsyncEvent();
|
|
31
|
-
public readonly errors = new ErrorStream();
|
|
32
|
-
|
|
33
|
-
private _channel: RTCDataChannel | undefined;
|
|
34
|
-
private _stream: Duplex | undefined;
|
|
35
|
-
private _streamDataFlushedCallback: PendingStreamFlushedCallback | null = null;
|
|
36
|
-
private _isChannelCreationInProgress = false;
|
|
37
|
-
|
|
38
|
-
constructor(
|
|
39
|
-
private readonly _connection: RtcPeerConnection,
|
|
40
|
-
private readonly _options: TransportOptions,
|
|
41
|
-
) {
|
|
42
|
-
super();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public get isRtcChannelCreationInProgress() {
|
|
46
|
-
return this._isChannelCreationInProgress;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public onConnectionError(error: Error) {
|
|
50
|
-
if (this.isOpen) {
|
|
51
|
-
this.errors.raise(error);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
protected override async _open() {
|
|
56
|
-
invariant(!this._isChannelCreationInProgress);
|
|
57
|
-
this._isChannelCreationInProgress = true;
|
|
58
|
-
this._connection
|
|
59
|
-
.createDataChannel(this._options.topic)
|
|
60
|
-
.then((channel) => {
|
|
61
|
-
if (this.isOpen) {
|
|
62
|
-
this._channel = channel;
|
|
63
|
-
this._initChannel(this._channel);
|
|
64
|
-
} else {
|
|
65
|
-
this._safeCloseChannel(channel);
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
.catch((err) => {
|
|
69
|
-
if (this.isOpen) {
|
|
70
|
-
this.errors.raise(new ConnectivityError(`Failed to create a channel: ${err?.message ?? 'unknown reason.'}`));
|
|
71
|
-
}
|
|
72
|
-
})
|
|
73
|
-
.finally(() => {
|
|
74
|
-
this._isChannelCreationInProgress = false;
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
protected override async _close() {
|
|
79
|
-
if (this._channel) {
|
|
80
|
-
this._safeCloseChannel(this._channel);
|
|
81
|
-
this._channel = undefined;
|
|
82
|
-
this._stream = undefined;
|
|
83
|
-
}
|
|
84
|
-
this.closed.emit();
|
|
85
|
-
|
|
86
|
-
log('closed');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private _initChannel(channel: RTCDataChannel) {
|
|
90
|
-
Object.assign<RTCDataChannel, Partial<RTCDataChannel>>(channel, {
|
|
91
|
-
onopen: () => {
|
|
92
|
-
if (!this.isOpen) {
|
|
93
|
-
log.warn('channel opened in a closed transport', { topic: this._options.topic });
|
|
94
|
-
this._safeCloseChannel(channel);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
log('onopen');
|
|
99
|
-
const duplex = new Duplex({
|
|
100
|
-
read: () => {},
|
|
101
|
-
write: (chunk, encoding, callback) => {
|
|
102
|
-
return this._handleChannelWrite(chunk, callback);
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
duplex.pipe(this._options.stream).pipe(duplex);
|
|
106
|
-
this._stream = duplex;
|
|
107
|
-
this.connected.emit();
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
onclose: async () => {
|
|
111
|
-
log('onclose');
|
|
112
|
-
await this.close();
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
onmessage: (event: MessageEvent) => {
|
|
116
|
-
if (!this._stream) {
|
|
117
|
-
log.warn('ignoring message on a closed channel');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let data = event.data;
|
|
122
|
-
if (data instanceof ArrayBuffer) {
|
|
123
|
-
data = Buffer.from(data);
|
|
124
|
-
}
|
|
125
|
-
this._stream.push(data);
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
onerror: (event: Event & any) => {
|
|
129
|
-
if (this.isOpen) {
|
|
130
|
-
const err = event.error instanceof Error ? event.error : new Error(`Datachannel error: ${event.type}.`);
|
|
131
|
-
this.errors.raise(err);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
onbufferedamountlow: () => {
|
|
136
|
-
const cb = this._streamDataFlushedCallback;
|
|
137
|
-
this._streamDataFlushedCallback = null;
|
|
138
|
-
cb?.();
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private async _handleChannelWrite(chunk: any, callback: PendingStreamFlushedCallback) {
|
|
144
|
-
if (!this._channel) {
|
|
145
|
-
log.warn('writing to a channel after a connection was closed');
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (chunk.length > MAX_MESSAGE_SIZE) {
|
|
150
|
-
const error = new Error(`Message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}.`);
|
|
151
|
-
this.errors.raise(error);
|
|
152
|
-
callback();
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
this._channel.send(chunk);
|
|
158
|
-
} catch (err: any) {
|
|
159
|
-
this.errors.raise(err);
|
|
160
|
-
callback();
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
|
|
165
|
-
if (this._streamDataFlushedCallback !== null) {
|
|
166
|
-
log.error('consumer trying to write before we are ready for more data');
|
|
167
|
-
}
|
|
168
|
-
this._streamDataFlushedCallback = callback;
|
|
169
|
-
} else {
|
|
170
|
-
callback();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private _safeCloseChannel(channel: RTCDataChannel) {
|
|
175
|
-
try {
|
|
176
|
-
channel.close();
|
|
177
|
-
} catch (error: any) {
|
|
178
|
-
log.catch(error);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
public onSignal(signal: Signal): Promise<void> {
|
|
183
|
-
return this._connection.onSignal(signal);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async getDetails(): Promise<string> {
|
|
187
|
-
return describeSelectedRemoteCandidate(this._connection.currentConnection);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async getStats(): Promise<TransportStats> {
|
|
191
|
-
return createRtcTransportStats(this._connection.currentConnection, this._options.topic);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
type PendingStreamFlushedCallback = () => void;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { getRtcConnectionFactory } from './rtc-connection-factory';
|
|
6
|
-
import { RtcPeerConnection } from './rtc-peer-connection';
|
|
7
|
-
import type { IceProvider } from '../../signal';
|
|
8
|
-
import type { TransportFactory } from '../transport';
|
|
9
|
-
|
|
10
|
-
export const createRtcTransportFactory = (
|
|
11
|
-
webrtcConfig?: RTCConfiguration,
|
|
12
|
-
iceProvider?: IceProvider,
|
|
13
|
-
): TransportFactory => {
|
|
14
|
-
const connectionFactory = getRtcConnectionFactory();
|
|
15
|
-
return {
|
|
16
|
-
createTransport: (options) => {
|
|
17
|
-
// TODO(yaroslav): sendSignal is scoped to a swarm, RtcConnections can be cached if it's scoped to a peer
|
|
18
|
-
const connection = new RtcPeerConnection(connectionFactory, {
|
|
19
|
-
ownPeerKey: options.ownPeerKey,
|
|
20
|
-
remotePeerKey: options.remotePeerKey,
|
|
21
|
-
sendSignal: options.sendSignal,
|
|
22
|
-
webrtcConfig,
|
|
23
|
-
iceProvider,
|
|
24
|
-
});
|
|
25
|
-
return connection.createTransportChannel(options);
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
};
|
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2020 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Duplex } from 'stream';
|
|
6
|
-
import { onTestFinished, describe, test, expect } from 'vitest';
|
|
7
|
-
|
|
8
|
-
import { Event as AsyncEvent, TestStream, Trigger, sleep } from '@dxos/async';
|
|
9
|
-
import { ErrorStream } from '@dxos/debug';
|
|
10
|
-
import { PublicKey } from '@dxos/keys';
|
|
11
|
-
import { schema } from '@dxos/protocols/proto';
|
|
12
|
-
import { type BridgeService } from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
13
|
-
import { createLinkedPorts, createProtoRpcPeer, type RpcPort } from '@dxos/rpc';
|
|
14
|
-
|
|
15
|
-
import { RtcTransportProxy } from './rtc-transport-proxy';
|
|
16
|
-
import { RtcTransportService } from './rtc-transport-service';
|
|
17
|
-
import { handleChannelErrors } from './test-utils';
|
|
18
|
-
import { type Transport, type TransportFactory, type TransportOptions, type TransportStats } from '../transport';
|
|
19
|
-
|
|
20
|
-
describe('RtcPeerTransportProxy', () => {
|
|
21
|
-
test('open and close', async () => {
|
|
22
|
-
const { proxy } = await setupProxy();
|
|
23
|
-
await proxy.open();
|
|
24
|
-
const wait = proxy.closed.waitForCount(1);
|
|
25
|
-
expect(proxy.isOpen).toBeTruthy();
|
|
26
|
-
await proxy.close();
|
|
27
|
-
await wait;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('transport open failure closes proxy', async () => {
|
|
31
|
-
const mockTransport = createMockTransport({
|
|
32
|
-
open: async () => {
|
|
33
|
-
await sleep(20);
|
|
34
|
-
throw new Error();
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
38
|
-
const errors = handleChannelErrors(peer.proxy);
|
|
39
|
-
await peer.proxy.open();
|
|
40
|
-
await sleep(20);
|
|
41
|
-
expect(peer.proxy.isOpen).toBeFalsy();
|
|
42
|
-
await errors.expectErrorRaised();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('transport error before connected closes proxy', async () => {
|
|
46
|
-
const mockTransport = createMockTransport();
|
|
47
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
48
|
-
const errors = handleChannelErrors(peer.proxy);
|
|
49
|
-
await peer.proxy.open();
|
|
50
|
-
mockTransport.transport.errors.raise(new Error());
|
|
51
|
-
await sleep(20);
|
|
52
|
-
expect(peer.proxy.isOpen).toBeFalsy();
|
|
53
|
-
await errors.expectErrorRaised();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('transport close closes proxy', async () => {
|
|
57
|
-
const mockTransport = createMockTransport();
|
|
58
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
59
|
-
await peer.proxy.open();
|
|
60
|
-
expect(peer.proxy.isOpen).toBeTruthy();
|
|
61
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
62
|
-
await closeAndWaitProxy(peer, mockTransport);
|
|
63
|
-
expect(peer.proxy.isOpen).toBeFalsy();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('transport connected after proxy is closed is ignored', async () => {
|
|
67
|
-
const mockTransport = createMockTransport();
|
|
68
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
69
|
-
await peer.proxy.open();
|
|
70
|
-
expect(peer.proxy.isOpen).toBeTruthy();
|
|
71
|
-
await peer.proxy.close();
|
|
72
|
-
|
|
73
|
-
let connected = false;
|
|
74
|
-
peer.proxy.connected.on(() => {
|
|
75
|
-
connected = true;
|
|
76
|
-
});
|
|
77
|
-
mockTransport.transport.connected.emit();
|
|
78
|
-
await sleep(20);
|
|
79
|
-
|
|
80
|
-
expect(connected).toBeFalsy();
|
|
81
|
-
peer.proxy.errors.assertNoUnhandledErrors();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('transport error after connection passed to proxy raises an error', async () => {
|
|
85
|
-
const mockTransport = createMockTransport();
|
|
86
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
87
|
-
const errors = handleChannelErrors(peer.proxy);
|
|
88
|
-
await peer.proxy.open();
|
|
89
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
90
|
-
mockTransport.transport.errors.raise(new Error());
|
|
91
|
-
await sleep(20);
|
|
92
|
-
expect(peer.proxy.isOpen).toBeFalsy();
|
|
93
|
-
await errors.expectErrorRaised();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('transport error raised after close is ignored', async () => {
|
|
97
|
-
const mockTransport = createMockTransport();
|
|
98
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
99
|
-
await peer.proxy.open();
|
|
100
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
101
|
-
await sleep(20);
|
|
102
|
-
await closeAndWaitProxy(peer, mockTransport);
|
|
103
|
-
mockTransport.transport.errors.raise(new Error());
|
|
104
|
-
await sleep(20);
|
|
105
|
-
expect(peer.proxy.isOpen).toBeFalsy();
|
|
106
|
-
peer.proxy.errors.assertNoUnhandledErrors();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('ice candidate signal failure tolerated', async () => {
|
|
110
|
-
const mockTransport = createMockTransport();
|
|
111
|
-
const failed = new Trigger();
|
|
112
|
-
const peer = await setupProxy(
|
|
113
|
-
{
|
|
114
|
-
sendSignal: () => {
|
|
115
|
-
failed.wake();
|
|
116
|
-
throw new Error();
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
mockTransport.factory,
|
|
120
|
-
);
|
|
121
|
-
await peer.proxy.open();
|
|
122
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
123
|
-
await mockTransport.sendSignalFromTransport({ payload: { data: { type: 'candidate' } } });
|
|
124
|
-
await failed.wait();
|
|
125
|
-
|
|
126
|
-
await sleep(20);
|
|
127
|
-
expect(peer.proxy.isOpen).toBeTruthy();
|
|
128
|
-
peer.proxy.errors.assertNoUnhandledErrors();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('error raised when fails to send an offer/answer', async () => {
|
|
132
|
-
for (const type of ['offer', 'answer']) {
|
|
133
|
-
const mockTransport = createMockTransport();
|
|
134
|
-
const failed = new Trigger();
|
|
135
|
-
const peer = await setupProxy(
|
|
136
|
-
{
|
|
137
|
-
sendSignal: () => {
|
|
138
|
-
failed.wake();
|
|
139
|
-
throw new Error();
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
mockTransport.factory,
|
|
143
|
-
);
|
|
144
|
-
await peer.proxy.open();
|
|
145
|
-
const errors = handleChannelErrors(peer.proxy);
|
|
146
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
147
|
-
await mockTransport.sendSignalFromTransport({ payload: { data: { type } } });
|
|
148
|
-
await failed.wait();
|
|
149
|
-
|
|
150
|
-
await sleep(20);
|
|
151
|
-
await errors.expectErrorRaised();
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('error raised when transport fails to handle a signal', async () => {
|
|
156
|
-
const failed = new Trigger();
|
|
157
|
-
const mockTransport = createMockTransport({
|
|
158
|
-
onSignal: async (): Promise<void> => {
|
|
159
|
-
await sleep(20);
|
|
160
|
-
failed.wake();
|
|
161
|
-
throw new Error();
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
165
|
-
await peer.proxy.open();
|
|
166
|
-
const errors = handleChannelErrors(peer.proxy);
|
|
167
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
168
|
-
await peer.proxy.onSignal({ payload: { data: { type: 'offer' } } });
|
|
169
|
-
await failed.wait();
|
|
170
|
-
|
|
171
|
-
await sleep(20);
|
|
172
|
-
await errors.expectErrorRaised();
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('transport close error tolerated', async () => {
|
|
176
|
-
const failed = new Trigger();
|
|
177
|
-
const mockTransport = createMockTransport({
|
|
178
|
-
close: async () => {
|
|
179
|
-
await sleep(20);
|
|
180
|
-
failed.wake();
|
|
181
|
-
throw new Error();
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
185
|
-
await peer.proxy.open();
|
|
186
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
187
|
-
await sleep(20);
|
|
188
|
-
await peer.proxy.close();
|
|
189
|
-
await failed.wait();
|
|
190
|
-
|
|
191
|
-
peer.proxy.errors.assertNoUnhandledErrors();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test('transport data push after proxy close is ignored', async () => {
|
|
195
|
-
const mockTransport = createMockTransport();
|
|
196
|
-
const peer = await setupProxy({}, mockTransport.factory);
|
|
197
|
-
await peer.proxy.open();
|
|
198
|
-
const messageParts = [Buffer.from('hello,'), Buffer.from('world!')];
|
|
199
|
-
await connectAndWaitProxy(peer, mockTransport);
|
|
200
|
-
mockTransport.stream.push(messageParts[0]);
|
|
201
|
-
mockTransport.stream.push(messageParts[1]);
|
|
202
|
-
await sleep(20);
|
|
203
|
-
await peer.stream.assertReceivedAsync(Buffer.concat(messageParts));
|
|
204
|
-
await peer.proxy.close();
|
|
205
|
-
await sleep(20);
|
|
206
|
-
mockTransport.stream.push(Buffer.from('!!!'));
|
|
207
|
-
await sleep(20);
|
|
208
|
-
|
|
209
|
-
await peer.stream.assertReceivedAsync(Buffer.concat(messageParts));
|
|
210
|
-
peer.proxy.errors.assertNoUnhandledErrors();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('RtcPeerConnection', () => {
|
|
214
|
-
test('establish connection and send data through with protocol', async () => {
|
|
215
|
-
const peer1 = await setupProxy({
|
|
216
|
-
sendSignal: async (signal) => {
|
|
217
|
-
await peer2.proxy.onSignal(signal);
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
await peer1.proxy.open();
|
|
221
|
-
assertNoErrorsAfterTest(peer1);
|
|
222
|
-
|
|
223
|
-
const peer2 = await setupProxy({
|
|
224
|
-
ownPeerKey: peer1.options.remotePeerKey,
|
|
225
|
-
remotePeerKey: peer1.options.ownPeerKey,
|
|
226
|
-
topic: peer1.options.topic,
|
|
227
|
-
sendSignal: async (signal) => {
|
|
228
|
-
await peer1.proxy.onSignal(signal);
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
await peer2.proxy.open();
|
|
232
|
-
assertNoErrorsAfterTest(peer2);
|
|
233
|
-
|
|
234
|
-
await TestStream.assertConnectivity(peer1.stream, peer2.stream, { timeout: 1500 });
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('establish connection and send data through with protocol with multiplexing', async () => {
|
|
238
|
-
const [port1, port2] = createLinkedPorts();
|
|
239
|
-
await createService(port1);
|
|
240
|
-
const rpcClient = await createClient(port2);
|
|
241
|
-
|
|
242
|
-
const proxy1 = await createProxy(rpcClient, {
|
|
243
|
-
sendSignal: async (signal) => {
|
|
244
|
-
await proxy2.proxy.onSignal(signal);
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
assertNoErrorsAfterTest(proxy1);
|
|
248
|
-
|
|
249
|
-
const proxy2 = await createProxy(rpcClient, {
|
|
250
|
-
ownPeerKey: proxy1.options.remotePeerKey,
|
|
251
|
-
remotePeerKey: proxy1.options.ownPeerKey,
|
|
252
|
-
topic: proxy1.options.topic,
|
|
253
|
-
sendSignal: async (signal) => {
|
|
254
|
-
await proxy1.proxy.onSignal(signal);
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
assertNoErrorsAfterTest(proxy2);
|
|
258
|
-
|
|
259
|
-
await proxy1.proxy.open();
|
|
260
|
-
await proxy2.proxy.open();
|
|
261
|
-
|
|
262
|
-
await TestStream.assertConnectivity(proxy1.stream, proxy2.stream, { timeout: 1500 });
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const setupProxy = async (overrides?: Partial<TransportOptions>, transportFactory?: TransportFactory) => {
|
|
267
|
-
const [port1, port2] = createLinkedPorts();
|
|
268
|
-
const service = await createService(port1, transportFactory);
|
|
269
|
-
const rpcClient = await createClient(port2);
|
|
270
|
-
return { service, ...(await createProxy(rpcClient, overrides)) };
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const createProxy = async (
|
|
274
|
-
client: { rpc: { BridgeService: BridgeService } },
|
|
275
|
-
overrides?: Partial<TransportOptions>,
|
|
276
|
-
) => {
|
|
277
|
-
const stream = (overrides?.stream as TestStream) ?? new TestStream();
|
|
278
|
-
const options = createTransportOptions({ stream, ...overrides });
|
|
279
|
-
const proxy = new RtcTransportProxy({ ...options, bridgeService: client.rpc.BridgeService });
|
|
280
|
-
onTestFinished(async () => {
|
|
281
|
-
await proxy.close();
|
|
282
|
-
});
|
|
283
|
-
return { proxy, options, stream };
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const createService = async (port: RpcPort, transportFactory?: TransportFactory) => {
|
|
287
|
-
const rtcTransportService = new RtcTransportService(undefined, undefined, transportFactory);
|
|
288
|
-
const service = createProtoRpcPeer({
|
|
289
|
-
requested: {},
|
|
290
|
-
exposed: {
|
|
291
|
-
BridgeService: schema.getService('dxos.mesh.bridge.BridgeService'),
|
|
292
|
-
},
|
|
293
|
-
handlers: { BridgeService: rtcTransportService },
|
|
294
|
-
port,
|
|
295
|
-
noHandshake: true,
|
|
296
|
-
encodingOptions: {
|
|
297
|
-
preserveAny: true,
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
await service.open();
|
|
301
|
-
onTestFinished(async () => {
|
|
302
|
-
expect(rtcTransportService.hasOpenTransports()).toBeFalsy();
|
|
303
|
-
await service.close();
|
|
304
|
-
});
|
|
305
|
-
return service;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const createClient = async (port: RpcPort) => {
|
|
309
|
-
const rpcClient = createProtoRpcPeer({
|
|
310
|
-
requested: {
|
|
311
|
-
BridgeService: schema.getService('dxos.mesh.bridge.BridgeService'),
|
|
312
|
-
},
|
|
313
|
-
port,
|
|
314
|
-
noHandshake: true,
|
|
315
|
-
encodingOptions: {
|
|
316
|
-
preserveAny: true,
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
await rpcClient.open();
|
|
320
|
-
onTestFinished(async () => {
|
|
321
|
-
await rpcClient.close();
|
|
322
|
-
});
|
|
323
|
-
return rpcClient;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
const assertNoErrorsAfterTest = (args: { proxy: Transport }) => {
|
|
327
|
-
onTestFinished(() => {
|
|
328
|
-
args.proxy.errors.assertNoUnhandledErrors();
|
|
329
|
-
});
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
const closeAndWaitProxy = (peer: { proxy: RtcTransportProxy }, mockTransport: { transport: MockTransport }) => {
|
|
333
|
-
const waitForConnected = peer.proxy.closed.waitForCount(1);
|
|
334
|
-
mockTransport.transport.closed.emit();
|
|
335
|
-
return waitForConnected;
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const connectAndWaitProxy = (peer: { proxy: RtcTransportProxy }, mockTransport: { transport: MockTransport }) => {
|
|
339
|
-
const waitForConnected = peer.proxy.connected.waitForCount(1);
|
|
340
|
-
mockTransport.transport.connected.emit();
|
|
341
|
-
return waitForConnected;
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
const createTransportOptions = (options: Partial<TransportOptions>): TransportOptions => {
|
|
346
|
-
return {
|
|
347
|
-
initiator: false,
|
|
348
|
-
stream: new Duplex(),
|
|
349
|
-
sendSignal: async () => {},
|
|
350
|
-
remotePeerKey: PublicKey.random().toHex(),
|
|
351
|
-
ownPeerKey: PublicKey.random().toHex(),
|
|
352
|
-
topic: PublicKey.random().toHex(),
|
|
353
|
-
...options,
|
|
354
|
-
};
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const createMockTransport = (delegate?: Partial<Transport>) => {
|
|
358
|
-
const transport = new MockTransport(delegate);
|
|
359
|
-
const receivedMessages: string[] = [];
|
|
360
|
-
const stream = new Duplex({
|
|
361
|
-
read: () => {},
|
|
362
|
-
write: (chunk: any, _: BufferEncoding, callback: () => void) => {
|
|
363
|
-
receivedMessages.push(Buffer.from(chunk).toString());
|
|
364
|
-
callback();
|
|
365
|
-
},
|
|
366
|
-
});
|
|
367
|
-
let sendSignal: any | undefined;
|
|
368
|
-
return {
|
|
369
|
-
receivedMessages,
|
|
370
|
-
transport,
|
|
371
|
-
stream,
|
|
372
|
-
sendSignalFromTransport: async (signal: any) => {
|
|
373
|
-
sendSignal(signal);
|
|
374
|
-
},
|
|
375
|
-
factory: {
|
|
376
|
-
createTransport: (options: TransportOptions): Transport => {
|
|
377
|
-
sendSignal = options.sendSignal;
|
|
378
|
-
stream.pipe(options.stream).pipe(stream);
|
|
379
|
-
return transport;
|
|
380
|
-
},
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
class MockTransport implements Transport {
|
|
386
|
-
public readonly closed = new AsyncEvent();
|
|
387
|
-
public readonly connected = new AsyncEvent();
|
|
388
|
-
public readonly errors = new ErrorStream();
|
|
389
|
-
|
|
390
|
-
constructor(private readonly _delegate: Partial<Transport> = {}) {}
|
|
391
|
-
|
|
392
|
-
public async open(): Promise<this> {
|
|
393
|
-
await this._delegate.open?.();
|
|
394
|
-
return this;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
public async close(): Promise<this> {
|
|
398
|
-
await this._delegate.close?.();
|
|
399
|
-
return this;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
public async onSignal(signal: any): Promise<void> {
|
|
403
|
-
return this._delegate.onSignal?.(signal);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
public async getStats(): Promise<TransportStats> {
|
|
407
|
-
return {} as any;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
public async getDetails(): Promise<string> {
|
|
411
|
-
return '';
|
|
412
|
-
}
|
|
413
|
-
}
|