node-datachannel 0.10.1 → 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.
Files changed (38) hide show
  1. package/.github/workflows/build-win.yml +3 -3
  2. package/.gitmodules +3 -0
  3. package/CMakeLists.txt +1 -1
  4. package/README.md +18 -4
  5. package/examples/client-server/client-benchmark.js +7 -9
  6. package/examples/client-server/client-periodic.js +7 -9
  7. package/examples/client-server/client.js +7 -9
  8. package/examples/client-server/package-lock.json +0 -27
  9. package/examples/client-server/package.json +0 -1
  10. package/examples/client-server/signaling-server.js +21 -10
  11. package/examples/electron-demo/README.md +1 -1
  12. package/examples/websocket/websocket-client.js +20 -0
  13. package/examples/websocket/websocket-server.js +22 -0
  14. package/lib/index.cjs +52 -9
  15. package/lib/index.js +22 -15
  16. package/lib/node-datachannel.js +7 -0
  17. package/lib/websocket-server.js +34 -0
  18. package/package.json +4 -4
  19. package/polyfill/Events.js +5 -3
  20. package/polyfill/Exception.js +23 -0
  21. package/polyfill/README.md +4 -0
  22. package/polyfill/RTCDataChannel.js +19 -11
  23. package/polyfill/RTCPeerConnection.js +148 -41
  24. package/polyfill/index.cjs +252 -59
  25. package/src/peer-connection-wrapper.cpp +11 -3
  26. package/src/web-socket-server-wrapper.cpp +23 -23
  27. package/src/web-socket-server-wrapper.h +8 -6
  28. package/src/web-socket-wrapper.cpp +7 -1
  29. package/test/connectivity.js +1 -23
  30. package/test/wpt-tests/README.md +46 -0
  31. package/test/wpt-tests/chrome-failed-tests.js +42 -0
  32. package/test/wpt-tests/index.js +96 -0
  33. package/test/wpt-tests/last-test-results.md +205 -0
  34. package/test/wpt-tests/package-lock.json +1712 -0
  35. package/test/wpt-tests/package.json +19 -0
  36. package/test/wpt-tests/wpt-test-list.js +137 -0
  37. package/test/wpt-tests/wpt.js +131 -0
  38. package/test/wpt.js +0 -50
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var DOMException$1 = 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
- this.#readyState = 'closed';
65
- this.dispatchEvent(new Event('close'));
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$1(
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 new DOMException$1(
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.#readyState = 'closing';
196
- this.dispatchEvent(new Event('closing'));
197
-
225
+ this.#closeRequested = true;
198
226
  this.#dataChannel.close();
199
227
  }
200
228
  }
@@ -451,6 +479,11 @@ class _RTCDtlsTransport extends EventTarget {
451
479
  }
452
480
  }
453
481
 
482
+ // createRequire is native in node version >= 12
483
+ 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)));
484
+
485
+ const nodeDataChannel = require$1('../build/Release/node_datachannel.node');
486
+
454
487
  /**
455
488
  * Turns a node-datachannel DataChannel into a real Node.js stream, complete with buffering,
456
489
  * backpressure (up to a point - if the buffer fills up, messages are dropped), and
@@ -546,13 +579,65 @@ class DataChannelStream extends stream.Duplex {
546
579
  }
547
580
  }
548
581
 
549
- // createRequire is native in node version >= 12
550
- 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)));
582
+ class WebSocketServer extends events.EventEmitter {
583
+ #server;
584
+ #clients = [];
551
585
 
552
- const nodeDataChannel = require$1('../build/Release/node_datachannel.node');
586
+ constructor(options) {
587
+ super();
588
+ this.#server = new nodeDataChannel.WebSocketServer(options);
589
+
590
+ this.#server.onClient((client) => {
591
+ this.emit('client', client);
592
+ this.#clients.push(client);
593
+ });
594
+ }
595
+
596
+ port() {
597
+ return this.#server?.port() || 0;
598
+ }
599
+
600
+ stop() {
601
+ this.#clients.forEach((client) => {
602
+ client?.close();
603
+ });
604
+ this.#server?.stop();
605
+ this.#server = null;
606
+ this.removeAllListeners();
607
+ }
608
+
609
+ onClient(cb) {
610
+ if (this.#server) this.on('client', cb);
611
+ }
612
+ }
613
+
614
+ const {
615
+ initLogger,
616
+ cleanup,
617
+ preload,
618
+ setSctpSettings,
619
+ RtcpReceivingSession,
620
+ Track,
621
+ Video,
622
+ Audio,
623
+ DataChannel,
624
+ PeerConnection,
625
+ WebSocket,
626
+ } = nodeDataChannel;
553
627
 
554
628
  var NodeDataChannel = {
555
- ...nodeDataChannel,
629
+ initLogger,
630
+ cleanup,
631
+ preload,
632
+ setSctpSettings,
633
+ RtcpReceivingSession,
634
+ Track,
635
+ Video,
636
+ Audio,
637
+ DataChannel,
638
+ PeerConnection,
639
+ WebSocket,
640
+ WebSocketServer,
556
641
  DataChannelStream,
557
642
  };
558
643
 
@@ -611,10 +696,12 @@ class RTCPeerConnectionIceEvent extends Event {
611
696
  class RTCDataChannelEvent extends Event {
612
697
  #channel;
613
698
 
614
- constructor(channel) {
615
- super('datachannel');
699
+ constructor(type, eventInitDict) {
700
+ super(type);
701
+
702
+ if (type && !eventInitDict.channel) throw new TypeError('channel member is required');
616
703
 
617
- this.#channel = channel;
704
+ this.#channel = eventInitDict?.channel;
618
705
  }
619
706
 
620
707
  get channel() {
@@ -683,6 +770,7 @@ class _RTCPeerConnection extends EventTarget {
683
770
  #localOffer;
684
771
  #localAnswer;
685
772
  #dataChannels;
773
+ #dataChannelsClosed = 0;
686
774
  #config;
687
775
  #canTrickleIceCandidates;
688
776
  #sctp;
@@ -700,32 +788,98 @@ class _RTCPeerConnection extends EventTarget {
700
788
  onsignalingstatechange;
701
789
  ontrack;
702
790
 
703
- constructor(init = {}) {
791
+ _checkConfiguration(config) {
792
+ if (config && config.iceServers === undefined) config.iceServers = [];
793
+ if (config && config.iceTransportPolicy === undefined) config.iceTransportPolicy = 'all';
794
+
795
+ if (config?.iceServers === null) throw new TypeError('IceServers cannot be null');
796
+
797
+ // Check for all the properties of iceServers
798
+ if (Array.isArray(config?.iceServers)) {
799
+ for (let i = 0; i < config.iceServers.length; i++) {
800
+ if (config.iceServers[i] === null) throw new TypeError('IceServers cannot be null');
801
+ if (config.iceServers[i] === undefined) throw new TypeError('IceServers cannot be undefined');
802
+ if (Object.keys(config.iceServers[i]).length === 0) throw new TypeError('IceServers cannot be empty');
803
+
804
+ // If iceServers is string convert to array
805
+ if (typeof config.iceServers[i].urls === 'string')
806
+ config.iceServers[i].urls = [config.iceServers[i].urls];
807
+
808
+ // urls can not be empty
809
+ if (config.iceServers[i].urls?.some((url) => url == ''))
810
+ throw SyntaxError('IceServers urls cannot be empty');
811
+
812
+ // urls should match the regex "stun\:\w*|turn\:\w*|turns\:\w*"
813
+ if (
814
+ config.iceServers[i].urls?.some(
815
+ (url) => !/^(stun:[\w,\.,:]*|turn:[\w,\.,:]*|turns:[\w,\.,:]*)$/.test(url),
816
+ )
817
+ )
818
+ throw SyntaxError('IceServers urls wrong format');
819
+
820
+ // If this is a turn server check for username and credential
821
+ if (config.iceServers[i].urls?.some((url) => url.startsWith('turn'))) {
822
+ if (!config.iceServers[i].username)
823
+ throw InvalidAccessError('IceServers username cannot be null');
824
+ if (!config.iceServers[i].credential)
825
+ throw InvalidAccessError('IceServers username cannot be undefined');
826
+ }
827
+
828
+ // length of urls can not be 0
829
+ if (config.iceServers[i].urls?.length === 0)
830
+ throw SyntaxError('IceServers urls cannot be empty');
831
+ }
832
+ }
833
+
834
+ if (
835
+ config &&
836
+ config.iceTransportPolicy &&
837
+ config.iceTransportPolicy !== 'all' &&
838
+ config.iceTransportPolicy !== 'relay'
839
+ )
840
+ throw new TypeError('IceTransportPolicy must be either "all" or "relay"');
841
+ }
842
+
843
+ setConfiguration(config) {
844
+ this._checkConfiguration(config);
845
+ this.#config = config;
846
+ }
847
+
848
+ constructor(config = { iceServers: [], iceTransportPolicy: 'all' }) {
704
849
  super();
705
850
 
706
- this.#config = init;
851
+ this._checkConfiguration(config);
852
+ this.#config = config;
707
853
  this.#localOffer = createDeferredPromise();
708
854
  this.#localAnswer = createDeferredPromise();
709
855
  this.#dataChannels = new Set();
710
856
  this.#canTrickleIceCandidates = null;
711
857
 
712
- this.#peerConnection = new NodeDataChannel.PeerConnection(init?.peerIdentity ?? `peer-${getRandomString(7)}`, {
713
- ...init,
714
- iceServers:
715
- init?.iceServers
716
- ?.map((server) => {
717
- const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
718
-
719
- return urls.map((url) => {
720
- if (server.username && server.credential) {
721
- const [protocol, rest] = url.split(/:(.*)/);
722
- return `${protocol}:${server.username}:${server.credential}@${rest}`;
723
- }
724
- return url;
725
- });
726
- })
727
- .flat() ?? [],
728
- });
858
+ try {
859
+ this.#peerConnection = new NodeDataChannel.PeerConnection(
860
+ config?.peerIdentity ?? `peer-${getRandomString(7)}`,
861
+ {
862
+ ...config,
863
+ iceServers:
864
+ config?.iceServers
865
+ ?.map((server) => {
866
+ const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
867
+
868
+ return urls.map((url) => {
869
+ if (server.username && server.credential) {
870
+ const [protocol, rest] = url.split(/:(.*)/);
871
+ return `${protocol}:${server.username}:${server.credential}@${rest}`;
872
+ }
873
+ return url;
874
+ });
875
+ })
876
+ .flat() ?? [],
877
+ },
878
+ );
879
+ } catch (error) {
880
+ if (!error || !error.message) throw NotFoundError('Unknown error');
881
+ throw SyntaxError(error.message);
882
+ }
729
883
 
730
884
  // forward peerConnection events
731
885
  this.#peerConnection.onStateChange(() => {
@@ -745,9 +899,9 @@ class _RTCPeerConnection extends EventTarget {
745
899
  });
746
900
 
747
901
  this.#peerConnection.onDataChannel((channel) => {
748
- const dataChannel = new _RTCDataChannel(channel);
749
- this.#dataChannels.add(dataChannel);
750
- this.dispatchEvent(new RTCDataChannelEvent(dataChannel));
902
+ const dc = new _RTCDataChannel(channel);
903
+ this.#dataChannels.add(dc);
904
+ this.dispatchEvent(new RTCDataChannelEvent('datachannel', { channel: dc }));
751
905
  });
752
906
 
753
907
  this.#peerConnection.onLocalDescription((sdp, type) => {
@@ -821,7 +975,11 @@ class _RTCPeerConnection extends EventTarget {
821
975
  }
822
976
 
823
977
  get iceConnectionState() {
824
- return this.#peerConnection.iceState();
978
+ let state = this.#peerConnection.iceState();
979
+ // libdatachannel uses 'completed' instead of 'connected'
980
+ // see /webrtc/getstats.html
981
+ if (state == 'completed') state = 'connected';
982
+ return state;
825
983
  }
826
984
 
827
985
  get iceGatheringState() {
@@ -861,14 +1019,45 @@ class _RTCPeerConnection extends EventTarget {
861
1019
  }
862
1020
 
863
1021
  async addIceCandidate(candidate) {
864
- if (candidate == null || candidate.candidate == null) {
865
- throw new DOMException('Candidate invalid');
1022
+ if (!candidate || !candidate.candidate) {
1023
+ return;
1024
+ }
1025
+
1026
+ if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) {
1027
+ throw new TypeError('sdpMid must be set');
1028
+ }
1029
+
1030
+ if (candidate.sdpMid === undefined && candidate.sdpMLineIndex == undefined) {
1031
+ throw new TypeError('sdpMid must be set');
1032
+ }
1033
+
1034
+ // Reject if sdpMid format is not valid
1035
+ // ??
1036
+ if (candidate.sdpMid && candidate.sdpMid.length > 3) {
1037
+ // console.log(candidate.sdpMid);
1038
+ throw OperationError('Invalid sdpMid format');
1039
+ }
1040
+
1041
+ // We don't care about sdpMLineIndex, just for test
1042
+ if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) {
1043
+ throw OperationError('This is only for test case.');
866
1044
  }
867
1045
 
868
- this.#remoteCandidates.push(
869
- new _RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
870
- );
871
- this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
1046
+ try {
1047
+ this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
1048
+ this.#remoteCandidates.push(
1049
+ new _RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
1050
+ );
1051
+ } catch (error) {
1052
+ if (!error || !error.message) throw NotFoundError('Unknown error');
1053
+
1054
+ // Check error Message if contains specific message
1055
+ if (error.message.includes('remote candidate without remote description'))
1056
+ throw InvalidStateError(error.message);
1057
+ if (error.message.includes('Invalid candidate format')) throw OperationError(error.message);
1058
+
1059
+ throw NotFoundError(error.message);
1060
+ }
872
1061
  }
873
1062
 
874
1063
  addTrack(track, ...streams) {
@@ -883,6 +1072,7 @@ class _RTCPeerConnection extends EventTarget {
883
1072
  // close all channels before shutting down
884
1073
  this.#dataChannels.forEach((channel) => {
885
1074
  channel.close();
1075
+ this.#dataChannelsClosed++;
886
1076
  });
887
1077
 
888
1078
  this.#peerConnection.close();
@@ -900,6 +1090,7 @@ class _RTCPeerConnection extends EventTarget {
900
1090
  this.#dataChannels.add(dataChannel);
901
1091
  dataChannel.addEventListener('close', () => {
902
1092
  this.#dataChannels.delete(dataChannel);
1093
+ this.#dataChannelsClosed++;
903
1094
  });
904
1095
 
905
1096
  return dataChannel;
@@ -933,7 +1124,7 @@ class _RTCPeerConnection extends EventTarget {
933
1124
  let localId = 'RTCIceCandidate_' + localIdRs;
934
1125
  report.set(localId, {
935
1126
  id: localId,
936
- type: 'localcandidate',
1127
+ type: 'local-candidate',
937
1128
  timestamp: Date.now(),
938
1129
  candidateType: cp.local.type,
939
1130
  ip: cp.local.address,
@@ -944,7 +1135,7 @@ class _RTCPeerConnection extends EventTarget {
944
1135
  let remoteId = 'RTCIceCandidate_' + remoteIdRs;
945
1136
  report.set(remoteId, {
946
1137
  id: remoteId,
947
- type: 'remotecandidate',
1138
+ type: 'remote-candidate',
948
1139
  timestamp: Date.now(),
949
1140
  candidateType: cp.remote.type,
950
1141
  ip: cp.remote.address,
@@ -979,6 +1170,15 @@ class _RTCPeerConnection extends EventTarget {
979
1170
  selectedCandidatePairChanges: 1,
980
1171
  });
981
1172
 
1173
+ // peer-connection'
1174
+ report.set('P', {
1175
+ id: 'P',
1176
+ type: 'peer-connection',
1177
+ timestamp: Date.now(),
1178
+ dataChannelsOpened: this.#dataChannels.size,
1179
+ dataChannelsClosed: this.#dataChannelsClosed,
1180
+ });
1181
+
982
1182
  return resolve(report);
983
1183
  });
984
1184
  }
@@ -995,20 +1195,13 @@ class _RTCPeerConnection extends EventTarget {
995
1195
  throw new DOMException('Not implemented');
996
1196
  }
997
1197
 
998
- setConfiguration(config) {
999
- this.#config = config;
1000
- }
1001
-
1002
1198
  async setLocalDescription(description) {
1003
- if (description == null || description.type == null) {
1004
- throw new DOMException('Local description type must be set');
1005
- }
1006
-
1007
- if (description.type !== 'offer') {
1199
+ if (description?.type !== 'offer') {
1008
1200
  // any other type causes libdatachannel to throw
1009
1201
  return;
1010
1202
  }
1011
- this.#peerConnection.setLocalDescription(description.type);
1203
+
1204
+ this.#peerConnection.setLocalDescription(description?.type);
1012
1205
  }
1013
1206
 
1014
1207
  async setRemoteDescription(description) {
@@ -100,8 +100,17 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
100
100
  Napi::Array iceServers = config.Get("iceServers").As<Napi::Array>();
101
101
  for (uint32_t i = 0; i < iceServers.Length(); i++)
102
102
  {
103
- if (iceServers.Get(i).IsString())
104
- rtcConfig.iceServers.emplace_back(iceServers.Get(i).As<Napi::String>().ToString());
103
+ if (iceServers.Get(i).IsString()){
104
+ try
105
+ {
106
+ rtcConfig.iceServers.emplace_back(iceServers.Get(i).As<Napi::String>().ToString());
107
+ }
108
+ catch(std::exception &ex)
109
+ {
110
+ Napi::TypeError::New(env, "SyntaxError: IceServer config error: " + std::string(ex.what())).ThrowAsJavaScriptException();
111
+ return;
112
+ }
113
+ }
105
114
  else
106
115
  {
107
116
  if (!iceServers.Get(i).IsObject())
@@ -116,7 +125,6 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
116
125
  Napi::TypeError::New(env, "IceServer config error (hostname OR/AND port is not suitable)").ThrowAsJavaScriptException();
117
126
  return;
118
127
  }
119
-
120
128
  if (iceServer.Get("relayType").IsString() &&
121
129
  (!iceServer.Get("username").IsString() || !iceServer.Get("password").IsString()))
122
130
  {
@@ -2,6 +2,9 @@
2
2
 
3
3
  #include "plog/Log.h"
4
4
 
5
+ Napi::FunctionReference WebSocketServerWrapper::constructor;
6
+ std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;
7
+
5
8
  void WebSocketServerWrapper::StopAll()
6
9
  {
7
10
  PLOG_DEBUG << "StopAll() called";
@@ -10,9 +13,6 @@ void WebSocketServerWrapper::StopAll()
10
13
  inst->doStop();
11
14
  }
12
15
 
13
- Napi::FunctionReference WebSocketServerWrapper::constructor;
14
- std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;
15
-
16
16
  Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
17
17
  {
18
18
  Napi::HandleScope scope(env);
@@ -36,7 +36,6 @@ Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
36
36
  WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketServerWrapper>(info)
37
37
  {
38
38
  PLOG_DEBUG << "Constructor called";
39
-
40
39
  Napi::Env env = info.Env();
41
40
 
42
41
  // Create WebSocketServer without config
@@ -52,7 +51,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
52
51
  Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer without config: ") + ex.what()).ThrowAsJavaScriptException();
53
52
  return;
54
53
  }
55
-
54
+
56
55
  PLOG_DEBUG << "WebSocketServer created without config";
57
56
 
58
57
  instances.insert(this);
@@ -60,14 +59,14 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
60
59
  }
61
60
 
62
61
  // Create WebSocketServer with config
63
-
64
- Napi::Object config = info[0].As<Napi::Object>();
62
+
63
+ Napi::Object config = info[0].As<Napi::Object>();
65
64
  rtc::WebSocketServerConfiguration webSocketServerConfig;
66
65
 
67
66
  // Port
68
67
  if (config.Has("port"))
69
68
  {
70
- if (!config.Get("port").IsNumber())
69
+ if (!config.Get("port").IsNumber())
71
70
  {
72
71
  Napi::TypeError::New(info.Env(), "port must be a number").ThrowAsJavaScriptException();
73
72
  return;
@@ -78,7 +77,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
78
77
  // Enable TLS
79
78
  if (config.Has("enableTls"))
80
79
  {
81
- if (!config.Get("enableTls").IsBoolean())
80
+ if (!config.Get("enableTls").IsBoolean())
82
81
  {
83
82
  Napi::TypeError::New(info.Env(), "enableTls must be boolean").ThrowAsJavaScriptException();
84
83
  return;
@@ -89,7 +88,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
89
88
  // Certificate PEM File
90
89
  if (config.Has("certificatePemFile"))
91
90
  {
92
- if (!config.Get("certificatePemFile").IsString())
91
+ if (!config.Get("certificatePemFile").IsString())
93
92
  {
94
93
  Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException();
95
94
  return;
@@ -100,7 +99,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
100
99
  // Key PEM File
101
100
  if (config.Has("keyPemFile"))
102
101
  {
103
- if (!config.Get("keyPemFile").IsString())
102
+ if (!config.Get("keyPemFile").IsString())
104
103
  {
105
104
  Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException();
106
105
  return;
@@ -111,7 +110,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
111
110
  // Key PEM Pass
112
111
  if (config.Has("keyPemPass"))
113
112
  {
114
- if (!config.Get("keyPemPass").IsString())
113
+ if (!config.Get("keyPemPass").IsString())
115
114
  {
116
115
  Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException();
117
116
  return;
@@ -122,7 +121,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
122
121
  // Bind Address
123
122
  if (config.Has("bindAddress"))
124
123
  {
125
- if (!config.Get("bindAddress").IsString())
124
+ if (!config.Get("bindAddress").IsString())
126
125
  {
127
126
  Napi::TypeError::New(info.Env(), "bindAddress must be a string").ThrowAsJavaScriptException();
128
127
  return;
@@ -133,7 +132,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
133
132
  // Connection Timeout
134
133
  if (config.Has("connectionTimeout"))
135
134
  {
136
- if (!config.Get("connectionTimeout").IsNumber())
135
+ if (!config.Get("connectionTimeout").IsNumber())
137
136
  {
138
137
  Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException();
139
138
  return;
@@ -144,7 +143,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
144
143
  // Max Message Size
145
144
  if (config.Has("maxMessageSize"))
146
145
  {
147
- if (!config.Get("maxMessageSize").IsNumber())
146
+ if (!config.Get("maxMessageSize").IsNumber())
148
147
  {
149
148
  Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException();
150
149
  return;
@@ -163,7 +162,7 @@ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) :
163
162
  Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer: ") + ex.what()).ThrowAsJavaScriptException();
164
163
  return;
165
164
  }
166
-
165
+
167
166
  PLOG_DEBUG << "WebSocketServer created";
168
167
  instances.insert(this);
169
168
  }
@@ -179,7 +178,7 @@ void WebSocketServerWrapper::doStop()
179
178
  PLOG_DEBUG << "doStop() called";
180
179
  if (mWebSocketServerPtr)
181
180
  {
182
- PLOG_DEBUG << "Closing...";
181
+ PLOG_DEBUG << "Stopping...";
183
182
  try
184
183
  {
185
184
  mWebSocketServerPtr->stop();
@@ -187,13 +186,12 @@ void WebSocketServerWrapper::doStop()
187
186
  }
188
187
  catch (std::exception &ex)
189
188
  {
190
- std::cerr << std::string("libWebRtc error while closing WebSocketServer: ") + ex.what() << std::endl;
189
+ std::cerr << std::string("libdatachannel error while closing WebSocketServer: ") + ex.what() << std::endl;
191
190
  return;
192
191
  }
193
192
  }
194
193
 
195
194
  mOnClientCallback.reset();
196
-
197
195
  instances.erase(this);
198
196
  }
199
197
 
@@ -238,7 +236,7 @@ void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
238
236
 
239
237
  if (length < 1 || !info[0].IsFunction())
240
238
  {
241
- Napi::TypeError::New(env, "Function expected as onClient calback").ThrowAsJavaScriptException();
239
+ Napi::TypeError::New(env, "Function expected as onClient callback").ThrowAsJavaScriptException();
242
240
  return;
243
241
  }
244
242
 
@@ -246,7 +244,7 @@ void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
246
244
  mOnClientCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
247
245
 
248
246
  mWebSocketServerPtr->onClient([&](std::shared_ptr<rtc::WebSocket> ws)
249
- {
247
+ {
250
248
  PLOG_DEBUG << "onClient ws received from WebSocketServer";
251
249
  if (mOnClientCallback)
252
250
  mOnClientCallback->call([this, ws](Napi::Env env, std::vector<napi_value> &args) {
@@ -258,8 +256,10 @@ void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
258
256
  // This will run in main thread and needs to construct the
259
257
  // arguments for the call
260
258
  std::shared_ptr<rtc::WebSocket> webSocket = ws;
261
- auto instance = WebSocketWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, nullptr), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
259
+ // First argument is just a placeholder
260
+ auto instance = WebSocketWrapper::constructor.New({Napi::Boolean::New(env, false), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
262
261
  args = {instance};
263
262
  PLOG_DEBUG << "mOnClientCallback call(2)";
264
- }); });
263
+ });
264
+ });
265
265
  }