Skip to content

Commit 2a56537

Browse files
committed
Support secure TLS connections via Server
1 parent 13001a9 commit 2a56537

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,48 @@ $server = new Server('[::1]:8080', $loop, array(
401401
For BC reasons, you can also pass the TCP socket context options as a simple
402402
array without wrapping this in another array under the `tcp` key.
403403

404+
You can start a secure TLS (formerly known as SSL) server by simply prepending
405+
the `tls://` URI scheme.
406+
Internally, it will wait for plaintext TCP/IP connections and then performs a
407+
TLS handshake for each connection.
408+
It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php),
409+
which in its most basic form may look something like this if you're using a
410+
PEM encoded certificate file:
411+
412+
```php
413+
$server = new Server('tls://127.0.0.1:8080', $loop, array(
414+
'tls' => array(
415+
'local_cert' => 'server.pem'
416+
)
417+
));
418+
```
419+
420+
> Note that the certificate file will not be loaded on instantiation but when an
421+
incoming connection initializes its TLS context.
422+
This implies that any invalid certificate file paths or contents will only cause
423+
an `error` event at a later time.
424+
425+
If your private key is encrypted with a passphrase, you have to specify it
426+
like this:
427+
428+
```php
429+
$server = new Server('tls://127.0.0.1:8000', $loop, array(
430+
'tls' => array(
431+
'local_cert' => 'server.pem',
432+
'passphrase' => 'secret'
433+
)
434+
));
435+
```
436+
437+
> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
438+
their defaults and effects of changing these may vary depending on your system
439+
and/or PHP version.
440+
The outer context array allows you to also use `tcp` (and possibly more)
441+
context options at the same time.
442+
Passing unknown context options has no effect.
443+
If you do not use the `tls://` scheme, then passing `tls` context options
444+
has no effect.
445+
404446
Whenever a client connects, it will emit a `connection` event with a connection
405447
instance implementing [`ConnectionInterface`](#connectioninterface):
406448

src/Server.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,28 @@ final class Server extends EventEmitter implements ServerInterface
1212
public function __construct($uri, LoopInterface $loop, array $context = array())
1313
{
1414
// sanitize TCP context options if not properly wrapped
15-
if ($context && !isset($context['tcp'])) {
15+
if ($context && (!isset($context['tcp']) && !isset($context['tls']))) {
1616
$context = array('tcp' => $context);
1717
}
1818

1919
// apply default options if not explicitly given
2020
$context += array(
2121
'tcp' => array(),
22+
'tls' => array(),
2223
);
2324

24-
$server = new TcpServer($uri, $loop, $context['tcp']);
25+
$scheme = 'tcp';
26+
$pos = strpos($uri, '://');
27+
if ($pos !== false) {
28+
$scheme = substr($uri, 0, $pos);
29+
}
30+
31+
$server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);
32+
33+
if ($scheme === 'tls') {
34+
$server = new SecureServer($server, $loop, $context['tls']);
35+
}
36+
2537
$this->server = $server;
2638

2739
$that = $this;

tests/ServerTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,24 @@ public function testEmitsConnectionWithInheritedContextOptions()
107107

108108
$this->assertEquals(array('socket' => array('backlog' => 4)), $all);
109109
}
110+
111+
public function testDoesNotEmitSecureConnectionForNewPlainConnection()
112+
{
113+
if (!function_exists('stream_socket_enable_crypto')) {
114+
$this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
115+
}
116+
117+
$loop = Factory::create();
118+
119+
$server = new Server('tls://127.0.0.1:0', $loop, array(
120+
'tls' => array(
121+
'local_cert' => __DIR__ . '/../examples/localhost.pem'
122+
)
123+
));
124+
$server->on('connection', $this->expectCallableNever());
125+
126+
$client = stream_socket_client($server->getAddress());
127+
128+
Block\sleep(0.1, $loop);
129+
}
110130
}

0 commit comments

Comments
 (0)