@@ -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 their defaults and effects of changing these may vary depending on your system
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 you SHOULD pass a ` 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 required * context options* on the underlying stream resource.
13941497It should therefor be used with a ` TcpConnector ` somewhere in the connector
0 commit comments