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/dist/index.mjs CHANGED
@@ -2398,6 +2398,9 @@ function requireNativePeerConnectionFactory () {
2398
2398
  this._secureConnection = null;
2399
2399
  this._udpTransport = null;
2400
2400
  this._iceCandidates = [];
2401
+ this._remoteCandidates = [];
2402
+ this._selectedLocalCandidate = null;
2403
+ this._selectedRemoteCandidate = null;
2401
2404
 
2402
2405
  console.log('[NativePeerConnection] Created with STUN/ICE/Encryption support');
2403
2406
  console.log(` - Encryption: ${this._useEncryption ? 'enabled' : 'disabled (requires valid certs)'}`);
@@ -2543,22 +2546,106 @@ function requireNativePeerConnectionFactory () {
2543
2546
  }
2544
2547
 
2545
2548
  if (!candidate || !candidate.candidate) {
2549
+ // null candidate signals end of candidates - try to select best pair
2550
+ if (this._remoteCandidates.length > 0 && !this._selectedRemoteCandidate) {
2551
+ this._selectBestCandidatePair();
2552
+ }
2553
+ return;
2554
+ }
2555
+
2556
+ // Parse ICE candidate
2557
+ const parsed = this._parseIceCandidate(candidate.candidate);
2558
+ if (!parsed) {
2546
2559
  return;
2547
2560
  }
2548
2561
 
2549
- // Parse ICE candidate to extract address and port
2550
- const parts = candidate.candidate.split(' ');
2551
- if (parts.length >= 6) {
2552
- this._remoteAddress = parts[4];
2553
- this._remotePort = parseInt(parts[5], 10);
2562
+ // Store the remote candidate
2563
+ this._remoteCandidates.push(parsed);
2564
+ console.log(`[NativePeerConnection] Added remote ICE candidate (${parsed.type}): ${parsed.ip}:${parsed.port}`);
2565
+
2566
+ // For backward compatibility, set remote address immediately if not set
2567
+ if (!this._remoteAddress) {
2568
+ this._remoteAddress = parsed.ip;
2569
+ this._remotePort = parsed.port;
2554
2570
  console.log(`[NativePeerConnection] Remote address: ${this._remoteAddress}:${this._remotePort}`);
2555
-
2556
- // If we have both local and remote info, and we're offerer, connect
2557
- if (this._isOfferer && this._localAddress && this._remoteAddress &&
2558
- this._signalingState === 0 && !this._socket) {
2559
- await this._connectToPeer();
2571
+ }
2572
+
2573
+ // If we haven't selected a pair yet, try to select now
2574
+ if (!this._selectedRemoteCandidate && this._iceCandidates.length > 0) {
2575
+ this._selectBestCandidatePair();
2576
+ }
2577
+
2578
+ // If we selected a pair and are offerer, connect
2579
+ if (this._selectedRemoteCandidate && this._isOfferer &&
2580
+ this._signalingState === 0 && !this._socket) {
2581
+ await this._connectToPeer();
2582
+ }
2583
+ }
2584
+
2585
+ /**
2586
+ * Parse ICE candidate string
2587
+ * @private
2588
+ */
2589
+ _parseIceCandidate(candidateStr) {
2590
+ const parts = candidateStr.split(' ');
2591
+ if (parts.length < 6) {
2592
+ return null;
2593
+ }
2594
+
2595
+ return {
2596
+ foundation: parts[0].replace('candidate:', ''),
2597
+ component: parts[1],
2598
+ protocol: parts[2],
2599
+ priority: parseInt(parts[3], 10),
2600
+ ip: parts[4],
2601
+ port: parseInt(parts[5], 10),
2602
+ type: parts[7], // typ host/srflx/relay
2603
+ relatedAddress: parts[9] || null,
2604
+ relatedPort: parts[11] ? parseInt(parts[11], 10) : null
2605
+ };
2606
+ }
2607
+
2608
+ /**
2609
+ * Select best candidate pair for connection
2610
+ * Prioritizes: relay > srflx > host
2611
+ * @private
2612
+ */
2613
+ _selectBestCandidatePair() {
2614
+ if (this._remoteCandidates.length === 0 || this._iceCandidates.length === 0) {
2615
+ return;
2616
+ }
2617
+
2618
+ // Priority order: relay > srflx > host
2619
+ const typePriority = { 'relay': 3, 'srflx': 2, 'host': 1 };
2620
+
2621
+ // Find best local candidate
2622
+ let bestLocal = this._iceCandidates[0];
2623
+ for (const candidate of this._iceCandidates) {
2624
+ if (typePriority[candidate.type] > typePriority[bestLocal.type]) {
2625
+ bestLocal = candidate;
2626
+ }
2627
+ }
2628
+
2629
+ // Find best remote candidate
2630
+ let bestRemote = this._remoteCandidates[0];
2631
+ for (const candidate of this._remoteCandidates) {
2632
+ if (typePriority[candidate.type] > typePriority[bestRemote.type]) {
2633
+ bestRemote = candidate;
2560
2634
  }
2561
2635
  }
2636
+
2637
+ this._selectedLocalCandidate = bestLocal;
2638
+ this._selectedRemoteCandidate = bestRemote;
2639
+
2640
+ // Update addresses for connection
2641
+ this._localAddress = bestLocal.ip;
2642
+ this._localPort = bestLocal.port;
2643
+ this._remoteAddress = bestRemote.ip;
2644
+ this._remotePort = bestRemote.port;
2645
+
2646
+ console.log(`[NativePeerConnection] Selected candidate pair:`);
2647
+ console.log(` Local: ${bestLocal.type} ${this._localAddress}:${this._localPort}`);
2648
+ console.log(` Remote: ${bestRemote.type} ${this._remoteAddress}:${this._remotePort}`);
2562
2649
  }
2563
2650
 
2564
2651
  /**
@@ -2694,14 +2781,24 @@ function requireNativePeerConnectionFactory () {
2694
2781
  return;
2695
2782
  }
2696
2783
 
2697
- // Tie-breaking: only connect if our port is higher than remote port
2698
- // This ensures only one peer connects, avoiding the race condition
2699
- if (this._localPort < this._remotePort) {
2700
- console.log(`[NativePeerConnection] Not connecting (local port ${this._localPort} < remote port ${this._remotePort}), waiting for incoming`);
2701
- return;
2784
+ // Check if we're using relay candidates - if so, skip tie-breaking
2785
+ const usingRelay = this._selectedLocalCandidate?.type === 'relay' ||
2786
+ this._selectedRemoteCandidate?.type === 'relay';
2787
+
2788
+ if (!usingRelay) {
2789
+ // Tie-breaking: only connect if our port is higher than remote port
2790
+ // This ensures only one peer connects, avoiding the race condition
2791
+ // Note: This only works for direct connections (host/srflx)
2792
+ if (this._localPort < this._remotePort) {
2793
+ console.log(`[NativePeerConnection] Not connecting (local port ${this._localPort} < remote port ${this._remotePort}), waiting for incoming`);
2794
+ return;
2795
+ }
2702
2796
  }
2703
2797
 
2704
2798
  console.log(`[NativePeerConnection] Connecting to ${this._remoteAddress}:${this._remotePort}`);
2799
+ if (usingRelay) {
2800
+ console.log(`[NativePeerConnection] Using TURN relay connection`);
2801
+ }
2705
2802
 
2706
2803
  this._socket = new net.Socket();
2707
2804
 
@@ -2994,7 +3091,12 @@ a=max-message-size:262144
2994
3091
 
2995
3092
  // Emit each candidate
2996
3093
  for (const candidate of candidates) {
2997
- this._iceCandidates.push(candidate);
3094
+ // Parse and store the candidate
3095
+ const parsed = this._parseIceCandidate(candidate.candidate);
3096
+ if (parsed) {
3097
+ this._iceCandidates.push(parsed);
3098
+ }
3099
+
2998
3100
  this.emit('icecandidate', {
2999
3101
  candidate: candidate.candidate,
3000
3102
  sdpMid: candidate.sdpMid,
@@ -3008,8 +3110,14 @@ a=max-message-size:262144
3008
3110
 
3009
3111
  // Fallback: emit only local host candidate
3010
3112
  if (this._localAddress && this._localPort) {
3113
+ const candidateStr = `candidate:1 1 tcp 2130706431 ${this._localAddress} ${this._localPort} typ host`;
3114
+ const parsed = this._parseIceCandidate(candidateStr);
3115
+ if (parsed) {
3116
+ this._iceCandidates.push(parsed);
3117
+ }
3118
+
3011
3119
  const candidate = {
3012
- candidate: `candidate:1 1 tcp 2130706431 ${this._localAddress} ${this._localPort} typ host`,
3120
+ candidate: candidateStr,
3013
3121
  sdpMid: 'data',
3014
3122
  sdpMLineIndex: 0
3015
3123
  };