Skip to content

Commit

Permalink
Merge pull request #27 from clue-labs/default-loop
Browse files Browse the repository at this point in the history
Simplify usage by supporting new default loop
  • Loading branch information
clue authored Jul 20, 2021
2 parents 7491db0 + 27315a7 commit b2d791f
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 101 deletions.
87 changes: 45 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,9 @@ The following example code demonstrates how this library can be used to send a
plaintext HTTP request to google.com through a remote SSH server:

```php
$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');

$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false
));
Expand All @@ -92,8 +91,6 @@ $connector->connect('tcp://google.com:80')->then(function (React\Socket\Connecti
echo '[DONE]';
});
}, 'printf');

$loop->run();
```

See also the [examples](examples).
Expand Down Expand Up @@ -121,17 +118,22 @@ systems, you may simply install it like this:
$ sudo apt install openssh-client
```

Its constructor simply accepts an SSH proxy server URL and a loop to bind to:
Its constructor simply accepts an SSH proxy server URL:

```php
$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
```

The proxy URL may or may not contain a scheme and port definition. The default
port will be `22` for SSH, but you may have to use a custom port depending on
your SSH server setup.

This class takes an optional `LoopInterface|null $loop` parameter that can be used to
pass the event loop instance to use for this object. You can use a `null` value
here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
This value SHOULD NOT be given unless you're sure you want to explicitly use a
given event loop instance.

Keep in mind that this class is implemented as a lightweight process wrapper
around the `ssh` client binary and that it will spawn one `ssh` process for each
connection. If you open more connections, it will spawn one `ssh` process for
Expand Down Expand Up @@ -160,7 +162,7 @@ higher-level component:

```diff
- $acme = new AcmeApi($connector);
+ $proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
+ $proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
+ $acme = new AcmeApi($proxy);
```

Expand Down Expand Up @@ -189,17 +191,22 @@ simply install it like this:
$ sudo apt install openssh-client
```

Its constructor simply accepts an SSH proxy server URL and a loop to bind to:
Its constructor simply accepts an SSH proxy server URL:

```php
$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');
```

The proxy URL may or may not contain a scheme and port definition. The default
port will be `22` for SSH, but you may have to use a custom port depending on
your SSH server setup.

This class takes an optional `LoopInterface|null $loop` parameter that can be used to
pass the event loop instance to use for this object. You can use a `null` value
here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
This value SHOULD NOT be given unless you're sure you want to explicitly use a
given event loop instance.

Keep in mind that this class is implemented as a lightweight process wrapper
around the `ssh` client binary and that it will spawn one `ssh` process for
multiple connections. This process will take some time to create a new SSH
Expand All @@ -216,7 +223,7 @@ to use multiple instances of this class to connect to different SSH proxy
servers, you may optionally pass a unique bind address like this:

```php
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]?bind=127.1.1.1:1081', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]?bind=127.1.1.1:1081',);
```

> *Security note for multi-user systems*: This class will spawn the SSH client
Expand Down Expand Up @@ -244,7 +251,7 @@ higher-level component:

```diff
- $acme = new AcmeApi($connector);
+ $proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
+ $proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');
+ $acme = new AcmeApi($proxy);
```

Expand All @@ -260,9 +267,9 @@ a streaming plain TCP/IP connection on the `SshProcessConnector` or `SshSocksCon
and use any higher level protocol like so:

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\ConnectionInterface $connection) {
$connection->write("EHLO local\r\n");
Expand All @@ -276,11 +283,11 @@ You can either use the `SshProcessConnector` or `SshSocksConnector` directly or
may want to wrap this connector in ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector):

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false
));
Expand Down Expand Up @@ -309,9 +316,9 @@ ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector) or the
low-level [`SecureConnector`](https://github.com/reactphp/socket#secureconnector):

```php
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false
));
Expand Down Expand Up @@ -341,14 +348,14 @@ In order to send HTTP requests, you first have to add a dependency for
This allows you to send both plain HTTP and TLS-encrypted HTTPS requests like this:

```php
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false
));

$browser = new React\Http\Browser($loop, $connector);
$browser = new React\Http\Browser(null, $connector);

$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
var_dump($response->getHeaders(), (string) $response->getBody());
Expand Down Expand Up @@ -378,11 +385,10 @@ the above SSH proxy server setup, so we can access a firewalled MySQL database
server through an SSH tunnel. Here's the gist:

```php
$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');

$uri = 'test:test@localhost/test';
$factory = new React\MySQL\Factory($loop, $proxy);
$factory = new React\MySQL\Factory(null, $proxy);
$connection = $factory->createLazyConnection($uri);

$connection->query('SELECT * FROM book')->then(
Expand All @@ -395,8 +401,6 @@ $connection->query('SELECT * FROM book')->then(
);

$connection->quit();

$loop->run();
```

See also [example #21](examples) for more details.
Expand Down Expand Up @@ -427,11 +431,11 @@ It provides the same `connect()` method, but will automatically reject the
underlying connection attempt if it takes too long:

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false,
'timeout' => 3.0
Expand Down Expand Up @@ -473,11 +477,11 @@ Given that remote DNS resolution is assumed to be the preferred mode, all
other examples explicitly disable DNS resolution like this:

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => false
));
Expand All @@ -486,12 +490,12 @@ $connector = new React\Socket\Connector($loop, array(
If you want to explicitly use *local DNS resolution*, you can use the following code:

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('[email protected]');

// set up Connector which uses Google's public DNS (8.8.8.8)
$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'dns' => '8.8.8.8'
));
Expand Down Expand Up @@ -525,9 +529,9 @@ If your SSH proxy server requires password authentication, you may pass the
username and password as part of the SSH proxy server URL like this:

```php
$proxy = new Clue\React\SshProxy\SshProcessConnector('user:[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user:[email protected]');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user:[email protected]', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user:[email protected]');
```

For this to work, you will have to have the `sshpass` binary installed. On
Expand All @@ -545,8 +549,7 @@ $user = 'he:llo';
$pass = 'p@ss';

$proxy = new Clue\React\SshProxy\SshProcessConnector(
rawurlencode($user) . ':' . rawurlencode($pass) . '@example.com:2222',
$loop
rawurlencode($user) . ':' . rawurlencode($pass) . '@example.com:2222'
);
```

Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
"php": ">=5.3",
"clue/socks-react": "^1.0",
"react/child-process": "^0.6",
"react/event-loop": "^1.0 || ^0.5",
"react/event-loop": "^1.2",
"react/promise": "^2.1 || ^1.2.1",
"react/socket": "^1.1",
"react/stream": "^1.0 || ^0.7.2"
"react/socket": "^1.8",
"react/stream": "^1.2"
},
"require-dev": {
"clue/block-react": "^1.3",
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36",
"react/http": "^1.0",
"react/mysql": "^0.5.3"
"react/http": "^1.4",
"react/mysql": "^0.5.5"
}
}
9 changes: 3 additions & 6 deletions examples/01-https-request.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,18 @@

$url = getenv('SSH_PROXY') !== false ? getenv('SSH_PROXY') : 'ssh://localhost:22';

$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshProcessConnector($url, $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector($url);

$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));

$browser = new React\Http\Browser($loop, $connector);
$browser = new React\Http\Browser(null, $connector);

$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
var_dump($response->getHeaders(), (string) $response->getBody());
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
14 changes: 5 additions & 9 deletions examples/02-optional-proxy-https-request.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,22 @@

require __DIR__ . '/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();

// SSH_PROXY environment given? use this as the proxy URL
$connector = null;
if (getenv('SSH_PROXY') !== false) {
$proxy = new Clue\React\SshProxy\SshProcessConnector(getenv('SSH_PROXY'), $loop);
$connector = new React\Socket\Connector($loop, array(
$proxy = new Clue\React\SshProxy\SshProcessConnector(getenv('SSH_PROXY'));

$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));
} else {
$connector = new React\Socket\Connector($loop);
}

$browser = new React\Http\Browser($loop, $connector);
$browser = new React\Http\Browser(null, $connector);

$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
var_dump($response->getHeaders(), (string) $response->getBody());
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
7 changes: 2 additions & 5 deletions examples/11-proxy-raw-http-protocol.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

$url = getenv('SSH_PROXY') !== false ? getenv('SSH_PROXY') : 'ssh://localhost:22';

$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshProcessConnector($url);

$proxy = new Clue\React\SshProxy\SshProcessConnector($url, $loop);
$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
Expand All @@ -33,5 +32,3 @@
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
11 changes: 4 additions & 7 deletions examples/12-optional-proxy-raw-http-protocol.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@

require __DIR__ . '/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();

// SSH_PROXY environment given? use this as the proxy URL
if (getenv('SSH_PROXY') !== false) {
$proxy = new Clue\React\SshProxy\SshProcessConnector(getenv('SSH_PROXY'), $loop);
$connector = new React\Socket\Connector($loop, array(
$proxy = new Clue\React\SshProxy\SshProcessConnector(getenv('SSH_PROXY'));

$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));
} else {
$connector = new React\Socket\Connector($loop);
$connector = new React\Socket\Connector();
}

$connector->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
Expand All @@ -40,5 +39,3 @@
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
7 changes: 2 additions & 5 deletions examples/21-proxy-raw-https-protocol.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

$url = getenv('SSH_PROXY') !== false ? getenv('SSH_PROXY') : 'ssh://localhost:22';

$loop = React\EventLoop\Factory::create();
$proxy = new Clue\React\SshProxy\SshSocksConnector($url);

$proxy = new Clue\React\SshProxy\SshSocksConnector($url, $loop);
$connector = new React\Socket\Connector($loop, array(
$connector = new React\Socket\Connector(null, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
Expand All @@ -33,5 +32,3 @@
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
Loading

0 comments on commit b2d791f

Please sign in to comment.