Skip to content

Commit f39856e

Browse files
committed
Add timeout handling
1 parent cd74d45 commit f39856e

File tree

4 files changed

+74
-10
lines changed

4 files changed

+74
-10
lines changed

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,25 @@ $connector->connect('localhost:80')->then(function (ConnectionInterface $connect
295295
});
296296
```
297297

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+
298317
By default, the `Connector` supports the `tcp://`, `tls://` and `unix://`
299318
URI schemes. If you want to explicitly prohibit any of these, you can simply
300319
pass boolean flags like this:
@@ -357,9 +376,11 @@ $unix = new UnixConnector($loop);
357376

358377
$connector = new Connector($loop, array(
359378
'tcp' => $tcp,
360-
'dns' => false,
361379
'tls' => $tls,
362380
'unix' => $unix,
381+
382+
'dns' => false,
383+
'timeout' => false,
363384
));
364385

365386
$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
@@ -377,6 +398,8 @@ $connector->connect('google.com:80')->then(function (ConnectionInterface $connec
377398
TCP/IP connection before enabling secure TLS mode. If you want to use a custom
378399
underlying `tcp://` connector for secure TLS connections only, you may
379400
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.
380403

381404
## Advanced Usage
382405

src/Connector.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ public function __construct(LoopInterface $loop, array $options = array())
3232
// apply default options if not explicitly given
3333
$options += array(
3434
'tcp' => true,
35-
'dns' => true,
3635
'tls' => true,
3736
'unix' => true,
37+
38+
'dns' => true,
39+
'timeout' => true,
3840
);
3941

42+
if ($options['timeout'] === true) {
43+
$options['timeout'] = (float)ini_get("default_socket_timeout");
44+
}
45+
4046
if ($options['tcp'] instanceof ConnectorInterface) {
4147
$tcp = $options['tcp'];
4248
} else {
@@ -63,7 +69,17 @@ public function __construct(LoopInterface $loop, array $options = array())
6369
$connectors = array();
6470

6571
if ($options['tcp'] !== false) {
66-
$connectors['tcp'] = $tcp;
72+
$options['tcp'] = $tcp;
73+
74+
if ($options['timeout'] !== false) {
75+
$options['tcp'] = new TimeoutConnector(
76+
$options['tcp'],
77+
$options['timeout'],
78+
$loop
79+
);
80+
}
81+
82+
$connectors['tcp'] = $options['tcp'];
6783
}
6884

6985
if ($options['tls'] !== false) {
@@ -74,6 +90,15 @@ public function __construct(LoopInterface $loop, array $options = array())
7490
is_array($options['tls']) ? $options['tls'] : array()
7591
);
7692
}
93+
94+
if ($options['timeout'] !== false) {
95+
$options['tls'] = new TimeoutConnector(
96+
$options['tls'],
97+
$options['timeout'],
98+
$loop
99+
);
100+
}
101+
77102
$connectors['tls'] = $options['tls'];
78103
}
79104

tests/ConnectorTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ public function testConnectorUsesTcpAsDefaultScheme()
1111
{
1212
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
1313

14+
$promise = new Promise(function () { });
1415
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
15-
$tcp->expects($this->once())->method('connect')->with('127.0.0.1:80');
16+
$tcp->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
1617

1718
$connector = new Connector($loop, array(
1819
'tcp' => $tcp
@@ -25,8 +26,9 @@ public function testConnectorPassedThroughHostnameIfDnsIsDisabled()
2526
{
2627
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
2728

29+
$promise = new Promise(function () { });
2830
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
29-
$tcp->expects($this->once())->method('connect')->with('tcp://google.com:80');
31+
$tcp->expects($this->once())->method('connect')->with('tcp://google.com:80')->willReturn($promise);
3032

3133
$connector = new Connector($loop, array(
3234
'tcp' => $tcp,
@@ -112,8 +114,9 @@ public function testConnectorUsesResolvedHostnameIfDnsIsUsed()
112114
$resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
113115
$resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
114116

117+
$promise = new Promise(function () { });
115118
$tcp = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
116-
$tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com');
119+
$tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com')->willReturn($promise);
117120

118121
$connector = new Connector($loop, array(
119122
'tcp' => $tcp,

tests/IntegrationTest.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ public function gettingEncryptedStuffFromGoogleShouldWorkIfHostIsResolvedFirst()
8484
/** @test */
8585
public function testConnectingFailsIfDnsUsesInvalidResolver()
8686
{
87-
if (!function_exists('stream_socket_enable_crypto')) {
88-
$this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
89-
}
90-
9187
$loop = new StreamSelectLoop();
9288

9389
$factory = new Factory();
@@ -101,6 +97,23 @@ public function testConnectingFailsIfDnsUsesInvalidResolver()
10197
Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
10298
}
10399

100+
/** @test */
101+
public function testConnectingFailsIfTimeoutIsTooSmall()
102+
{
103+
if (!function_exists('stream_socket_enable_crypto')) {
104+
$this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
105+
}
106+
107+
$loop = new StreamSelectLoop();
108+
109+
$connector = new Connector($loop, array(
110+
'timeout' => 0.001
111+
));
112+
113+
$this->setExpectedException('RuntimeException');
114+
Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
115+
}
116+
104117
/** @test */
105118
public function testSelfSignedRejectsIfVerificationIsEnabled()
106119
{

0 commit comments

Comments
 (0)