Skip to content

Update to reactphp/http v1.0.0 and add improved HTTP examples #21

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

Merged
merged 2 commits into from
Oct 23, 2020
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
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,15 +315,37 @@ $connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionI

### HTTP requests

HTTP operates on a higher layer than this low-level SSH proxy implementation.
If you want to issue HTTP requests, you can add a dependency for
[clue/reactphp-buzz](https://github.com/clue/reactphp-buzz).
It can interact with this library by issuing all
[HTTP requests through an SSH proxy server](https://github.com/clue/reactphp-buzz#ssh-proxy).
When using the `SshSocksConnector` (recommended), this works for both plain HTTP
This library also allows you to send
[HTTP requests through an SSH proxy server](https://github.com/reactphp/http#ssh-proxy).

In order to send HTTP requests, you first have to add a dependency for
[ReactPHP's async HTTP client](https://github.com/reactphp/http#client-usage).
This allows you to send both plain HTTP and TLS-encrypted HTTPS requests like this:

```php
$proxy = new Clue\React\SshProxy\SshSocksConnector('me@localhost:22', $loop);

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

$browser = new React\Http\Browser($loop, $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;
});
```

We recommend using the `SshSocksConnector`, this works for both plain HTTP
and TLS-encrypted HTTPS requests. When using the `SshProcessConnector`, this only
works for plaintext HTTP requests.

See also [ReactPHP's HTTP client](https://github.com/reactphp/http#client-usage)
and any of the [examples](examples) for more details.

### Database tunnel

We should now have a basic understanding of how we can tunnel any TCP/IP-based
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"require-dev": {
"clue/block-react": "^1.3",
"phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.36",
"react/http": "^1.0",
"react/mysql": "^0.5.3"
}
}
33 changes: 33 additions & 0 deletions examples/01-https-request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

// A simple example which uses an HTTP client to requesthttps://example.com/ through an SSH proxy server.
// The proxy can be given through the SSH_PROXY env and defaults to localhost otherwise.
//
// You can assign the SSH_PROXY environment and prefix this with a space to make
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/01-https-request.php

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

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

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

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

$browser = new React\Http\Browser($loop, $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();
36 changes: 36 additions & 0 deletions examples/02-optional-proxy-https-request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

// A simple example which uses an HTTP client to request https://example.com/ (optional: Through an SSH proxy server.)
//
// The proxy can be given through the SSH_PROXY env or does not use a proxy otherwise.
// You can assign the SSH_PROXY environment and prefix this with a space to make
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/02-optional-proxy-https-request.php

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(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));
} else {
$connector = new React\Socket\Connector($loop);
}

$browser = new React\Http\Browser($loop, $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();
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,31 @@
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/01-proxy-http.php
// $ php examples/11-proxy-raw-http-protocol.php
//
// For illustration purposes only. If you want to send HTTP requests in a real
// world project, take a look at https://github.com/clue/reactphp-buzz#ssh-proxy

use Clue\React\SshProxy\SshProcessConnector;
use React\Socket\Connector;
use React\Socket\ConnectionInterface;
// world project, take a look at example #01, example #02 and https://github.com/reactphp/http#client-usage.

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

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

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

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

$connector->connect('tcp://google.com:80')->then(function (ConnectionInterface $stream) {
$connector->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $stream) {
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
$stream->on('data', function ($chunk) {
echo $chunk;
});
}, 'printf');
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
<?php

// A simple example which requests http://google.com/ either directly or through
// an SSH proxy server.
// A simple example which requests http://google.com/ either directly or through an SSH proxy server.
// The proxy can be given through the SSH_PROXY env or does not use a proxy otherwise.
// This example highlights how changing from direct connection to using a proxy
// actually adds very little complexity and does not mess with your actual
// network protocol otherwise.
//
// You can assign the SSH_PROXY environment and prefix this with a space to make
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/02-optional-proxy-http.php
// $ php examples/12-optional-proxy-raw-http-protocol.php
//
// This example highlights how changing from direct connection to using a proxy
// actually adds very little complexity and does not mess with your actual
// network protocol otherwise.
//
// For illustration purposes only. If you want to send HTTP requests in a real
// world project, take a look at https://github.com/clue/reactphp-buzz#ssh-proxy

use Clue\React\SshProxy\SshProcessConnector;
use React\Socket\Connector;
use React\Socket\ConnectionInterface;
// world project, take a look at example #01, example #02 and https://github.com/reactphp/http#client-usage.

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 SshProcessConnector(getenv('SSH_PROXY'), $loop);
$connector = new Connector($loop, array(
$proxy = new Clue\React\SshProxy\SshProcessConnector(getenv('SSH_PROXY'), $loop);
$connector = new React\Socket\Connector($loop, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));
} else {
$connector = new Connector($loop);
$connector = new React\Socket\Connector($loop);
}

$connector->connect('tcp://google.com:80')->then(function (ConnectionInterface $stream) {
$connector->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $stream) {
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
$stream->on('data', function ($chunk) {
echo $chunk;
});
}, 'printf');
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,31 @@
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/11-proxy-https.php
// $ php examples/21-proxy-raw-https-protocol.php
//
// For illustration purposes only. If you want to send HTTP requests in a real
// world project, take a look at https://github.com/clue/reactphp-buzz#ssh-proxy

use Clue\React\SshProxy\SshSocksConnector;
use React\Socket\Connector;
use React\Socket\ConnectionInterface;
// world project, take a look at example #01, example #02 and https://github.com/reactphp/http#client-usage.

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

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

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

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

$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
$connector->connect('tls://google.com:443')->then(function (React\Socket\ConnectionInterface $stream) {
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
$stream->on('data', function ($chunk) {
echo $chunk;
});
}, 'printf');
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
44 changes: 44 additions & 0 deletions examples/22-optional-proxy-raw-https-protocol.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

// A simple example which requests https://google.com/ either directly or through an SSH proxy server.
// The proxy can be given through the SSH_PROXY env or does not use a proxy otherwise.
//
// You can assign the SSH_PROXY environment and prefix this with a space to make
// sure your login credentials are not stored in your bash history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ php examples/22-optional-proxy-raw-https-protocol.php
//
// This example highlights how changing from direct connection to using a proxy
// actually adds very little complexity and does not mess with your actual
// network protocol otherwise.
//
// For illustration purposes only. If you want to send HTTP requests in a real
// world project, take a look at example #01, example #02 and https://github.com/reactphp/http#client-usage.

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(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false
));
} else {
$connector = new React\Socket\Connector($loop);
}

$connector->connect('tls://google.com:443')->then(function (React\Socket\ConnectionInterface $stream) {
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
$stream->on('data', function ($chunk) {
echo $chunk;
});
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

$loop->run();
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,23 @@
// history like this:
//
// $ export SSH_PROXY=user:secret@example.com
// $ export MYSQL_LOGIN=user:password@localhost
// $ php examples/21-mysql-ssh-tunnel.php
// $ export MYSQL_LOGIN=user:password@localhost
// $ php examples/31-mysql-ssh-tunnel.php
//
// See also https://github.com/friends-of-reactphp/mysql

use Clue\React\SshProxy\SshProcessConnector;
use React\MySQL\Factory;
use React\MySQL\QueryResult;

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

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

$url = getenv('SSH_PROXY') !== false ? getenv('SSH_PROXY') : 'ssh://localhost:22';
$proxy = new SshProcessConnector($url, $loop);
$proxy = new Clue\React\SshProxy\SshProcessConnector($url, $loop);

$url = getenv('MYSQL_LOGIN') !== false ? getenv('MYSQL_LOGIN') : 'user:pass@localhost';
$factory = new Factory($loop, $proxy);
$factory = new React\MySQL\Factory($loop, $proxy);
$client = $factory->createLazyConnection($url);

$client->query('SELECT * FROM (SELECT "foo" UNION SELECT "bar") data')->then(function (QueryResult $query) {
$client->query('SELECT * FROM (SELECT "foo" UNION SELECT "bar") data')->then(function (React\MySQL\QueryResult $query) {
var_dump($query->resultRows);
}, 'printf');
$client->quit();
Expand Down