node-rtc-connection 1.0.5 → 1.0.6

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/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
  };