Skip to content

Commit 91198c9

Browse files
committed
Support explicitly passing connectors
1 parent 6c88baf commit 91198c9

3 files changed

Lines changed: 107 additions & 12 deletions

File tree

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,44 @@ $connector->connect('tls://localhost:443')->then(function (ConnectionInterface $
340340
about [socket context options](http://php.net/manual/en/context.socket.php)
341341
and [SSL context options](http://php.net/manual/en/context.ssl.php).
342342

343+
Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and
344+
`unix://` URI schemes.
345+
For this, it sets up the required connector classes automatically.
346+
If you want to explicitly pass custom connectors for any of these, you can simply
347+
pass an instance implementing the `ConnectorInterface` like this:
348+
349+
```php
350+
$dnsResolverFactory = new React\Dns\Resolver\Factory();
351+
$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
352+
$tcp = new DnsConnector(new TcpConnector($loop), $resolver);
353+
354+
$tls = new SecureConnector($tcp, $loop);
355+
356+
$unix = new UnixConnector($loop);
357+
358+
$connector = new Connector($loop, array(
359+
'tcp' => $tcp,
360+
'dns' => false,
361+
'tls' => $tls,
362+
'unix' => $unix,
363+
));
364+
365+
$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
366+
$connection->write('...');
367+
$connection->end();
368+
});
369+
```
370+
371+
> Internally, the `tcp://` connector will always be wrapped by the DNS resolver,
372+
unless you disable DNS like in the above example. In this case, the `tcp://`
373+
connector receives the actual hostname instead of only the resolved IP address
374+
and is thus responsible for performing the lookup.
375+
Internally, the automatically created `tls://` connector will always wrap the
376+
underlying `tcp://` connector for establishing the underlying plaintext
377+
TCP/IP connection before enabling secure TLS mode. If you want to use a custom
378+
underlying `tcp://` connector for secure TLS connections only, you may
379+
explicitly pass a `tls://` connector like above instead.
380+
343381
## Advanced Usage
344382

345383
### TcpConnector

src/Connector.php

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ public function __construct(LoopInterface $loop, array $options = array())
3737
'unix' => true,
3838
);
3939

40-
$tcp = new TcpConnector(
41-
$loop,
42-
is_array($options['tcp']) ? $options['tcp'] : array()
43-
);
40+
if ($options['tcp'] instanceof ConnectorInterface) {
41+
$tcp = $options['tcp'];
42+
} else {
43+
$tcp = new TcpConnector(
44+
$loop,
45+
is_array($options['tcp']) ? $options['tcp'] : array()
46+
);
47+
}
48+
4449
if ($options['dns'] !== false) {
4550
if ($options['dns'] instanceof Resolver) {
4651
$resolver = $options['dns'];
@@ -60,17 +65,21 @@ public function __construct(LoopInterface $loop, array $options = array())
6065
}
6166

6267
if ($options['tls'] !== false) {
63-
$tls = new SecureConnector(
64-
$tcp,
65-
$loop,
66-
is_array($options['tls']) ? $options['tls'] : array()
67-
);
68-
$this->connectors['tls'] = $tls;
68+
if (!$options['tls'] instanceof ConnectorInterface) {
69+
$options['tls'] = new SecureConnector(
70+
$tcp,
71+
$loop,
72+
is_array($options['tls']) ? $options['tls'] : array()
73+
);
74+
}
75+
$this->connectors['tls'] = $options['tls'];
6976
}
7077

7178
if ($options['unix'] !== false) {
72-
$unix = new UnixConnector($loop);
73-
$this->connectors['unix'] = $unix;
79+
if (!$options['unix'] instanceof ConnectorInterface) {
80+
$options['unix'] = new UnixConnector($loop);
81+
}
82+
$this->connectors['unix'] = $options['unix'];
7483
}
7584
}
7685

tests/ConnectorTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@
77

88
class ConnectorTest extends TestCase
99
{
10+
public function testConnectorUsesTcpAsDefaultScheme()
11+
{
12+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
13+
14+
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
15+
$tcp->expects($this->once())->method('connect')->with('127.0.0.1:80');
16+
17+
$connector = new Connector($loop, array(
18+
'tcp' => $tcp
19+
));
20+
21+
$connector->connect('127.0.0.1:80');
22+
}
23+
24+
public function testConnectorPassedThroughHostnameIfDnsIsDisabled()
25+
{
26+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
27+
28+
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
29+
$tcp->expects($this->once())->method('connect')->with('tcp://google.com:80');
30+
31+
$connector = new Connector($loop, array(
32+
'tcp' => $tcp,
33+
'dns' => false
34+
));
35+
36+
$connector->connect('tcp://google.com:80');
37+
}
38+
1039
public function testConnectorWithUnknownSchemeAlwaysFails()
1140
{
1241
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
@@ -74,4 +103,23 @@ public function testConnectorUsesGivenResolverInstance()
74103

75104
$connector->connect('google.com:80');
76105
}
106+
107+
public function testConnectorUsesResolvedHostnameIfDnsIsUsed()
108+
{
109+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
110+
111+
$promise = new Promise(function ($resolve) { $resolve('127.0.0.1'); });
112+
$resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
113+
$resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
114+
115+
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
116+
$tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com');
117+
118+
$connector = new Connector($loop, array(
119+
'tcp' => $tcp,
120+
'dns' => $resolver
121+
));
122+
123+
$connector->connect('tcp://google.com:80');
124+
}
77125
}

0 commit comments

Comments
 (0)