Skip to content

Commit 1a0b360

Browse files
authored
Merge pull request #93 from clue-labs/connector
Add main Connector facade
2 parents e9efc9e + 03504a1 commit 1a0b360

File tree

8 files changed

+573
-104
lines changed

8 files changed

+573
-104
lines changed

README.md

Lines changed: 234 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,19 @@ handle multiple connections without blocking.
2323
* [ConnectionInterface](#connectioninterface)
2424
* [getRemoteAddress()](#getremoteaddress)
2525
* [getLocalAddress()](#getlocaladdress)
26-
* [Plaintext TCP/IP connections](#plaintext-tcpip-connections)
27-
* [DNS resolution](#dns-resolution)
28-
* [Secure TLS connections](#secure-tls-connections)
29-
* [Connection timeout](#connection-timeouts)
30-
* [Unix domain sockets](#unix-domain-sockets)
26+
* [Connector](#connector)
27+
* [Advanced Usage](#advanced-usage)
28+
* [TcpConnector](#tcpconnector)
29+
* [DnsConnector](#dnsconnector)
30+
* [SecureConnector](#secureconnector)
31+
* [TimeoutConnector](#timeoutconnector)
32+
* [UnixConnector](#unixconnector)
3133
* [Install](#install)
3234
* [Tests](#tests)
3335
* [License](#license)
3436

3537
## Usage
3638

37-
In order to use this project, you'll need the following react boilerplate code
38-
to initialize the main loop.
39-
40-
```php
41-
$loop = React\EventLoop\Factory::create();
42-
```
43-
4439
### ConnectorInterface
4540

4641
The `ConnectorInterface` is responsible for providing an interface for
@@ -187,7 +182,228 @@ If your system has multiple interfaces (e.g. a WAN and a LAN interface),
187182
you can use this method to find out which interface was actually
188183
used for this connection.
189184

190-
### Plaintext TCP/IP connections
185+
### Connector
186+
187+
The `Connector` class is the main class in this package that implements the
188+
[`ConnectorInterface`](#connectorinterface) and allows you to create streaming connections.
189+
190+
You can use this connector to create any kind of streaming connections, such
191+
as plaintext TCP/IP, secure TLS or local Unix connection streams.
192+
193+
It binds to the main event loop and can be used like this:
194+
195+
```php
196+
$loop = React\EventLoop\Factory::create();
197+
$connector = new Connector($loop);
198+
199+
$connector->connect($uri)->then(function (ConnectionInterface $connection) {
200+
$connection->write('...');
201+
$connection->end();
202+
});
203+
204+
$loop->run();
205+
```
206+
207+
In order to create a plaintext TCP/IP connection, you can simply pass a host
208+
and port combination like this:
209+
210+
```php
211+
$connector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) {
212+
$connection->write('...');
213+
$connection->end();
214+
});
215+
```
216+
217+
> If you do no specify a URI scheme in the destination URI, it will assume
218+
`tcp://` as a default and establish a plaintext TCP/IP connection.
219+
Note that TCP/IP connections require a host and port part in the destination
220+
URI like above, all other URI components are optional.
221+
222+
In order to create a secure TLS connection, you can use the `tls://` URI scheme
223+
like this:
224+
225+
```php
226+
$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $connection) {
227+
$connection->write('...');
228+
$connection->end();
229+
});
230+
```
231+
232+
In order to create a local Unix domain socket connection, you can use the
233+
`unix://` URI scheme like this:
234+
235+
```php
236+
$connector->connect('unix:///tmp/demo.sock')->then(function (ConnectionInterface $connection) {
237+
$connection->write('...');
238+
$connection->end();
239+
});
240+
```
241+
242+
Under the hood, the `Connector` is implemented as a *higher-level facade*
243+
for the lower-level connectors implemented in this package. This means it
244+
also shares all of their features and implementation details.
245+
If you want to typehint in your higher-level protocol implementation, you SHOULD
246+
use the generic [`ConnectorInterface`](#connectorinterface) instead.
247+
248+
In particular, the `Connector` class uses Google's public DNS server `8.8.8.8`
249+
to resolve all hostnames into underlying IP addresses by default.
250+
This implies that it also ignores your `hosts` file and `resolve.conf`, which
251+
means you won't be able to connect to `localhost` and other non-public
252+
hostnames by default.
253+
If you want to use a custom DNS server (such as a local DNS relay), you can set
254+
up the `Connector` like this:
255+
256+
```php
257+
$connector = new Connector($loop, array(
258+
'dns' => '127.0.1.1'
259+
));
260+
261+
$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
262+
$connection->write('...');
263+
$connection->end();
264+
});
265+
```
266+
267+
If you do not want to use a DNS resolver at all and want to connect to IP
268+
addresses only, you can also set up your `Connector` like this:
269+
270+
```php
271+
$connector = new Connector($loop, array(
272+
'dns' => false
273+
));
274+
275+
$connector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) {
276+
$connection->write('...');
277+
$connection->end();
278+
});
279+
```
280+
281+
Advanced: If you need a custom DNS `Resolver` instance, you can also set up
282+
your `Connector` like this:
283+
284+
```php
285+
$dnsResolverFactory = new React\Dns\Resolver\Factory();
286+
$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
287+
288+
$connector = new Connector($loop, array(
289+
'dns' => $resolver
290+
));
291+
292+
$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
293+
$connection->write('...');
294+
$connection->end();
295+
});
296+
```
297+
298+
By default, the `tcp://` and `tls://` URI schemes will use timeout value that
299+
repects your `default_socket_timeout` ini setting (which defaults to 60s).
300+
If you want a custom timeout value, you can simply pass this like this:
301+
302+
```php
303+
$connector = new Connector($loop, array(
304+
'timeout' => 10.0
305+
));
306+
```
307+
308+
Similarly, if you do not want to apply a timeout at all and let the operating
309+
system handle this, you can pass a boolean flag like this:
310+
311+
```php
312+
$connector = new Connector($loop, array(
313+
'timeout' => false
314+
));
315+
```
316+
317+
By default, the `Connector` supports the `tcp://`, `tls://` and `unix://`
318+
URI schemes. If you want to explicitly prohibit any of these, you can simply
319+
pass boolean flags like this:
320+
321+
```php
322+
// only allow secure TLS connections
323+
$connector = new Connector($loop, array(
324+
'tcp' => false,
325+
'tls' => true,
326+
'unix' => false,
327+
));
328+
329+
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $connection) {
330+
$connection->write('...');
331+
$connection->end();
332+
});
333+
```
334+
335+
The `tcp://` and `tls://` also accept additional context options passed to
336+
the underlying connectors.
337+
If you want to explicitly pass additional context options, you can simply
338+
pass arrays of context options like this:
339+
340+
```php
341+
// allow insecure TLS connections
342+
$connector = new Connector($loop, array(
343+
'tcp' => array(
344+
'bindto' => '192.168.0.1:0'
345+
),
346+
'tls' => array(
347+
'verify_peer' => false,
348+
'verify_peer_name' => false
349+
),
350+
));
351+
352+
$connector->connect('tls://localhost:443')->then(function (ConnectionInterface $connection) {
353+
$connection->write('...');
354+
$connection->end();
355+
});
356+
```
357+
358+
> For more details about context options, please refer to the PHP documentation
359+
about [socket context options](http://php.net/manual/en/context.socket.php)
360+
and [SSL context options](http://php.net/manual/en/context.ssl.php).
361+
362+
Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and
363+
`unix://` URI schemes.
364+
For this, it sets up the required connector classes automatically.
365+
If you want to explicitly pass custom connectors for any of these, you can simply
366+
pass an instance implementing the `ConnectorInterface` like this:
367+
368+
```php
369+
$dnsResolverFactory = new React\Dns\Resolver\Factory();
370+
$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
371+
$tcp = new DnsConnector(new TcpConnector($loop), $resolver);
372+
373+
$tls = new SecureConnector($tcp, $loop);
374+
375+
$unix = new UnixConnector($loop);
376+
377+
$connector = new Connector($loop, array(
378+
'tcp' => $tcp,
379+
'tls' => $tls,
380+
'unix' => $unix,
381+
382+
'dns' => false,
383+
'timeout' => false,
384+
));
385+
386+
$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
387+
$connection->write('...');
388+
$connection->end();
389+
});
390+
```
391+
392+
> Internally, the `tcp://` connector will always be wrapped by the DNS resolver,
393+
unless you disable DNS like in the above example. In this case, the `tcp://`
394+
connector receives the actual hostname instead of only the resolved IP address
395+
and is thus responsible for performing the lookup.
396+
Internally, the automatically created `tls://` connector will always wrap the
397+
underlying `tcp://` connector for establishing the underlying plaintext
398+
TCP/IP connection before enabling secure TLS mode. If you want to use a custom
399+
underlying `tcp://` connector for secure TLS connections only, you may
400+
explicitly pass a `tls://` connector like above instead.
401+
Internally, the `tcp://` and `tls://` connectors will always be wrapped by
402+
`TimeoutConnector`, unless you disable timeouts like in the above example.
403+
404+
## Advanced Usage
405+
406+
### TcpConnector
191407

192408
The `React\SocketClient\TcpConnector` class implements the
193409
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
@@ -246,7 +462,7 @@ be used to set up the TLS peer name.
246462
This is used by the `SecureConnector` and `DnsConnector` to verify the peer
247463
name and can also be used if you want a custom TLS peer name.
248464

249-
### DNS resolution
465+
### DnsConnector
250466

251467
The `DnsConnector` class implements the
252468
[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
@@ -260,7 +476,7 @@ Make sure to set up your DNS resolver and underlying TCP connector like this:
260476

261477
```php
262478
$dnsResolverFactory = new React\Dns\Resolver\Factory();
263-
$dns = $dnsResolverFactory->connectCached('8.8.8.8', $loop);
479+
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
264480

265481
$dnsConnector = new React\SocketClient\DnsConnector($tcpConnector, $dns);
266482

@@ -306,7 +522,7 @@ hostname and is used by the `TcpConnector` to set up the TLS peer name.
306522
If a `hostname` is given explicitly, this query parameter will not be modified,
307523
which can be useful if you want a custom TLS peer name.
308524

309-
### Secure TLS connections
525+
### SecureConnector
310526

311527
The `SecureConnector` class implements the
312528
[`ConnectorInterface`](#connectorinterface) and allows you to create secure
@@ -360,7 +576,7 @@ Failing to do so may result in a TLS peer name mismatch error or some hard to
360576
trace race conditions, because all stream resources will use a single, shared
361577
*default context* resource otherwise.
362578

363-
### Connection timeouts
579+
### TimeoutConnector
364580

365581
The `TimeoutConnector` class implements the
366582
[`ConnectorInterface`](#connectorinterface) and allows you to add timeout
@@ -391,7 +607,7 @@ $promise->cancel();
391607
Calling `cancel()` on a pending promise will cancel the underlying connection
392608
attempt, abort the timer and reject the resulting promise.
393609

394-
### Unix domain sockets
610+
### UnixConnector
395611

396612
The `UnixConnector` class implements the
397613
[`ConnectorInterface`](#connectorinterface) and allows you to connect to

examples/01-http.php

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
11
<?php
22

33
use React\EventLoop\Factory;
4-
use React\SocketClient\TcpConnector;
5-
use React\SocketClient\DnsConnector;
6-
use React\SocketClient\TimeoutConnector;
4+
use React\SocketClient\Connector;
75
use React\SocketClient\ConnectionInterface;
86

7+
$target = isset($argv[1]) ? $argv[1] : 'www.google.com:80';
8+
99
require __DIR__ . '/../vendor/autoload.php';
1010

1111
$loop = Factory::create();
12+
$connector = new Connector($loop);
1213

13-
$factory = new \React\Dns\Resolver\Factory();
14-
$resolver = $factory->create('8.8.8.8', $loop);
15-
16-
$tcp = new TcpConnector($loop);
17-
$dns = new DnsConnector($tcp, $resolver);
18-
19-
// time out connection attempt in 3.0s
20-
$dns = new TimeoutConnector($dns, 3.0, $loop);
21-
22-
$target = isset($argv[1]) ? $argv[1] : 'www.google.com:80';
23-
24-
$dns->connect($target)->then(function (ConnectionInterface $connection) use ($target) {
14+
$connector->connect($target)->then(function (ConnectionInterface $connection) use ($target) {
2515
$connection->on('data', function ($data) {
2616
echo $data;
2717
});

examples/02-https.php

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,17 @@
11
<?php
22

33
use React\EventLoop\Factory;
4-
use React\SocketClient\TcpConnector;
5-
use React\SocketClient\DnsConnector;
6-
use React\SocketClient\SecureConnector;
7-
use React\SocketClient\TimeoutConnector;
4+
use React\SocketClient\Connector;
85
use React\SocketClient\ConnectionInterface;
96

7+
$target = isset($argv[1]) ? $argv[1] : 'www.google.com:443';
8+
109
require __DIR__ . '/../vendor/autoload.php';
1110

1211
$loop = Factory::create();
12+
$connector = new Connector($loop);
1313

14-
$factory = new \React\Dns\Resolver\Factory();
15-
$resolver = $factory->create('8.8.8.8', $loop);
16-
17-
$tcp = new TcpConnector($loop);
18-
$dns = new DnsConnector($tcp, $resolver);
19-
$tls = new SecureConnector($dns, $loop);
20-
21-
// time out connection attempt in 3.0s
22-
$tls = new TimeoutConnector($tls, 3.0, $loop);
23-
24-
$target = isset($argv[1]) ? $argv[1] : 'www.google.com:443';
25-
26-
$tls->connect($target)->then(function (ConnectionInterface $connection) use ($target) {
14+
$connector->connect('tls://' . $target)->then(function (ConnectionInterface $connection) use ($target) {
2715
$connection->on('data', function ($data) {
2816
echo $data;
2917
});

0 commit comments

Comments
 (0)