@@ -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
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` 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* context options*  on the underlying stream resource.
13941501It should therefor be used with a ` TcpConnector `  somewhere in the connector
0 commit comments