From fd74e4d6602f04cd667695882efc0668271f6e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 12 Oct 2026 02:03:10 +0200 Subject: [PATCH] Improve PHP 8.1+ support by avoiding deprecation when SSH client dies --- src/SshProcessConnector.php | 2 +- src/SshSocksConnector.php | 2 +- tests/IntegrationSshProcessConnectorTest.php | 18 +++++++++ tests/IntegrationSshSocksConnectorTest.php | 41 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/SshProcessConnector.php b/src/SshProcessConnector.php index 995316f..372e892 100644 --- a/src/SshProcessConnector.php +++ b/src/SshProcessConnector.php @@ -153,7 +153,7 @@ public function connect($uri) } $deferred->reject(new \RuntimeException( 'Connection to ' . $uri . ' failed because SSH client died (' . $last . ')', - $code + (int) $code )); }); diff --git a/src/SshSocksConnector.php b/src/SshSocksConnector.php index c66c7f6..ffff526 100644 --- a/src/SshSocksConnector.php +++ b/src/SshSocksConnector.php @@ -246,7 +246,7 @@ private function listen() $process->on('exit', function ($code) use ($deferred, &$last) { $deferred->reject(new \RuntimeException( $last, - $code + (int) $code )); }); diff --git a/tests/IntegrationSshProcessConnectorTest.php b/tests/IntegrationSshProcessConnectorTest.php index 873b72a..25551d1 100644 --- a/tests/IntegrationSshProcessConnectorTest.php +++ b/tests/IntegrationSshProcessConnectorTest.php @@ -56,6 +56,24 @@ public function testConnectWillRejectWithExceptionWhenProcessOutputsEndsWithoutC Loop::run(); } + public function testConnectCancellationWillRejectWithException() + { + $connector = new SshProcessConnector('host'); + + $ref = new \ReflectionProperty($connector, 'cmd'); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } + $ref->setValue($connector, 'exec sleep 10; #'); + + $promise = $connector->connect('example.com:80'); + $promise->cancel(); + + $promise->then(null, $this->expectCallableOnceWith($this->isInstanceOf('RuntimeException'))); + + Loop::run(); + } + public function testConnectWillResolveWithConnectionThatWillEmitImmediateDataFromProcessStdoutAfterChannelOpenConfirmMessage() { $connector = new SshProcessConnector('host'); diff --git a/tests/IntegrationSshSocksConnectorTest.php b/tests/IntegrationSshSocksConnectorTest.php index 82cdbda..87681c6 100644 --- a/tests/IntegrationSshSocksConnectorTest.php +++ b/tests/IntegrationSshSocksConnectorTest.php @@ -5,6 +5,7 @@ use Clue\React\SshProxy\SshSocksConnector; use React\EventLoop\Loop; use React\Promise\Deferred; +use React\Promise\PromiseInterface; class IntegrationSshSocksConnectorTest extends TestCase { @@ -76,4 +77,44 @@ public function testConnectWillRejectWithExceptionAndNotInvokeSocksConnectorWhen Loop::run(); } + + public function testConnectWillRejectWithExceptionAndTerminateIdleProcessWhenSocksConnectorRejects() + { + $connector = new SshSocksConnector('host'); + + $ref = new \ReflectionProperty($connector, 'cmd'); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } + $ref->setValue($connector, 'echo "debug1: channel 0: new [port listener]" >&2; exec sleep 10; #'); + + $socks = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + $socks->expects($this->once())->method('connect')->willReturn(\React\Promise\reject(new \RuntimeException('Connection refused'))); + + $ref = new \ReflectionProperty($connector, 'socks'); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } + $ref->setValue($connector, $socks); + + $promise = $connector->connect('example.com:80'); + $promise->then(null, $this->expectCallableOnceWith($this->isInstanceOf('RuntimeException'))); + + $ref = new \ReflectionProperty($connector, 'listen'); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } + $listen = $ref->getValue($connector); + assert($listen instanceof PromiseInterface); + + $process = null; + $listen->then(function ($value) use (&$process) { + $process = $value; + }); + + Loop::run(); + + $this->assertInstanceOf('React\ChildProcess\Process', $process); + $this->assertTrue($process->isTerminated()); + } }