@@ -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,62 @@ 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 [ ` ConnectionInterface ` ] ( #connectioninterface )  and adds the ability of 
201+ enabling the TLS encryption on the connection when desired.
202+ 
203+ #### enableEncryption  
204+ 
205+ When negotiated with the server when to start encrypting traffic using TLS you enable it by calling 
206+ ` enableEncryption() `  which returns a promise that resolve with a ` OpportunisticTlsConnectionInterface `  connection but now all 
207+ traffic back and forth will be encrypted.
208+ 
209+ In the following example we ask the server if they want to encrypt the connection, and when it responds with ` yes `  we 
210+ enable the encryption:
211+ 
212+ ``` php 
213+ $connector = new React\Socket\Connector();
214+ $connector->connect('opportunistic+tls://example.com:5432/')->then(function (React\Socket\OpportunisticTlsConnectionInterface $startTlsConnection) {
215+     $connection->write('let\'s encrypt?');
216+ 
217+     return React\Promise\Stream\first($connection)->then(function ($data) use ($connection) {
218+         if ($data === 'yes') {
219+             return $connection->enableEncryption();
220+         }
221+         
222+         return $stream;
223+     });
224+ })->then(function (React\Socket\ConnectionInterface $connection) {
225+     $connection->write('Hello!');
226+ });
227+ ``` 
228+ 
229+ The ` enableEncryption `  function resolves with itself. As such you can't see the data encrypted when you hook into the 
230+ events before enabling, as shown below:
231+ 
232+ ``` php 
233+ $connector = new React\Socket\Connector();
234+ $connector->connect('opportunistic+tls://example.com:5432/')->then(function (React\Socket\OpportunisticTlsConnectionInterface $startTlsConnection) {   
235+     $connection->on('data', function ($data) {
236+         echo 'Raw: ', $data, PHP_EOL;
237+     });
238+     
239+     return $connection->enableEncryption();
240+ })->then(function (React\Socket\ConnectionInterface $connection) {
241+     $connection->on('data', function ($data) {
242+         echo 'TLS: ', $data, PHP_EOL;
243+     });
244+ });
245+ ``` 
246+ 
247+ When the other side send ` Hello World! `  over the encrypted connection, the output will be the following:
248+ 
249+ ``` 
250+ Raw: Hello World! 
251+ TLS: Hello World! 
252+ ``` 
253+ 
196254## Server usage  
197255
198256### ServerInterface  
@@ -253,10 +311,10 @@ If the address can not be determined or is unknown at this time (such as
253311after the socket has been closed), it MAY return a ` NULL `  value instead.
254312
255313Otherwise, 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.
314+ as ` tcp://127.0.0.1:8080 ` , ` tcp://[::1]:80 ` , ` tls://127.0.0.1:443 ` , 
315+ ` unix://example.sock ` ,  ` unix:///path/to/example.sock ` , or  
316+ ` opportunistic+tls://127.0.0.1:443 ` .   Note that individual URI components 
317+ are application specific and depend  on the underlying transport protocol.
260318
261319If this is a TCP/IP based server and you only want the local port, you may
262320use something like this:
@@ -478,6 +536,22 @@ $socket = new React\Socket\SocketServer('tls://127.0.0.1:8000', array(
478536));
479537``` 
480538
539+ To start a server with opportunistic TLS support use ` opportunistic+tls:// `  as the scheme instead of ` tls:// ` :
540+ 
541+ ``` php 
542+ $socket = new React\Socket\SocketServer('opportunistic+tls://127.0.0.1:8000', array(
543+     'tls' => array(
544+         'local_cert' => 'server.pem',
545+         'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
546+     )
547+ ));
548+ $server->on('connection', static function (OpportunisticTlsConnectionInterface $connection) use ($server) {
549+     return $connection->enableEncryption();
550+ });
551+ ``` 
552+ 
553+ See also the [ examples] ( examples ) .
554+ 
481555>  Note that available [ TLS context options] ( https://www.php.net/manual/en/context.ssl.php ) ,
482556
483557  and/or PHP version.
@@ -697,6 +771,21 @@ here in order to use the [default loop](https://github.com/reactphp/event-loop#l
697771This value SHOULD NOT be given unless you're sure you want to explicitly use a
698772given event loop instance.
699773
774+ Opportunistic TLS is supported by the secure server by passing true in as 4th constructor 
775+ parameter. This, when a client connects, emits a 
776+ [ ` OpportunisticTlsConnectionInterface ` ] ( #opportunistictlsconnectioninterface )  instead 
777+ of the default [ ` ConnectionInterface ` ] ( #connectioninterface )  and won't be TLS encrypted 
778+ from the start so you can enable the TLS encryption on the connection after negotiating 
779+ with the client.
780+ 
781+ ``` php 
782+ $server = new React\Socket\TcpServer(8000);
783+ $server = new React\Socket\SecureServer($server, null, array(
784+     'local_cert' => 'server.pem',
785+     'passphrase' => 'secret'
786+ ), true);
787+ ``` 
788+ 
700789>  Advanced usage: Despite allowing any ` ServerInterface `  as first parameter,
701790` TcpServer `  instance as first parameter, unless you
702791know what you're doing.
@@ -1389,6 +1478,20 @@ $secureConnector = new React\Socket\SecureConnector($dnsConnector, null, array(
13891478));
13901479``` 
13911480
1481+ Opportunistic TLS is supported by the secure connector by using ` opportunistic-tls:// `  as scheme instead of ` tls:// ` . This, when 
1482+ connected, returns a [ ` OpportunisticTlsConnectionInterface ` ] ( #opportunistictlsconnectioninterface )  instead of the default 
1483+ [ ` ConnectionInterface ` ] ( #connectioninterface )  and won't be TLS encrypted from the start so you can enable the TLS 
1484+ encryption on the connection after negotiating with the server.
1485+ 
1486+ ``` php 
1487+ $secureConnector = new React\Socket\SecureConnector($dnsConnector);
1488+ $secureConnector->connect('opportunistic-tls://example.com:5432')->then(function (OpportunisticTlsConnectionInterface $connection) {
1489+     return $connection->enableEncryption();
1490+ })->then(function (OpportunisticTlsConnectionInterface $connection) {
1491+     $connection->write('Encrypted hi!');
1492+ });
1493+ ``` 
1494+ 
13921495>  Advanced usage: Internally, the ` SecureConnector `  relies on setting up the
13931496* context options*  on the underlying stream resource.
13941497It should therefor be used with a ` TcpConnector `  somewhere in the connector
0 commit comments