@@ -23,6 +23,8 @@ handle multiple concurrent connections without blocking.
23
23
* [ ConnectionInterface] ( #connectioninterface )
24
24
* [ getRemoteAddress()] ( #getremoteaddress )
25
25
* [ getLocalAddress()] ( #getlocaladdress )
26
+ * [ OpportunisticTlsConnectionInterface] ( #opportunistictlsconnectioninterface )
27
+ * [ enableEncryption()] ( #enableencryption )
26
28
* [ Server usage] ( #server-usage )
27
29
* [ ServerInterface] ( #serverinterface )
28
30
* [ connection event] ( #connection-event )
@@ -193,6 +195,64 @@ If your system has multiple interfaces (e.g. a WAN and a LAN interface),
193
195
you can use this method to find out which interface was actually
194
196
used for this connection.
195
197
198
+ ### OpportunisticTlsConnectionInterface
199
+
200
+ The ` OpportunisticTlsConnectionInterface ` extends the
201
+ [ ` ConnectionInterface ` ] ( #connectioninterface ) and adds the ability of
202
+ enabling the TLS encryption on the connection when desired.
203
+
204
+ #### enableEncryption
205
+
206
+ When negotiated with the server when to start encrypting traffic using TLS, you
207
+ can enable it by calling ` enableEncryption() ` . This will either return a promise
208
+ that resolves with a ` OpportunisticTlsConnectionInterface ` connection or throw a
209
+ ` RuntimeException ` if the encryption failed. If successful, all traffic back and
210
+ forth will be encrypted. In the following example we ask the server if they want
211
+ to encrypt the connection, and when it responds with ` yes ` we enable the encryption:
212
+
213
+ ``` php
214
+ $connector = new React\Socket\Connector();
215
+ $connector->connect('opportunistic+tls://example.com:5432/')->then(function (React\Socket\OpportunisticTlsConnectionInterface $startTlsConnection) {
216
+ $connection->write('let\'s encrypt?');
217
+
218
+ return React\Promise\Stream\first($connection)->then(function ($data) use ($connection) {
219
+ if ($data === 'yes') {
220
+ return $connection->enableEncryption();
221
+ }
222
+
223
+ return $stream;
224
+ });
225
+ })->then(function (React\Socket\ConnectionInterface $connection) {
226
+ $connection->write('Hello!');
227
+ });
228
+ ```
229
+
230
+ The ` enableEncryption ` function resolves with itself. As such you can't see the data
231
+ encrypted when you hook into the events before enabling, as shown below:
232
+
233
+ ``` php
234
+ $connector = new React\Socket\Connector();
235
+ $connector->connect('opportunistic+tls://example.com:5432/')->then(function (React\Socket\OpportunisticTlsConnectionInterface $startTlsConnection) {
236
+ $connection->on('data', function ($data) {
237
+ echo 'Raw: ', $data, PHP_EOL;
238
+ });
239
+
240
+ return $connection->enableEncryption();
241
+ })->then(function (React\Socket\ConnectionInterface $connection) {
242
+ $connection->on('data', function ($data) {
243
+ echo 'TLS: ', $data, PHP_EOL;
244
+ });
245
+ });
246
+ ```
247
+
248
+ When the other side sends ` Hello World! ` over the encrypted connection, the output
249
+ will be the following:
250
+
251
+ ```
252
+ Raw: Hello World!
253
+ TLS: Hello World!
254
+ ```
255
+
196
256
## Server usage
197
257
198
258
### ServerInterface
@@ -253,10 +313,10 @@ If the address can not be determined or is unknown at this time (such as
253
313
after the socket has been closed), it MAY return a ` NULL ` value instead.
254
314
255
315
Otherwise, it will return the full address (URI) as a string value, such
256
- as ` tcp://127.0.0.1:8080 ` , ` tcp://[::1]:80 ` , ` tls://127.0.0.1:443 `
257
- ` unix://example.sock ` or ` unix:///path/to/example.sock ` .
258
- Note that individual URI components are application specific and depend
259
- on the underlying transport protocol.
316
+ as ` tcp://127.0.0.1:8080 ` , ` tcp://[::1]:80 ` , ` tls://127.0.0.1:443 ` ,
317
+ ` unix://example.sock ` , ` unix:///path/to/example.sock ` , or
318
+ ` opportunistic+tls://127.0.0.1:443 ` . Note that individual URI components
319
+ are application specific and depend on the underlying transport protocol.
260
320
261
321
If this is a TCP/IP based server and you only want the local port, you may
262
322
use something like this:
@@ -478,6 +538,22 @@ $socket = new React\Socket\SocketServer('tls://127.0.0.1:8000', array(
478
538
));
479
539
```
480
540
541
+ To start a server with opportunistic TLS support use ` opportunistic+tls:// ` as the scheme instead of ` tls:// ` :
542
+
543
+ ``` php
544
+ $socket = new React\Socket\SocketServer('opportunistic+tls://127.0.0.1:8000', array(
545
+ 'tls' => array(
546
+ 'local_cert' => 'server.pem',
547
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
548
+ )
549
+ ));
550
+ $server->on('connection', static function (OpportunisticTlsConnectionInterface $connection) use ($server) {
551
+ return $connection->enableEncryption();
552
+ });
553
+ ```
554
+
555
+ See also the [ examples] ( examples ) .
556
+
481
557
> Note that available [ TLS context options] ( https://www.php.net/manual/en/context.ssl.php ) ,
482
558
their defaults and effects of changing these may vary depending on your system
483
559
and/or PHP version.
@@ -697,6 +773,21 @@ here in order to use the [default loop](https://github.com/reactphp/event-loop#l
697
773
This value SHOULD NOT be given unless you're sure you want to explicitly use a
698
774
given event loop instance.
699
775
776
+ The ` SecureServer ` class supports opportunistic TLS by passing true in as a 4th
777
+ constructor parameter. This, when a client connects, emits a
778
+ [ ` OpportunisticTlsConnectionInterface ` ] ( #opportunistictlsconnectioninterface )
779
+ instead of the default [ ` ConnectionInterface ` ] ( #connectioninterface ) . It won't be
780
+ TLS encrypted from the start, but you can enable the TLS encryption on the connection
781
+ after negotiating with the client.
782
+
783
+ ``` php
784
+ $server = new React\Socket\TcpServer(8000);
785
+ $server = new React\Socket\SecureServer($server, null, array(
786
+ 'local_cert' => 'server.pem',
787
+ 'passphrase' => 'secret'
788
+ ), true);
789
+ ```
790
+
700
791
> Advanced usage: Despite allowing any ` ServerInterface ` as first parameter,
701
792
you SHOULD pass a ` TcpServer ` instance as first parameter, unless you
702
793
know what you're doing.
@@ -1389,6 +1480,22 @@ $secureConnector = new React\Socket\SecureConnector($dnsConnector, null, array(
1389
1480
));
1390
1481
```
1391
1482
1483
+ The ` SecureConnector ` class supports opportunistic TLS by using
1484
+ ` opportunistic-tls:// ` as scheme instead of ` tls:// ` . This, when connected,
1485
+ returns a [ ` OpportunisticTlsConnectionInterface ` ] ( #opportunistictlsconnectioninterface )
1486
+ instead of the default [ ` ConnectionInterface ` ] ( #connectioninterface ) . It won't be
1487
+ TLS encrypted from the start, but you can enable the TLS encryption on the connection
1488
+ after negotiating with the server.
1489
+
1490
+ ``` php
1491
+ $secureConnector = new React\Socket\SecureConnector($dnsConnector);
1492
+ $secureConnector->connect('opportunistic-tls://example.com:5432')->then(function (OpportunisticTlsConnectionInterface $connection) {
1493
+ return $connection->enableEncryption();
1494
+ })->then(function (OpportunisticTlsConnectionInterface $connection) {
1495
+ $connection->write('Encrypted hi!');
1496
+ });
1497
+ ```
1498
+
1392
1499
> Advanced usage: Internally, the ` SecureConnector ` relies on setting up the
1393
1500
required * context options* on the underlying stream resource.
1394
1501
It should therefor be used with a ` TcpConnector ` somewhere in the connector
0 commit comments