node-rtc-connection 1.0.5 → 1.0.7

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # NodeRTC - WebRTC DataChannels for Node.js
1
+ # node-rtc-connection - WebRTC DataChannels for Node.js
2
2
 
3
- [![npm version](https://badge.fury.io/js/nodertc.svg)](https://www.npmjs.com/package/nodertc)
3
+ [![npm version](https://badge.fury.io/js/node-rtc-connection.svg)](https://www.npmjs.com/package/node-rtc-connection)
4
4
  [![Node.js CI](https://github.com/nmhung1210/nodertc/workflows/Test/badge.svg)](https://github.com/nmhung1210/nodertc/actions)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
@@ -8,7 +8,7 @@ A production-ready WebRTC DataChannel implementation for Node.js with **real net
8
8
 
9
9
  ## Overview
10
10
 
11
- NodeRTC provides WebRTC-style peer-to-peer data connections using Node.js built-in modules. It supports NAT traversal via STUN/TURN servers with full RFC 5766 MESSAGE-INTEGRITY authentication, optional TLS encryption, and works across the internet for most network configurations.
11
+ node-rtc-connection provides WebRTC-style peer-to-peer data connections using Node.js built-in modules. It supports NAT traversal via STUN/TURN servers with full RFC 5766 MESSAGE-INTEGRITY authentication, optional TLS encryption, and works across the internet for most network configurations.
12
12
 
13
13
  ## Features
14
14
 
@@ -28,7 +28,7 @@ NodeRTC provides WebRTC-style peer-to-peer data connections using Node.js built-
28
28
  ## Installation
29
29
 
30
30
  ```bash
31
- npm install nodertc
31
+ npm install node-rtc-connection
32
32
  ```
33
33
 
34
34
  ## Quick Start
@@ -36,7 +36,7 @@ npm install nodertc
36
36
  ### Basic Usage
37
37
 
38
38
  ```javascript
39
- const { createPeerConnection } = require('nodertc');
39
+ const { createPeerConnection } = require('node-rtc-connection');
40
40
 
41
41
  // Create peer connection
42
42
  const pc = createPeerConnection();
@@ -68,7 +68,7 @@ await pc.setLocalDescription(offer);
68
68
  ### With STUN and TURN (Recommended)
69
69
 
70
70
  ```javascript
71
- const { createPeerConnection } = require('nodertc');
71
+ const { createPeerConnection } = require('node-rtc-connection');
72
72
 
73
73
  // Configuration with STUN and TURN
74
74
  const config = {
@@ -295,7 +295,7 @@ new RTCPeerConnection(configuration, nativePeerConnectionFactory)
295
295
 
296
296
  ## Testing
297
297
 
298
- NodeRTC includes comprehensive unit tests covering all components:
298
+ node-rtc-connection includes comprehensive unit tests covering all components:
299
299
 
300
300
  ```bash
301
301
  # Run all unit tests (fast, ~500ms)
@@ -421,7 +421,7 @@ Ported from Chromium's WebRTC implementation:
421
421
 
422
422
  ## Links
423
423
 
424
- - [NPM Package](https://www.npmjs.com/package/nodertc)
424
+ - [NPM Package](https://www.npmjs.com/package/node-rtc-connection)
425
425
  - [GitHub Repository](https://github.com/nmhung1210/nodertc)
426
426
  - [Issues](https://github.com/nmhung1210/nodertc/issues)
427
427
  - [Contributing Guide](CONTRIBUTING.md)
package/dist/index.cjs CHANGED
@@ -2400,6 +2400,9 @@ function requireNativePeerConnectionFactory () {
2400
2400
  this._secureConnection = null;
2401
2401
  this._udpTransport = null;
2402
2402
  this._iceCandidates = [];
2403
+ this._remoteCandidates = [];
2404
+ this._selectedLocalCandidate = null;
2405
+ this._selectedRemoteCandidate = null;
2403
2406
 
2404
2407
  console.log('[NativePeerConnection] Created with STUN/ICE/Encryption support');
2405
2408
  console.log(` - Encryption: ${this._useEncryption ? 'enabled' : 'disabled (requires valid certs)'}`);
@@ -2545,22 +2548,106 @@ function requireNativePeerConnectionFactory () {
2545
2548
  }
2546
2549
 
2547
2550
  if (!candidate || !candidate.candidate) {
2551
+ // null candidate signals end of candidates - try to select best pair
2552
+ if (this._remoteCandidates.length > 0 && !this._selectedRemoteCandidate) {
2553
+ this._selectBestCandidatePair();
2554
+ }
2555
+ return;
2556
+ }
2557
+
2558
+ // Parse ICE candidate
2559
+ const parsed = this._parseIceCandidate(candidate.candidate);
2560
+ if (!parsed) {
2548
2561
  return;
2549
2562
  }
2550
2563
 
2551
- // Parse ICE candidate to extract address and port
2552
- const parts = candidate.candidate.split(' ');
2553
- if (parts.length >= 6) {
2554
- this._remoteAddress = parts[4];
2555
- this._remotePort = parseInt(parts[5], 10);
2564
+ // Store the remote candidate
2565
+ this._remoteCandidates.push(parsed);
2566
+ console.log(`[NativePeerConnection] Added remote ICE candidate (${parsed.type}): ${parsed.ip}:${parsed.port}`);
2567
+
2568
+ // For backward compatibility, set remote address immediately if not set
2569
+ if (!this._remoteAddress) {
2570
+ this._remoteAddress = parsed.ip;
2571
+ this._remotePort = parsed.port;
2556
2572
  console.log(`[NativePeerConnection] Remote address: ${this._remoteAddress}:${this._remotePort}`);
2557
-
2558
- // If we have both local and remote info, and we're offerer, connect
2559
- if (this._isOfferer && this._localAddress && this._remoteAddress &&
2560
- this._signalingState === 0 && !this._socket) {
2561
- await this._connectToPeer();
2573
+ }
2574
+
2575
+ // If we haven't selected a pair yet, try to select now
2576
+ if (!this._selectedRemoteCandidate && this._iceCandidates.length > 0) {
2577
+ this._selectBestCandidatePair();
2578
+ }
2579
+
2580
+ // If we selected a pair and are offerer, connect
2581
+ if (this._selectedRemoteCandidate && this._isOfferer &&
2582
+ this._signalingState === 0 && !this._socket) {
2583
+ await this._connectToPeer();
2584
+ }
2585
+ }
2586
+
2587
+ /**
2588
+ * Parse ICE candidate string
2589
+ * @private
2590
+ */
2591
+ _parseIceCandidate(candidateStr) {
2592
+ const parts = candidateStr.split(' ');
2593
+ if (parts.length < 6) {
2594
+ return null;
2595
+ }
2596
+
2597
+ return {
2598
+ foundation: parts[0].replace('candidate:', ''),
2599
+ component: parts[1],
2600
+ protocol: parts[2],
2601
+ priority: parseInt(parts[3], 10),
2602
+ ip: parts[4],
2603
+ port: parseInt(parts[5], 10),
2604
+ type: parts[7], // typ host/srflx/relay
2605
+ relatedAddress: parts[9] || null,
2606
+ relatedPort: parts[11] ? parseInt(parts[11], 10) : null
2607
+ };
2608
+ }
2609
+
2610
+ /**
2611
+ * Select best candidate pair for connection
2612
+ * Prioritizes: relay > srflx > host
2613
+ * @private
2614
+ */
2615
+ _selectBestCandidatePair() {
2616
+ if (this._remoteCandidates.length === 0 || this._iceCandidates.length === 0) {
2617
+ return;
2618
+ }
2619
+
2620
+ // Priority order: relay > srflx > host
2621
+ const typePriority = { 'relay': 3, 'srflx': 2, 'host': 1 };
2622
+
2623
+ // Find best local candidate
2624
+ let bestLocal = this._iceCandidates[0];
2625
+ for (const candidate of this._iceCandidates) {
2626
+ if (typePriority[candidate.type] > typePriority[bestLocal.type]) {
2627
+ bestLocal = candidate;
2628
+ }
2629
+ }
2630
+
2631
+ // Find best remote candidate
2632
+ let bestRemote = this._remoteCandidates[0];
2633
+ for (const candidate of this._remoteCandidates) {
2634
+ if (typePriority[candidate.type] > typePriority[bestRemote.type]) {
2635
+ bestRemote = candidate;
2562
2636
  }
2563
2637
  }
2638
+
2639
+ this._selectedLocalCandidate = bestLocal;
2640
+ this._selectedRemoteCandidate = bestRemote;
2641
+
2642
+ // Update addresses for connection
2643
+ this._localAddress = bestLocal.ip;
2644
+ this._localPort = bestLocal.port;
2645
+ this._remoteAddress = bestRemote.ip;
2646
+ this._remotePort = bestRemote.port;
2647
+
2648
+ console.log(`[NativePeerConnection] Selected candidate pair:`);
2649
+ console.log(` Local: ${bestLocal.type} ${this._localAddress}:${this._localPort}`);
2650
+ console.log(` Remote: ${bestRemote.type} ${this._remoteAddress}:${this._remotePort}`);
2564
2651
  }
2565
2652
 
2566
2653
  /**
@@ -2696,14 +2783,24 @@ function requireNativePeerConnectionFactory () {
2696
2783
  return;
2697
2784
  }
2698
2785
 
2699
- // Tie-breaking: only connect if our port is higher than remote port
2700
- // This ensures only one peer connects, avoiding the race condition
2701
- if (this._localPort < this._remotePort) {
2702
- console.log(`[NativePeerConnection] Not connecting (local port ${this._localPort} < remote port ${this._remotePort}), waiting for incoming`);
2703
- return;
2786
+ // Check if we're using relay candidates - if so, skip tie-breaking
2787
+ const usingRelay = this._selectedLocalCandidate?.type === 'relay' ||
2788
+ this._selectedRemoteCandidate?.type === 'relay';
2789
+
2790
+ if (!usingRelay) {
2791
+ // Tie-breaking: only connect if our port is higher than remote port
2792
+ // This ensures only one peer connects, avoiding the race condition
2793
+ // Note: This only works for direct connections (host/srflx)
2794
+ if (this._localPort < this._remotePort) {
2795
+ console.log(`[NativePeerConnection] Not connecting (local port ${this._localPort} < remote port ${this._remotePort}), waiting for incoming`);
2796
+ return;
2797
+ }
2704
2798
  }
2705
2799
 
2706
2800
  console.log(`[NativePeerConnection] Connecting to ${this._remoteAddress}:${this._remotePort}`);
2801
+ if (usingRelay) {
2802
+ console.log(`[NativePeerConnection] Using TURN relay connection`);
2803
+ }
2707
2804
 
2708
2805
  this._socket = new net.Socket();
2709
2806
 
@@ -2996,7 +3093,12 @@ a=max-message-size:262144
2996
3093
 
2997
3094
  // Emit each candidate
2998
3095
  for (const candidate of candidates) {
2999
- this._iceCandidates.push(candidate);
3096
+ // Parse and store the candidate
3097
+ const parsed = this._parseIceCandidate(candidate.candidate);
3098
+ if (parsed) {
3099
+ this._iceCandidates.push(parsed);
3100
+ }
3101
+
3000
3102
  this.emit('icecandidate', {
3001
3103
  candidate: candidate.candidate,
3002
3104
  sdpMid: candidate.sdpMid,
@@ -3010,8 +3112,14 @@ a=max-message-size:262144
3010
3112
 
3011
3113
  // Fallback: emit only local host candidate
3012
3114
  if (this._localAddress && this._localPort) {
3115
+ const candidateStr = `candidate:1 1 tcp 2130706431 ${this._localAddress} ${this._localPort} typ host`;
3116
+ const parsed = this._parseIceCandidate(candidateStr);
3117
+ if (parsed) {
3118
+ this._iceCandidates.push(parsed);
3119
+ }
3120
+
3013
3121
  const candidate = {
3014
- candidate: `candidate:1 1 tcp 2130706431 ${this._localAddress} ${this._localPort} typ host`,
3122
+ candidate: candidateStr,
3015
3123
  sdpMid: 'data',
3016
3124
  sdpMLineIndex: 0
3017
3125
  };