fix SSH ping CPU spin and Linux releases
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package sshclient
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -57,3 +59,94 @@ func TestJumpHostClientConfigUsesFinalHostKeyCallback(t *testing.T) {
|
||||
t.Fatal("final host key callback was not used")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPingReturnsAfterSSHTransportCloses(t *testing.T) {
|
||||
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey() error = %v", err)
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(privateKey)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSignerFromKey() error = %v", err)
|
||||
}
|
||||
|
||||
serverConfig := &ssh.ServerConfig{NoClientAuth: true}
|
||||
serverConfig.AddHostKey(signer)
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Listen() error = %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = listener.Close() })
|
||||
|
||||
serverReady := make(chan *ssh.ServerConn, 1)
|
||||
serverErrors := make(chan error, 1)
|
||||
go func() {
|
||||
conn, acceptErr := listener.Accept()
|
||||
if acceptErr != nil {
|
||||
serverErrors <- acceptErr
|
||||
return
|
||||
}
|
||||
serverConn, _, _, handshakeErr := ssh.NewServerConn(conn, serverConfig)
|
||||
if handshakeErr != nil {
|
||||
_ = conn.Close()
|
||||
serverErrors <- handshakeErr
|
||||
return
|
||||
}
|
||||
serverReady <- serverConn
|
||||
}()
|
||||
|
||||
conn, err := net.DialTimeout("tcp", listener.Addr().String(), 2*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("DialTimeout() error = %v", err)
|
||||
}
|
||||
clientConn, channels, requests, err := ssh.NewClientConn(conn, listener.Addr().String(), &ssh.ClientConfig{
|
||||
User: "test",
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // Test server uses an ephemeral key.
|
||||
})
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
t.Fatalf("NewClientConn() error = %v", err)
|
||||
}
|
||||
rawClient := ssh.NewClient(clientConn, channels, requests)
|
||||
|
||||
var serverConn *ssh.ServerConn
|
||||
select {
|
||||
case serverConn = <-serverReady:
|
||||
case serverErr := <-serverErrors:
|
||||
t.Fatalf("SSH server handshake error = %v", serverErr)
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for SSH server handshake")
|
||||
}
|
||||
|
||||
client := NewClient(&models.Profile{})
|
||||
client.mu.Lock()
|
||||
client.sshClient = rawClient
|
||||
client.connected = true
|
||||
client.mu.Unlock()
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
if err := serverConn.Close(); err != nil {
|
||||
t.Fatalf("server Close() error = %v", err)
|
||||
}
|
||||
waitDone := make(chan error, 1)
|
||||
go func() { waitDone <- rawClient.Wait() }()
|
||||
select {
|
||||
case <-waitDone:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for SSH transport to close")
|
||||
}
|
||||
|
||||
pingDone := make(chan error, 1)
|
||||
go func() {
|
||||
_, pingErr := client.Ping()
|
||||
pingDone <- pingErr
|
||||
}()
|
||||
select {
|
||||
case pingErr := <-pingDone:
|
||||
if pingErr == nil {
|
||||
t.Fatal("Ping() succeeded after the SSH transport closed")
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("Ping() did not return after the SSH transport closed")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user