node-datachannel 0.2.4 → 0.3.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/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # http://editorconfig.org
4
+
5
+ root = true
6
+
7
+ [*]
8
+ indent_style = space
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = true
12
+ insert_final_newline = true
package/.eslintignore ADDED
@@ -0,0 +1,2 @@
1
+ node_modules
2
+ build
package/.eslintrc.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ parser: '@typescript-eslint/parser',
3
+ plugins: ['@typescript-eslint'],
4
+ extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
5
+ env: {
6
+ node: true,
7
+ },
8
+ };
@@ -0,0 +1,2 @@
1
+ # These are supported funding model platforms
2
+ open_collective: node-datachannel
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: ubuntu-20.04
15
15
  strategy:
16
16
  matrix:
17
- node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
17
+ node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
18
18
 
19
19
  steps:
20
20
  - uses: actions/checkout@v2
@@ -40,7 +40,7 @@ jobs:
40
40
 
41
41
  strategy:
42
42
  matrix:
43
- node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
43
+ node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
44
44
  steps:
45
45
  - uses: actions/checkout@v2
46
46
  - name: Prepare Cross Compile
@@ -68,7 +68,7 @@ jobs:
68
68
 
69
69
  strategy:
70
70
  matrix:
71
- node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
71
+ node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
72
72
  steps:
73
73
  - uses: actions/checkout@v2
74
74
  - name: Prepare Cross Compile
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: macos-latest
15
15
  strategy:
16
16
  matrix:
17
- node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
17
+ node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
18
18
 
19
19
  steps:
20
20
  - uses: actions/checkout@v2
@@ -11,10 +11,10 @@ env:
11
11
 
12
12
  jobs:
13
13
  build-windows-x64:
14
- runs-on: windows-latest
14
+ runs-on: windows-2019
15
15
  strategy:
16
16
  matrix:
17
- node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
17
+ node-version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
18
18
 
19
19
  steps:
20
20
  - uses: actions/checkout@v2
@@ -39,10 +39,10 @@ jobs:
39
39
  CI: true
40
40
 
41
41
  build-windows-x86:
42
- runs-on: windows-latest
42
+ runs-on: windows-2019
43
43
  strategy:
44
44
  matrix:
45
- node_version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x]
45
+ node_version: [10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x]
46
46
  node_arch:
47
47
  - x86
48
48
  steps:
package/.prettierrc.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ semi: true,
3
+ trailingComma: 'all',
4
+ singleQuote: true,
5
+ printWidth: 120,
6
+ tabWidth: 4,
7
+ };
package/CMakeLists.txt CHANGED
@@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.15)
2
2
  cmake_policy(SET CMP0091 NEW)
3
3
  project(node_datachannel VERSION 0.2.4)
4
4
 
5
+ # Workaround for https://github.com/murat-dogan/node-datachannel/issues/65
6
+ if( NODE_RUNTIMEVERSION VERSION_GREATER_EQUAL "17.0.0")
7
+ add_compile_definitions(OPENSSL_API_COMPAT=0x10100001L)
8
+ add_compile_definitions(OPENSSL_CONFIGURED_API=0x30000000L)
9
+ endif()
10
+
5
11
  include_directories(${CMAKE_JS_INC})
6
12
 
7
13
  set(CMAKE_BUILD_TYPE Release)
@@ -27,7 +33,7 @@ include(FetchContent)
27
33
  FetchContent_Declare(
28
34
  libdatachannel
29
35
  GIT_REPOSITORY https://github.com/paullouisageneau/libdatachannel.git
30
- GIT_TAG "v0.16.6"
36
+ GIT_TAG "v0.17.0"
31
37
  )
32
38
 
33
39
  option(NO_MEDIA "Disable media transport support in libdatachannel" OFF)
package/README.md CHANGED
@@ -118,7 +118,7 @@ export const enum RelayType {
118
118
 
119
119
  export interface IceServer {
120
120
  hostname: string;
121
- port: Number;
121
+ port: number;
122
122
  username?: string;
123
123
  password?: string;
124
124
  relayType?: RelayType;
@@ -271,15 +271,15 @@ Send Message as binary
271
271
 
272
272
  Query data-channel
273
273
 
274
- **bufferedAmount: () => Number**
274
+ **bufferedAmount: () => number**
275
275
 
276
276
  Get current buffered amount level
277
277
 
278
- **maxMessageSize: () => Number**
278
+ **maxMessageSize: () => number**
279
279
 
280
280
  Get max message size of the data-channel, that could be sent
281
281
 
282
- **setBufferedAmountLowThreshold: (newSize: Number) => void**
282
+ **setBufferedAmountLowThreshold: (newSize: number) => void**
283
283
 
284
284
  Set buffer level of the `onBufferedAmountLow` callback
285
285
 
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1
2
  const stream = require('stream');
2
3
 
3
4
  /**
@@ -9,12 +10,11 @@ const stream = require('stream');
9
10
  * the protocol level, and is preserved here throughout.
10
11
  */
11
12
  module.exports = class DataChannelStream extends stream.Duplex {
12
-
13
13
  constructor(rawChannel, streamOptions) {
14
14
  super({
15
15
  allowHalfOpen: false, // Default to autoclose on end().
16
16
  ...streamOptions,
17
- objectMode: true // Preserve the string/buffer distinction (WebRTC treats them differently)
17
+ objectMode: true, // Preserve the string/buffer distinction (WebRTC treats them differently)
18
18
  });
19
19
 
20
20
  this._rawChannel = rawChannel;
@@ -51,20 +51,24 @@ module.exports = class DataChannelStream extends stream.Duplex {
51
51
 
52
52
  _write(chunk, encoding, callback) {
53
53
  let sentOk;
54
- if (Buffer.isBuffer(chunk)) {
55
- sentOk = this._rawChannel.sendMessageBinary(chunk);
56
- } else if (typeof chunk === 'string') {
57
- sentOk = this._rawChannel.sendMessage(chunk);
58
- } else {
59
- const typeName = chunk.constructor.name || typeof chunk;
60
- callback(new Error(`Cannot write ${typeName} to DataChannel stream`));
61
- return;
54
+
55
+ try {
56
+ if (Buffer.isBuffer(chunk)) {
57
+ sentOk = this._rawChannel.sendMessageBinary(chunk);
58
+ } else if (typeof chunk === 'string') {
59
+ sentOk = this._rawChannel.sendMessage(chunk);
60
+ } else {
61
+ const typeName = chunk.constructor.name || typeof chunk;
62
+ throw new Error(`Cannot write ${typeName} to DataChannel stream`);
63
+ }
64
+ } catch (err) {
65
+ return callback(err);
62
66
  }
63
67
 
64
68
  if (sentOk) {
65
69
  callback(null);
66
70
  } else {
67
- callback(new Error("Failed to write to DataChannel"));
71
+ callback(new Error('Failed to write to DataChannel'));
68
72
  }
69
73
  }
70
74
 
@@ -83,4 +87,11 @@ module.exports = class DataChannelStream extends stream.Duplex {
83
87
  return this._rawChannel.getLabel();
84
88
  }
85
89
 
86
- }
90
+ get id() {
91
+ return this._rawChannel.getId();
92
+ }
93
+
94
+ get protocol() {
95
+ return this._rawChannel.getProtocol();
96
+ }
97
+ };
package/lib/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as stream from 'stream';
3
3
  export as namespace NodeDataChannel;
4
4
 
5
5
  // Enum in d.ts is tricky
6
- export type LogLevel = "Verbose" | "Debug" | "Info" | "Warning" | "Error" | "Fatal";
6
+ export type LogLevel = 'Verbose' | 'Debug' | 'Info' | 'Warning' | 'Error' | 'Fatal';
7
7
 
8
8
  // SCTP Settings
9
9
  export interface SctpSettings {
@@ -26,7 +26,7 @@ export type ProxyServerType = 'None' | 'Socks5' | 'Http';
26
26
  export interface ProxyServer {
27
27
  type: ProxyServerType;
28
28
  ip: string;
29
- port: Number;
29
+ port: number;
30
30
  username?: string;
31
31
  password?: string;
32
32
  }
@@ -34,12 +34,12 @@ export interface ProxyServer {
34
34
  export const enum RelayType {
35
35
  TurnUdp = 'TurnUdp',
36
36
  TurnTcp = 'TurnTcp',
37
- TurnTls = 'TurnTls'
37
+ TurnTls = 'TurnTls',
38
38
  }
39
39
 
40
40
  export interface IceServer {
41
41
  hostname: string;
42
- port: Number;
42
+ port: number;
43
43
  username?: string;
44
44
  password?: string;
45
45
  relayType?: RelayType;
@@ -61,11 +61,13 @@ export const enum DescriptionType {
61
61
  Offer = 'Offer',
62
62
  Answer = 'Answer',
63
63
  Pranswer = 'Pranswer',
64
- Rollback = 'Rollback'
64
+ Rollback = 'Rollback',
65
65
  }
66
66
 
67
67
  export const enum ReliabilityType {
68
- Reliable = 0, Rexmit = 1, Timed = 2
68
+ Reliable = 0,
69
+ Rexmit = 1,
70
+ Timed = 2,
69
71
  }
70
72
 
71
73
  export interface DataChannelInitConfig {
@@ -81,7 +83,7 @@ export interface DataChannelInitConfig {
81
83
  type?: ReliabilityType;
82
84
  unordered?: boolean;
83
85
  rexmit?: number;
84
- }
86
+ };
85
87
  }
86
88
 
87
89
  export interface SelectedCandidateInfo {
@@ -97,18 +99,18 @@ export const enum Direction {
97
99
  RecvOnly = 'RecvOnly',
98
100
  SendRecv = 'SendRecv',
99
101
  Inactive = 'Inactive',
100
- Unknown = 'Unknown'
102
+ Unknown = 'Unknown',
101
103
  }
102
104
 
103
105
  export class RtcpReceivingSession {
104
- requestBitrate: (bitRate: Number) => void;
106
+ requestBitrate: (bitRate: number) => void;
105
107
  requestKeyframe: () => boolean;
106
108
  }
107
109
 
108
110
  export class Audio {
109
111
  constructor(mid: string, dir: Direction);
110
- addAudioCodec: (payloadType: Number, codec: string, profile?: string) => void;
111
- addOpusCodec: (payloadType: Number, profile?: string) => string;
112
+ addAudioCodec: (payloadType: number, codec: string, profile?: string) => void;
113
+ addOpusCodec: (payloadType: number, profile?: string) => string;
112
114
 
113
115
  direction: () => Direction;
114
116
  generateSdp: (eol: string, addr: string, port: string) => string;
@@ -116,26 +118,26 @@ export class Audio {
116
118
  setDirection: (dir: Direction) => void;
117
119
  description: () => string;
118
120
  removeFormat: (fmt: string) => void;
119
- addSSRC: (ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
120
- removeSSRC: (ssrc: Number) => void;
121
- replaceSSRC: (oldSsrc: Number, ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
122
- hasSSRC: (ssrc: Number) => boolean;
123
- getSSRCs: () => Number[];
124
- getCNameForSsrc: (ssrc: Number) => string;
125
- setBitrate: (bitRate: Number) => void;
126
- getBitrate: () => Number;
127
- hasPayloadType: (payloadType: Number) => boolean;
128
- addRTXCodec: (payloadType: Number, originalPayloadType: Number, clockRate: Number) => void;
121
+ addSSRC: (ssrc: number, name?: string, msid?: string, trackID?: string) => void;
122
+ removeSSRC: (ssrc: number) => void;
123
+ replaceSSRC: (oldSsrc: number, ssrc: number, name?: string, msid?: string, trackID?: string) => void;
124
+ hasSSRC: (ssrc: number) => boolean;
125
+ getSSRCs: () => number[];
126
+ getCNameForSsrc: (ssrc: number) => string;
127
+ setBitrate: (bitRate: number) => void;
128
+ getBitrate: () => number;
129
+ hasPayloadType: (payloadType: number) => boolean;
130
+ addRTXCodec: (payloadType: number, originalPayloadType: number, clockRate: number) => void;
129
131
  addRTPMap: () => void;
130
132
  parseSdpLine: (line: string) => void;
131
133
  }
132
134
 
133
135
  export class Video {
134
136
  constructor(mid: string, dir: Direction);
135
- addVideoCodec: (payloadType: Number, codec: string, profile?: string) => void;
136
- addH264Codec: (payloadType: Number, profile?: string) => void;
137
- addVP8Codec: (payloadType: Number) => void;
138
- addVP9Codec: (payloadType: Number) => void;
137
+ addVideoCodec: (payloadType: number, codec: string, profile?: string) => void;
138
+ addH264Codec: (payloadType: number, profile?: string) => void;
139
+ addVP8Codec: (payloadType: number) => void;
140
+ addVP9Codec: (payloadType: number) => void;
139
141
 
140
142
  direction: () => Direction;
141
143
  generateSdp: (eol: string, addr: string, port: string) => string;
@@ -143,16 +145,16 @@ export class Video {
143
145
  setDirection: (dir: Direction) => void;
144
146
  description: () => string;
145
147
  removeFormat: (fmt: string) => void;
146
- addSSRC: (ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
147
- removeSSRC: (ssrc: Number) => void;
148
- replaceSSRC: (oldSsrc: Number, ssrc: Number, name?: string, msid?: string, trackID?: string) => void;
149
- hasSSRC: (ssrc: Number) => boolean;
150
- getSSRCs: () => Number[];
151
- getCNameForSsrc: (ssrc: Number) => string;
152
- setBitrate: (bitRate: Number) => void;
153
- getBitrate: () => Number;
154
- hasPayloadType: (payloadType: Number) => boolean;
155
- addRTXCodec: (payloadType: Number, originalPayloadType: Number, clockRate: Number) => void;
148
+ addSSRC: (ssrc: number, name?: string, msid?: string, trackID?: string) => void;
149
+ removeSSRC: (ssrc: number) => void;
150
+ replaceSSRC: (oldSsrc: number, ssrc: number, name?: string, msid?: string, trackID?: string) => void;
151
+ hasSSRC: (ssrc: number) => boolean;
152
+ getSSRCs: () => number[];
153
+ getCNameForSsrc: (ssrc: number) => string;
154
+ setBitrate: (bitRate: number) => void;
155
+ getBitrate: () => number;
156
+ hasPayloadType: (payloadType: number) => boolean;
157
+ addRTXCodec: (payloadType: number, originalPayloadType: number, clockRate: number) => void;
156
158
  addRTPMap: () => void;
157
159
  parseSdpLine: (line: string) => void;
158
160
  }
@@ -160,39 +162,37 @@ export class Video {
160
162
  export class Track {
161
163
  direction: () => Direction;
162
164
  mid: () => string;
165
+ type: () => string;
163
166
  close: () => void;
164
167
  sendMessage: (msg: string) => boolean;
165
168
  sendMessageBinary: (buffer: Buffer) => boolean;
166
169
  isOpen: () => boolean;
167
170
  isClosed: () => boolean;
168
- availableAmount: () => Number;
169
- bufferedAmount: () => Number;
170
- maxMessageSize: () => Number;
171
- setBufferedAmountLowThreshold: (newSize: Number) => void;
171
+ bufferedAmount: () => number;
172
+ maxMessageSize: () => number;
173
+ setBufferedAmountLowThreshold: (newSize: number) => void;
172
174
  requestKeyframe: () => boolean;
173
- setMediaHandler: (handler: RtcpReceivingSession) => void
175
+ setMediaHandler: (handler: RtcpReceivingSession) => void;
174
176
  onOpen: (cb: () => void) => void;
175
177
  onClosed: (cb: () => void) => void;
176
178
  onError: (cb: (err: string) => void) => void;
177
- onAvailable: (cb: () => void) => void;
178
- onBufferedAmountLow: (cb: () => void) => void;
179
- onMessage: (cb: (msg: string | Buffer) => void) => void;
179
+ onMessage: (cb: (msg: Buffer) => void) => void;
180
180
  }
181
181
 
182
182
  export class DataChannel {
183
183
  close: () => void;
184
184
  getLabel: () => string;
185
+ getId: () => number;
186
+ getProtocol: () => string;
185
187
  sendMessage: (msg: string) => boolean;
186
188
  sendMessageBinary: (buffer: Buffer) => boolean;
187
189
  isOpen: () => boolean;
188
- availableAmount: () => Number;
189
- bufferedAmount: () => Number;
190
- maxMessageSize: () => Number;
191
- setBufferedAmountLowThreshold: (newSize: Number) => void;
190
+ bufferedAmount: () => number;
191
+ maxMessageSize: () => number;
192
+ setBufferedAmountLowThreshold: (newSize: number) => void;
192
193
  onOpen: (cb: () => void) => void;
193
194
  onClosed: (cb: () => void) => void;
194
195
  onError: (cb: (err: string) => void) => void;
195
- onAvailable: (cb: () => void) => void;
196
196
  onBufferedAmountLow: (cb: () => void) => void;
197
197
  onMessage: (cb: (msg: string | Buffer) => void) => void;
198
198
  }
@@ -200,9 +200,9 @@ export class DataChannel {
200
200
  export class PeerConnection {
201
201
  constructor(peerName: string, config: RtcConfig);
202
202
  close: () => void;
203
- setLocalDescription: (type: DescriptionType) => void;
203
+ setLocalDescription: (type?: DescriptionType) => void;
204
204
  setRemoteDescription: (sdp: string, type: DescriptionType) => void;
205
- localDescription: () => { type: string, sdp: string };
205
+ localDescription: () => { type: string; sdp: string };
206
206
  addRemoteCandidate: (candidate: string, mid: string) => void;
207
207
  createDataChannel: (label: string, config?: DataChannelInitConfig) => DataChannel;
208
208
  addTrack: (media: Video | Audio) => Track;
@@ -216,17 +216,14 @@ export class PeerConnection {
216
216
  onSignalingStateChange: (state: (sdp: string) => void) => void;
217
217
  onGatheringStateChange: (state: (sdp: string) => void) => void;
218
218
  onDataChannel: (cb: (dc: DataChannel) => void) => void;
219
- onTrack: () => Track;
219
+ onTrack: (cb: (track: Track) => void) => void;
220
220
  bytesSent: () => number;
221
221
  bytesReceived: () => number;
222
222
  rtt: () => number;
223
- getSelectedCandidatePair: () => { local: SelectedCandidateInfo, remote: SelectedCandidateInfo } | null;
223
+ getSelectedCandidatePair: () => { local: SelectedCandidateInfo; remote: SelectedCandidateInfo } | null;
224
224
  }
225
225
 
226
226
  export class DataChannelStream extends stream.Duplex {
227
- constructor(
228
- rawChannel: DataChannel,
229
- options?: Omit<stream.DuplexOptions, 'objectMode'>
230
- );
227
+ constructor(rawChannel: DataChannel, options?: Omit<stream.DuplexOptions, 'objectMode'>);
231
228
  get label(): string;
232
- }
229
+ }
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
- const nodeDataChannel = require("../build/Release/node_datachannel.node");
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
2
+ const nodeDataChannel = require('../build/Release/node_datachannel.node');
2
3
  module.exports = nodeDataChannel;
3
4
 
4
- module.exports.DataChannelStream = require('./datachannel-stream');
5
+ module.exports.DataChannelStream = require('./datachannel-stream');
@@ -22,6 +22,7 @@
22
22
  "fetchSpec": "2.0.1"
23
23
  },
24
24
  "_requiredBy": [
25
+ "/prebuild",
25
26
  "/prebuild-install"
26
27
  ],
27
28
  "_resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
@@ -22,9 +22,10 @@
22
22
  "fetchSpec": "6.0.0"
23
23
  },
24
24
  "_requiredBy": [
25
+ "/@typescript-eslint/eslint-plugin/semver",
26
+ "/@typescript-eslint/typescript-estree/semver",
25
27
  "/jest-snapshot/semver",
26
- "/node-abi/semver",
27
- "/prebuild/node-abi/semver"
28
+ "/node-abi/semver"
28
29
  ],
29
30
  "_resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
30
31
  "_spec": "6.0.0",
@@ -70,7 +70,7 @@ module.exports = function (args, opts) {
70
70
  var o = obj;
71
71
  for (var i = 0; i < keys.length-1; i++) {
72
72
  var key = keys[i];
73
- if (key === '__proto__') return;
73
+ if (isConstructorOrProto(o, key)) return;
74
74
  if (o[key] === undefined) o[key] = {};
75
75
  if (o[key] === Object.prototype || o[key] === Number.prototype
76
76
  || o[key] === String.prototype) o[key] = {};
@@ -79,7 +79,7 @@ module.exports = function (args, opts) {
79
79
  }
80
80
 
81
81
  var key = keys[keys.length - 1];
82
- if (key === '__proto__') return;
82
+ if (isConstructorOrProto(o, key)) return;
83
83
  if (o === Object.prototype || o === Number.prototype
84
84
  || o === String.prototype) o = {};
85
85
  if (o === Array.prototype) o = [];
@@ -243,3 +243,7 @@ function isNumber (x) {
243
243
  return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
244
244
  }
245
245
 
246
+
247
+ function isConstructorOrProto (obj, key) {
248
+ return key === 'constructor' && typeof obj[key] === 'function' || key === '__proto__';
249
+ }
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "_args": [
3
3
  [
4
- "minimist@1.2.5",
4
+ "minimist@1.2.6",
5
5
  "/home/runner/work/node-datachannel/node-datachannel"
6
6
  ]
7
7
  ],
8
- "_from": "minimist@1.2.5",
9
- "_id": "minimist@1.2.5",
8
+ "_from": "minimist@1.2.6",
9
+ "_id": "minimist@1.2.6",
10
10
  "_inBundle": false,
11
- "_integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
11
+ "_integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
12
12
  "_location": "/minimist",
13
13
  "_phantomChildren": {},
14
14
  "_requested": {
15
15
  "type": "version",
16
16
  "registry": true,
17
- "raw": "minimist@1.2.5",
17
+ "raw": "minimist@1.2.6",
18
18
  "name": "minimist",
19
19
  "escapedName": "minimist",
20
- "rawSpec": "1.2.5",
20
+ "rawSpec": "1.2.6",
21
21
  "saveSpec": null,
22
- "fetchSpec": "1.2.5"
22
+ "fetchSpec": "1.2.6"
23
23
  },
24
24
  "_requiredBy": [
25
25
  "/json5",
@@ -28,8 +28,8 @@
28
28
  "/prebuild-install",
29
29
  "/rc"
30
30
  ],
31
- "_resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
32
- "_spec": "1.2.5",
31
+ "_resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
32
+ "_spec": "1.2.6",
33
33
  "_where": "/home/runner/work/node-datachannel/node-datachannel",
34
34
  "author": {
35
35
  "name": "James Halliday",
@@ -76,5 +76,5 @@
76
76
  "opera/12"
77
77
  ]
78
78
  },
79
- "version": "1.2.5"
79
+ "version": "1.2.6"
80
80
  }
@@ -34,7 +34,10 @@ $ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
34
34
  Previous versions had a prototype pollution bug that could cause privilege
35
35
  escalation in some circumstances when handling untrusted user input.
36
36
 
37
- Please use version 1.2.3 or later: https://snyk.io/vuln/SNYK-JS-MINIMIST-559764
37
+ Please use version 1.2.6 or later:
38
+
39
+ * https://security.snyk.io/vuln/SNYK-JS-MINIMIST-2429795 (version <=1.2.5)
40
+ * https://snyk.io/vuln/SNYK-JS-MINIMIST-559764 (version <=1.2.3)
38
41
 
39
42
  # methods
40
43
 
@@ -42,3 +42,19 @@ test('proto pollution (constructor)', function (t) {
42
42
  t.equal(argv.y, undefined);
43
43
  t.end();
44
44
  });
45
+
46
+ test('proto pollution (constructor function)', function (t) {
47
+ var argv = parse(['--_.concat.constructor.prototype.y', '123']);
48
+ function fnToBeTested() {}
49
+ t.equal(fnToBeTested.y, undefined);
50
+ t.equal(argv.y, undefined);
51
+ t.end();
52
+ });
53
+
54
+ // powered by snyk - https://github.com/backstage/backstage/issues/10343
55
+ test('proto pollution (constructor function) snyk', function (t) {
56
+ var argv = parse('--_.constructor.constructor.prototype.foo bar'.split(' '));
57
+ t.equal((function(){}).foo, undefined);
58
+ t.equal(argv.y, undefined);
59
+ t.end();
60
+ })
@@ -24,6 +24,7 @@
24
24
  "fetchSpec": "3.8.0"
25
25
  },
26
26
  "_requiredBy": [
27
+ "/prebuild",
27
28
  "/prebuild-install"
28
29
  ],
29
30
  "_resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-datachannel",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "libdatachannel node bindings",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -15,6 +15,7 @@
15
15
  "build": "cmake-js build",
16
16
  "_prebuild": "prebuild --backend cmake-js",
17
17
  "clean": "cmake-js clean",
18
+ "lint": "eslint lib/**/*",
18
19
  "test": "jest"
19
20
  },
20
21
  "repository": {
@@ -46,18 +47,25 @@
46
47
  },
47
48
  "homepage": "https://github.com/murat-dogan/node-datachannel#readme",
48
49
  "devDependencies": {
50
+ "@types/node": "^17.0.23",
51
+ "@typescript-eslint/eslint-plugin": "^5.18.0",
52
+ "@typescript-eslint/parser": "^5.18.0",
49
53
  "cmake-js": "^6.3.0",
54
+ "eslint": "^8.12.0",
55
+ "eslint-config-prettier": "^8.5.0",
56
+ "eslint-plugin-prettier": "^4.0.0",
50
57
  "jest": "^27.5.1",
51
58
  "nan": "^2.15.0",
52
59
  "napi-thread-safe-callback-cancellable": "^0.0.7",
53
- "node-addon-api": "^4.2.0",
54
- "prebuild": "^11.0.3"
60
+ "node-addon-api": "^4.3.0",
61
+ "prebuild": "^11.0.3",
62
+ "prettier": "^2.6.2",
63
+ "typescript": "^4.6.3"
55
64
  },
56
65
  "bundledDependencies": [
57
66
  "prebuild-install"
58
67
  ],
59
68
  "dependencies": {
60
- "@types/node": "^17.0.21",
61
69
  "prebuild-install": "^7.0.1"
62
70
  }
63
71
  }
@@ -20,6 +20,8 @@ Napi::Object DataChannelWrapper::Init(Napi::Env env, Napi::Object exports)
20
20
  {
21
21
  InstanceMethod("close", &DataChannelWrapper::close),
22
22
  InstanceMethod("getLabel", &DataChannelWrapper::getLabel),
23
+ InstanceMethod("getId", &DataChannelWrapper::getId),
24
+ InstanceMethod("getProtocol", &DataChannelWrapper::getProtocol),
23
25
  InstanceMethod("sendMessage", &DataChannelWrapper::sendMessage),
24
26
  InstanceMethod("sendMessageBinary", &DataChannelWrapper::sendMessageBinary),
25
27
  InstanceMethod("isOpen", &DataChannelWrapper::isOpen),
@@ -42,8 +44,26 @@ Napi::Object DataChannelWrapper::Init(Napi::Env env, Napi::Object exports)
42
44
 
43
45
  DataChannelWrapper::DataChannelWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<DataChannelWrapper>(info)
44
46
  {
45
- mDataChannelPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::DataChannel>>>().Data());
47
+ mDataChannelPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::DataChannel>>>().Data());
46
48
 
49
+ mDataChannelPtr->onClosed([&]()
50
+ {
51
+ if (mOnClosedCallback)
52
+ mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
53
+ // Check the peer connection is not closed
54
+ if(instances.find(this) == instances.end())
55
+ throw ThreadSafeCallback::CancelException();
56
+
57
+ cleanCbsAndEraseInstance();
58
+
59
+ // This will run in main thread and needs to construct the
60
+ // arguments for the call
61
+ args = {};
62
+ });
63
+ else {
64
+ cleanCbsAndEraseInstance();
65
+ } });
66
+
47
67
  instances.insert(this);
48
68
  }
49
69
 
@@ -54,20 +74,11 @@ DataChannelWrapper::~DataChannelWrapper()
54
74
 
55
75
  void DataChannelWrapper::doClose()
56
76
  {
57
- instances.erase(this);
58
-
59
77
  if (mDataChannelPtr)
60
78
  {
61
79
  try
62
80
  {
63
- mOnOpenCallback.reset();
64
- mOnClosedCallback.reset();
65
- mOnErrorCallback.reset();
66
- mOnBufferedAmountLowCallback.reset();
67
- mOnMessageCallback.reset();
68
-
69
81
  mDataChannelPtr->close();
70
- mDataChannelPtr.reset();
71
82
  }
72
83
  catch (std::exception &ex)
73
84
  {
@@ -77,6 +88,18 @@ void DataChannelWrapper::doClose()
77
88
  }
78
89
  }
79
90
 
91
+ void DataChannelWrapper::cleanCbsAndEraseInstance()
92
+ {
93
+ mOnOpenCallback.reset();
94
+ mOnClosedCallback.reset();
95
+ mOnErrorCallback.reset();
96
+ mOnBufferedAmountLowCallback.reset();
97
+ mOnMessageCallback.reset();
98
+
99
+ instances.erase(this);
100
+ mDataChannelPtr.reset();
101
+ }
102
+
80
103
  void DataChannelWrapper::close(const Napi::CallbackInfo &info)
81
104
  {
82
105
  doClose();
@@ -93,6 +116,28 @@ Napi::Value DataChannelWrapper::getLabel(const Napi::CallbackInfo &info)
93
116
  return Napi::String::New(info.Env(), mDataChannelPtr->label());
94
117
  }
95
118
 
119
+ Napi::Value DataChannelWrapper::getId(const Napi::CallbackInfo &info)
120
+ {
121
+ if (!mDataChannelPtr)
122
+ {
123
+ Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
124
+ return info.Env().Null();
125
+ }
126
+
127
+ return Napi::Number::New(info.Env(), mDataChannelPtr->id());
128
+ }
129
+
130
+ Napi::Value DataChannelWrapper::getProtocol(const Napi::CallbackInfo &info)
131
+ {
132
+ if (!mDataChannelPtr)
133
+ {
134
+ Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
135
+ return info.Env().Null();
136
+ }
137
+
138
+ return Napi::String::New(info.Env(), mDataChannelPtr->protocol());
139
+ }
140
+
96
141
  Napi::Value DataChannelWrapper::sendMessage(const Napi::CallbackInfo &info)
97
142
  {
98
143
  if (!mDataChannelPtr)
@@ -278,18 +323,7 @@ void DataChannelWrapper::onClosed(const Napi::CallbackInfo &info)
278
323
  // Callback
279
324
  mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
280
325
 
281
- mDataChannelPtr->onClosed([&]()
282
- {
283
- if (mOnClosedCallback)
284
- mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
285
- // Check the peer connection is not closed
286
- if(instances.find(this) == instances.end())
287
- throw ThreadSafeCallback::CancelException();
288
-
289
- // This will run in main thread and needs to construct the
290
- // arguments for the call
291
- args = {};
292
- }); });
326
+ // Object cb call moved to the constructor
293
327
  }
294
328
 
295
329
  void DataChannelWrapper::onError(const Napi::CallbackInfo &info)
@@ -23,6 +23,8 @@ public:
23
23
  // Functions
24
24
  void close(const Napi::CallbackInfo &info);
25
25
  Napi::Value getLabel(const Napi::CallbackInfo &info);
26
+ Napi::Value getId(const Napi::CallbackInfo &info);
27
+ Napi::Value getProtocol(const Napi::CallbackInfo &info);
26
28
  Napi::Value sendMessage(const Napi::CallbackInfo &info);
27
29
  Napi::Value sendMessageBinary(const Napi::CallbackInfo &info);
28
30
  Napi::Value isOpen(const Napi::CallbackInfo &info);
@@ -44,6 +46,7 @@ private:
44
46
  static std::unordered_set<DataChannelWrapper*> instances;
45
47
 
46
48
  void doClose();
49
+ void cleanCbsAndEraseInstance();
47
50
 
48
51
  std::string mLabel;
49
52
  std::shared_ptr<rtc::DataChannel> mDataChannelPtr = nullptr;
@@ -157,15 +157,15 @@ Napi::Value AudioWrapper::generateSdp(const Napi::CallbackInfo &info)
157
157
  Napi::Env env = info.Env();
158
158
  int length = info.Length();
159
159
 
160
- if (length < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString())
160
+ if (length < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsNumber())
161
161
  {
162
- Napi::TypeError::New(env, "We expect (String, String, String) as param").ThrowAsJavaScriptException();
162
+ Napi::TypeError::New(env, "We expect (String, String, Number) as param").ThrowAsJavaScriptException();
163
163
  return Napi::String::New(env, "");
164
164
  }
165
165
 
166
166
  std::string eol = info[0].As<Napi::String>().ToString();
167
167
  std::string addr = info[1].As<Napi::String>().ToString();
168
- std::string port = info[2].As<Napi::String>().ToString();
168
+ uint16_t port = info[2].As<Napi::Number>().Uint32Value();
169
169
 
170
170
  return Napi::String::New(env, mAudioPtr->generateSdp(eol, addr, port));
171
171
  }
@@ -398,7 +398,7 @@ void AudioWrapper::setBitrate(const Napi::CallbackInfo &info)
398
398
  Napi::Value AudioWrapper::getBitrate(const Napi::CallbackInfo &info)
399
399
  {
400
400
 
401
- return Napi::Number::New(info.Env(), mAudioPtr->getBitrate());
401
+ return Napi::Number::New(info.Env(), mAudioPtr->bitrate());
402
402
  }
403
403
 
404
404
  Napi::Value AudioWrapper::hasPayloadType(const Napi::CallbackInfo &info)
@@ -432,7 +432,7 @@ void AudioWrapper::addRTXCodec(const Napi::CallbackInfo &info)
432
432
  unsigned int originalPayloadType = static_cast<unsigned int>(info[1].As<Napi::Number>().ToNumber());
433
433
  unsigned int clockRate = static_cast<unsigned int>(info[2].As<Napi::Number>().ToNumber());
434
434
 
435
- mAudioPtr->addRTXCodec(payloadType, originalPayloadType, clockRate);
435
+ mAudioPtr->addRtxCodec(payloadType, originalPayloadType, clockRate);
436
436
  }
437
437
 
438
438
  void AudioWrapper::addRTPMap(const Napi::CallbackInfo &info)
@@ -22,6 +22,7 @@ Napi::Object TrackWrapper::Init(Napi::Env env, Napi::Object exports)
22
22
  {
23
23
  InstanceMethod("direction", &TrackWrapper::direction),
24
24
  InstanceMethod("mid", &TrackWrapper::mid),
25
+ InstanceMethod("type", &TrackWrapper::type),
25
26
  InstanceMethod("close", &TrackWrapper::close),
26
27
  InstanceMethod("sendMessage", &TrackWrapper::sendMessage),
27
28
  InstanceMethod("sendMessageBinary", &TrackWrapper::sendMessageBinary),
@@ -45,8 +46,26 @@ Napi::Object TrackWrapper::Init(Napi::Env env, Napi::Object exports)
45
46
 
46
47
  TrackWrapper::TrackWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<TrackWrapper>(info)
47
48
  {
48
- mTrackPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::Track>>>().Data());
49
+ mTrackPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::Track>>>().Data());
49
50
 
51
+ mTrackPtr->onClosed([&]()
52
+ {
53
+ if (mOnClosedCallback)
54
+ mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
55
+ // Check the peer connection is not closed
56
+ if(instances.find(this) == instances.end())
57
+ throw ThreadSafeCallback::CancelException();
58
+
59
+ cleanCbsAndEraseInstance();
60
+
61
+ // This will run in main thread and needs to construct the
62
+ // arguments for the call
63
+ args = {};
64
+ });
65
+ else {
66
+ cleanCbsAndEraseInstance();
67
+ } });
68
+
50
69
  instances.insert(this);
51
70
  }
52
71
 
@@ -57,19 +76,11 @@ TrackWrapper::~TrackWrapper()
57
76
 
58
77
  void TrackWrapper::doClose()
59
78
  {
60
- instances.erase(this);
61
-
62
79
  if (mTrackPtr)
63
80
  {
64
81
  try
65
82
  {
66
- mOnOpenCallback.reset();
67
- mOnClosedCallback.reset();
68
- mOnErrorCallback.reset();
69
- mOnMessageCallback.reset();
70
-
71
83
  mTrackPtr->close();
72
- mTrackPtr.reset();
73
84
  }
74
85
  catch (std::exception &ex)
75
86
  {
@@ -79,6 +90,17 @@ void TrackWrapper::doClose()
79
90
  }
80
91
  }
81
92
 
93
+ void TrackWrapper::cleanCbsAndEraseInstance()
94
+ {
95
+ mOnOpenCallback.reset();
96
+ mOnClosedCallback.reset();
97
+ mOnErrorCallback.reset();
98
+ mOnMessageCallback.reset();
99
+
100
+ instances.erase(this);
101
+ mTrackPtr.reset();
102
+ }
103
+
82
104
  void TrackWrapper::close(const Napi::CallbackInfo &info)
83
105
  {
84
106
  doClose();
@@ -96,6 +118,12 @@ Napi::Value TrackWrapper::mid(const Napi::CallbackInfo &info)
96
118
  return Napi::String::New(env, mTrackPtr->mid());
97
119
  }
98
120
 
121
+ Napi::Value TrackWrapper::type(const Napi::CallbackInfo &info)
122
+ {
123
+ Napi::Env env = info.Env();
124
+ return Napi::String::New(env, mTrackPtr->description().type());
125
+ }
126
+
99
127
  Napi::Value TrackWrapper::sendMessage(const Napi::CallbackInfo &info)
100
128
  {
101
129
  if (!mTrackPtr)
@@ -254,18 +282,7 @@ void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
254
282
  // Callback
255
283
  mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
256
284
 
257
- mTrackPtr->onClosed([&]()
258
- {
259
- if (mOnClosedCallback)
260
- mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
261
- // Check the peer connection is not closed
262
- if(instances.find(this) == instances.end())
263
- throw ThreadSafeCallback::CancelException();
264
-
265
- // This will run in main thread and needs to construct the
266
- // arguments for the call
267
- args = {};
268
- }); });
285
+ // Object cb call moved to the constructor
269
286
  }
270
287
 
271
288
  void TrackWrapper::onError(const Napi::CallbackInfo &info)
@@ -323,6 +340,8 @@ void TrackWrapper::onMessage(const Napi::CallbackInfo &info)
323
340
  Napi::Object payload = Napi::Object::New(env);
324
341
  if (std::holds_alternative<std::string>(message))
325
342
  {
343
+ // Track will always send message as binary
344
+ // So this code is not needed actually
326
345
  args = {Napi::String::New(env, std::get<std::string>(message))};
327
346
  }
328
347
  else
@@ -23,6 +23,7 @@ public:
23
23
  // Functions
24
24
  Napi::Value direction(const Napi::CallbackInfo &info);
25
25
  Napi::Value mid(const Napi::CallbackInfo &info);
26
+ Napi::Value type(const Napi::CallbackInfo &info);
26
27
  void close(const Napi::CallbackInfo &info);
27
28
  Napi::Value sendMessage(const Napi::CallbackInfo &info);
28
29
  Napi::Value sendMessageBinary(const Napi::CallbackInfo &info);
@@ -45,6 +46,7 @@ private:
45
46
  static std::unordered_set<TrackWrapper *> instances;
46
47
 
47
48
  void doClose();
49
+ void cleanCbsAndEraseInstance();
48
50
 
49
51
  std::shared_ptr<rtc::Track> mTrackPtr = nullptr;
50
52
 
@@ -190,15 +190,15 @@ Napi::Value VideoWrapper::generateSdp(const Napi::CallbackInfo &info)
190
190
  Napi::Env env = info.Env();
191
191
  int length = info.Length();
192
192
 
193
- if (length < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString())
193
+ if (length < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsNumber())
194
194
  {
195
- Napi::TypeError::New(env, "We expect (String, String, String) as param").ThrowAsJavaScriptException();
195
+ Napi::TypeError::New(env, "We expect (String, String, Number) as param").ThrowAsJavaScriptException();
196
196
  return Napi::String::New(env, "");
197
197
  }
198
198
 
199
199
  std::string eol = info[0].As<Napi::String>().ToString();
200
200
  std::string addr = info[1].As<Napi::String>().ToString();
201
- std::string port = info[2].As<Napi::String>().ToString();
201
+ uint16_t port = info[2].As<Napi::Number>().Uint32Value();
202
202
 
203
203
  return Napi::String::New(env, mVideoPtr->generateSdp(eol, addr, port));
204
204
  }
@@ -431,7 +431,7 @@ void VideoWrapper::setBitrate(const Napi::CallbackInfo &info)
431
431
  Napi::Value VideoWrapper::getBitrate(const Napi::CallbackInfo &info)
432
432
  {
433
433
 
434
- return Napi::Number::New(info.Env(), mVideoPtr->getBitrate());
434
+ return Napi::Number::New(info.Env(), mVideoPtr->bitrate());
435
435
  }
436
436
 
437
437
  Napi::Value VideoWrapper::hasPayloadType(const Napi::CallbackInfo &info)
@@ -465,7 +465,7 @@ void VideoWrapper::addRTXCodec(const Napi::CallbackInfo &info)
465
465
  unsigned int originalPayloadType = static_cast<unsigned int>(info[1].As<Napi::Number>().ToNumber());
466
466
  unsigned int clockRate = static_cast<unsigned int>(info[2].As<Napi::Number>().ToNumber());
467
467
 
468
- mVideoPtr->addRTXCodec(payloadType, originalPayloadType, clockRate);
468
+ mVideoPtr->addRtxCodec(payloadType, originalPayloadType, clockRate);
469
469
  }
470
470
 
471
471
  void VideoWrapper::addRTPMap(const Napi::CallbackInfo &info)
@@ -147,10 +147,6 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
147
147
  std::string strType = proxyServer.Get("type").As<Napi::String>().ToString();
148
148
  rtc::ProxyServer::Type type = rtc::ProxyServer::Type::Http;
149
149
 
150
- if (strType == "None")
151
- type = rtc::ProxyServer::Type::None;
152
- if (strType == "Http")
153
- type = rtc::ProxyServer::Type::Http;
154
150
  if (strType == "Socks5")
155
151
  type = rtc::ProxyServer::Type::Socks5;
156
152
 
@@ -176,6 +172,14 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
176
172
  if (config.Get("enableIceTcp").IsBoolean())
177
173
  rtcConfig.enableIceTcp = config.Get("enableIceTcp").As<Napi::Boolean>();
178
174
 
175
+ // enableIceUdpMux option
176
+ if (config.Get("enableIceUdpMux").IsBoolean())
177
+ rtcConfig.enableIceUdpMux = config.Get("enableIceUdpMux").As<Napi::Boolean>();
178
+
179
+ // disableAutoNegotiation option
180
+ if (config.Get("disableAutoNegotiation").IsBoolean())
181
+ rtcConfig.disableAutoNegotiation = config.Get("disableAutoNegotiation").As<Napi::Boolean>();
182
+
179
183
  // Max Message Size
180
184
  if (config.Get("maxMessageSize").IsNumber())
181
185
  rtcConfig.maxMessageSize = config.Get("maxMessageSize").As<Napi::Number>().Uint32Value();
@@ -211,6 +215,27 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
211
215
  return;
212
216
  }
213
217
 
218
+ mRtcPeerConnPtr->onStateChange([&](rtc::PeerConnection::State state) {
219
+ if (mOnStateChangeCallback)
220
+ mOnStateChangeCallback->call([this, state](Napi::Env env, std::vector<napi_value> &args) {
221
+ // Check the peer connection is not closed
222
+ if(instances.find(this) == instances.end())
223
+ throw ThreadSafeCallback::CancelException();
224
+
225
+ if(mDoCloseFlag && state == rtc::PeerConnection::State::Closed)
226
+ cleanCbsAndEraseInstance();
227
+
228
+ // This will run in main thread and needs to construct the
229
+ // arguments for the call
230
+ std::ostringstream stream;
231
+ stream << state;
232
+ args = {Napi::String::New(env, stream.str())};
233
+ });
234
+ else {
235
+ if(mDoCloseFlag && state == rtc::PeerConnection::State::Closed)
236
+ cleanCbsAndEraseInstance();
237
+ } });
238
+
214
239
  instances.insert(this);
215
240
  }
216
241
 
@@ -221,20 +246,14 @@ PeerConnectionWrapper::~PeerConnectionWrapper()
221
246
 
222
247
  void PeerConnectionWrapper::doClose()
223
248
  {
224
- instances.erase(this);
225
-
226
- if (mRtcPeerConnPtr)
249
+ if (mRtcPeerConnPtr && !mDoCloseFlag)
227
250
  {
228
251
  try
229
252
  {
230
- mOnLocalDescriptionCallback.reset();
231
- mOnLocalCandidateCallback.reset();
232
- mOnStateChangeCallback.reset();
233
- mOnGatheringStateChangeCallback.reset();
234
- mOnDataChannelCallback.reset();
235
- mOnTrackCallback.reset();
236
-
253
+ mDoCloseFlag = true;
237
254
  mRtcPeerConnPtr->close();
255
+
256
+ // Resetting in cleanCbsAndEraseInstance causes a deadlock
238
257
  mRtcPeerConnPtr.reset();
239
258
  }
240
259
  catch (std::exception &ex)
@@ -245,6 +264,20 @@ void PeerConnectionWrapper::doClose()
245
264
  }
246
265
  }
247
266
 
267
+ void PeerConnectionWrapper::cleanCbsAndEraseInstance()
268
+ {
269
+ mOnLocalDescriptionCallback.reset();
270
+ mOnLocalCandidateCallback.reset();
271
+ mOnStateChangeCallback.reset();
272
+ mOnGatheringStateChangeCallback.reset();
273
+ mOnDataChannelCallback.reset();
274
+ mOnTrackCallback.reset();
275
+
276
+ instances.erase(this);
277
+
278
+ mDoCloseFlag = false;
279
+ }
280
+
248
281
  void PeerConnectionWrapper::close(const Napi::CallbackInfo &info)
249
282
  {
250
283
  doClose();
@@ -598,19 +631,7 @@ void PeerConnectionWrapper::onStateChange(const Napi::CallbackInfo &info)
598
631
  // Callback
599
632
  mOnStateChangeCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
600
633
 
601
- mRtcPeerConnPtr->onStateChange([&](rtc::PeerConnection::State state) {
602
- if (mOnStateChangeCallback)
603
- mOnStateChangeCallback->call([this, state](Napi::Env env, std::vector<napi_value> &args) {
604
- // Check the peer connection is not closed
605
- if(instances.find(this) == instances.end())
606
- throw ThreadSafeCallback::CancelException();
607
-
608
- // This will run in main thread and needs to construct the
609
- // arguments for the call
610
- std::ostringstream stream;
611
- stream << state;
612
- args = {Napi::String::New(env, stream.str())};
613
- }); });
634
+ // onState cb will be called from constructor
614
635
  }
615
636
 
616
637
  void PeerConnectionWrapper::onSignalingStateChange(const Napi::CallbackInfo &info)
@@ -55,6 +55,8 @@ private:
55
55
  static std::unordered_set<PeerConnectionWrapper *> instances;
56
56
 
57
57
  void doClose();
58
+ void cleanCbsAndEraseInstance();
59
+ bool mDoCloseFlag = false;
58
60
 
59
61
  std::string mPeerName;
60
62
  std::unique_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
@@ -66,6 +66,9 @@ peer2.onDataChannel((dc) => {
66
66
  console.log('Peer2 Received Msg:', msg);
67
67
  });
68
68
  dc2.sendMessage("Hello From Peer2");
69
+ dc2.onClosed(() => {
70
+ console.log('dc2 closed');
71
+ });
69
72
  });
70
73
 
71
74
  // DataChannel Options
@@ -85,17 +88,14 @@ dc1.onOpen(() => {
85
88
  dc1.onMessage((msg) => {
86
89
  console.log('Peer1 Received Msg:', msg);
87
90
  });
91
+ dc1.onClosed(() => {
92
+ console.log('dc1 closed');
93
+ });
88
94
 
89
95
  setTimeout(() => {
90
96
  dc1.close();
91
97
  dc2.close();
92
98
  peer1.close();
93
99
  peer2.close();
94
- dc1 = null;
95
- dc2 = null;
96
- peer1 = null;
97
- peer2 = null;
98
100
  nodeDataChannel.cleanup();
99
- // No need for this (>= V0.1.14)
100
- // process.exit();
101
101
  }, 5 * 1000);
package/test/test.js CHANGED
@@ -24,8 +24,11 @@ describe('PeerConnection Classes', () => {
24
24
 
25
25
  test('Create Data Channel', () => {
26
26
  let peer = new nodeDataChannel.PeerConnection("Peer", { iceServers: ["stun:stun.l.google.com:19302"] });
27
- let dc = peer.createDataChannel('test');
27
+ let dc = peer.createDataChannel('test', { protocol: 'test-protocol' });
28
28
  expect(dc).toBeDefined();
29
+ expect(dc.getId()).toBeDefined();
30
+ expect(dc.getProtocol()).toBe('test-protocol');
31
+ expect(dc.getLabel()).toBe('test');
29
32
  expect(dc.onOpen).toBeDefined();
30
33
  expect(dc.onMessage).toBeDefined();
31
34
 
@@ -184,9 +187,6 @@ describe('DataChannel streams', () => {
184
187
 
185
188
  expect(await clientResponsePromise).toBe("test message");
186
189
 
187
- clientChannel.close();
188
- await new Promise((resolve) => echoStream.on('end', resolve));
189
-
190
190
  clientPeer.close();
191
191
  echoPeer.close();
192
192
  });