Skip to content

Commit 234c84a

Browse files
authored
Merge pull request #70 from clue-labs/no-master
Do not expose $master property and accept any ServerInterface for SecureServer
2 parents ca73095 + e4fb0f2 commit 234c84a

File tree

6 files changed

+50
-49
lines changed

6 files changed

+50
-49
lines changed

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,14 +295,19 @@ Note that the `SecureServer` class is a concrete implementation for TLS sockets.
295295
If you want to typehint in your higher-level protocol implementation, you SHOULD
296296
use the generic [`ServerInterface`](#serverinterface) instead.
297297

298-
> Advanced usage: Internally, the `SecureServer` has to set the required
299-
context options on the underlying stream resources.
300-
It should therefor be used with an unmodified `Server` instance as first
301-
parameter so that it can allocate an empty context resource which this
302-
class uses to set required TLS context options.
303-
Failing to do so may result in some hard to trace race conditions,
304-
because all stream resources will use a single, shared default context
305-
resource otherwise.
298+
> Advanced usage: Despite allowing any `ServerInterface` as first parameter,
299+
you SHOULD pass an unmodified `Server` instance as first parameter, unless you
300+
know what you're doing.
301+
Internally, the `SecureServer` has to set the required TLS context options on
302+
the underlying stream resources.
303+
These resources are not exposed through any of the interfaces defined in this
304+
package, but only through the `React\Stream\Stream` class.
305+
The unmodified `Server` class is guaranteed to emit connections that implement
306+
the `ConnectionInterface` and also extend the `Stream` class in order to
307+
expose these underlying resources.
308+
If you use a custom `ServerInterface` and its `connection` event does not
309+
meet this requirement, the `SecureServer` will emit an `error` event and
310+
then close the underlying connection.
306311

307312
### ConnectionInterface
308313

src/SecureServer.php

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class SecureServer extends EventEmitter implements ServerInterface
5656
{
5757
private $tcp;
5858
private $encryption;
59+
private $context;
5960

6061
/**
6162
* Creates a secure TLS server and starts waiting for incoming connections
@@ -94,39 +95,36 @@ class SecureServer extends EventEmitter implements ServerInterface
9495
* and/or PHP version.
9596
* Passing unknown context options has no effect.
9697
*
97-
* Advanced usage: Internally, the `SecureServer` has to set the required
98-
* context options on the underlying stream resources.
99-
* It should therefor be used with an unmodified `Server` instance as first
100-
* parameter so that it can allocate an empty context resource which this
101-
* class uses to set required TLS context options.
102-
* Failing to do so may result in some hard to trace race conditions,
103-
* because all stream resources will use a single, shared default context
104-
* resource otherwise.
98+
* Advanced usage: Despite allowing any `ServerInterface` as first parameter,
99+
* you SHOULD pass an unmodified `Server` instance as first parameter, unless you
100+
* know what you're doing.
101+
* Internally, the `SecureServer` has to set the required TLS context options on
102+
* the underlying stream resources.
103+
* These resources are not exposed through any of the interfaces defined in this
104+
* package, but only through the `React\Stream\Stream` class.
105+
* The unmodified `Server` class is guaranteed to emit connections that implement
106+
* the `ConnectionInterface` and also extend the `Stream` class in order to
107+
* expose these underlying resources.
108+
* If you use a custom `ServerInterface` and its `connection` event does not
109+
* meet this requirement, the `SecureServer` will emit an `error` event and
110+
* then close the underlying connection.
105111
*
106-
* @param Server $tcp
112+
* @param ServerInterface|Server $tcp
107113
* @param LoopInterface $loop
108114
* @param array $context
109-
* @throws ConnectionException
110115
* @see Server
111116
* @link http://php.net/manual/en/context.ssl.php for TLS context options
112117
*/
113-
public function __construct(Server $tcp, LoopInterface $loop, array $context)
118+
public function __construct(ServerInterface $tcp, LoopInterface $loop, array $context)
114119
{
115-
if (!is_resource($tcp->master)) {
116-
throw new ConnectionException('TCP server already shut down');
117-
}
118-
119120
// default to empty passphrase to surpress blocking passphrase prompt
120121
$context += array(
121122
'passphrase' => ''
122123
);
123124

124-
foreach ($context as $name => $value) {
125-
stream_context_set_option($tcp->master, 'ssl', $name, $value);
126-
}
127-
128125
$this->tcp = $tcp;
129126
$this->encryption = new StreamEncryption($loop);
127+
$this->context = $context;
130128

131129
$that = $this;
132130
$this->tcp->on('connection', function ($connection) use ($that) {
@@ -156,6 +154,10 @@ public function handleConnection(ConnectionInterface $connection)
156154
return;
157155
}
158156

157+
foreach ($this->context as $name => $value) {
158+
stream_context_set_option($connection->stream, 'ssl', $name, $value);
159+
}
160+
159161
$that = $this;
160162

161163
$this->encryption->enable($connection)->then(

src/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*/
3737
class Server extends EventEmitter implements ServerInterface
3838
{
39-
public $master;
39+
private $master;
4040
private $loop;
4141

4242
/**

tests/FunctionalSecureServerTest.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -368,19 +368,6 @@ public function testEmitsErrorIfConnectionIsNotSecureHandshake()
368368
Block\sleep(self::TIMEOUT, $loop);
369369
}
370370

371-
/**
372-
* @expectedException React\Socket\ConnectionException
373-
*/
374-
public function testFailsToCreateSecureServerOnClosedSocket()
375-
{
376-
$loop = Factory::create();
377-
378-
$server = new Server(0, $loop);
379-
$server->close();
380-
381-
new SecureServer($server, $loop, array());
382-
}
383-
384371
private function getPort(ServerInterface $server)
385372
{
386373
return parse_url($server->getAddress(), PHP_URL_PORT);

tests/FunctionalServerTest.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public function testEmitsConnectionWithLocalIpv6()
229229
$this->assertEquals($server->getAddress(), $local);
230230
}
231231

232-
public function testAppliesContextOptionsToSocketStreamResource()
232+
public function testEmitsConnectionWithInheritedContextOptions()
233233
{
234234
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) {
235235
// https://3v4l.org/hB4Tc
@@ -242,7 +242,18 @@ public function testAppliesContextOptionsToSocketStreamResource()
242242
'backlog' => 4
243243
));
244244

245-
$all = stream_context_get_options($server->master);
245+
$all = null;
246+
$server->on('connection', function (ConnectionInterface $conn) use (&$all) {
247+
$all = stream_context_get_options($conn->stream);
248+
});
249+
$port = $this->getPort($server);
250+
251+
$connector = new TcpConnector($loop);
252+
$promise = $connector->create('127.0.0.1', $port);
253+
254+
$promise->then($this->expectCallableOnce());
255+
256+
Block\sleep(0.1, $loop);
246257

247258
$this->assertEquals(array('socket' => array('backlog' => 4)), $all);
248259
}

tests/SecureServerTest.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ public function setUp()
1515

1616
public function testGetAddressWillBePassedThroughToTcpServer()
1717
{
18-
$tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->getMock();
18+
$tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
1919
$tcp->expects($this->once())->method('getAddress')->willReturn('127.0.0.1:1234');
20-
$tcp->master = stream_socket_server('tcp://localhost:0');
2120

2221
$loop = $this->getMock('React\EventLoop\LoopInterface');
2322

@@ -28,9 +27,8 @@ public function testGetAddressWillBePassedThroughToTcpServer()
2827

2928
public function testCloseWillBePassedThroughToTcpServer()
3029
{
31-
$tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->getMock();
30+
$tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
3231
$tcp->expects($this->once())->method('close');
33-
$tcp->master = stream_socket_server('tcp://localhost:0');
3432

3533
$loop = $this->getMock('React\EventLoop\LoopInterface');
3634

@@ -42,7 +40,6 @@ public function testCloseWillBePassedThroughToTcpServer()
4240
public function testConnectionWillBeEndedWithErrorIfItIsNotAStream()
4341
{
4442
$tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->setMethods(null)->getMock();
45-
$tcp->master = stream_socket_server('tcp://localhost:0');
4643

4744
$loop = $this->getMock('React\EventLoop\LoopInterface');
4845

@@ -59,7 +56,6 @@ public function testConnectionWillBeEndedWithErrorIfItIsNotAStream()
5956
public function testSocketErrorWillBeForwarded()
6057
{
6158
$tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->setMethods(null)->getMock();
62-
$tcp->master = stream_socket_server('tcp://localhost:0');
6359

6460
$loop = $this->getMock('React\EventLoop\LoopInterface');
6561

0 commit comments

Comments
 (0)