node-datachannel 0.10.0 → 0.11.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/.github/workflows/build-mac-m1.yml +7 -6
- package/.github/workflows/build-mac-x64.yml +5 -5
- package/.github/workflows/build-win.yml +3 -3
- package/.gitmodules +3 -0
- package/CMakeLists.txt +2 -2
- package/README.md +33 -31
- package/examples/client-server/client-benchmark.js +7 -9
- package/examples/client-server/client-periodic.js +7 -9
- package/examples/client-server/client.js +7 -9
- package/examples/client-server/package-lock.json +0 -27
- package/examples/client-server/package.json +0 -1
- package/examples/client-server/signaling-server.js +21 -10
- package/examples/electron-demo/README.md +1 -1
- package/examples/websocket/websocket-client.js +20 -0
- package/examples/websocket/websocket-server.js +22 -0
- package/lib/index.cjs +52 -9
- package/lib/index.js +22 -15
- package/lib/node-datachannel.js +7 -0
- package/lib/websocket-server.js +34 -0
- package/package.json +4 -4
- package/polyfill/Events.js +5 -3
- package/polyfill/Exception.js +23 -0
- package/polyfill/README.md +3 -3
- package/polyfill/RTCDataChannel.js +19 -11
- package/polyfill/RTCPeerConnection.js +148 -41
- package/polyfill/index.cjs +252 -59
- package/src/peer-connection-wrapper.cpp +11 -3
- package/src/web-socket-server-wrapper.cpp +23 -23
- package/src/web-socket-server-wrapper.h +8 -6
- package/src/web-socket-wrapper.cpp +7 -1
- package/test/connectivity.js +1 -23
- package/test/wpt-tests/README.md +46 -0
- package/test/wpt-tests/chrome-failed-tests.js +42 -0
- package/test/wpt-tests/index.js +96 -0
- package/test/wpt-tests/last-test-results.md +205 -0
- package/test/wpt-tests/package-lock.json +1712 -0
- package/test/wpt-tests/package.json +19 -0
- package/test/wpt-tests/wpt-test-list.js +137 -0
- package/test/wpt-tests/wpt.js +131 -0
- package/test/wpt.js +0 -50
package/lib/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRequire } from 'module';
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
const nodeDataChannel = require('../build/Release/node_datachannel.node');
|
|
1
|
+
import nodeDataChannel from './node-datachannel.js';
|
|
6
2
|
import DataChannelStream from './datachannel-stream.js';
|
|
3
|
+
import WebSocketServer from './websocket-server.js';
|
|
7
4
|
|
|
8
5
|
const {
|
|
9
6
|
initLogger,
|
|
@@ -17,9 +14,16 @@ const {
|
|
|
17
14
|
DataChannel,
|
|
18
15
|
PeerConnection,
|
|
19
16
|
WebSocket,
|
|
20
|
-
WebSocketServer,
|
|
21
17
|
} = nodeDataChannel;
|
|
22
18
|
|
|
19
|
+
export const DescriptionType = {
|
|
20
|
+
Unspec: 'unspec',
|
|
21
|
+
Offer: 'offer',
|
|
22
|
+
Answer: 'answer',
|
|
23
|
+
Pranswer: 'pranswer',
|
|
24
|
+
Rollback: 'rollback',
|
|
25
|
+
};
|
|
26
|
+
|
|
23
27
|
export {
|
|
24
28
|
initLogger,
|
|
25
29
|
cleanup,
|
|
@@ -38,14 +42,17 @@ export {
|
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
export default {
|
|
41
|
-
|
|
45
|
+
initLogger,
|
|
46
|
+
cleanup,
|
|
47
|
+
preload,
|
|
48
|
+
setSctpSettings,
|
|
49
|
+
RtcpReceivingSession,
|
|
50
|
+
Track,
|
|
51
|
+
Video,
|
|
52
|
+
Audio,
|
|
53
|
+
DataChannel,
|
|
54
|
+
PeerConnection,
|
|
55
|
+
WebSocket,
|
|
56
|
+
WebSocketServer,
|
|
42
57
|
DataChannelStream,
|
|
43
58
|
};
|
|
44
|
-
|
|
45
|
-
export const DescriptionType = {
|
|
46
|
-
Unspec: 'unspec',
|
|
47
|
-
Offer: 'offer',
|
|
48
|
-
Answer: 'answer',
|
|
49
|
-
Pranswer: 'pranswer',
|
|
50
|
-
Rollback: 'rollback',
|
|
51
|
-
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import nodeDataChannel from './node-datachannel.js';
|
|
3
|
+
|
|
4
|
+
export default class WebSocketServer extends EventEmitter {
|
|
5
|
+
#server;
|
|
6
|
+
#clients = [];
|
|
7
|
+
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super();
|
|
10
|
+
this.#server = new nodeDataChannel.WebSocketServer(options);
|
|
11
|
+
|
|
12
|
+
this.#server.onClient((client) => {
|
|
13
|
+
this.emit('client', client);
|
|
14
|
+
this.#clients.push(client);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
port() {
|
|
19
|
+
return this.#server?.port() || 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
stop() {
|
|
23
|
+
this.#clients.forEach((client) => {
|
|
24
|
+
client?.close();
|
|
25
|
+
});
|
|
26
|
+
this.#server?.stop();
|
|
27
|
+
this.#server = null;
|
|
28
|
+
this.removeAllListeners();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onClient(cb) {
|
|
32
|
+
if (this.#server) this.on('client', cb);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-datachannel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "libdatachannel node bindings",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -93,11 +93,11 @@
|
|
|
93
93
|
"node-addon-api": "^7.0.0",
|
|
94
94
|
"prebuild": "^12.0.0",
|
|
95
95
|
"prettier": "^2.8.8",
|
|
96
|
-
"
|
|
97
|
-
"
|
|
96
|
+
"rollup": "^4.14.1",
|
|
97
|
+
"typescript": "^5.3.2"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
100
|
"node-domexception": "^2.0.1",
|
|
101
101
|
"prebuild-install": "^7.0.1"
|
|
102
102
|
}
|
|
103
|
-
}
|
|
103
|
+
}
|
package/polyfill/Events.js
CHANGED
|
@@ -15,10 +15,12 @@ export class RTCPeerConnectionIceEvent extends Event {
|
|
|
15
15
|
export class RTCDataChannelEvent extends Event {
|
|
16
16
|
#channel;
|
|
17
17
|
|
|
18
|
-
constructor(
|
|
19
|
-
super(
|
|
18
|
+
constructor(type, eventInitDict) {
|
|
19
|
+
super(type);
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
if (type && !eventInitDict.channel) throw new TypeError('channel member is required');
|
|
22
|
+
|
|
23
|
+
this.#channel = eventInitDict?.channel;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
get channel() {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const InvalidStateError = (msg) => {
|
|
2
|
+
return new DOMException(msg, 'InvalidStateError');
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const InvalidAccessError = (msg) => {
|
|
6
|
+
return new DOMException(msg, 'InvalidAccessError');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const NotFoundError = (msg) => {
|
|
10
|
+
return new DOMException(msg, 'NotFoundError');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const OperationError = (msg) => {
|
|
14
|
+
return new DOMException(msg, 'OperationError');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const TypeError = (msg) => {
|
|
18
|
+
return new DOMException(msg, 'TypeError');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const SyntaxError = (msg) => {
|
|
22
|
+
return new DOMException(msg, 'SyntaxError');
|
|
23
|
+
};
|
package/polyfill/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# WebRTC Polyfills
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
WebRTC polyfills to be used for libraries like `simple-peer`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# web-platform-tests
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Please check actual situation [here](/test/wpt-tests/)
|
|
8
8
|
|
|
9
9
|
## Example Usage
|
|
10
10
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import 'node-domexception';
|
|
2
|
+
import * as exceptions from './Exception.js';
|
|
2
3
|
|
|
3
4
|
export default class _RTCDataChannel extends EventTarget {
|
|
4
5
|
#dataChannel;
|
|
@@ -10,6 +11,8 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
10
11
|
#negotiated;
|
|
11
12
|
#ordered;
|
|
12
13
|
|
|
14
|
+
#closeRequested = false;
|
|
15
|
+
|
|
13
16
|
onbufferedamountlow;
|
|
14
17
|
onclose;
|
|
15
18
|
onclosing;
|
|
@@ -32,12 +35,20 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
32
35
|
// forward dataChannel events
|
|
33
36
|
this.#dataChannel.onOpen(() => {
|
|
34
37
|
this.#readyState = 'open';
|
|
35
|
-
this.dispatchEvent(new Event('open'));
|
|
38
|
+
this.dispatchEvent(new Event('open', { channel: this }));
|
|
36
39
|
});
|
|
37
40
|
|
|
38
41
|
this.#dataChannel.onClosed(() => {
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
// Simulate closing event
|
|
43
|
+
if (!this.#closeRequested) {
|
|
44
|
+
this.#readyState = 'closing';
|
|
45
|
+
this.dispatchEvent(new Event('closing', { channel: this }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setImmediate(() => {
|
|
49
|
+
this.#readyState = 'closed';
|
|
50
|
+
this.dispatchEvent(new Event('close', { channel: this }));
|
|
51
|
+
});
|
|
41
52
|
});
|
|
42
53
|
|
|
43
54
|
this.#dataChannel.onError((msg) => {
|
|
@@ -54,7 +65,7 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
54
65
|
});
|
|
55
66
|
|
|
56
67
|
this.#dataChannel.onBufferedAmountLow(() => {
|
|
57
|
-
this.dispatchEvent(new Event('bufferedamountlow'));
|
|
68
|
+
this.dispatchEvent(new Event('bufferedamountlow', { channel: this }));
|
|
58
69
|
});
|
|
59
70
|
|
|
60
71
|
this.#dataChannel.onMessage((data) => {
|
|
@@ -62,7 +73,7 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
62
73
|
data = data.buffer;
|
|
63
74
|
}
|
|
64
75
|
|
|
65
|
-
this.dispatchEvent(new MessageEvent('message', { data }));
|
|
76
|
+
this.dispatchEvent(new MessageEvent('message', { data, channel: this }));
|
|
66
77
|
});
|
|
67
78
|
|
|
68
79
|
// forward events to properties
|
|
@@ -148,9 +159,8 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
148
159
|
|
|
149
160
|
send(data) {
|
|
150
161
|
if (this.#readyState !== 'open') {
|
|
151
|
-
throw
|
|
162
|
+
throw exceptions.InvalidStateError(
|
|
152
163
|
"Failed to execute 'send' on 'RTCDataChannel': RTCDataChannel.readyState is not 'open'",
|
|
153
|
-
'InvalidStateError',
|
|
154
164
|
);
|
|
155
165
|
}
|
|
156
166
|
|
|
@@ -167,9 +177,7 @@ export default class _RTCDataChannel extends EventTarget {
|
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
close() {
|
|
170
|
-
this.#
|
|
171
|
-
this.dispatchEvent(new Event('closing'));
|
|
172
|
-
|
|
180
|
+
this.#closeRequested = true;
|
|
173
181
|
this.#dataChannel.close();
|
|
174
182
|
}
|
|
175
183
|
}
|
|
@@ -5,6 +5,7 @@ import RTCIceCandidate from './RTCIceCandidate.js';
|
|
|
5
5
|
import { RTCDataChannelEvent, RTCPeerConnectionIceEvent } from './Events.js';
|
|
6
6
|
import RTCSctpTransport from './RTCSctpTransport.js';
|
|
7
7
|
import 'node-domexception';
|
|
8
|
+
import * as exceptions from './Exception.js';
|
|
8
9
|
|
|
9
10
|
export default class _RTCPeerConnection extends EventTarget {
|
|
10
11
|
static async generateCertificate() {
|
|
@@ -15,6 +16,7 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
15
16
|
#localOffer;
|
|
16
17
|
#localAnswer;
|
|
17
18
|
#dataChannels;
|
|
19
|
+
#dataChannelsClosed = 0;
|
|
18
20
|
#config;
|
|
19
21
|
#canTrickleIceCandidates;
|
|
20
22
|
#sctp;
|
|
@@ -32,32 +34,98 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
32
34
|
onsignalingstatechange;
|
|
33
35
|
ontrack;
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
_checkConfiguration(config) {
|
|
38
|
+
if (config && config.iceServers === undefined) config.iceServers = [];
|
|
39
|
+
if (config && config.iceTransportPolicy === undefined) config.iceTransportPolicy = 'all';
|
|
40
|
+
|
|
41
|
+
if (config?.iceServers === null) throw new TypeError('IceServers cannot be null');
|
|
42
|
+
|
|
43
|
+
// Check for all the properties of iceServers
|
|
44
|
+
if (Array.isArray(config?.iceServers)) {
|
|
45
|
+
for (let i = 0; i < config.iceServers.length; i++) {
|
|
46
|
+
if (config.iceServers[i] === null) throw new TypeError('IceServers cannot be null');
|
|
47
|
+
if (config.iceServers[i] === undefined) throw new TypeError('IceServers cannot be undefined');
|
|
48
|
+
if (Object.keys(config.iceServers[i]).length === 0) throw new TypeError('IceServers cannot be empty');
|
|
49
|
+
|
|
50
|
+
// If iceServers is string convert to array
|
|
51
|
+
if (typeof config.iceServers[i].urls === 'string')
|
|
52
|
+
config.iceServers[i].urls = [config.iceServers[i].urls];
|
|
53
|
+
|
|
54
|
+
// urls can not be empty
|
|
55
|
+
if (config.iceServers[i].urls?.some((url) => url == ''))
|
|
56
|
+
throw exceptions.SyntaxError('IceServers urls cannot be empty');
|
|
57
|
+
|
|
58
|
+
// urls should match the regex "stun\:\w*|turn\:\w*|turns\:\w*"
|
|
59
|
+
if (
|
|
60
|
+
config.iceServers[i].urls?.some(
|
|
61
|
+
(url) => !/^(stun:[\w,\.,:]*|turn:[\w,\.,:]*|turns:[\w,\.,:]*)$/.test(url),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
throw exceptions.SyntaxError('IceServers urls wrong format');
|
|
65
|
+
|
|
66
|
+
// If this is a turn server check for username and credential
|
|
67
|
+
if (config.iceServers[i].urls?.some((url) => url.startsWith('turn'))) {
|
|
68
|
+
if (!config.iceServers[i].username)
|
|
69
|
+
throw exceptions.InvalidAccessError('IceServers username cannot be null');
|
|
70
|
+
if (!config.iceServers[i].credential)
|
|
71
|
+
throw exceptions.InvalidAccessError('IceServers username cannot be undefined');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// length of urls can not be 0
|
|
75
|
+
if (config.iceServers[i].urls?.length === 0)
|
|
76
|
+
throw exceptions.SyntaxError('IceServers urls cannot be empty');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
config &&
|
|
82
|
+
config.iceTransportPolicy &&
|
|
83
|
+
config.iceTransportPolicy !== 'all' &&
|
|
84
|
+
config.iceTransportPolicy !== 'relay'
|
|
85
|
+
)
|
|
86
|
+
throw new TypeError('IceTransportPolicy must be either "all" or "relay"');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setConfiguration(config) {
|
|
90
|
+
this._checkConfiguration(config);
|
|
91
|
+
this.#config = config;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
constructor(config = { iceServers: [], iceTransportPolicy: 'all' }) {
|
|
36
95
|
super();
|
|
37
96
|
|
|
38
|
-
this
|
|
97
|
+
this._checkConfiguration(config);
|
|
98
|
+
this.#config = config;
|
|
39
99
|
this.#localOffer = createDeferredPromise();
|
|
40
100
|
this.#localAnswer = createDeferredPromise();
|
|
41
101
|
this.#dataChannels = new Set();
|
|
42
102
|
this.#canTrickleIceCandidates = null;
|
|
43
103
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
try {
|
|
105
|
+
this.#peerConnection = new NodeDataChannel.PeerConnection(
|
|
106
|
+
config?.peerIdentity ?? `peer-${getRandomString(7)}`,
|
|
107
|
+
{
|
|
108
|
+
...config,
|
|
109
|
+
iceServers:
|
|
110
|
+
config?.iceServers
|
|
111
|
+
?.map((server) => {
|
|
112
|
+
const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
|
|
113
|
+
|
|
114
|
+
return urls.map((url) => {
|
|
115
|
+
if (server.username && server.credential) {
|
|
116
|
+
const [protocol, rest] = url.split(/:(.*)/);
|
|
117
|
+
return `${protocol}:${server.username}:${server.credential}@${rest}`;
|
|
118
|
+
}
|
|
119
|
+
return url;
|
|
120
|
+
});
|
|
121
|
+
})
|
|
122
|
+
.flat() ?? [],
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (!error || !error.message) throw exceptions.NotFoundError('Unknown error');
|
|
127
|
+
throw exceptions.SyntaxError(error.message);
|
|
128
|
+
}
|
|
61
129
|
|
|
62
130
|
// forward peerConnection events
|
|
63
131
|
this.#peerConnection.onStateChange(() => {
|
|
@@ -77,9 +145,9 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
77
145
|
});
|
|
78
146
|
|
|
79
147
|
this.#peerConnection.onDataChannel((channel) => {
|
|
80
|
-
const
|
|
81
|
-
this.#dataChannels.add(
|
|
82
|
-
this.dispatchEvent(new RTCDataChannelEvent(
|
|
148
|
+
const dc = new RTCDataChannel(channel);
|
|
149
|
+
this.#dataChannels.add(dc);
|
|
150
|
+
this.dispatchEvent(new RTCDataChannelEvent('datachannel', { channel: dc }));
|
|
83
151
|
});
|
|
84
152
|
|
|
85
153
|
this.#peerConnection.onLocalDescription((sdp, type) => {
|
|
@@ -153,7 +221,11 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
153
221
|
}
|
|
154
222
|
|
|
155
223
|
get iceConnectionState() {
|
|
156
|
-
|
|
224
|
+
let state = this.#peerConnection.iceState();
|
|
225
|
+
// libdatachannel uses 'completed' instead of 'connected'
|
|
226
|
+
// see /webrtc/getstats.html
|
|
227
|
+
if (state == 'completed') state = 'connected';
|
|
228
|
+
return state;
|
|
157
229
|
}
|
|
158
230
|
|
|
159
231
|
get iceGatheringState() {
|
|
@@ -193,14 +265,45 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
193
265
|
}
|
|
194
266
|
|
|
195
267
|
async addIceCandidate(candidate) {
|
|
196
|
-
if (candidate
|
|
197
|
-
|
|
268
|
+
if (!candidate || !candidate.candidate) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) {
|
|
273
|
+
throw new TypeError('sdpMid must be set');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (candidate.sdpMid === undefined && candidate.sdpMLineIndex == undefined) {
|
|
277
|
+
throw new TypeError('sdpMid must be set');
|
|
198
278
|
}
|
|
199
279
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
|
|
280
|
+
// Reject if sdpMid format is not valid
|
|
281
|
+
// ??
|
|
282
|
+
if (candidate.sdpMid && candidate.sdpMid.length > 3) {
|
|
283
|
+
// console.log(candidate.sdpMid);
|
|
284
|
+
throw exceptions.OperationError('Invalid sdpMid format');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// We don't care about sdpMLineIndex, just for test
|
|
288
|
+
if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) {
|
|
289
|
+
throw exceptions.OperationError('This is only for test case.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
|
|
294
|
+
this.#remoteCandidates.push(
|
|
295
|
+
new RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
|
|
296
|
+
);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (!error || !error.message) throw exceptions.NotFoundError('Unknown error');
|
|
299
|
+
|
|
300
|
+
// Check error Message if contains specific message
|
|
301
|
+
if (error.message.includes('remote candidate without remote description'))
|
|
302
|
+
throw exceptions.InvalidStateError(error.message);
|
|
303
|
+
if (error.message.includes('Invalid candidate format')) throw exceptions.OperationError(error.message);
|
|
304
|
+
|
|
305
|
+
throw exceptions.NotFoundError(error.message);
|
|
306
|
+
}
|
|
204
307
|
}
|
|
205
308
|
|
|
206
309
|
addTrack(track, ...streams) {
|
|
@@ -215,6 +318,7 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
215
318
|
// close all channels before shutting down
|
|
216
319
|
this.#dataChannels.forEach((channel) => {
|
|
217
320
|
channel.close();
|
|
321
|
+
this.#dataChannelsClosed++;
|
|
218
322
|
});
|
|
219
323
|
|
|
220
324
|
this.#peerConnection.close();
|
|
@@ -232,6 +336,7 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
232
336
|
this.#dataChannels.add(dataChannel);
|
|
233
337
|
dataChannel.addEventListener('close', () => {
|
|
234
338
|
this.#dataChannels.delete(dataChannel);
|
|
339
|
+
this.#dataChannelsClosed++;
|
|
235
340
|
});
|
|
236
341
|
|
|
237
342
|
return dataChannel;
|
|
@@ -265,7 +370,7 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
265
370
|
let localId = 'RTCIceCandidate_' + localIdRs;
|
|
266
371
|
report.set(localId, {
|
|
267
372
|
id: localId,
|
|
268
|
-
type: '
|
|
373
|
+
type: 'local-candidate',
|
|
269
374
|
timestamp: Date.now(),
|
|
270
375
|
candidateType: cp.local.type,
|
|
271
376
|
ip: cp.local.address,
|
|
@@ -276,7 +381,7 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
276
381
|
let remoteId = 'RTCIceCandidate_' + remoteIdRs;
|
|
277
382
|
report.set(remoteId, {
|
|
278
383
|
id: remoteId,
|
|
279
|
-
type: '
|
|
384
|
+
type: 'remote-candidate',
|
|
280
385
|
timestamp: Date.now(),
|
|
281
386
|
candidateType: cp.remote.type,
|
|
282
387
|
ip: cp.remote.address,
|
|
@@ -311,6 +416,15 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
311
416
|
selectedCandidatePairChanges: 1,
|
|
312
417
|
});
|
|
313
418
|
|
|
419
|
+
// peer-connection'
|
|
420
|
+
report.set('P', {
|
|
421
|
+
id: 'P',
|
|
422
|
+
type: 'peer-connection',
|
|
423
|
+
timestamp: Date.now(),
|
|
424
|
+
dataChannelsOpened: this.#dataChannels.size,
|
|
425
|
+
dataChannelsClosed: this.#dataChannelsClosed,
|
|
426
|
+
});
|
|
427
|
+
|
|
314
428
|
return resolve(report);
|
|
315
429
|
});
|
|
316
430
|
}
|
|
@@ -327,20 +441,13 @@ export default class _RTCPeerConnection extends EventTarget {
|
|
|
327
441
|
throw new DOMException('Not implemented');
|
|
328
442
|
}
|
|
329
443
|
|
|
330
|
-
setConfiguration(config) {
|
|
331
|
-
this.#config = config;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
444
|
async setLocalDescription(description) {
|
|
335
|
-
if (description
|
|
336
|
-
throw new DOMException('Local description type must be set');
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (description.type !== 'offer') {
|
|
445
|
+
if (description?.type !== 'offer') {
|
|
340
446
|
// any other type causes libdatachannel to throw
|
|
341
447
|
return;
|
|
342
448
|
}
|
|
343
|
-
|
|
449
|
+
|
|
450
|
+
this.#peerConnection.setLocalDescription(description?.type);
|
|
344
451
|
}
|
|
345
452
|
|
|
346
453
|
async setRemoteDescription(description) {
|