|
6 | 6 | use React\EventLoop\LoopInterface;
|
7 | 7 | use React\Socket\Server;
|
8 | 8 | use React\Socket\ConnectionInterface;
|
| 9 | +use React\Stream\Stream; |
9 | 10 |
|
10 | 11 | /**
|
11 | 12 | * The `SecureServer` class implements the `ServerInterface` and is responsible
|
12 | 13 | * for providing a secure TLS (formerly known as SSL) server.
|
13 | 14 | *
|
14 | 15 | * It does so by wrapping a `Server` instance which waits for plaintext
|
15 | 16 | * TCP/IP connections and then performs a TLS handshake for each connection.
|
16 |
| - * It thus requires valid [TLS context options], |
17 |
| - * which in its most basic form may look something like this if you're using a |
18 |
| - * PEM encoded certificate file: |
19 | 17 | *
|
| 18 | + * ```php |
| 19 | + * $server = new Server(8000, $loop); |
| 20 | + * $server = new SecureServer($server, $loop, array( |
| 21 | + * // tls context options here… |
| 22 | + * )); |
20 | 23 | * ```
|
21 |
| - * $context = array( |
22 |
| - * 'local_cert' => __DIR__ . '/localhost.pem' |
23 |
| - * ); |
| 24 | + * |
| 25 | + * Whenever a client completes the TLS handshake, it will emit a `connection` event |
| 26 | + * with a connection instance implementing [`ConnectionInterface`](#connectioninterface): |
| 27 | + * |
| 28 | + * ```php |
| 29 | + * $server->on('connection', function (ConnectionInterface $connection) { |
| 30 | + * echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL; |
| 31 | + * |
| 32 | + * $connection->write('hello there!' . PHP_EOL); |
| 33 | + * … |
| 34 | + * }); |
24 | 35 | * ```
|
25 | 36 | *
|
26 |
| - * If your private key is encrypted with a passphrase, you have to specify it |
27 |
| - * like this: |
| 37 | + * Whenever a client fails to perform a successful TLS handshake, it will emit an |
| 38 | + * `error` event and then close the underlying TCP/IP connection: |
28 | 39 | *
|
29 | 40 | * ```php
|
30 |
| - * $context = array( |
31 |
| - * 'local_cert' => 'server.pem', |
32 |
| - * 'passphrase' => 'secret' |
33 |
| - * ); |
| 41 | + * $server->on('error', function (Exception $e) { |
| 42 | + * echo 'Error' . $e->getMessage() . PHP_EOL; |
| 43 | + * }); |
34 | 44 | * ```
|
35 | 45 | *
|
36 |
| - * @see Server |
37 |
| - * @link http://php.net/manual/en/context.ssl.php for TLS context options |
| 46 | + * See also the `ServerInterface` for more details. |
| 47 | + * |
| 48 | + * Note that the `SecureServer` class is a concrete implementation for TLS sockets. |
| 49 | + * If you want to typehint in your higher-level protocol implementation, you SHOULD |
| 50 | + * use the generic `ServerInterface` instead. |
| 51 | + * |
| 52 | + * @see ServerInterface |
| 53 | + * @see ConnectionInterface |
38 | 54 | */
|
39 | 55 | class SecureServer extends EventEmitter implements ServerInterface
|
40 | 56 | {
|
41 | 57 | private $tcp;
|
42 | 58 | private $encryption;
|
43 | 59 |
|
| 60 | + /** |
| 61 | + * Creates a secure TLS server and starts waiting for incoming connections |
| 62 | + * |
| 63 | + * It does so by wrapping a `Server` instance which waits for plaintext |
| 64 | + * TCP/IP connections and then performs a TLS handshake for each connection. |
| 65 | + * It thus requires valid [TLS context options], |
| 66 | + * which in its most basic form may look something like this if you're using a |
| 67 | + * PEM encoded certificate file: |
| 68 | + * |
| 69 | + * ```php |
| 70 | + * $server = new Server(8000, $loop); |
| 71 | + * $server = new SecureServer($server, $loop, array( |
| 72 | + * 'local_cert' => 'server.pem' |
| 73 | + * )); |
| 74 | + * ``` |
| 75 | + * |
| 76 | + * Note that the certificate file will not be loaded on instantiation but when an |
| 77 | + * incoming connection initializes its TLS context. |
| 78 | + * This implies that any invalid certificate file paths or contents will only cause |
| 79 | + * an `error` event at a later time. |
| 80 | + * |
| 81 | + * If your private key is encrypted with a passphrase, you have to specify it |
| 82 | + * like this: |
| 83 | + * |
| 84 | + * ```php |
| 85 | + * $server = new Server(8000, $loop); |
| 86 | + * $server = new SecureServer($server, $loop, array( |
| 87 | + * 'local_cert' => 'server.pem', |
| 88 | + * 'passphrase' => 'secret' |
| 89 | + * )); |
| 90 | + * ``` |
| 91 | + * |
| 92 | + * Note that available [TLS context options], |
| 93 | + * their defaults and effects of changing these may vary depending on your system |
| 94 | + * and/or PHP version. |
| 95 | + * Passing unknown context options has no effect. |
| 96 | + * |
| 97 | + * Advanced usage: Internally, the `SecureServer` has to set the required |
| 98 | + * context options on the underlying stream resources. |
| 99 | + * It should therefor be used with an unmodified `Server` instance as first |
| 100 | + * parameter so that it can allocate an empty context resource which this |
| 101 | + * class uses to set required TLS context options. |
| 102 | + * Failing to do so may result in some hard to trace race conditions, |
| 103 | + * because all stream resources will use a single, shared default context |
| 104 | + * resource otherwise. |
| 105 | + * |
| 106 | + * @param Server $tcp |
| 107 | + * @param LoopInterface $loop |
| 108 | + * @param array $context |
| 109 | + * @throws ConnectionException |
| 110 | + * @see Server |
| 111 | + * @link http://php.net/manual/en/context.ssl.php for TLS context options |
| 112 | + */ |
44 | 113 | public function __construct(Server $tcp, LoopInterface $loop, array $context)
|
45 | 114 | {
|
46 | 115 | if (!is_resource($tcp->master)) {
|
@@ -81,6 +150,12 @@ public function close()
|
81 | 150 | /** @internal */
|
82 | 151 | public function handleConnection(ConnectionInterface $connection)
|
83 | 152 | {
|
| 153 | + if (!$connection instanceof Stream) { |
| 154 | + $this->emit('error', array(new \UnexpectedValueException('Connection event MUST emit an instance extending Stream in order to access underlying stream resource'))); |
| 155 | + $connection->end(); |
| 156 | + return; |
| 157 | + } |
| 158 | + |
84 | 159 | $that = $this;
|
85 | 160 |
|
86 | 161 | $this->encryption->enable($connection)->then(
|
|
0 commit comments