forked from clue/reactphp-ssh-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFunctionalSshSocksConnectorTest.php
More file actions
131 lines (102 loc) · 4.64 KB
/
Copy pathFunctionalSshSocksConnectorTest.php
File metadata and controls
131 lines (102 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
namespace Clue\Tests\React\SshProxy;
use Clue\React\SshProxy\SshSocksConnector;
use React\EventLoop\Loop;
class FunctionalSshSocksConnectorTest extends TestCase
{
const TIMEOUT = 10.0;
private $connector;
/**
* @before
*/
public function setUpConnector()
{
$url = getenv('SSH_PROXY');
if ($url === false) {
$this->markTestSkipped('No SSH_PROXY env set');
}
$this->connector = new SshSocksConnector($url);
}
/**
* @after
*/
public function tearDownSSHClientProcess()
{
// run loop in order to shut down SSH client process again
\Clue\React\Block\sleep(0.001, Loop::get());
}
public function testConnectInvalidProxyUriWillReturnRejectedPromise()
{
$this->connector = new SshSocksConnector(getenv('SSH_PROXY') . '.invalid');
$promise = $this->connector->connect('example.com:80');
$this->setExpectedException('RuntimeException', 'Connection to example.com:80 failed because SSH client process died');
\Clue\React\Block\await($promise, Loop::get(), self::TIMEOUT);
}
public function testConnectInvalidTargetWillReturnRejectedPromise()
{
$promise = $this->connector->connect('example.invalid:80');
$this->setExpectedException('RuntimeException', 'Connection to tcp://example.invalid:80 failed because connection to proxy was lost');
\Clue\React\Block\await($promise, Loop::get(), self::TIMEOUT);
}
public function testCancelConnectWillReturnRejectedPromise()
{
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
$this->setExpectedException('RuntimeException', 'Connection to example.com:80 cancelled while waiting for SSH client');
\Clue\React\Block\await($promise, Loop::get(), 0);
}
public function testConnectValidTargetWillReturnPromiseWhichResolvesToConnection()
{
$promise = $this->connector->connect('example.com:80');
$connection = \Clue\React\Block\await($promise, Loop::get(), self::TIMEOUT);
$this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
$this->assertTrue($connection->isReadable());
$this->assertTrue($connection->isWritable());
$connection->close();
}
public function testConnectValidTargetWillReturnPromiseWhichResolvesToConnectionForCustomBindAddress()
{
$this->connector = new SshSocksConnector(getenv('SSH_PROXY') . '?bind=127.0.0.1:1081', Loop::get());
$promise = $this->connector->connect('example.com:80');
$connection = \Clue\React\Block\await($promise, Loop::get(), self::TIMEOUT);
$this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
$this->assertTrue($connection->isReadable());
$this->assertTrue($connection->isWritable());
$connection->close();
}
public function testConnectPendingWillNotInheritActiveFileDescriptors()
{
$server = stream_socket_server('tcp://127.0.0.1:0');
$address = stream_socket_get_name($server, false);
// ensure that we can not listen on the same address twice
$copy = @stream_socket_server('tcp://' . $address);
if ($copy !== false) {
fclose($server);
fclose($copy);
$this->markTestSkipped('Platform does not prevent binding to same address (Windows?)');
}
$promise = $this->connector->connect('example.com:80');
// close server and ensure we can start a new server on the previous address
// the pending SSH connection process should not inherit the existing server socket
fclose($server);
$server = @stream_socket_server('tcp://' . $address);
if ($server === false) {
// There's a very short race condition where the forked php process
// first has to `dup()` the file descriptor specs before invoking
// `exec()` to switch to the actual `ssh` child process. We don't
// need to wait for the child process to be ready, but only for the
// forked process to close the file descriptors. This happens ~80%
// of times on single core machines and almost never on multi core
// systems, so simply wait 5ms (plenty of time!) and retry again twice.
usleep(5000);
$server = @stream_socket_server('tcp://' . $address);
if ($server === false) {
usleep(5000);
$server = stream_socket_server('tcp://' . $address);
}
}
$this->assertTrue(is_resource($server));
fclose($server);
$promise->cancel();
}
}