@@ -23,6 +23,8 @@ handle multiple concurrent connections without blocking.
2323 * [ ConnectionInterface] ( #connectioninterface )
2424 * [ getRemoteAddress()] ( #getremoteaddress )
2525 * [ getLocalAddress()] ( #getlocaladdress )
26+ * [ OpportunisticTlsConnectionInterface] ( #opportunistictlsconnectioninterface )
27+ * [ enableEncryption()] ( #enableencryption )
2628* [ Server usage] ( #server-usage )
2729 * [ ServerInterface] ( #serverinterface )
2830 * [ connection event] ( #connection-event )
@@ -193,6 +195,64 @@ If your system has multiple interfaces (e.g. a WAN and a LAN interface),
193195you can use this method to find out which interface was actually
194196used for this connection.
195197
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+
196256## Server usage
197257
198258### ServerInterface
@@ -253,10 +313,10 @@ If the address can not be determined or is unknown at this time (such as
253313after the socket has been closed), it MAY return a ` NULL ` value instead.
254314
255315Otherwise, 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.
260320
261321If this is a TCP/IP based server and you only want the local port, you may
262322use something like this:
@@ -478,6 +538,22 @@ $socket = new React\Socket\SocketServer('tls://127.0.0.1:8000', array(
478538));
479539```
480540
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+
481557> Note that available [ TLS context options] ( https://www.php.net/manual/en/context.ssl.php ) ,
482558 their defaults and effects of changing these may vary depending on your system
483559 and/or PHP version.
@@ -697,6 +773,21 @@ here in order to use the [default loop](https://github.com/reactphp/event-loop#l
697773This value SHOULD NOT be given unless you're sure you want to explicitly use a
698774given event loop instance.
699775
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+
700791> Advanced usage: Despite allowing any ` ServerInterface ` as first parameter,
701792 you SHOULD pass a ` TcpServer ` instance as first parameter, unless you
702793know what you're doing.
@@ -1389,6 +1480,22 @@ $secureConnector = new React\Socket\SecureConnector($dnsConnector, null, array(
13891480));
13901481```
13911482
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+
13921499> Advanced usage: Internally, the ` SecureConnector ` relies on setting up the
13931500 required * context options* on the underlying stream resource.
13941501It should therefor be used with a ` TcpConnector ` somewhere in the connector
0 commit comments