node-datachannel 0.10.1 → 0.12.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-linux.yml +7 -2
- package/.github/workflows/build-win.yml +3 -3
- package/.gitmodules +3 -0
- package/CMakeLists.txt +1 -1
- package/README.md +18 -4
- package/cmake/toolchain/ci.cmake +1 -1
- 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/electron-demo/package-lock.json +316 -372
- package/examples/electron-demo/package.json +2 -1
- package/examples/electron-demo/webpack.main.config.js +10 -0
- package/examples/websocket/websocket-client.js +20 -0
- package/examples/websocket/websocket-server.js +22 -0
- package/jest.config.cjs +1 -1
- 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 +3 -3
- package/polyfill/Events.js +5 -3
- package/polyfill/Exception.js +23 -0
- package/polyfill/README.md +4 -0
- package/polyfill/RTCDataChannel.js +19 -11
- package/polyfill/RTCError.d.ts +11 -0
- package/polyfill/RTCError.js +65 -0
- package/polyfill/RTCIceCandidate.js +22 -23
- package/polyfill/RTCPeerConnection.js +157 -42
- package/polyfill/index.cjs +350 -82
- package/polyfill/index.js +3 -0
- 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 +301 -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 +139 -0
- package/test/wpt-tests/wpt.js +131 -0
- package/test/wpt.js +0 -50
package/polyfill/index.cjs
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
require('node-domexception');
|
|
6
6
|
var module$1 = require('module');
|
|
7
7
|
var stream = require('stream');
|
|
8
|
+
var events = require('events');
|
|
8
9
|
|
|
9
10
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
10
11
|
class RTCCertificate {
|
|
@@ -25,6 +26,26 @@ class RTCCertificate {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
const InvalidStateError = (msg) => {
|
|
30
|
+
return new DOMException(msg, 'InvalidStateError');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const InvalidAccessError = (msg) => {
|
|
34
|
+
return new DOMException(msg, 'InvalidAccessError');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const NotFoundError = (msg) => {
|
|
38
|
+
return new DOMException(msg, 'NotFoundError');
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const OperationError = (msg) => {
|
|
42
|
+
return new DOMException(msg, 'OperationError');
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const SyntaxError = (msg) => {
|
|
46
|
+
return new DOMException(msg, 'SyntaxError');
|
|
47
|
+
};
|
|
48
|
+
|
|
28
49
|
class _RTCDataChannel extends EventTarget {
|
|
29
50
|
#dataChannel;
|
|
30
51
|
#readyState;
|
|
@@ -35,6 +56,8 @@ class _RTCDataChannel extends EventTarget {
|
|
|
35
56
|
#negotiated;
|
|
36
57
|
#ordered;
|
|
37
58
|
|
|
59
|
+
#closeRequested = false;
|
|
60
|
+
|
|
38
61
|
onbufferedamountlow;
|
|
39
62
|
onclose;
|
|
40
63
|
onclosing;
|
|
@@ -57,12 +80,20 @@ class _RTCDataChannel extends EventTarget {
|
|
|
57
80
|
// forward dataChannel events
|
|
58
81
|
this.#dataChannel.onOpen(() => {
|
|
59
82
|
this.#readyState = 'open';
|
|
60
|
-
this.dispatchEvent(new Event('open'));
|
|
83
|
+
this.dispatchEvent(new Event('open', { channel: this }));
|
|
61
84
|
});
|
|
62
85
|
|
|
63
86
|
this.#dataChannel.onClosed(() => {
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
// Simulate closing event
|
|
88
|
+
if (!this.#closeRequested) {
|
|
89
|
+
this.#readyState = 'closing';
|
|
90
|
+
this.dispatchEvent(new Event('closing', { channel: this }));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setImmediate(() => {
|
|
94
|
+
this.#readyState = 'closed';
|
|
95
|
+
this.dispatchEvent(new Event('close', { channel: this }));
|
|
96
|
+
});
|
|
66
97
|
});
|
|
67
98
|
|
|
68
99
|
this.#dataChannel.onError((msg) => {
|
|
@@ -79,7 +110,7 @@ class _RTCDataChannel extends EventTarget {
|
|
|
79
110
|
});
|
|
80
111
|
|
|
81
112
|
this.#dataChannel.onBufferedAmountLow(() => {
|
|
82
|
-
this.dispatchEvent(new Event('bufferedamountlow'));
|
|
113
|
+
this.dispatchEvent(new Event('bufferedamountlow', { channel: this }));
|
|
83
114
|
});
|
|
84
115
|
|
|
85
116
|
this.#dataChannel.onMessage((data) => {
|
|
@@ -87,7 +118,7 @@ class _RTCDataChannel extends EventTarget {
|
|
|
87
118
|
data = data.buffer;
|
|
88
119
|
}
|
|
89
120
|
|
|
90
|
-
this.dispatchEvent(new MessageEvent('message', { data }));
|
|
121
|
+
this.dispatchEvent(new MessageEvent('message', { data, channel: this }));
|
|
91
122
|
});
|
|
92
123
|
|
|
93
124
|
// forward events to properties
|
|
@@ -113,7 +144,7 @@ class _RTCDataChannel extends EventTarget {
|
|
|
113
144
|
|
|
114
145
|
set binaryType(type) {
|
|
115
146
|
if (type !== 'blob' && type !== 'arraybuffer') {
|
|
116
|
-
throw new DOMException
|
|
147
|
+
throw new DOMException(
|
|
117
148
|
"Failed to set the 'binaryType' property on 'RTCDataChannel': Unknown binary type : " + type,
|
|
118
149
|
'TypeMismatchError',
|
|
119
150
|
);
|
|
@@ -173,9 +204,8 @@ class _RTCDataChannel extends EventTarget {
|
|
|
173
204
|
|
|
174
205
|
send(data) {
|
|
175
206
|
if (this.#readyState !== 'open') {
|
|
176
|
-
throw
|
|
207
|
+
throw InvalidStateError(
|
|
177
208
|
"Failed to execute 'send' on 'RTCDataChannel': RTCDataChannel.readyState is not 'open'",
|
|
178
|
-
'InvalidStateError',
|
|
179
209
|
);
|
|
180
210
|
}
|
|
181
211
|
|
|
@@ -192,9 +222,7 @@ class _RTCDataChannel extends EventTarget {
|
|
|
192
222
|
}
|
|
193
223
|
|
|
194
224
|
close() {
|
|
195
|
-
this.#
|
|
196
|
-
this.dispatchEvent(new Event('closing'));
|
|
197
|
-
|
|
225
|
+
this.#closeRequested = true;
|
|
198
226
|
this.#dataChannel.close();
|
|
199
227
|
}
|
|
200
228
|
}
|
|
@@ -219,41 +247,40 @@ class _RTCIceCandidate {
|
|
|
219
247
|
#tcpType;
|
|
220
248
|
#type;
|
|
221
249
|
#usernameFragment;
|
|
222
|
-
#ip;
|
|
223
250
|
|
|
224
251
|
constructor({ candidate, sdpMLineIndex, sdpMid, usernameFragment }) {
|
|
225
|
-
if (
|
|
226
|
-
throw new
|
|
227
|
-
}
|
|
252
|
+
if (sdpMLineIndex == null && sdpMid == null)
|
|
253
|
+
throw new TypeError('At least one of sdpMLineIndex or sdpMid must be specified');
|
|
228
254
|
|
|
229
|
-
this.#candidate = candidate;
|
|
230
|
-
this.#sdpMLineIndex = sdpMLineIndex
|
|
231
|
-
this.#sdpMid = sdpMid
|
|
232
|
-
this.#usernameFragment = usernameFragment
|
|
255
|
+
this.#candidate = candidate === null ? 'null' : candidate ?? '';
|
|
256
|
+
this.#sdpMLineIndex = sdpMLineIndex ?? null;
|
|
257
|
+
this.#sdpMid = sdpMid ?? null;
|
|
258
|
+
this.#usernameFragment = usernameFragment ?? null;
|
|
233
259
|
|
|
234
260
|
if (candidate) {
|
|
235
261
|
const fields = candidate.split(' ');
|
|
236
|
-
this.#foundation = fields[0];
|
|
262
|
+
this.#foundation = fields[0].replace('candidate:', ''); // remove text candidate:
|
|
237
263
|
this.#component = fields[1] == '1' ? 'rtp' : 'rtcp';
|
|
238
264
|
this.#protocol = fields[2];
|
|
239
265
|
this.#priority = parseInt(fields[3], 10);
|
|
240
|
-
this.#
|
|
266
|
+
this.#address = fields[4];
|
|
241
267
|
this.#port = parseInt(fields[5], 10);
|
|
242
268
|
this.#type = fields[7];
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
this.#tcpType = fields[7];
|
|
247
|
-
this.#type = fields[8];
|
|
248
|
-
}
|
|
269
|
+
this.#tcpType = null;
|
|
270
|
+
this.#relatedAddress = null;
|
|
271
|
+
this.#relatedPort = null;
|
|
249
272
|
|
|
250
273
|
// Parse the candidate string to extract relatedPort and relatedAddress
|
|
251
|
-
for (let i =
|
|
274
|
+
for (let i = 8; i < fields.length; i++) {
|
|
252
275
|
const field = fields[i];
|
|
253
|
-
if (field
|
|
254
|
-
this.#relatedAddress =
|
|
255
|
-
} else if (field
|
|
256
|
-
this.#relatedPort = parseInt(
|
|
276
|
+
if (field === 'raddr') {
|
|
277
|
+
this.#relatedAddress = fields[i + 1];
|
|
278
|
+
} else if (field === 'rport') {
|
|
279
|
+
this.#relatedPort = parseInt(fields[i + 1], 10);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (this.#protocol === 'tcp' && field === 'tcptype') {
|
|
283
|
+
this.#tcpType = fields[i + 1];
|
|
257
284
|
}
|
|
258
285
|
}
|
|
259
286
|
}
|
|
@@ -264,7 +291,7 @@ class _RTCIceCandidate {
|
|
|
264
291
|
}
|
|
265
292
|
|
|
266
293
|
get candidate() {
|
|
267
|
-
return this.#candidate
|
|
294
|
+
return this.#candidate;
|
|
268
295
|
}
|
|
269
296
|
|
|
270
297
|
get component() {
|
|
@@ -451,6 +478,11 @@ class _RTCDtlsTransport extends EventTarget {
|
|
|
451
478
|
}
|
|
452
479
|
}
|
|
453
480
|
|
|
481
|
+
// createRequire is native in node version >= 12
|
|
482
|
+
const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
483
|
+
|
|
484
|
+
const nodeDataChannel = require$1('../build/Release/node_datachannel.node');
|
|
485
|
+
|
|
454
486
|
/**
|
|
455
487
|
* Turns a node-datachannel DataChannel into a real Node.js stream, complete with buffering,
|
|
456
488
|
* backpressure (up to a point - if the buffer fills up, messages are dropped), and
|
|
@@ -546,13 +578,65 @@ class DataChannelStream extends stream.Duplex {
|
|
|
546
578
|
}
|
|
547
579
|
}
|
|
548
580
|
|
|
549
|
-
|
|
550
|
-
|
|
581
|
+
class WebSocketServer extends events.EventEmitter {
|
|
582
|
+
#server;
|
|
583
|
+
#clients = [];
|
|
551
584
|
|
|
552
|
-
|
|
585
|
+
constructor(options) {
|
|
586
|
+
super();
|
|
587
|
+
this.#server = new nodeDataChannel.WebSocketServer(options);
|
|
588
|
+
|
|
589
|
+
this.#server.onClient((client) => {
|
|
590
|
+
this.emit('client', client);
|
|
591
|
+
this.#clients.push(client);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
port() {
|
|
596
|
+
return this.#server?.port() || 0;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
stop() {
|
|
600
|
+
this.#clients.forEach((client) => {
|
|
601
|
+
client?.close();
|
|
602
|
+
});
|
|
603
|
+
this.#server?.stop();
|
|
604
|
+
this.#server = null;
|
|
605
|
+
this.removeAllListeners();
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
onClient(cb) {
|
|
609
|
+
if (this.#server) this.on('client', cb);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const {
|
|
614
|
+
initLogger,
|
|
615
|
+
cleanup,
|
|
616
|
+
preload,
|
|
617
|
+
setSctpSettings,
|
|
618
|
+
RtcpReceivingSession,
|
|
619
|
+
Track,
|
|
620
|
+
Video,
|
|
621
|
+
Audio,
|
|
622
|
+
DataChannel,
|
|
623
|
+
PeerConnection,
|
|
624
|
+
WebSocket,
|
|
625
|
+
} = nodeDataChannel;
|
|
553
626
|
|
|
554
627
|
var NodeDataChannel = {
|
|
555
|
-
|
|
628
|
+
initLogger,
|
|
629
|
+
cleanup,
|
|
630
|
+
preload,
|
|
631
|
+
setSctpSettings,
|
|
632
|
+
RtcpReceivingSession,
|
|
633
|
+
Track,
|
|
634
|
+
Video,
|
|
635
|
+
Audio,
|
|
636
|
+
DataChannel,
|
|
637
|
+
PeerConnection,
|
|
638
|
+
WebSocket,
|
|
639
|
+
WebSocketServer,
|
|
556
640
|
DataChannelStream,
|
|
557
641
|
};
|
|
558
642
|
|
|
@@ -611,10 +695,12 @@ class RTCPeerConnectionIceEvent extends Event {
|
|
|
611
695
|
class RTCDataChannelEvent extends Event {
|
|
612
696
|
#channel;
|
|
613
697
|
|
|
614
|
-
constructor(
|
|
615
|
-
super(
|
|
698
|
+
constructor(type, eventInitDict) {
|
|
699
|
+
super(type);
|
|
700
|
+
|
|
701
|
+
if (type && !eventInitDict.channel) throw new TypeError('channel member is required');
|
|
616
702
|
|
|
617
|
-
this.#channel = channel;
|
|
703
|
+
this.#channel = eventInitDict?.channel;
|
|
618
704
|
}
|
|
619
705
|
|
|
620
706
|
get channel() {
|
|
@@ -683,6 +769,7 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
683
769
|
#localOffer;
|
|
684
770
|
#localAnswer;
|
|
685
771
|
#dataChannels;
|
|
772
|
+
#dataChannelsClosed = 0;
|
|
686
773
|
#config;
|
|
687
774
|
#canTrickleIceCandidates;
|
|
688
775
|
#sctp;
|
|
@@ -700,32 +787,106 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
700
787
|
onsignalingstatechange;
|
|
701
788
|
ontrack;
|
|
702
789
|
|
|
703
|
-
|
|
790
|
+
_checkConfiguration(config) {
|
|
791
|
+
if (config && config.iceServers === undefined) config.iceServers = [];
|
|
792
|
+
if (config && config.iceTransportPolicy === undefined) config.iceTransportPolicy = 'all';
|
|
793
|
+
|
|
794
|
+
if (config?.iceServers === null) throw new TypeError('IceServers cannot be null');
|
|
795
|
+
|
|
796
|
+
// Check for all the properties of iceServers
|
|
797
|
+
if (Array.isArray(config?.iceServers)) {
|
|
798
|
+
for (let i = 0; i < config.iceServers.length; i++) {
|
|
799
|
+
if (config.iceServers[i] === null) throw new TypeError('IceServers cannot be null');
|
|
800
|
+
if (config.iceServers[i] === undefined) throw new TypeError('IceServers cannot be undefined');
|
|
801
|
+
if (Object.keys(config.iceServers[i]).length === 0) throw new TypeError('IceServers cannot be empty');
|
|
802
|
+
|
|
803
|
+
// If iceServers is string convert to array
|
|
804
|
+
if (typeof config.iceServers[i].urls === 'string')
|
|
805
|
+
config.iceServers[i].urls = [config.iceServers[i].urls];
|
|
806
|
+
|
|
807
|
+
// urls can not be empty
|
|
808
|
+
if (config.iceServers[i].urls?.some((url) => url == ''))
|
|
809
|
+
throw SyntaxError('IceServers urls cannot be empty');
|
|
810
|
+
|
|
811
|
+
// urls should be valid URLs and match the protocols "stun:|turn:|turns:"
|
|
812
|
+
if (
|
|
813
|
+
config.iceServers[i].urls?.some(
|
|
814
|
+
(url) => {
|
|
815
|
+
try {
|
|
816
|
+
const parsedURL = new URL(url);
|
|
817
|
+
|
|
818
|
+
return !/^(stun:|turn:|turns:)$/.test(parsedURL.protocol)
|
|
819
|
+
} catch (error) {
|
|
820
|
+
return true
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
)
|
|
824
|
+
)
|
|
825
|
+
throw SyntaxError('IceServers urls wrong format');
|
|
826
|
+
|
|
827
|
+
// If this is a turn server check for username and credential
|
|
828
|
+
if (config.iceServers[i].urls?.some((url) => url.startsWith('turn'))) {
|
|
829
|
+
if (!config.iceServers[i].username)
|
|
830
|
+
throw InvalidAccessError('IceServers username cannot be null');
|
|
831
|
+
if (!config.iceServers[i].credential)
|
|
832
|
+
throw InvalidAccessError('IceServers username cannot be undefined');
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// length of urls can not be 0
|
|
836
|
+
if (config.iceServers[i].urls?.length === 0)
|
|
837
|
+
throw SyntaxError('IceServers urls cannot be empty');
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (
|
|
842
|
+
config &&
|
|
843
|
+
config.iceTransportPolicy &&
|
|
844
|
+
config.iceTransportPolicy !== 'all' &&
|
|
845
|
+
config.iceTransportPolicy !== 'relay'
|
|
846
|
+
)
|
|
847
|
+
throw new TypeError('IceTransportPolicy must be either "all" or "relay"');
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
setConfiguration(config) {
|
|
851
|
+
this._checkConfiguration(config);
|
|
852
|
+
this.#config = config;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
constructor(config = { iceServers: [], iceTransportPolicy: 'all' }) {
|
|
704
856
|
super();
|
|
705
857
|
|
|
706
|
-
this
|
|
858
|
+
this._checkConfiguration(config);
|
|
859
|
+
this.#config = config;
|
|
707
860
|
this.#localOffer = createDeferredPromise();
|
|
708
861
|
this.#localAnswer = createDeferredPromise();
|
|
709
862
|
this.#dataChannels = new Set();
|
|
710
863
|
this.#canTrickleIceCandidates = null;
|
|
711
864
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
return
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
865
|
+
try {
|
|
866
|
+
this.#peerConnection = new NodeDataChannel.PeerConnection(
|
|
867
|
+
config?.peerIdentity ?? `peer-${getRandomString(7)}`,
|
|
868
|
+
{
|
|
869
|
+
...config,
|
|
870
|
+
iceServers:
|
|
871
|
+
config?.iceServers
|
|
872
|
+
?.map((server) => {
|
|
873
|
+
const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
|
|
874
|
+
|
|
875
|
+
return urls.map((url) => {
|
|
876
|
+
if (server.username && server.credential) {
|
|
877
|
+
const [protocol, rest] = url.split(/:(.*)/);
|
|
878
|
+
return `${protocol}:${server.username}:${server.credential}@${rest}`;
|
|
879
|
+
}
|
|
880
|
+
return url;
|
|
881
|
+
});
|
|
882
|
+
})
|
|
883
|
+
.flat() ?? [],
|
|
884
|
+
},
|
|
885
|
+
);
|
|
886
|
+
} catch (error) {
|
|
887
|
+
if (!error || !error.message) throw NotFoundError('Unknown error');
|
|
888
|
+
throw SyntaxError(error.message);
|
|
889
|
+
}
|
|
729
890
|
|
|
730
891
|
// forward peerConnection events
|
|
731
892
|
this.#peerConnection.onStateChange(() => {
|
|
@@ -745,9 +906,9 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
745
906
|
});
|
|
746
907
|
|
|
747
908
|
this.#peerConnection.onDataChannel((channel) => {
|
|
748
|
-
const
|
|
749
|
-
this.#dataChannels.add(
|
|
750
|
-
this.dispatchEvent(new RTCDataChannelEvent(
|
|
909
|
+
const dc = new _RTCDataChannel(channel);
|
|
910
|
+
this.#dataChannels.add(dc);
|
|
911
|
+
this.dispatchEvent(new RTCDataChannelEvent('datachannel', { channel: dc }));
|
|
751
912
|
});
|
|
752
913
|
|
|
753
914
|
this.#peerConnection.onLocalDescription((sdp, type) => {
|
|
@@ -821,7 +982,11 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
821
982
|
}
|
|
822
983
|
|
|
823
984
|
get iceConnectionState() {
|
|
824
|
-
|
|
985
|
+
let state = this.#peerConnection.iceState();
|
|
986
|
+
// libdatachannel uses 'completed' instead of 'connected'
|
|
987
|
+
// see /webrtc/getstats.html
|
|
988
|
+
if (state == 'completed') state = 'connected';
|
|
989
|
+
return state;
|
|
825
990
|
}
|
|
826
991
|
|
|
827
992
|
get iceGatheringState() {
|
|
@@ -861,14 +1026,45 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
861
1026
|
}
|
|
862
1027
|
|
|
863
1028
|
async addIceCandidate(candidate) {
|
|
864
|
-
if (candidate
|
|
865
|
-
|
|
1029
|
+
if (!candidate || !candidate.candidate) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) {
|
|
1034
|
+
throw new TypeError('sdpMid must be set');
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (candidate.sdpMid === undefined && candidate.sdpMLineIndex == undefined) {
|
|
1038
|
+
throw new TypeError('sdpMid must be set');
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Reject if sdpMid format is not valid
|
|
1042
|
+
// ??
|
|
1043
|
+
if (candidate.sdpMid && candidate.sdpMid.length > 3) {
|
|
1044
|
+
// console.log(candidate.sdpMid);
|
|
1045
|
+
throw OperationError('Invalid sdpMid format');
|
|
866
1046
|
}
|
|
867
1047
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1048
|
+
// We don't care about sdpMLineIndex, just for test
|
|
1049
|
+
if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) {
|
|
1050
|
+
throw OperationError('This is only for test case.');
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
try {
|
|
1054
|
+
this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
|
|
1055
|
+
this.#remoteCandidates.push(
|
|
1056
|
+
new _RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
|
|
1057
|
+
);
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
if (!error || !error.message) throw NotFoundError('Unknown error');
|
|
1060
|
+
|
|
1061
|
+
// Check error Message if contains specific message
|
|
1062
|
+
if (error.message.includes('remote candidate without remote description'))
|
|
1063
|
+
throw InvalidStateError(error.message);
|
|
1064
|
+
if (error.message.includes('Invalid candidate format')) throw OperationError(error.message);
|
|
1065
|
+
|
|
1066
|
+
throw NotFoundError(error.message);
|
|
1067
|
+
}
|
|
872
1068
|
}
|
|
873
1069
|
|
|
874
1070
|
addTrack(track, ...streams) {
|
|
@@ -883,6 +1079,7 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
883
1079
|
// close all channels before shutting down
|
|
884
1080
|
this.#dataChannels.forEach((channel) => {
|
|
885
1081
|
channel.close();
|
|
1082
|
+
this.#dataChannelsClosed++;
|
|
886
1083
|
});
|
|
887
1084
|
|
|
888
1085
|
this.#peerConnection.close();
|
|
@@ -900,6 +1097,7 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
900
1097
|
this.#dataChannels.add(dataChannel);
|
|
901
1098
|
dataChannel.addEventListener('close', () => {
|
|
902
1099
|
this.#dataChannels.delete(dataChannel);
|
|
1100
|
+
this.#dataChannelsClosed++;
|
|
903
1101
|
});
|
|
904
1102
|
|
|
905
1103
|
return dataChannel;
|
|
@@ -933,7 +1131,7 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
933
1131
|
let localId = 'RTCIceCandidate_' + localIdRs;
|
|
934
1132
|
report.set(localId, {
|
|
935
1133
|
id: localId,
|
|
936
|
-
type: '
|
|
1134
|
+
type: 'local-candidate',
|
|
937
1135
|
timestamp: Date.now(),
|
|
938
1136
|
candidateType: cp.local.type,
|
|
939
1137
|
ip: cp.local.address,
|
|
@@ -944,7 +1142,7 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
944
1142
|
let remoteId = 'RTCIceCandidate_' + remoteIdRs;
|
|
945
1143
|
report.set(remoteId, {
|
|
946
1144
|
id: remoteId,
|
|
947
|
-
type: '
|
|
1145
|
+
type: 'remote-candidate',
|
|
948
1146
|
timestamp: Date.now(),
|
|
949
1147
|
candidateType: cp.remote.type,
|
|
950
1148
|
ip: cp.remote.address,
|
|
@@ -979,6 +1177,15 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
979
1177
|
selectedCandidatePairChanges: 1,
|
|
980
1178
|
});
|
|
981
1179
|
|
|
1180
|
+
// peer-connection'
|
|
1181
|
+
report.set('P', {
|
|
1182
|
+
id: 'P',
|
|
1183
|
+
type: 'peer-connection',
|
|
1184
|
+
timestamp: Date.now(),
|
|
1185
|
+
dataChannelsOpened: this.#dataChannels.size,
|
|
1186
|
+
dataChannelsClosed: this.#dataChannelsClosed,
|
|
1187
|
+
});
|
|
1188
|
+
|
|
982
1189
|
return resolve(report);
|
|
983
1190
|
});
|
|
984
1191
|
}
|
|
@@ -995,20 +1202,13 @@ class _RTCPeerConnection extends EventTarget {
|
|
|
995
1202
|
throw new DOMException('Not implemented');
|
|
996
1203
|
}
|
|
997
1204
|
|
|
998
|
-
setConfiguration(config) {
|
|
999
|
-
this.#config = config;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
1205
|
async setLocalDescription(description) {
|
|
1003
|
-
if (description
|
|
1004
|
-
throw new DOMException('Local description type must be set');
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
if (description.type !== 'offer') {
|
|
1206
|
+
if (description?.type !== 'offer') {
|
|
1008
1207
|
// any other type causes libdatachannel to throw
|
|
1009
1208
|
return;
|
|
1010
1209
|
}
|
|
1011
|
-
|
|
1210
|
+
|
|
1211
|
+
this.#peerConnection.setLocalDescription(description?.type);
|
|
1012
1212
|
}
|
|
1013
1213
|
|
|
1014
1214
|
async setRemoteDescription(description) {
|
|
@@ -1039,6 +1239,72 @@ function getRandomString(length) {
|
|
|
1039
1239
|
.substring(2, 2 + length);
|
|
1040
1240
|
}
|
|
1041
1241
|
|
|
1242
|
+
let RTCError$1 = class RTCError extends DOMException {
|
|
1243
|
+
constructor(init, message = '') {
|
|
1244
|
+
super(message, 'OperationError');
|
|
1245
|
+
|
|
1246
|
+
if (!init || !init.errorDetail) throw new TypeError('Cannot construct RTCError, errorDetail is required');
|
|
1247
|
+
if (
|
|
1248
|
+
[
|
|
1249
|
+
'data-channel-failure',
|
|
1250
|
+
'dtls-failure',
|
|
1251
|
+
'fingerprint-failure',
|
|
1252
|
+
'hardware-encoder-error',
|
|
1253
|
+
'hardware-encoder-not-available',
|
|
1254
|
+
'sctp-failure',
|
|
1255
|
+
'sdp-syntax-error',
|
|
1256
|
+
].indexOf(init.errorDetail) === -1
|
|
1257
|
+
)
|
|
1258
|
+
throw new TypeError('Cannot construct RTCError, errorDetail is invalid');
|
|
1259
|
+
|
|
1260
|
+
this._errorDetail = init.errorDetail;
|
|
1261
|
+
this._receivedAlert = init.receivedAlert ?? null;
|
|
1262
|
+
this._sctpCauseCode = init.sctpCauseCode ?? null;
|
|
1263
|
+
this._sdpLineNumber = init.sdpLineNumber ?? null;
|
|
1264
|
+
this._sentAlert = init.sentAlert ?? null;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
get errorDetail() {
|
|
1268
|
+
return this._errorDetail;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
set errorDetail(value) {
|
|
1272
|
+
throw new TypeError('Cannot set errorDetail, it is read-only');
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
get receivedAlert() {
|
|
1276
|
+
return this._receivedAlert;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
set receivedAlert(value) {
|
|
1280
|
+
throw new TypeError('Cannot set receivedAlert, it is read-only');
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
get sctpCauseCode() {
|
|
1284
|
+
return this._sctpCauseCode;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
set sctpCauseCode(value) {
|
|
1288
|
+
throw new TypeError('Cannot set sctpCauseCode, it is read-only');
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
get sdpLineNumber() {
|
|
1292
|
+
return this._sdpLineNumber;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
set sdpLineNumber(value) {
|
|
1296
|
+
throw new TypeError('Cannot set sdpLineNumber, it is read-only');
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
get sentAlert() {
|
|
1300
|
+
return this._sentAlert;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
set sentAlert(value) {
|
|
1304
|
+
throw new TypeError('Cannot set sentAlert, it is read-only');
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1042
1308
|
var index = {
|
|
1043
1309
|
RTCCertificate,
|
|
1044
1310
|
RTCDataChannel: _RTCDataChannel,
|
|
@@ -1050,12 +1316,14 @@ var index = {
|
|
|
1050
1316
|
RTCSessionDescription: _RTCSessionDescription,
|
|
1051
1317
|
RTCDataChannelEvent,
|
|
1052
1318
|
RTCPeerConnectionIceEvent,
|
|
1319
|
+
RTCError: RTCError$1,
|
|
1053
1320
|
};
|
|
1054
1321
|
|
|
1055
1322
|
exports.RTCCertificate = RTCCertificate;
|
|
1056
1323
|
exports.RTCDataChannel = _RTCDataChannel;
|
|
1057
1324
|
exports.RTCDataChannelEvent = RTCDataChannelEvent;
|
|
1058
1325
|
exports.RTCDtlsTransport = _RTCDtlsTransport;
|
|
1326
|
+
exports.RTCError = RTCError$1;
|
|
1059
1327
|
exports.RTCIceCandidate = _RTCIceCandidate;
|
|
1060
1328
|
exports.RTCIceTransport = _RTCIceTransport;
|
|
1061
1329
|
exports.RTCPeerConnection = _RTCPeerConnection;
|
package/polyfill/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import RTCPeerConnection from './RTCPeerConnection.js';
|
|
|
7
7
|
import RTCSctpTransport from './RTCSctpTransport.js';
|
|
8
8
|
import RTCSessionDescription from './RTCSessionDescription.js';
|
|
9
9
|
import { RTCDataChannelEvent, RTCPeerConnectionIceEvent } from './Events.js';
|
|
10
|
+
import RTCError from './RTCError.js';
|
|
10
11
|
|
|
11
12
|
export {
|
|
12
13
|
RTCCertificate,
|
|
@@ -19,6 +20,7 @@ export {
|
|
|
19
20
|
RTCSessionDescription,
|
|
20
21
|
RTCDataChannelEvent,
|
|
21
22
|
RTCPeerConnectionIceEvent,
|
|
23
|
+
RTCError,
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export default {
|
|
@@ -32,4 +34,5 @@ export default {
|
|
|
32
34
|
RTCSessionDescription,
|
|
33
35
|
RTCDataChannelEvent,
|
|
34
36
|
RTCPeerConnectionIceEvent,
|
|
37
|
+
RTCError,
|
|
35
38
|
};
|