Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify usage by supporting new default loop #27

Merged
merged 1 commit into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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('user@example.com');

$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com', $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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
```

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('user@example.com', $loop);
+ $proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
+ $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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');
```

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('user@example.com?bind=127.1.1.1:1081', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com?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('user@example.com', $loop);
+ $proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');
+ $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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

$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('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user@example.com');

// 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:pass@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector('user:pass@example.com');
// or
$proxy = new Clue\React\SshProxy\SshSocksConnector('user:pass@example.com', $loop);
$proxy = new Clue\React\SshProxy\SshSocksConnector('user:pass@example.com');
```

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