@electerm/ssh2 1.17.0 → 1.17.1

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 (2) hide show
  1. package/lib/client.js +47 -7
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -362,6 +362,10 @@ class Client extends EventEmitter {
362
362
  },
363
363
  onHandshakeComplete: (negotiated) => {
364
364
  this.emit('handshake', negotiated);
365
+ // Start keepalive monitoring after handshake
366
+ if (this.config.keepaliveInterval > 0) {
367
+ resetKA();
368
+ }
365
369
  if (!ready) {
366
370
  ready = true;
367
371
  proto.service('ssh-userauth');
@@ -540,10 +544,14 @@ class Client extends EventEmitter {
540
544
  }
541
545
  },
542
546
  REQUEST_SUCCESS: (p, data) => {
547
+ // Reset keepalive on any protocol activity
548
+ this._resetKA();
543
549
  if (callbacks.length)
544
550
  callbacks.shift()(false, data);
545
551
  },
546
552
  REQUEST_FAILURE: (p) => {
553
+ // Reset keepalive on any protocol activity
554
+ this._resetKA();
547
555
  if (callbacks.length)
548
556
  callbacks.shift()(true);
549
557
  },
@@ -748,39 +756,71 @@ class Client extends EventEmitter {
748
756
 
749
757
  sock.pause();
750
758
 
751
- // TODO: check keepalive implementation
752
- // Keepalive-related
759
+ // Enhanced keepalive implementation with TCP-level and SSH protocol-level keepalives
753
760
  const kainterval = this.config.keepaliveInterval;
754
761
  const kacountmax = this.config.keepaliveCountMax;
755
762
  let kacount = 0;
756
763
  let katimer;
764
+ let lastActivity = Date.now();
765
+
766
+ // Enable TCP keepalive for network-level connection monitoring
767
+ // This helps detect dead connections even when SSH protocol keepalive is not used
768
+ if (typeof sock.setKeepAlive === 'function') {
769
+ sock.setKeepAlive(true, 10000); // Start probing after 10 seconds idle
770
+ }
771
+
757
772
  const sendKA = () => {
773
+ const now = Date.now();
774
+ const timeSinceLastActivity = now - lastActivity;
775
+
776
+ // If we've received any activity recently, reset counter and continue
777
+ if (timeSinceLastActivity < kainterval) {
778
+ kacount = 0;
779
+ return;
780
+ }
781
+
758
782
  if (++kacount > kacountmax) {
759
783
  clearInterval(katimer);
760
784
  if (sock.readable) {
761
- const err = new Error('Keepalive timeout');
785
+ const err = new Error(
786
+ `Keepalive timeout: no response after ${kacountmax} attempts`
787
+ );
762
788
  err.level = 'client-timeout';
763
789
  this.emit('error', err);
764
790
  sock.destroy();
765
791
  }
766
792
  return;
767
793
  }
794
+
768
795
  if (isWritable(sock)) {
769
- // Append dummy callback to keep correct callback order
770
- callbacks.push(resetKA);
796
+ if (debug) {
797
+ debug(
798
+ `Client: Sending keepalive (${kacount}/${kacountmax}), ` +
799
+ `${timeSinceLastActivity}ms since last activity`
800
+ );
801
+ }
802
+ // Send SSH protocol-level keepalive
803
+ // Use global request instead of relying on callbacks
771
804
  proto.ping();
805
+
806
+ // Don't rely on callback - we'll reset based on any activity
772
807
  } else {
773
808
  clearInterval(katimer);
774
809
  }
775
810
  };
811
+
776
812
  function resetKA() {
813
+ lastActivity = Date.now();
814
+ kacount = 0;
815
+
777
816
  if (kainterval > 0) {
778
- kacount = 0;
779
817
  clearInterval(katimer);
780
- if (isWritable(sock))
818
+ if (isWritable(sock)) {
781
819
  katimer = setInterval(sendKA, kainterval);
820
+ }
782
821
  }
783
822
  }
823
+
784
824
  this._resetKA = resetKA;
785
825
 
786
826
  const onDone = (() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/ssh2",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "author": "Brian White <mscdex@mscdex.net>",
5
5
  "description": "SSH2 client and server modules written in pure JavaScript for node.js",
6
6
  "main": "./lib/index.js",