aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/php/vendor/react/socket
diff options
context:
space:
mode:
authormarvin-borner@live.com2018-04-10 21:50:16 +0200
committermarvin-borner@live.com2018-04-10 21:54:48 +0200
commitfc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch)
treeb0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/react/socket
parent286d643180672f20526f3dc3bd19d7b751e2fa97 (diff)
Initial Commit
Diffstat (limited to 'assets/php/vendor/react/socket')
-rw-r--r--assets/php/vendor/react/socket/.gitignore2
-rw-r--r--assets/php/vendor/react/socket/.travis.yml49
-rw-r--r--assets/php/vendor/react/socket/CHANGELOG.md451
-rw-r--r--assets/php/vendor/react/socket/LICENSE19
-rw-r--r--assets/php/vendor/react/socket/README.md1419
-rw-r--r--assets/php/vendor/react/socket/composer.json29
-rw-r--r--assets/php/vendor/react/socket/examples/01-echo-server.php42
-rw-r--r--assets/php/vendor/react/socket/examples/02-chat-server.php59
-rw-r--r--assets/php/vendor/react/socket/examples/03-http-server.php57
-rw-r--r--assets/php/vendor/react/socket/examples/11-http-client.php36
-rw-r--r--assets/php/vendor/react/socket/examples/12-https-client.php36
-rw-r--r--assets/php/vendor/react/socket/examples/21-netcat-client.php68
-rw-r--r--assets/php/vendor/react/socket/examples/22-http-client.php60
-rw-r--r--assets/php/vendor/react/socket/examples/91-benchmark-server.php60
-rw-r--r--assets/php/vendor/react/socket/examples/99-generate-self-signed.php31
-rw-r--r--assets/php/vendor/react/socket/examples/localhost.pem49
-rw-r--r--assets/php/vendor/react/socket/examples/localhost_swordfish.pem51
-rw-r--r--assets/php/vendor/react/socket/phpunit.xml.dist25
-rw-r--r--assets/php/vendor/react/socket/src/Connection.php178
-rw-r--r--assets/php/vendor/react/socket/src/ConnectionInterface.php119
-rw-r--r--assets/php/vendor/react/socket/src/Connector.php136
-rw-r--r--assets/php/vendor/react/socket/src/ConnectorInterface.php58
-rw-r--r--assets/php/vendor/react/socket/src/DnsConnector.php111
-rw-r--r--assets/php/vendor/react/socket/src/FixedUriConnector.php41
-rw-r--r--assets/php/vendor/react/socket/src/LimitingServer.php203
-rw-r--r--assets/php/vendor/react/socket/src/SecureConnector.php64
-rw-r--r--assets/php/vendor/react/socket/src/SecureServer.php192
-rw-r--r--assets/php/vendor/react/socket/src/Server.php73
-rw-r--r--assets/php/vendor/react/socket/src/ServerInterface.php151
-rw-r--r--assets/php/vendor/react/socket/src/StreamEncryption.php146
-rw-r--r--assets/php/vendor/react/socket/src/TcpConnector.php122
-rw-r--r--assets/php/vendor/react/socket/src/TcpServer.php236
-rw-r--r--assets/php/vendor/react/socket/src/TimeoutConnector.php25
-rw-r--r--assets/php/vendor/react/socket/src/UnixConnector.php44
-rw-r--r--assets/php/vendor/react/socket/src/UnixServer.php130
-rw-r--r--assets/php/vendor/react/socket/tests/ConnectionTest.php47
-rw-r--r--assets/php/vendor/react/socket/tests/ConnectorTest.php128
-rw-r--r--assets/php/vendor/react/socket/tests/DnsConnectorTest.php111
-rw-r--r--assets/php/vendor/react/socket/tests/FixedUriConnectorTest.php19
-rw-r--r--assets/php/vendor/react/socket/tests/FunctionalConnectorTest.php32
-rw-r--r--assets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php438
-rw-r--r--assets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php324
-rw-r--r--assets/php/vendor/react/socket/tests/IntegrationTest.php171
-rw-r--r--assets/php/vendor/react/socket/tests/LimitingServerTest.php195
-rw-r--r--assets/php/vendor/react/socket/tests/SecureConnectorTest.php74
-rw-r--r--assets/php/vendor/react/socket/tests/SecureIntegrationTest.php204
-rw-r--r--assets/php/vendor/react/socket/tests/SecureServerTest.php105
-rw-r--r--assets/php/vendor/react/socket/tests/ServerTest.php173
-rw-r--r--assets/php/vendor/react/socket/tests/Stub/CallableStub.php10
-rw-r--r--assets/php/vendor/react/socket/tests/Stub/ConnectionStub.php63
-rw-r--r--assets/php/vendor/react/socket/tests/Stub/ServerStub.php18
-rw-r--r--assets/php/vendor/react/socket/tests/TcpConnectorTest.php255
-rw-r--r--assets/php/vendor/react/socket/tests/TcpServerTest.php285
-rw-r--r--assets/php/vendor/react/socket/tests/TestCase.php101
-rw-r--r--assets/php/vendor/react/socket/tests/TimeoutConnectorTest.php103
-rw-r--r--assets/php/vendor/react/socket/tests/UnixConnectorTest.php64
-rw-r--r--assets/php/vendor/react/socket/tests/UnixServerTest.php283
57 files changed, 7775 insertions, 0 deletions
diff --git a/assets/php/vendor/react/socket/.gitignore b/assets/php/vendor/react/socket/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/assets/php/vendor/react/socket/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/assets/php/vendor/react/socket/.travis.yml b/assets/php/vendor/react/socket/.travis.yml
new file mode 100644
index 0000000..917dc0c
--- /dev/null
+++ b/assets/php/vendor/react/socket/.travis.yml
@@ -0,0 +1,49 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+# - 7.0 # Mac OS X, ignore errors, see below
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ include:
+ - os: osx
+ language: generic
+ php: 7.0 # just to look right on travis
+ env:
+ - PACKAGE: php70
+ allow_failures:
+ - php: hhvm
+ - os: osx
+
+sudo: false
+
+install:
+ # OSX install inspired by https://github.com/kiler129/TravisCI-OSX-PHP
+ - |
+ if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+ brew tap homebrew/homebrew-php
+ echo "Installing PHP ..."
+ brew install "${PACKAGE}"
+ brew install "${PACKAGE}"-xdebug
+ brew link "${PACKAGE}"
+ echo "Installing composer ..."
+ curl -s http://getcomposer.org/installer | php
+ mv composer.phar /usr/local/bin/composer
+ fi
+ - composer install --no-interaction
+
+script:
+ - ./vendor/bin/phpunit --coverage-text
diff --git a/assets/php/vendor/react/socket/CHANGELOG.md b/assets/php/vendor/react/socket/CHANGELOG.md
new file mode 100644
index 0000000..03c2eec
--- /dev/null
+++ b/assets/php/vendor/react/socket/CHANGELOG.md
@@ -0,0 +1,451 @@
+# Changelog
+
+## 0.8.10 (2018-02-28)
+
+* Feature: Update DNS dependency to support loading system default DNS
+ nameserver config on all supported platforms
+ (`/etc/resolv.conf` on Unix/Linux/Mac/Docker/WSL and WMIC on Windows)
+ (#152 by @clue)
+
+ This means that connecting to hosts that are managed by a local DNS server,
+ such as a corporate DNS server or when using Docker containers, will now
+ work as expected across all platforms with no changes required:
+
+ ```php
+ $connector = new Connector($loop);
+ $connector->connect('intranet.example:80')->then(function ($connection) {
+ // …
+ });
+ ```
+
+## 0.8.9 (2018-01-18)
+
+* Feature: Support explicitly choosing TLS version to negotiate with remote side
+ by respecting `crypto_method` context parameter for all classes.
+ (#149 by @clue)
+
+ By default, all connector and server classes support TLSv1.0+ and exclude
+ support for legacy SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly
+ choose the TLS version you want to negotiate with the remote side:
+
+ ```php
+ // new: now supports 'crypto_method` context parameter for all classes
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+ )
+ ));
+ ```
+
+* Minor internal clean up to unify class imports
+ (#148 by @clue)
+
+## 0.8.8 (2018-01-06)
+
+* Improve test suite by adding test group to skip integration tests relying on
+ internet connection and fix minor documentation typo.
+ (#146 by @clue and #145 by @cn007b)
+
+## 0.8.7 (2017-12-24)
+
+* Fix: Fix closing socket resource before removing from loop
+ (#141 by @clue)
+
+ This fixes the root cause of an uncaught `Exception` that only manifested
+ itself after the recent Stream v0.7.4 component update and only if you're
+ using `ext-event` (`ExtEventLoop`).
+
+* Improve test suite by testing against PHP 7.2
+ (#140 by @carusogabriel)
+
+## 0.8.6 (2017-11-18)
+
+* Feature: Add Unix domain socket (UDS) support to `Server` with `unix://` URI scheme
+ and add advanced `UnixServer` class.
+ (#120 by @andig)
+
+ ```php
+ // new: Server now supports "unix://" scheme
+ $server = new Server('unix:///tmp/server.sock', $loop);
+
+ // new: advanced usage
+ $server = new UnixServer('/tmp/server.sock', $loop);
+ ```
+
+* Restructure examples to ease getting started
+ (#136 by @clue)
+
+* Improve test suite by adding forward compatibility with PHPUnit 6 and
+ ignore Mac OS X test failures for now until Travis tests work again
+ (#133 by @gabriel-caruso and #134 by @clue)
+
+## 0.8.5 (2017-10-23)
+
+* Fix: Work around PHP bug with Unix domain socket (UDS) paths for Mac OS X
+ (#123 by @andig)
+
+* Fix: Fix `SecureServer` to return `null` URI if server socket is already closed
+ (#129 by @clue)
+
+* Improve test suite by adding forward compatibility with PHPUnit v5 and
+ forward compatibility with upcoming EventLoop releases in tests and
+ test Mac OS X on Travis
+ (#122 by @andig and #125, #127 and #130 by @clue)
+
+* Readme improvements
+ (#118 by @jsor)
+
+## 0.8.4 (2017-09-16)
+
+* Feature: Add `FixedUriConnector` decorator to use fixed, preconfigured URI instead
+ (#117 by @clue)
+
+ This can be useful for consumers that do not support certain URIs, such as
+ when you want to explicitly connect to a Unix domain socket (UDS) path
+ instead of connecting to a default address assumed by an higher-level API:
+
+ ```php
+ $connector = new FixedUriConnector(
+ 'unix:///var/run/docker.sock',
+ new UnixConnector($loop)
+ );
+
+ // destination will be ignored, actually connects to Unix domain socket
+ $promise = $connector->connect('localhost:80');
+ ```
+
+## 0.8.3 (2017-09-08)
+
+* Feature: Reduce memory consumption for failed connections
+ (#113 by @valga)
+
+* Fix: Work around write chunk size for TLS streams for PHP < 7.1.14
+ (#114 by @clue)
+
+## 0.8.2 (2017-08-25)
+
+* Feature: Update DNS dependency to support hosts file on all platforms
+ (#112 by @clue)
+
+ This means that connecting to hosts such as `localhost` will now work as
+ expected across all platforms with no changes required:
+
+ ```php
+ $connector = new Connector($loop);
+ $connector->connect('localhost:8080')->then(function ($connection) {
+ // …
+ });
+ ```
+
+## 0.8.1 (2017-08-15)
+
+* Feature: Forward compatibility with upcoming EventLoop v1.0 and v0.5 and
+ target evenement 3.0 a long side 2.0 and 1.0
+ (#104 by @clue and #111 by @WyriHaximus)
+
+* Improve test suite by locking Travis distro so new defaults will not break the build and
+ fix HHVM build for now again and ignore future HHVM build errors
+ (#109 and #110 by @clue)
+
+* Minor documentation fixes
+ (#103 by @christiaan and #108 by @hansott)
+
+## 0.8.0 (2017-05-09)
+
+* Feature: New `Server` class now acts as a facade for existing server classes
+ and renamed old `Server` to `TcpServer` for advanced usage.
+ (#96 and #97 by @clue)
+
+ The `Server` class is now the main class in this package that implements the
+ `ServerInterface` and allows you to accept incoming streaming connections,
+ such as plaintext TCP/IP or secure TLS connection streams.
+
+ > This is not a BC break and consumer code does not have to be updated.
+
+* Feature / BC break: All addresses are now URIs that include the URI scheme
+ (#98 by @clue)
+
+ ```diff
+ - $parts = parse_url('tcp://' . $conn->getRemoteAddress());
+ + $parts = parse_url($conn->getRemoteAddress());
+ ```
+
+* Fix: Fix `unix://` addresses for Unix domain socket (UDS) paths
+ (#100 by @clue)
+
+* Feature: Forward compatibility with Stream v1.0 and v0.7
+ (#99 by @clue)
+
+## 0.7.2 (2017-04-24)
+
+* Fix: Work around latest PHP 7.0.18 and 7.1.4 no longer accepting full URIs
+ (#94 by @clue)
+
+## 0.7.1 (2017-04-10)
+
+* Fix: Ignore HHVM errors when closing connection that is already closing
+ (#91 by @clue)
+
+## 0.7.0 (2017-04-10)
+
+* Feature: Merge SocketClient component into this component
+ (#87 by @clue)
+
+ This means that this package now provides async, streaming plaintext TCP/IP
+ and secure TLS socket server and client connections for ReactPHP.
+
+ ```
+ $connector = new React\Socket\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+ ```
+
+ Accordingly, the `ConnectionInterface` is now used to represent both incoming
+ server side connections as well as outgoing client side connections.
+
+ If you've previously used the SocketClient component to establish outgoing
+ client connections, upgrading should take no longer than a few minutes.
+ All classes have been merged as-is from the latest `v0.7.0` release with no
+ other changes, so you can simply update your code to use the updated namespace
+ like this:
+
+ ```php
+ // old from SocketClient component and namespace
+ $connector = new React\SocketClient\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+
+ // new
+ $connector = new React\Socket\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+ ```
+
+## 0.6.0 (2017-04-04)
+
+* Feature: Add `LimitingServer` to limit and keep track of open connections
+ (#86 by @clue)
+
+ ```php
+ $server = new Server(0, $loop);
+ $server = new LimitingServer($server, 100);
+
+ $server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+ });
+ ```
+
+* Feature / BC break: Add `pause()` and `resume()` methods to limit active
+ connections
+ (#84 by @clue)
+
+ ```php
+ $server = new Server(0, $loop);
+ $server->pause();
+
+ $loop->addTimer(1.0, function() use ($server) {
+ $server->resume();
+ });
+ ```
+
+## 0.5.1 (2017-03-09)
+
+* Feature: Forward compatibility with Stream v0.5 and upcoming v0.6
+ (#79 by @clue)
+
+## 0.5.0 (2017-02-14)
+
+* Feature / BC break: Replace `listen()` call with URIs passed to constructor
+ and reject listening on hostnames with `InvalidArgumentException`
+ and replace `ConnectionException` with `RuntimeException` for consistency
+ (#61, #66 and #72 by @clue)
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080);
+
+ // new
+ $server = new Server(8080, $loop);
+ ```
+
+ Similarly, you can now pass a full listening URI to the constructor to change
+ the listening host:
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080, '127.0.0.1');
+
+ // new
+ $server = new Server('127.0.0.1:8080', $loop);
+ ```
+
+ Trying to start listening on (DNS) host names will now throw an
+ `InvalidArgumentException`, use IP addresses instead:
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080, 'localhost');
+
+ // new
+ $server = new Server('127.0.0.1:8080', $loop);
+ ```
+
+ If trying to listen fails (such as if port is already in use or port below
+ 1024 may require root access etc.), it will now throw a `RuntimeException`,
+ the `ConnectionException` class has been removed:
+
+ ```php
+ // old: throws React\Socket\ConnectionException
+ $server = new Server($loop);
+ $server->listen(80);
+
+ // new: throws RuntimeException
+ $server = new Server(80, $loop);
+ ```
+
+* Feature / BC break: Rename `shutdown()` to `close()` for consistency throughout React
+ (#62 by @clue)
+
+ ```php
+ // old
+ $server->shutdown();
+
+ // new
+ $server->close();
+ ```
+
+* Feature / BC break: Replace `getPort()` with `getAddress()`
+ (#67 by @clue)
+
+ ```php
+ // old
+ echo $server->getPort(); // 8080
+
+ // new
+ echo $server->getAddress(); // 127.0.0.1:8080
+ ```
+
+* Feature / BC break: `getRemoteAddress()` returns full address instead of only IP
+ (#65 by @clue)
+
+ ```php
+ // old
+ echo $connection->getRemoteAddress(); // 192.168.0.1
+
+ // new
+ echo $connection->getRemoteAddress(); // 192.168.0.1:51743
+ ```
+
+* Feature / BC break: Add `getLocalAddress()` method
+ (#68 by @clue)
+
+ ```php
+ echo $connection->getLocalAddress(); // 127.0.0.1:8080
+ ```
+
+* BC break: The `Server` and `SecureServer` class are now marked `final`
+ and you can no longer `extend` them
+ (which was never documented or recommended anyway).
+ Public properties and event handlers are now internal only.
+ Please use composition instead of extension.
+ (#71, #70 and #69 by @clue)
+
+## 0.4.6 (2017-01-26)
+
+* Feature: Support socket context options passed to `Server`
+ (#64 by @clue)
+
+* Fix: Properly return `null` for unknown addresses
+ (#63 by @clue)
+
+* Improve documentation for `ServerInterface` and lock test suite requirements
+ (#60 by @clue, #57 by @shaunbramley)
+
+## 0.4.5 (2017-01-08)
+
+* Feature: Add `SecureServer` for secure TLS connections
+ (#55 by @clue)
+
+* Add functional integration tests
+ (#54 by @clue)
+
+## 0.4.4 (2016-12-19)
+
+* Feature / Fix: `ConnectionInterface` should extend `DuplexStreamInterface` + documentation
+ (#50 by @clue)
+
+* Feature / Fix: Improve test suite and switch to normal stream handler
+ (#51 by @clue)
+
+* Feature: Add examples
+ (#49 by @clue)
+
+## 0.4.3 (2016-03-01)
+
+* Bug fix: Suppress errors on stream_socket_accept to prevent PHP from crashing
+* Support for PHP7 and HHVM
+* Support PHP 5.3 again
+
+## 0.4.2 (2014-05-25)
+
+* Verify stream is a valid resource in Connection
+
+## 0.4.1 (2014-04-13)
+
+* Bug fix: Check read buffer for data before shutdown signal and end emit (@ArtyDev)
+* Bug fix: v0.3.4 changes merged for v0.4.1
+
+## 0.3.4 (2014-03-30)
+
+* Bug fix: Reset socket to non-blocking after shutting down (PHP bug)
+
+## 0.4.0 (2014-02-02)
+
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: Update to React/Promise 2.0
+* BC break: Update to Evenement 2.0
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+* Bump React dependencies to v0.4
+
+## 0.3.3 (2013-07-08)
+
+* Version bump
+
+## 0.3.2 (2013-05-10)
+
+* Version bump
+
+## 0.3.1 (2013-04-21)
+
+* Feature: Support binding to IPv6 addresses (@clue)
+
+## 0.3.0 (2013-04-14)
+
+* Bump React dependencies to v0.3
+
+## 0.2.6 (2012-12-26)
+
+* Version bump
+
+## 0.2.3 (2012-11-14)
+
+* Version bump
+
+## 0.2.0 (2012-09-10)
+
+* Bump React dependencies to v0.2
+
+## 0.1.1 (2012-07-12)
+
+* Version bump
+
+## 0.1.0 (2012-07-11)
+
+* First tagged release
diff --git a/assets/php/vendor/react/socket/LICENSE b/assets/php/vendor/react/socket/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/assets/php/vendor/react/socket/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/assets/php/vendor/react/socket/README.md b/assets/php/vendor/react/socket/README.md
new file mode 100644
index 0000000..e8b53a0
--- /dev/null
+++ b/assets/php/vendor/react/socket/README.md
@@ -0,0 +1,1419 @@
+# Socket
+
+[![Build Status](https://travis-ci.org/reactphp/socket.svg?branch=master)](https://travis-ci.org/reactphp/socket)
+
+Async, streaming plaintext TCP/IP and secure TLS socket server and client
+connections for [ReactPHP](https://reactphp.org/).
+
+The socket library provides re-usable interfaces for a socket-layer
+server and client based on the [`EventLoop`](https://github.com/reactphp/event-loop)
+and [`Stream`](https://github.com/reactphp/stream) components.
+Its server component allows you to build networking servers that accept incoming
+connections from networking clients (such as an HTTP server).
+Its client component allows you to build networking clients that establish
+outgoing connections to networking servers (such as an HTTP or database client).
+This library provides async, streaming means for all of this, so you can
+handle multiple concurrent connections without blocking.
+
+**Table of Contents**
+
+* [Quickstart example](#quickstart-example)
+* [Connection usage](#connection-usage)
+ * [ConnectionInterface](#connectioninterface)
+ * [getRemoteAddress()](#getremoteaddress)
+ * [getLocalAddress()](#getlocaladdress)
+* [Server usage](#server-usage)
+ * [ServerInterface](#serverinterface)
+ * [connection event](#connection-event)
+ * [error event](#error-event)
+ * [getAddress()](#getaddress)
+ * [pause()](#pause)
+ * [resume()](#resume)
+ * [close()](#close)
+ * [Server](#server)
+ * [Advanced server usage](#advanced-server-usage)
+ * [TcpServer](#tcpserver)
+ * [SecureServer](#secureserver)
+ * [UnixServer](#unixserver)
+ * [LimitingServer](#limitingserver)
+ * [getConnections()](#getconnections)
+* [Client usage](#client-usage)
+ * [ConnectorInterface](#connectorinterface)
+ * [connect()](#connect)
+ * [Connector](#connector)
+ * [Advanced client usage](#advanced-client-usage)
+ * [TcpConnector](#tcpconnector)
+ * [DnsConnector](#dnsconnector)
+ * [SecureConnector](#secureconnector)
+ * [TimeoutConnector](#timeoutconnector)
+ * [UnixConnector](#unixconnector)
+ * [FixUriConnector](#fixeduriconnector)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+
+## Quickstart example
+
+Here is a server that closes the connection if you send it anything:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$socket = new React\Socket\Server('127.0.0.1:8080', $loop);
+
+$socket->on('connection', function (ConnectionInterface $conn) {
+ $conn->write("Hello " . $conn->getRemoteAddress() . "!\n");
+ $conn->write("Welcome to this amazing server!\n");
+ $conn->write("Here's a tip: don't say anything.\n");
+
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Here's a client that outputs the output of said server and then attempts to
+send it a string:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$connector = new React\Socket\Connector($loop);
+
+$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $conn) use ($loop) {
+ $conn->pipe(new React\Stream\WritableResourceStream(STDOUT, $loop));
+ $conn->write("Hello World!\n");
+});
+
+$loop->run();
+```
+
+## Connection usage
+
+### ConnectionInterface
+
+The `ConnectionInterface` is used to represent any incoming and outgoing
+connection, such as a normal TCP/IP connection.
+
+An incoming or outgoing connection is a duplex stream (both readable and
+writable) that implements React's
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+It contains additional properties for the local and remote address (client IP)
+where this connection has been established to/from.
+
+Most commonly, instances implementing this `ConnectionInterface` are emitted
+by all classes implementing the [`ServerInterface`](#serverinterface) and
+used by all classes implementing the [`ConnectorInterface`](#connectorinterface).
+
+Because the `ConnectionInterface` implements the underlying
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
+you can use any of its events and methods as usual:
+
+```php
+$connection->on('data', function ($chunk) {
+ echo $chunk;
+});
+
+$connection->on('end', function () {
+ echo 'ended';
+});
+
+$connection->on('error', function (Exception $e) {
+ echo 'error: ' . $e->getMessage();
+});
+
+$connection->on('close', function () {
+ echo 'closed';
+});
+
+$connection->write($data);
+$connection->end($data = null);
+$connection->close();
+// …
+```
+
+For more details, see the
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+
+#### getRemoteAddress()
+
+The `getRemoteAddress(): ?string` method returns the full remote address
+(URI) where this connection has been established with.
+
+```php
+$address = $connection->getRemoteAddress();
+echo 'Connection with ' . $address . PHP_EOL;
+```
+
+If the remote address can not be determined or is unknown at this time (such as
+after the connection has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+If this is a TCP/IP based connection and you only want the remote IP, you may
+use something like this:
+
+```php
+$address = $connection->getRemoteAddress();
+$ip = trim(parse_url($address, PHP_URL_HOST), '[]');
+echo 'Connection with ' . $ip . PHP_EOL;
+```
+
+#### getLocalAddress()
+
+The `getLocalAddress(): ?string` method returns the full local address
+(URI) where this connection has been established with.
+
+```php
+$address = $connection->getLocalAddress();
+echo 'Connection with ' . $address . PHP_EOL;
+```
+
+If the local address can not be determined or is unknown at this time (such as
+after the connection has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+This method complements the [`getRemoteAddress()`](#getremoteaddress) method,
+so they should not be confused.
+
+If your `TcpServer` instance is listening on multiple interfaces (e.g. using
+the address `0.0.0.0`), you can use this method to find out which interface
+actually accepted this connection (such as a public or local interface).
+
+If your system has multiple interfaces (e.g. a WAN and a LAN interface),
+you can use this method to find out which interface was actually
+used for this connection.
+
+## Server usage
+
+### ServerInterface
+
+The `ServerInterface` is responsible for providing an interface for accepting
+incoming streaming connections, such as a normal TCP/IP connection.
+
+Most higher-level components (such as a HTTP server) accept an instance
+implementing this interface to accept incoming streaming connections.
+This is usually done via dependency injection, so it's fairly simple to actually
+swap this implementation against any other implementation of this interface.
+This means that you SHOULD typehint against this interface instead of a concrete
+implementation of this interface.
+
+Besides defining a few methods, this interface also implements the
+[`EventEmitterInterface`](https://github.com/igorw/evenement)
+which allows you to react to certain events.
+
+#### connection event
+
+The `connection` event will be emitted whenever a new connection has been
+established, i.e. a new client connects to this server socket:
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'new connection' . PHP_EOL;
+});
+```
+
+See also the [`ConnectionInterface`](#connectioninterface) for more details
+about handling the incoming connection.
+
+#### error event
+
+The `error` event will be emitted whenever there's an error accepting a new
+connection from a client.
+
+```php
+$server->on('error', function (Exception $e) {
+ echo 'error: ' . $e->getMessage() . PHP_EOL;
+});
+```
+
+Note that this is not a fatal error event, i.e. the server keeps listening for
+new connections even after this event.
+
+
+#### getAddress()
+
+The `getAddress(): ?string` method can be used to
+return the full address (URI) this server is currently listening on.
+
+```php
+$address = $server->getAddress();
+echo 'Server listening on ' . $address . PHP_EOL;
+```
+
+If the address can not be determined or is unknown at this time (such as
+after the socket has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+If this is a TCP/IP based server and you only want the local port, you may
+use something like this:
+
+```php
+$address = $server->getAddress();
+$port = parse_url($address, PHP_URL_PORT);
+echo 'Server listening on port ' . $port . PHP_EOL;
+```
+
+#### pause()
+
+The `pause(): void` method can be used to
+pause accepting new incoming connections.
+
+Removes the socket resource from the EventLoop and thus stop accepting
+new connections. Note that the listening socket stays active and is not
+closed.
+
+This means that new incoming connections will stay pending in the
+operating system backlog until its configurable backlog is filled.
+Once the backlog is filled, the operating system may reject further
+incoming connections until the backlog is drained again by resuming
+to accept new connections.
+
+Once the server is paused, no futher `connection` events SHOULD
+be emitted.
+
+```php
+$server->pause();
+
+$server->on('connection', assertShouldNeverCalled());
+```
+
+This method is advisory-only, though generally not recommended, the
+server MAY continue emitting `connection` events.
+
+Unless otherwise noted, a successfully opened server SHOULD NOT start
+in paused state.
+
+You can continue processing events by calling `resume()` again.
+
+Note that both methods can be called any number of times, in particular
+calling `pause()` more than once SHOULD NOT have any effect.
+Similarly, calling this after `close()` is a NO-OP.
+
+#### resume()
+
+The `resume(): void` method can be used to
+resume accepting new incoming connections.
+
+Re-attach the socket resource to the EventLoop after a previous `pause()`.
+
+```php
+$server->pause();
+
+$loop->addTimer(1.0, function () use ($server) {
+ $server->resume();
+});
+```
+
+Note that both methods can be called any number of times, in particular
+calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+Similarly, calling this after `close()` is a NO-OP.
+
+#### close()
+
+The `close(): void` method can be used to
+shut down this listening socket.
+
+This will stop listening for new incoming connections on this socket.
+
+```php
+echo 'Shutting down server socket' . PHP_EOL;
+$server->close();
+```
+
+Calling this method more than once on the same instance is a NO-OP.
+
+### Server
+
+The `Server` class is the main class in this package that implements the
+[`ServerInterface`](#serverinterface) and allows you to accept incoming
+streaming connections, such as plaintext TCP/IP or secure TLS connection streams.
+Connections can also be accepted on Unix domain sockets.
+
+```php
+$server = new Server(8080, $loop);
+```
+
+As above, the `$uri` parameter can consist of only a port, in which case the
+server will default to listening on the localhost address `127.0.0.1`,
+which means it will not be reachable from outside of this system.
+
+In order to use a random port assignment, you can use the port `0`:
+
+```php
+$server = new Server(0, $loop);
+$address = $server->getAddress();
+```
+
+In order to change the host the socket is listening on, you can provide an IP
+address through the first parameter provided to the constructor, optionally
+preceded by the `tcp://` scheme:
+
+```php
+$server = new Server('192.168.0.1:8080', $loop);
+```
+
+If you want to listen on an IPv6 address, you MUST enclose the host in square
+brackets:
+
+```php
+$server = new Server('[::1]:8080', $loop);
+```
+
+To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the
+`unix://` scheme:
+
+```php
+$server = new Server('unix:///tmp/server.sock', $loop);
+```
+
+If the given URI is invalid, does not contain a port, any other scheme or if it
+contains a hostname, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException due to missing port
+$server = new Server('127.0.0.1', $loop);
+```
+
+If the given URI appears to be valid, but listening on it fails (such as if port
+is already in use or port below 1024 may require root access etc.), it will
+throw a `RuntimeException`:
+
+```php
+$first = new Server(8080, $loop);
+
+// throws RuntimeException because port is already in use
+$second = new Server(8080, $loop);
+```
+
+> Note that these error conditions may vary depending on your system and/or
+ configuration.
+ See the exception message and code for more details about the actual error
+ condition.
+
+Optionally, you can specify [TCP socket context options](http://php.net/manual/en/context.socket.php)
+for the underlying stream socket resource like this:
+
+```php
+$server = new Server('[::1]:8080', $loop, array(
+ 'tcp' => array(
+ 'backlog' => 200,
+ 'so_reuseport' => true,
+ 'ipv6_v6only' => true
+ )
+));
+```
+
+> Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+ their defaults and effects of changing these may vary depending on your system
+ and/or PHP version.
+ Passing unknown context options has no effect.
+ For BC reasons, you can also pass the TCP socket context options as a simple
+ array without wrapping this in another array under the `tcp` key.
+
+You can start a secure TLS (formerly known as SSL) server by simply prepending
+the `tls://` URI scheme.
+Internally, it will wait for plaintext TCP/IP connections and then performs a
+TLS handshake for each connection.
+It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php),
+which in its most basic form may look something like this if you're using a
+PEM encoded certificate file:
+
+```php
+$server = new Server('tls://127.0.0.1:8080', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem'
+ )
+));
+```
+
+> Note that the certificate file will not be loaded on instantiation but when an
+ incoming connection initializes its TLS context.
+ This implies that any invalid certificate file paths or contents will only cause
+ an `error` event at a later time.
+
+If your private key is encrypted with a passphrase, you have to specify it
+like this:
+
+```php
+$server = new Server('tls://127.0.0.1:8000', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem',
+ 'passphrase' => 'secret'
+ )
+));
+```
+
+By default, this server supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$server = new Server('tls://127.0.0.1:8000', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+ )
+));
+```
+
+> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
+ their defaults and effects of changing these may vary depending on your system
+ and/or PHP version.
+ The outer context array allows you to also use `tcp` (and possibly more)
+ context options at the same time.
+ Passing unknown context options has no effect.
+ If you do not use the `tls://` scheme, then passing `tls` context options
+ has no effect.
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+> Note that the `Server` class is a concrete implementation for TCP/IP sockets.
+ If you want to typehint in your higher-level protocol implementation, you SHOULD
+ use the generic [`ServerInterface`](#serverinterface) instead.
+
+### Advanced server usage
+
+#### TcpServer
+
+The `TcpServer` class implements the [`ServerInterface`](#serverinterface) and
+is responsible for accepting plaintext TCP/IP connections.
+
+```php
+$server = new TcpServer(8080, $loop);
+```
+
+As above, the `$uri` parameter can consist of only a port, in which case the
+server will default to listening on the localhost address `127.0.0.1`,
+which means it will not be reachable from outside of this system.
+
+In order to use a random port assignment, you can use the port `0`:
+
+```php
+$server = new TcpServer(0, $loop);
+$address = $server->getAddress();
+```
+
+In order to change the host the socket is listening on, you can provide an IP
+address through the first parameter provided to the constructor, optionally
+preceded by the `tcp://` scheme:
+
+```php
+$server = new TcpServer('192.168.0.1:8080', $loop);
+```
+
+If you want to listen on an IPv6 address, you MUST enclose the host in square
+brackets:
+
+```php
+$server = new TcpServer('[::1]:8080', $loop);
+```
+
+If the given URI is invalid, does not contain a port, any other scheme or if it
+contains a hostname, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException due to missing port
+$server = new TcpServer('127.0.0.1', $loop);
+```
+
+If the given URI appears to be valid, but listening on it fails (such as if port
+is already in use or port below 1024 may require root access etc.), it will
+throw a `RuntimeException`:
+
+```php
+$first = new TcpServer(8080, $loop);
+
+// throws RuntimeException because port is already in use
+$second = new TcpServer(8080, $loop);
+```
+
+> Note that these error conditions may vary depending on your system and/or
+configuration.
+See the exception message and code for more details about the actual error
+condition.
+
+Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php)
+for the underlying stream socket resource like this:
+
+```php
+$server = new TcpServer('[::1]:8080', $loop, array(
+ 'backlog' => 200,
+ 'so_reuseport' => true,
+ 'ipv6_v6only' => true
+));
+```
+
+> Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+their defaults and effects of changing these may vary depending on your system
+and/or PHP version.
+Passing unknown context options has no effect.
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+#### SecureServer
+
+The `SecureServer` class implements the [`ServerInterface`](#serverinterface)
+and is responsible for providing a secure TLS (formerly known as SSL) server.
+
+It does so by wrapping a [`TcpServer`](#tcpserver) instance which waits for plaintext
+TCP/IP connections and then performs a TLS handshake for each connection.
+It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php),
+which in its most basic form may look something like this if you're using a
+PEM encoded certificate file:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem'
+));
+```
+
+> Note that the certificate file will not be loaded on instantiation but when an
+incoming connection initializes its TLS context.
+This implies that any invalid certificate file paths or contents will only cause
+an `error` event at a later time.
+
+If your private key is encrypted with a passphrase, you have to specify it
+like this:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem',
+ 'passphrase' => 'secret'
+));
+```
+
+By default, this server supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+));
+```
+
+> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
+their defaults and effects of changing these may vary depending on your system
+and/or PHP version.
+Passing unknown context options has no effect.
+
+Whenever a client completes the TLS handshake, it will emit a `connection` event
+with a connection instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+Whenever a client fails to perform a successful TLS handshake, it will emit an
+`error` event and then close the underlying TCP/IP connection:
+
+```php
+$server->on('error', function (Exception $e) {
+ echo 'Error' . $e->getMessage() . PHP_EOL;
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+Note that the `SecureServer` class is a concrete implementation for TLS sockets.
+If you want to typehint in your higher-level protocol implementation, you SHOULD
+use the generic [`ServerInterface`](#serverinterface) instead.
+
+> Advanced usage: Despite allowing any `ServerInterface` as first parameter,
+you SHOULD pass a `TcpServer` instance as first parameter, unless you
+know what you're doing.
+Internally, the `SecureServer` has to set the required TLS context options on
+the underlying stream resources.
+These resources are not exposed through any of the interfaces defined in this
+package, but only through the internal `Connection` class.
+The `TcpServer` class is guaranteed to emit connections that implement
+the `ConnectionInterface` and uses the internal `Connection` class in order to
+expose these underlying resources.
+If you use a custom `ServerInterface` and its `connection` event does not
+meet this requirement, the `SecureServer` will emit an `error` event and
+then close the underlying connection.
+
+#### UnixServer
+
+The `UnixServer` class implements the [`ServerInterface`](#serverinterface) and
+is responsible for accepting connections on Unix domain sockets (UDS).
+
+```php
+$server = new UnixServer('/tmp/server.sock', $loop);
+```
+
+As above, the `$uri` parameter can consist of only a socket path or socket path
+prefixed by the `unix://` scheme.
+
+If the given URI appears to be valid, but listening on it fails (such as if the
+socket is already in use or the file not accessible etc.), it will throw a
+`RuntimeException`:
+
+```php
+$first = new UnixServer('/tmp/same.sock', $loop);
+
+// throws RuntimeException because socket is already in use
+$second = new UnixServer('/tmp/same.sock', $loop);
+```
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'New connection' . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+#### LimitingServer
+
+The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible
+for limiting and keeping track of open connections to this server instance.
+
+Whenever the underlying server emits a `connection` event, it will check its
+limits and then either
+ - keep track of this connection by adding it to the list of
+ open connections and then forward the `connection` event
+ - or reject (close) the connection when its limits are exceeded and will
+ forward an `error` event instead.
+
+Whenever a connection closes, it will remove this connection from the list of
+open connections.
+
+```php
+$server = new LimitingServer($server, 100);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [second example](examples) for more details.
+
+You have to pass a maximum number of open connections to ensure
+the server will automatically reject (close) connections once this limit
+is exceeded. In this case, it will emit an `error` event to inform about
+this and no `connection` event will be emitted.
+
+```php
+$server = new LimitingServer($server, 100);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+You MAY pass a `null` limit in order to put no limit on the number of
+open connections and keep accepting new connection until you run out of
+operating system resources (such as open file handles). This may be
+useful if you do not want to take care of applying a limit but still want
+to use the `getConnections()` method.
+
+You can optionally configure the server to pause accepting new
+connections once the connection limit is reached. In this case, it will
+pause the underlying server and no longer process any new connections at
+all, thus also no longer closing any excessive connections.
+The underlying operating system is responsible for keeping a backlog of
+pending connections until its limit is reached, at which point it will
+start rejecting further connections.
+Once the server is below the connection limit, it will continue consuming
+connections from the backlog and will process any outstanding data on
+each connection.
+This mode may be useful for some protocols that are designed to wait for
+a response message (such as HTTP), but may be less useful for other
+protocols that demand immediate responses (such as a "welcome" message in
+an interactive chat).
+
+```php
+$server = new LimitingServer($server, 100, true);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+##### getConnections()
+
+The `getConnections(): ConnectionInterface[]` method can be used to
+return an array with all currently active connections.
+
+```php
+foreach ($server->getConnection() as $connection) {
+ $connection->write('Hi!');
+}
+```
+
+## Client usage
+
+### ConnectorInterface
+
+The `ConnectorInterface` is responsible for providing an interface for
+establishing streaming connections, such as a normal TCP/IP connection.
+
+This is the main interface defined in this package and it is used throughout
+React's vast ecosystem.
+
+Most higher-level components (such as HTTP, database or other networking
+service clients) accept an instance implementing this interface to create their
+TCP/IP connection to the underlying networking service.
+This is usually done via dependency injection, so it's fairly simple to actually
+swap this implementation against any other implementation of this interface.
+
+The interface only offers a single method:
+
+#### connect()
+
+The `connect(string $uri): PromiseInterface<ConnectionInterface, Exception>` method
+can be used to create a streaming connection to the given remote address.
+
+It returns a [Promise](https://github.com/reactphp/promise) which either
+fulfills with a stream implementing [`ConnectionInterface`](#connectioninterface)
+on success or rejects with an `Exception` if the connection is not successful:
+
+```php
+$connector->connect('google.com:443')->then(
+ function (ConnectionInterface $connection) {
+ // connection successfully established
+ },
+ function (Exception $error) {
+ // failed to connect due to $error
+ }
+);
+```
+
+See also [`ConnectionInterface`](#connectioninterface) for more details.
+
+The returned Promise MUST be implemented in such a way that it can be
+cancelled when it is still pending. Cancelling a pending promise MUST
+reject its value with an `Exception`. It SHOULD clean up any underlying
+resources and references as applicable:
+
+```php
+$promise = $connector->connect($uri);
+
+$promise->cancel();
+```
+
+### Connector
+
+The `Connector` class is the main class in this package that implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create streaming connections.
+
+You can use this connector to create any kind of streaming connections, such
+as plaintext TCP/IP, secure TLS or local Unix connection streams.
+
+It binds to the main event loop and can be used like this:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$connector = new Connector($loop);
+
+$connector->connect($uri)->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+In order to create a plaintext TCP/IP connection, you can simply pass a host
+and port combination like this:
+
+```php
+$connector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> If you do no specify a URI scheme in the destination URI, it will assume
+ `tcp://` as a default and establish a plaintext TCP/IP connection.
+ Note that TCP/IP connections require a host and port part in the destination
+ URI like above, all other URI components are optional.
+
+In order to create a secure TLS connection, you can use the `tls://` URI scheme
+like this:
+
+```php
+$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+In order to create a local Unix domain socket connection, you can use the
+`unix://` URI scheme like this:
+
+```php
+$connector->connect('unix:///tmp/demo.sock')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
+ Unix domain socket (UDS) path as given to the `connect()` method, including
+ the `unix://` scheme, for example `unix:///tmp/demo.sock`.
+ The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
+ `null` value as this value is not applicable to UDS connections here.
+
+Under the hood, the `Connector` is implemented as a *higher-level facade*
+for the lower-level connectors implemented in this package. This means it
+also shares all of their features and implementation details.
+If you want to typehint in your higher-level protocol implementation, you SHOULD
+use the generic [`ConnectorInterface`](#connectorinterface) instead.
+
+The `Connector` class will try to detect your system DNS settings (and uses
+Google's public DNS server `8.8.8.8` as a fallback if unable to determine your
+system settings) to resolve all public hostnames into underlying IP addresses by
+default.
+If you explicitly want to use a custom DNS server (such as a local DNS relay or
+a company wide DNS server), you can set up the `Connector` like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'dns' => '127.0.1.1'
+));
+
+$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+If you do not want to use a DNS resolver at all and want to connect to IP
+addresses only, you can also set up your `Connector` like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'dns' => false
+));
+
+$connector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+Advanced: If you need a custom DNS `Resolver` instance, you can also set up
+your `Connector` like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
+
+$connector = new Connector($loop, array(
+ 'dns' => $resolver
+));
+
+$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+By default, the `tcp://` and `tls://` URI schemes will use timeout value that
+repects your `default_socket_timeout` ini setting (which defaults to 60s).
+If you want a custom timeout value, you can simply pass this like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'timeout' => 10.0
+));
+```
+
+Similarly, if you do not want to apply a timeout at all and let the operating
+system handle this, you can pass a boolean flag like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'timeout' => false
+));
+```
+
+By default, the `Connector` supports the `tcp://`, `tls://` and `unix://`
+URI schemes. If you want to explicitly prohibit any of these, you can simply
+pass boolean flags like this:
+
+```php
+// only allow secure TLS connections
+$connector = new Connector($loop, array(
+ 'tcp' => false,
+ 'tls' => true,
+ 'unix' => false,
+));
+
+$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+The `tcp://` and `tls://` also accept additional context options passed to
+the underlying connectors.
+If you want to explicitly pass additional context options, you can simply
+pass arrays of context options like this:
+
+```php
+// allow insecure TLS connections
+$connector = new Connector($loop, array(
+ 'tcp' => array(
+ 'bindto' => '192.168.0.1:0'
+ ),
+ 'tls' => array(
+ 'verify_peer' => false,
+ 'verify_peer_name' => false
+ ),
+));
+
+$connector->connect('tls://localhost:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+By default, this connector supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$connector = new Connector($loop, array(
+ 'tls' => array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+ )
+));
+```
+
+> For more details about context options, please refer to the PHP documentation
+ about [socket context options](http://php.net/manual/en/context.socket.php)
+ and [SSL context options](http://php.net/manual/en/context.ssl.php).
+
+Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and
+`unix://` URI schemes.
+For this, it sets up the required connector classes automatically.
+If you want to explicitly pass custom connectors for any of these, you can simply
+pass an instance implementing the `ConnectorInterface` like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
+$tcp = new DnsConnector(new TcpConnector($loop), $resolver);
+
+$tls = new SecureConnector($tcp, $loop);
+
+$unix = new UnixConnector($loop);
+
+$connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'tls' => $tls,
+ 'unix' => $unix,
+
+ 'dns' => false,
+ 'timeout' => false,
+));
+
+$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> Internally, the `tcp://` connector will always be wrapped by the DNS resolver,
+ unless you disable DNS like in the above example. In this case, the `tcp://`
+ connector receives the actual hostname instead of only the resolved IP address
+ and is thus responsible for performing the lookup.
+ Internally, the automatically created `tls://` connector will always wrap the
+ underlying `tcp://` connector for establishing the underlying plaintext
+ TCP/IP connection before enabling secure TLS mode. If you want to use a custom
+ underlying `tcp://` connector for secure TLS connections only, you may
+ explicitly pass a `tls://` connector like above instead.
+ Internally, the `tcp://` and `tls://` connectors will always be wrapped by
+ `TimeoutConnector`, unless you disable timeouts like in the above example.
+
+### Advanced client usage
+
+#### TcpConnector
+
+The `React\Socket\TcpConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
+TCP/IP connections to any IP-port-combination:
+
+```php
+$tcpConnector = new React\Socket\TcpConnector($loop);
+
+$tcpConnector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $tcpConnector->connect('127.0.0.1:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will close the underlying socket
+resource, thus cancelling the pending TCP/IP connection, and reject the
+resulting promise.
+
+You can optionally pass additional
+[socket context options](http://php.net/manual/en/context.socket.php)
+to the constructor like this:
+
+```php
+$tcpConnector = new React\Socket\TcpConnector($loop, array(
+ 'bindto' => '192.168.0.1:0'
+));
+```
+
+Note that this class only allows you to connect to IP-port-combinations.
+If the given URI is invalid, does not contain a valid IP address and port
+or contains any other scheme, it will reject with an
+`InvalidArgumentException`:
+
+If the given URI appears to be valid, but connecting to it fails (such as if
+the remote host rejects the connection etc.), it will reject with a
+`RuntimeException`.
+
+If you want to connect to hostname-port-combinations, see also the following chapter.
+
+> Advanced usage: Internally, the `TcpConnector` allocates an empty *context*
+resource for each stream resource.
+If the destination URI contains a `hostname` query parameter, its value will
+be used to set up the TLS peer name.
+This is used by the `SecureConnector` and `DnsConnector` to verify the peer
+name and can also be used if you want a custom TLS peer name.
+
+#### DnsConnector
+
+The `DnsConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
+TCP/IP connections to any hostname-port-combination.
+
+It does so by decorating a given `TcpConnector` instance so that it first
+looks up the given domain name via DNS (if applicable) and then establishes the
+underlying TCP/IP connection to the resolved target IP address.
+
+Make sure to set up your DNS resolver and underlying TCP connector like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
+
+$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
+
+$dnsConnector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $dnsConnector->connect('www.google.com:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying DNS lookup
+and/or the underlying TCP/IP connection and reject the resulting promise.
+
+> Advanced usage: Internally, the `DnsConnector` relies on a `Resolver` to
+look up the IP address for the given hostname.
+It will then replace the hostname in the destination URI with this IP and
+append a `hostname` query parameter and pass this updated URI to the underlying
+connector.
+The underlying connector is thus responsible for creating a connection to the
+target IP address, while this query parameter can be used to check the original
+hostname and is used by the `TcpConnector` to set up the TLS peer name.
+If a `hostname` is given explicitly, this query parameter will not be modified,
+which can be useful if you want a custom TLS peer name.
+
+#### SecureConnector
+
+The `SecureConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create secure
+TLS (formerly known as SSL) connections to any hostname-port-combination.
+
+It does so by decorating a given `DnsConnector` instance so that it first
+creates a plaintext TCP/IP connection and then enables TLS encryption on this
+stream.
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop);
+
+$secureConnector->connect('www.google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
+ ...
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $secureConnector->connect('www.google.com:443');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying TCP/IP
+connection and/or the SSL/TLS negotiation and reject the resulting promise.
+
+You can optionally pass additional
+[SSL context options](http://php.net/manual/en/context.ssl.php)
+to the constructor like this:
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
+ 'verify_peer' => false,
+ 'verify_peer_name' => false
+));
+```
+
+By default, this connector supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+));
+```
+
+> Advanced usage: Internally, the `SecureConnector` relies on setting up the
+required *context options* on the underlying stream resource.
+It should therefor be used with a `TcpConnector` somewhere in the connector
+stack so that it can allocate an empty *context* resource for each stream
+resource and verify the peer name.
+Failing to do so may result in a TLS peer name mismatch error or some hard to
+trace race conditions, because all stream resources will use a single, shared
+*default context* resource otherwise.
+
+#### TimeoutConnector
+
+The `TimeoutConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to add timeout
+handling to any existing connector instance.
+
+It does so by decorating any given [`ConnectorInterface`](#connectorinterface)
+instance and starting a timer that will automatically reject and abort any
+underlying connection attempt if it takes too long.
+
+```php
+$timeoutConnector = new React\Socket\TimeoutConnector($connector, 3.0, $loop);
+
+$timeoutConnector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
+ // connection succeeded within 3.0 seconds
+});
+```
+
+See also any of the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $timeoutConnector->connect('google.com:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying connection
+attempt, abort the timer and reject the resulting promise.
+
+#### UnixConnector
+
+The `UnixConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to connect to
+Unix domain socket (UDS) paths like this:
+
+```php
+$connector = new React\Socket\UnixConnector($loop);
+
+$connector->connect('/tmp/demo.sock')->then(function (ConnectionInterface $connection) {
+ $connection->write("HELLO\n");
+});
+
+$loop->run();
+```
+
+Connecting to Unix domain sockets is an atomic operation, i.e. its promise will
+settle (either resolve or reject) immediately.
+As such, calling `cancel()` on the resulting promise has no effect.
+
+> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
+ Unix domain socket (UDS) path as given to the `connect()` method, prepended
+ with the `unix://` scheme, for example `unix:///tmp/demo.sock`.
+ The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
+ `null` value as this value is not applicable to UDS connections here.
+
+#### FixedUriConnector
+
+The `FixedUriConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and decorates an existing Connector
+to always use a fixed, preconfigured URI.
+
+This can be useful for consumers that do not support certain URIs, such as
+when you want to explicitly connect to a Unix domain socket (UDS) path
+instead of connecting to a default address assumed by an higher-level API:
+
+```php
+$connector = new FixedUriConnector(
+ 'unix:///var/run/docker.sock',
+ new UnixConnector($loop)
+);
+
+// destination will be ignored, actually connects to Unix domain socket
+$promise = $connector->connect('localhost:80');
+```
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/socket:^0.8.10
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM.
+It's *highly recommended to use PHP 7+* for this project, partly due to its vast
+performance improvements and partly because legacy PHP versions require several
+workarounds as described below.
+
+Secure TLS connections received some major upgrades starting with PHP 5.6, with
+the defaults now being more secure, while older versions required explicit
+context options.
+This library does not take responsibility over these context options, so it's
+up to consumers of this library to take care of setting appropriate context
+options as described above.
+
+All versions of PHP prior to 5.6.8 suffered from a buffering issue where reading
+from a streaming TLS connection could be one `data` event behind.
+This library implements a work-around to try to flush the complete incoming
+data buffers on these legacy PHP versions, which has a penalty of around 10% of
+throughput on all connections.
+With this work-around, we have not been able to reproduce this issue anymore,
+but we have seen reports of people saying this could still affect some of the
+older PHP versions (`5.5.23`, `5.6.7`, and `5.6.8`).
+Note that this only affects *some* higher-level streaming protocols, such as
+IRC over TLS, but should not affect HTTP over TLS (HTTPS).
+Further investigation of this issue is needed.
+For more insights, this issue is also covered by our test suite.
+
+PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big
+chunks of data over TLS streams at once.
+We try to work around this by limiting the write chunk size to 8192
+bytes for older PHP versions only.
+This is only a work-around and has a noticable performance penalty on
+affected versions.
+
+This project also supports running on HHVM.
+Note that really old HHVM < 3.8 does not support secure TLS connections, as it
+lacks the required `stream_socket_enable_crypto()` function.
+As such, trying to create a secure TLS connections on affected versions will
+return a rejected promise instead.
+This issue is also covered by our test suite, which will skip related tests
+on affected versions.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+The test suite also contains a number of functional integration tests that rely
+on a stable internet connection.
+If you do not want to run these, they can simply be skipped like this:
+
+```bash
+$ php vendor/bin/phpunit --exclude-group internet
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
diff --git a/assets/php/vendor/react/socket/composer.json b/assets/php/vendor/react/socket/composer.json
new file mode 100644
index 0000000..bc85aab
--- /dev/null
+++ b/assets/php/vendor/react/socket/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "react/socket",
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": ["async", "socket", "stream", "connection", "ReactPHP"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.0",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "react/dns": "^0.4.13",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/stream": "^1.0 || ^0.7.1",
+ "react/promise": "^2.1 || ^1.2",
+ "react/promise-timer": "~1.0"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "React\\Tests\\Socket\\": "tests"
+ }
+ }
+}
diff --git a/assets/php/vendor/react/socket/examples/01-echo-server.php b/assets/php/vendor/react/socket/examples/01-echo-server.php
new file mode 100644
index 0000000..2c0be57
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/01-echo-server.php
@@ -0,0 +1,42 @@
+<?php
+
+// Just start this server and connect to it. Everything you send to it will be
+// sent back to you.
+//
+// $ php examples/01-echo-server.php 8000
+// $ telnet localhost 8000
+//
+// You can also run a secure TLS echo server like this:
+//
+// $ php examples/01-echo-server.php tls://127.0.0.1:8000 examples/localhost.pem
+// $ openssl s_client -connect localhost:8000
+//
+// You can also run a Unix domain socket (UDS) server like this:
+//
+// $ php examples/01-echo-server.php unix:///tmp/server.sock
+// $ nc -U /tmp/server.sock
+
+use React\EventLoop\Factory;
+use React\Socket\Server;
+use React\Socket\ConnectionInterface;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+
+$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop, array(
+ 'tls' => array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) {
+ echo '[' . $conn->getRemoteAddress() . ' connected]' . PHP_EOL;
+ $conn->pipe($conn);
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/02-chat-server.php b/assets/php/vendor/react/socket/examples/02-chat-server.php
new file mode 100644
index 0000000..46439e0
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/02-chat-server.php
@@ -0,0 +1,59 @@
+<?php
+
+// Just start this server and connect with any number of clients to it.
+// Everything a client sends will be broadcasted to all connected clients.
+//
+// $ php examples/02-chat-server.php 8000
+// $ telnet localhost 8000
+//
+// You can also run a secure TLS chat server like this:
+//
+// $ php examples/02-chat-server.php tls://127.0.0.1:8000 examples/localhost.pem
+// $ openssl s_client -connect localhost:8000
+//
+// You can also run a Unix domain socket (UDS) server like this:
+//
+// $ php examples/02-chat-server.php unix:///tmp/server.sock
+// $ nc -U /tmp/server.sock
+
+use React\EventLoop\Factory;
+use React\Socket\Server;
+use React\Socket\ConnectionInterface;
+use React\Socket\LimitingServer;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+
+$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop, array(
+ 'tls' => array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server = new LimitingServer($server, null);
+
+$server->on('connection', function (ConnectionInterface $client) use ($server) {
+ // whenever a new message comes in
+ $client->on('data', function ($data) use ($client, $server) {
+ // remove any non-word characters (just for the demo)
+ $data = trim(preg_replace('/[^\w\d \.\,\-\!\?]/u', '', $data));
+
+ // ignore empty messages
+ if ($data === '') {
+ return;
+ }
+
+ // prefix with client IP and broadcast to all connected clients
+ $data = trim(parse_url($client->getRemoteAddress(), PHP_URL_HOST), '[]') . ': ' . $data . PHP_EOL;
+ foreach ($server->getConnections() as $connection) {
+ $connection->write($data);
+ }
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/03-http-server.php b/assets/php/vendor/react/socket/examples/03-http-server.php
new file mode 100644
index 0000000..eb6d454
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/03-http-server.php
@@ -0,0 +1,57 @@
+<?php
+
+// Simple HTTP server example (for illustration purposes only).
+// This shows how a plaintext TCP/IP connection is accepted to then receive an
+// application level protocol message (HTTP request) and reply with an
+// application level protocol message (HTTP response) in return.
+//
+// This example exists for illustraion purposes only. It does not actually
+// parse incoming HTTP requests, so you can actually send *anything* and will
+// still respond with a valid HTTP response.
+// Real applications should use react/http instead!
+//
+// Just start this server and send a request to it:
+//
+// $ php examples/03-http-server.php 8000
+// $ curl -v http://localhost:8000/
+// $ ab -n1000 -c10 http://localhost:8000/
+// $ docker run -it --rm --net=host jordi/ab ab -n1000 -c10 http://localhost:8000/
+//
+// You can also run a secure HTTPS echo server like this:
+//
+// $ php examples/03-http-server.php tls://127.0.0.1:8000 examples/localhost.pem
+// $ curl -v --insecure https://localhost:8000/
+// $ ab -n1000 -c10 https://localhost:8000/
+// $ docker run -it --rm --net=host jordi/ab ab -n1000 -c10 https://localhost:8000/
+//
+// You can also run a Unix domain socket (UDS) server like this:
+//
+// $ php examples/03-http-server.php unix:///tmp/server.sock
+// $ nc -U /tmp/server.sock
+
+use React\EventLoop\Factory;
+use React\Socket\Server;
+use React\Socket\ConnectionInterface;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+
+$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop, array(
+ 'tls' => array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) {
+ $conn->once('data', function () use ($conn) {
+ $body = "<html><h1>Hello world!</h1></html>\r\n";
+ $conn->end("HTTP/1.1 200 OK\r\nContent-Length: " . strlen($body) . "\r\nConnection: close\r\n\r\n" . $body);
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . strtr($server->getAddress(), array('tcp:' => 'http:', 'tls:' => 'https:')) . PHP_EOL;
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/11-http-client.php b/assets/php/vendor/react/socket/examples/11-http-client.php
new file mode 100644
index 0000000..2b64a43
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/11-http-client.php
@@ -0,0 +1,36 @@
+<?php
+
+// Simple plaintext HTTP client example (for illustration purposes only).
+// This shows how a plaintext TCP/IP connection is established to then send an
+// application level protocol message (HTTP).
+// Real applications should use react/http-client instead!
+//
+// This simple example only accepts an optional host parameter to send the
+// request to. See also example #22 for proper URI parsing.
+//
+// $ php examples/11-http-client.php
+// $ php examples/11-http-client.php reactphp.org
+
+use React\EventLoop\Factory;
+use React\Socket\Connector;
+use React\Socket\ConnectionInterface;
+
+$host = isset($argv[1]) ? $argv[1] : 'www.google.com';
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+$connector->connect($host. ':80')->then(function (ConnectionInterface $connection) use ($host) {
+ $connection->on('data', function ($data) {
+ echo $data;
+ });
+ $connection->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+ });
+
+ $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/12-https-client.php b/assets/php/vendor/react/socket/examples/12-https-client.php
new file mode 100644
index 0000000..6e3f279
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/12-https-client.php
@@ -0,0 +1,36 @@
+<?php
+
+// Simple secure HTTPS client example (for illustration purposes only).
+// This shows how a secure TLS connection is established to then send an
+// application level protocol message (HTTP).
+// Real applications should use react/http-client instead
+//
+// This simple example only accepts an optional host parameter to send the
+// request to. See also example #22 for proper URI parsing.
+//
+// $ php examples/12-https-client.php
+// $ php examples/12-https-client.php reactphp.org
+
+use React\EventLoop\Factory;
+use React\Socket\Connector;
+use React\Socket\ConnectionInterface;
+
+$host = isset($argv[1]) ? $argv[1] : 'www.google.com';
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+$connector->connect('tls://' . $host . ':443')->then(function (ConnectionInterface $connection) use ($host) {
+ $connection->on('data', function ($data) {
+ echo $data;
+ });
+ $connection->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+ });
+
+ $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/21-netcat-client.php b/assets/php/vendor/react/socket/examples/21-netcat-client.php
new file mode 100644
index 0000000..9140e2c
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/21-netcat-client.php
@@ -0,0 +1,68 @@
+<?php
+
+// Simple plaintext TCP/IP and secure TLS client example that pipes console I/O.
+// This shows how a plaintext TCP/IP or secure TLS connection is established and
+// then everything you type on STDIN will be sent and everything the server
+// sends will be piped to your STDOUT.
+//
+// $ php examples/21-netcat-client.php www.google.com:80
+// $ php examples/21-netcat-client.php tls://www.google.com:443
+
+use React\EventLoop\Factory;
+use React\Socket\Connector;
+use React\Socket\ConnectionInterface;
+use React\Stream\ReadableResourceStream;
+use React\Stream\WritableResourceStream;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+if (!defined('STDIN')) {
+ echo 'STDIO streams require CLI SAPI' . PHP_EOL;
+ exit(1);
+}
+
+if (DIRECTORY_SEPARATOR === '\\') {
+ fwrite(STDERR, 'Non-blocking console I/O not supported on Microsoft Windows' . PHP_EOL);
+ exit(1);
+}
+
+if (!isset($argv[1])) {
+ fwrite(STDERR, 'Usage error: required argument <host:port>' . PHP_EOL);
+ exit(1);
+}
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+$stdin = new ReadableResourceStream(STDIN, $loop);
+$stdin->pause();
+$stdout = new WritableResourceStream(STDOUT, $loop);
+$stderr = new WritableResourceStream(STDERR, $loop);
+
+$stderr->write('Connecting' . PHP_EOL);
+
+$connector->connect($argv[1])->then(function (ConnectionInterface $connection) use ($stdin, $stdout, $stderr) {
+ // pipe everything from STDIN into connection
+ $stdin->resume();
+ $stdin->pipe($connection);
+
+ // pipe everything from connection to STDOUT
+ $connection->pipe($stdout);
+
+ // report errors to STDERR
+ $connection->on('error', function ($error) use ($stderr) {
+ $stderr->write('Stream ERROR: ' . $error . PHP_EOL);
+ });
+
+ // report closing and stop reading from input
+ $connection->on('close', function () use ($stderr, $stdin) {
+ $stderr->write('[CLOSED]' . PHP_EOL);
+ $stdin->close();
+ });
+
+ $stderr->write('Connected' . PHP_EOL);
+}, function ($error) use ($stderr) {
+ $stderr->write('Connection ERROR: ' . $error . PHP_EOL);
+});
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/22-http-client.php b/assets/php/vendor/react/socket/examples/22-http-client.php
new file mode 100644
index 0000000..fcb8107
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/22-http-client.php
@@ -0,0 +1,60 @@
+<?php
+
+// Simple plaintext HTTP and secure HTTPS client example (for illustration purposes only).
+// This shows how an URI parameter can parsed to decide whether to establish
+// a plaintext TCP/IP or secure TLS connection and then send an
+// application level protocol message (HTTP).
+// Real applications should use react/http-client instead!
+//
+// Unlike examples #11 and #12, this example will actually take an optional URI
+// parameter and parse it to connect to the correct default port and use the
+// correct transport protocol.
+//
+// $ php examples/22-http-client.php
+// $ php examples/22-http-client.php https://reactphp.org/
+
+use React\EventLoop\Factory;
+use React\Socket\ConnectionInterface;
+use React\Socket\Connector;
+use React\Stream\WritableResourceStream;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$uri = isset($argv[1]) ? $argv[1] : 'www.google.com';
+
+if (strpos($uri, '://') === false) {
+ $uri = 'http://' . $uri;
+}
+$parts = parse_url($uri);
+
+if (!$parts || !isset($parts['scheme'], $parts['host'])) {
+ fwrite(STDERR, 'Usage error: required argument <host:port>' . PHP_EOL);
+ exit(1);
+}
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+if (!isset($parts['port'])) {
+ $parts['port'] = $parts['scheme'] === 'https' ? 443 : 80;
+}
+
+$host = $parts['host'];
+if (($parts['scheme'] === 'http' && $parts['port'] !== 80) || ($parts['scheme'] === 'https' && $parts['port'] !== 443)) {
+ $host .= ':' . $parts['port'];
+}
+$target = ($parts['scheme'] === 'https' ? 'tls' : 'tcp') . '://' . $parts['host'] . ':' . $parts['port'];
+$resource = isset($parts['path']) ? $parts['path'] : '/';
+if (isset($parts['query'])) {
+ $resource .= '?' . $parts['query'];
+}
+
+$stdout = new WritableResourceStream(STDOUT, $loop);
+
+$connector->connect($target)->then(function (ConnectionInterface $connection) use ($resource, $host, $stdout) {
+ $connection->pipe($stdout);
+
+ $connection->write("GET $resource HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/91-benchmark-server.php b/assets/php/vendor/react/socket/examples/91-benchmark-server.php
new file mode 100644
index 0000000..420d474
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/91-benchmark-server.php
@@ -0,0 +1,60 @@
+<?php
+
+// Just start the server and connect to it. It will count the number of bytes
+// sent for each connection and will print the average throughput once the
+// connection closes.
+//
+// $ php examples/91-benchmark-server.php 8000
+// $ telnet localhost 8000
+// $ echo hello world | nc -N localhost 8000
+// $ dd if=/dev/zero bs=1M count=1000 | nc -N localhost 8000
+//
+// You can also run a secure TLS benchmarking server like this:
+//
+// $ php examples/91-benchmark-server.php tls://127.0.0.1:8000 examples/localhost.pem
+// $ openssl s_client -connect localhost:8000
+// $ echo hello world | openssl s_client -connect localhost:8000
+// $ dd if=/dev/zero bs=1M count=1000 | openssl s_client -connect localhost:8000
+//
+// You can also run a Unix domain socket (UDS) server benchmark like this:
+//
+// $ php examples/91-benchmark-server.php unix:///tmp/server.sock
+// $ nc -N -U /tmp/server.sock
+// $ dd if=/dev/zero bs=1M count=1000 | nc -N -U /tmp/server.sock
+
+use React\EventLoop\Factory;
+use React\Socket\Server;
+use React\Socket\ConnectionInterface;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$loop = Factory::create();
+
+$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop, array(
+ 'tls' => array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) use ($loop) {
+ echo '[connected]' . PHP_EOL;
+
+ // count the number of bytes received from this connection
+ $bytes = 0;
+ $conn->on('data', function ($chunk) use (&$bytes) {
+ $bytes += strlen($chunk);
+ });
+
+ // report average throughput once client disconnects
+ $t = microtime(true);
+ $conn->on('close', function () use ($conn, $t, &$bytes) {
+ $t = microtime(true) - $t;
+ echo '[disconnected after receiving ' . $bytes . ' bytes in ' . round($t, 3) . 's => ' . round($bytes / $t / 1024 / 1024, 1) . ' MiB/s]' . PHP_EOL;
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/assets/php/vendor/react/socket/examples/99-generate-self-signed.php b/assets/php/vendor/react/socket/examples/99-generate-self-signed.php
new file mode 100644
index 0000000..00f9314
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/99-generate-self-signed.php
@@ -0,0 +1,31 @@
+<?php
+
+// A very simple helper script used to generate self-signed certificates.
+// Accepts the CN and an optional passphrase to encrypt the private key.
+//
+// $ php 10-generate-self-signed.php localhost swordfish > secret.pem
+
+// certificate details (Distinguished Name)
+// (OpenSSL applies defaults to missing fields)
+$dn = array(
+ "commonName" => isset($argv[1]) ? $argv[1] : "localhost",
+// "countryName" => "AU",
+// "stateOrProvinceName" => "Some-State",
+// "localityName" => "London",
+// "organizationName" => "Internet Widgits Pty Ltd",
+// "organizationalUnitName" => "R&D",
+// "emailAddress" => "admin@example.com"
+);
+
+// create certificate which is valid for ~10 years
+$privkey = openssl_pkey_new();
+$cert = openssl_csr_new($dn, $privkey);
+$cert = openssl_csr_sign($cert, null, $privkey, 3650);
+
+// export public and (optionally encrypted) private key in PEM format
+openssl_x509_export($cert, $out);
+echo $out;
+
+$passphrase = isset($argv[2]) ? $argv[2] : null;
+openssl_pkey_export($privkey, $out, $passphrase);
+echo $out;
diff --git a/assets/php/vendor/react/socket/examples/localhost.pem b/assets/php/vendor/react/socket/examples/localhost.pem
new file mode 100644
index 0000000..be69279
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/localhost.pem
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu
+MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQ1OTA2WhcNMjYx
+MjI4MTQ1OTA2WjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8SZWNS+Ktg0Py
+W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
+2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
+zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
+UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
+wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
+YCUE54G/AgMBAAGjUDBOMB0GA1UdDgQWBBQ2GRz3QsQzdXaTMnPVCKfpigA10DAf
+BgNVHSMEGDAWgBQ2GRz3QsQzdXaTMnPVCKfpigA10DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQA77iZ4KrpPY18Ezjt0mngYAuAxunKddXYdLZ2khywN
+0uI/VzYnkFVtrsC7y2jLHSxlmE2/viPPGZDUplENV2acN6JNW+tlt7/bsrQHDQw3
+7VCF27EWiDxHsaghhLkqC+kcop5YR5c0oDQTdEWEKSbow2zayUXDYbRRs76SClTe
+824Yul+Ts8Mka+AX2PXDg47iZ84fJRN/nKavcJUTJ2iS1uYw0GNnFMge/uwsfMR3
+V47qN0X5emky8fcq99FlMCbcy0gHAeSWAjClgr2dd2i0LDatUbj7YmdmFcskOgII
+IwGfvuWR2yPevYGAE0QgFeLHniN3RW8zmpnX/XtrJ4a7
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8SZWNS+Ktg0Py
+W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
+2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
+zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
+UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
+wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
+YCUE54G/AgMBAAECggEBAKiO/3FE1CMddkCLZVtUp8ShqJgRokx9WI5ecwFApAkV
+ZHsjqDQQYRNmxhDUX/w0tOzLGyhde2xjJyZG29YviKsbHwu6zYwbeOzy/mkGOaK/
+g6DmmMmRs9Z6juifoQCu4GIFZ6il2adIL2vF7OeJh+eKudQj/7NFRSB7mXzNrQWK
+tZY3eux5zXWmio7pgZrx1HFZQiiL9NVLwT9J7oBnaoO3fREiu5J2xBpljG9Cr0j1
+LLiVLhukWJYRlHDtGt1CzI9w8iKo44PCRzpKyxpbsOrQxeSyEWUYQRv9VHA59LC7
+tVAJTbnTX1BNHkGZkOkoOpoZLwBaM2XbbDtcOGCAZMECgYEA+mTURFQ85/pxawvk
+9ndqZ+5He1u/bMLYIJDp0hdB/vgD+vw3gb2UyRwp0I6Wc6Si4FEEnbY7L0pzWsiR
+43CpLs+cyLfnD9NycuIasxs5fKb/1s1nGTkRAp7x9x/ZTtEf8v4YTmmMXFHzdo7V
+pv+czO89ppEDkxEtMf/b5SifhO8CgYEAwIDIUvXLduGhL+RPDwjc2SKdydXGV6om
+OEdt/V8oS801Z7k8l3gHXFm7zL/MpHmh9cag+F9dHK42kw2RSjDGsBlXXiAO1Z0I
+2A34OdPw/kow8fmIKWTMu3+28Kca+3RmUqeyaq0vazQ/bWMO9px+Ud3YfLo1Tn5I
+li0MecAx8DECgYEAvsLceKYYtL83c09fg2oc1ctSCCgw4WJcGAtvJ9DyRZacKbXH
+b/+H/+OF8879zmKqd+0hcCnqUzAMTCisBLPLIM+o6b45ufPkqKObpcJi/JWaKgLY
+vf2c+Psw6o4IF6T5Cz4MNIjzF06UBknxecYZpoPJ20F1kLCwVvxPgfl99l8CgYAb
+XfOcv67WTstgiJ+oroTfJamy+P5ClkDqvVTosW+EHz9ZaJ8xlXHOcj9do2LPey9I
+Rp250azmF+pQS5x9JKQKgv/FtN8HBVUtigbhCb14GUoODICMCfWFLmnumoMefnTR
+iV+3BLn6Dqp5vZxx+NuIffZ5/Or5JsDhALSGVomC8QKBgAi3Z/dNQrDHfkXMNn/L
++EAoLuAbFgLs76r9VGgNaRQ/q5gex2bZEGoBj4Sxvs95NUIcfD9wKT7FF8HdxARv
+y3o6Bfc8Xp9So9SlFXrje+gkdEJ0rQR67d+XBuJZh86bXJHVrMwpoNL+ahLGdVSe
+81oh1uCH1YPLM29hPyaohxL8
+-----END PRIVATE KEY-----
diff --git a/assets/php/vendor/react/socket/examples/localhost_swordfish.pem b/assets/php/vendor/react/socket/examples/localhost_swordfish.pem
new file mode 100644
index 0000000..7d1ee80
--- /dev/null
+++ b/assets/php/vendor/react/socket/examples/localhost_swordfish.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu
+MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQxMDQzWhcNMjYx
+MjI4MTQxMDQzWjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRXt83SrKIHr/i
+3lc8O8pz6NHE1DNHJa4xg2xalXWzCEV6m1qLd9VdaLT9cJD1afNmEMBgY6RblNL/
+paJWVoR9MOUeIoYl2PrhUCxsf7h6MRtezQQe3e+n+/0XunF0JUQIZuJqbxfRk5WT
+XmYnphqOZKEcistAYvFBjzl/D+Cl/nYsreADc+t9l5Vni89oTWEuqIrsM4WUZqqB
+VMAakd2nZJLWIrMxq9hbW1XNukOQfcmZVFTC6CUnLq8qzGbtfZYBuMBACnL1k/E/
+yPaAgR46l14VAcndDUJBtMeL2qYuNwvXQhg3KuBmpTUpH+yzxU+4T3lmv0xXmPqu
+ySH3xvW3AgMBAAGjUDBOMB0GA1UdDgQWBBRu68WTI4pVeTB7wuG9QGI3Ie441TAf
+BgNVHSMEGDAWgBRu68WTI4pVeTB7wuG9QGI3Ie441TAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQCc4pEjEHO47VRJkbHgC+c2gAVgxekkaA1czBA1uAvh
+ILRda0NLlvyftbjaG0zZp2ABUCfRfksl/Pf/PzWLUMEuH/9kEW2rgP43z6YgiL6k
+kBPlmAU607UjD726RPGkw8QPSXS/dWiNJ5CBpPWLpxC45pokqItYbY0ijQ5Piq09
+TchYlCX044oSRnPiP394PQ3HVdaGhJB2DnjDq3in5dVivFf8EdgzQSvp/wXy3WQs
+uFSVonSnrZGY/4AgT3psGaQ6fqKb4SBoqtf5bFQvp1XNNRkuEJnS/0dygEya0c+c
+aCe/1gXC2wDjx0/TekY5m1Nyw5SY6z7stOqL/ekwgejt
+-----END CERTIFICATE-----
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIG7idPRLgiHkCAggA
+MBQGCCqGSIb3DQMHBAg+MLPdepHWSwSCBMgVW9LseCjfTAmF9U1qRnKsq3kIwEnW
+6aERBqs/mnmEhrXgZYgcvRRK7kD12TdHt/Nz46Ymu0h+Lrvuwtl1fHQUARTk/gFh
+onLhc9kjMUhLRIR007vJe3HvWOb/v+SBSDB38OpUxUwJmBVBuSaYLWVuPR6J5kUj
+xOgBS049lN3E9cfrHvb3bF/epIQrU0OgfyyxEvIi5n30y+tlRn3y68PY6Qd46t4Y
+UN5VZUwvJBgoRy9TGxSkiSRjhxC2PWpLYq/HMzDbcRcFF5dVAIioUd/VZ7fdgBfA
+uMW4SFfpFLDUX0aaYe+ZdA5tM0Bc0cOtG8Z0sc9JYDNNcvjmSiGCi646h8F0D3O6
+JKAQMMxQGWiyQeJ979LVjtq4lJESXA8VEKz9rV03y5xunmFCLy6dGt+6GJwXgabn
+OH7nvEv4GqAOqKc6E9je4JM+AF/oUazrfPse1KEEtsPKarazjCB/SKYtHyDJaavD
+GGjtiU9zWwGMOgIDyNmXe3ga7/TWoGOAg5YlTr6Hbq2Y/5ycgjAgPFjuXtvnoT0+
+mF5TnNfMAqTgQsE2gjhonK1pdlOen0lN5FtoUXp3CXU0dOq0J70GiX+1YA7VDn30
+n5WNAgfOXX3l3E95jGN370pHXyli5RUNW0NZVHV+22jlNWCVtQHUh+DVswQZg+i5
++DqaIHz2jUetMo7gWtqGn/wwSopOs87VM1rcALhZL4EsJ+Zy81I/hA32RNnGbuol
+NAiZh+0KrtTcc/fPunpd8vRtOwGphM11dKucozUufuiPG2inR3aEqt5yNx54ec/f
+J6nryWRYiHEA/rCU9MSBM9cqKFtEmy9/8oxV41/SPxhXjHwDlABWTtFuJ3pf2sOF
+ILSYYFwB0ZGvdjE5yAJFBr9efno/L9fafmGk7a3vmVgK2AmUC9VNB5XHw1GjF8OP
+aQAXe4md9Bh0jk/D/iyp7e7IWNssul/7XejhabidWgFj6EXc9YxE59+FlhDqyMhn
+V6houc+QeUXuwsAKgRJJhJtpv/QSZ5BI3esxHHUt3ayGnvhFElpAc0t7C/EiXKIv
+DAFYP2jksBqijM8YtEgPWYzEP5buYxZnf/LK7FDocLsNcdF38UaKBbeF90e7bR8j
+SHspG9aJWICu8Yawnh8zuy/vQv+h9gWyGodd2p9lQzlbRXrutbwfmPf7xP6nzT9i
+9GcugJxTaZgkCfhhHxFk/nRHS2NAzagKVib1xkUlZJg2hX0fIFUdYteL1GGTvOx5
+m3mTOino4T19z9SEdZYb2OHYh29e/T74bJiLCYdXwevSYHxfZc8pYAf0jp4UnMT2
+f7B0ctX1iXuQ2uZVuxh+U1Mcu+v0gDla1jWh7AhcePSi4xBNUCak0kQip6r5e6Oi
+r4MIyMRk/Pc5pzEKo8G6nk26rNvX3aRvECoVfmK7IVdsqZ6IXlt9kOmWx3IeKzrO
+J5DxpzW+9oIRZJgPTkc4/XRb0tFmFQYTiChiQ1AJUEiCX0GpkFf7cq61aLGYtWyn
+vL2lmQhljzjrDo15hKErvk7eBZW7GW/6j/m/PfRdcBI4ceuP9zWQXnDOd9zmaE4b
+q3bJ+IbbyVZA2WwyzN7umCKWghsiPMAolxEnYM9JRf8BcqeqQiwVZlfO5KFuN6Ze
+le4=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/assets/php/vendor/react/socket/phpunit.xml.dist b/assets/php/vendor/react/socket/phpunit.xml.dist
new file mode 100644
index 0000000..13d3fab
--- /dev/null
+++ b/assets/php/vendor/react/socket/phpunit.xml.dist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="vendor/autoload.php"
+>
+ <testsuites>
+ <testsuite name="React Test Suite">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./src/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/assets/php/vendor/react/socket/src/Connection.php b/assets/php/vendor/react/socket/src/Connection.php
new file mode 100644
index 0000000..c6267cc
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/Connection.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+use React\Stream\DuplexResourceStream;
+use React\Stream\Util;
+use React\Stream\WritableResourceStream;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * The actual connection implementation for ConnectionInterface
+ *
+ * This class should only be used internally, see ConnectionInterface instead.
+ *
+ * @see ConnectionInterface
+ * @internal
+ */
+class Connection extends EventEmitter implements ConnectionInterface
+{
+ /**
+ * Internal flag whether this is a Unix domain socket (UDS) connection
+ *
+ * @internal
+ */
+ public $unix = false;
+
+ /**
+ * Internal flag whether encryption has been enabled on this connection
+ *
+ * Mostly used by internal StreamEncryption so that connection returns
+ * `tls://` scheme for encrypted connections instead of `tcp://`.
+ *
+ * @internal
+ */
+ public $encryptionEnabled = false;
+
+ /** @internal */
+ public $stream;
+
+ private $input;
+
+ public function __construct($resource, LoopInterface $loop)
+ {
+ // PHP < 5.6.8 suffers from a buffer indicator bug on secure TLS connections
+ // as a work-around we always read the complete buffer until its end.
+ // The buffer size is limited due to TCP/IP buffers anyway, so this
+ // should not affect usage otherwise.
+ // See https://bugs.php.net/bug.php?id=65137
+ // https://bugs.php.net/bug.php?id=41631
+ // https://github.com/reactphp/socket-client/issues/24
+ $clearCompleteBuffer = PHP_VERSION_ID < 50608;
+
+ // PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big
+ // chunks of data over TLS streams at once.
+ // We try to work around this by limiting the write chunk size to 8192
+ // bytes for older PHP versions only.
+ // This is only a work-around and has a noticable performance penalty on
+ // affected versions. Please update your PHP version.
+ // This applies to all streams because TLS may be enabled later on.
+ // See https://github.com/reactphp/socket/issues/105
+ $limitWriteChunks = (PHP_VERSION_ID < 70018 || (PHP_VERSION_ID >= 70100 && PHP_VERSION_ID < 70104));
+
+ $this->input = new DuplexResourceStream(
+ $resource,
+ $loop,
+ $clearCompleteBuffer ? -1 : null,
+ new WritableResourceStream($resource, $loop, null, $limitWriteChunks ? 8192 : null)
+ );
+
+ $this->stream = $resource;
+
+ Util::forwardEvents($this->input, $this, array('data', 'end', 'error', 'close', 'pipe', 'drain'));
+
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return $this->input->isReadable();
+ }
+
+ public function isWritable()
+ {
+ return $this->input->isWritable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return $this->input->pipe($dest, $options);
+ }
+
+ public function write($data)
+ {
+ return $this->input->write($data);
+ }
+
+ public function end($data = null)
+ {
+ $this->input->end($data);
+ }
+
+ public function close()
+ {
+ $this->input->close();
+ $this->handleClose();
+ $this->removeAllListeners();
+ }
+
+ public function handleClose()
+ {
+ if (!is_resource($this->stream)) {
+ return;
+ }
+
+ // Try to cleanly shut down socket and ignore any errors in case other
+ // side already closed. Shutting down may return to blocking mode on
+ // some legacy versions, so reset to non-blocking just in case before
+ // continuing to close the socket resource.
+ // Underlying Stream implementation will take care of closing file
+ // handle, so we otherwise keep this open here.
+ @stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR);
+ stream_set_blocking($this->stream, false);
+ }
+
+ public function getRemoteAddress()
+ {
+ return $this->parseAddress(@stream_socket_get_name($this->stream, true));
+ }
+
+ public function getLocalAddress()
+ {
+ return $this->parseAddress(@stream_socket_get_name($this->stream, false));
+ }
+
+ private function parseAddress($address)
+ {
+ if ($address === false) {
+ return null;
+ }
+
+ if ($this->unix) {
+ // remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo
+ // note that technically ":" is a valid address, so keep this in place otherwise
+ if (substr($address, -1) === ':' && defined('HHVM_VERSION_ID') && HHVM_VERSION_ID < 31900) {
+ $address = (string)substr($address, 0, -1);
+ }
+
+ // work around unknown addresses should return null value: https://3v4l.org/5C1lo and https://bugs.php.net/bug.php?id=74556
+ // PHP uses "\0" string and HHVM uses empty string (colon removed above)
+ if ($address === '' || $address[0] === "\x00" ) {
+ return null;
+ }
+
+ return 'unix://' . $address;
+ }
+
+ // check if this is an IPv6 address which includes multiple colons but no square brackets
+ $pos = strrpos($address, ':');
+ if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
+ $port = substr($address, $pos + 1);
+ $address = '[' . substr($address, 0, $pos) . ']:' . $port;
+ }
+
+ return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address;
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/ConnectionInterface.php b/assets/php/vendor/react/socket/src/ConnectionInterface.php
new file mode 100644
index 0000000..64613b5
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/ConnectionInterface.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace React\Socket;
+
+use React\Stream\DuplexStreamInterface;
+
+/**
+ * Any incoming and outgoing connection is represented by this interface,
+ * such as a normal TCP/IP connection.
+ *
+ * An incoming or outgoing connection is a duplex stream (both readable and
+ * writable) that implements React's
+ * [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+ * It contains additional properties for the local and remote address (client IP)
+ * where this connection has been established to/from.
+ *
+ * Most commonly, instances implementing this `ConnectionInterface` are emitted
+ * by all classes implementing the [`ServerInterface`](#serverinterface) and
+ * used by all classes implementing the [`ConnectorInterface`](#connectorinterface).
+ *
+ * Because the `ConnectionInterface` implements the underlying
+ * [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
+ * you can use any of its events and methods as usual:
+ *
+ * ```php
+ * $connection->on('data', function ($chunk) {
+ * echo $chunk;
+ * });
+ *
+ * $connection->on('end', function () {
+ * echo 'ended';
+ * });
+ *
+ * $connection->on('error', function (Exception $e) {
+ * echo 'error: ' . $e->getMessage();
+ * });
+ *
+ * $connection->on('close', function () {
+ * echo 'closed';
+ * });
+ *
+ * $connection->write($data);
+ * $connection->end($data = null);
+ * $connection->close();
+ * // …
+ * ```
+ *
+ * For more details, see the
+ * [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+ *
+ * @see DuplexStreamInterface
+ * @see ServerInterface
+ * @see ConnectorInterface
+ */
+interface ConnectionInterface extends DuplexStreamInterface
+{
+ /**
+ * Returns the full remote address (URI) where this connection has been established with
+ *
+ * ```php
+ * $address = $connection->getRemoteAddress();
+ * echo 'Connection with ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the remote address can not be determined or is unknown at this time (such as
+ * after the connection has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+ * `unix://example.sock` or `unix:///path/to/example.sock`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * If this is a TCP/IP based connection and you only want the remote IP, you may
+ * use something like this:
+ *
+ * ```php
+ * $address = $connection->getRemoteAddress();
+ * $ip = trim(parse_url($address, PHP_URL_HOST), '[]');
+ * echo 'Connection with ' . $ip . PHP_EOL;
+ * ```
+ *
+ * @return ?string remote address (URI) or null if unknown
+ */
+ public function getRemoteAddress();
+
+ /**
+ * Returns the full local address (full URI with scheme, IP and port) where this connection has been established with
+ *
+ * ```php
+ * $address = $connection->getLocalAddress();
+ * echo 'Connection with ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the local address can not be determined or is unknown at this time (such as
+ * after the connection has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+ * `unix://example.sock` or `unix:///path/to/example.sock`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * This method complements the [`getRemoteAddress()`](#getremoteaddress) method,
+ * so they should not be confused.
+ *
+ * If your `TcpServer` instance is listening on multiple interfaces (e.g. using
+ * the address `0.0.0.0`), you can use this method to find out which interface
+ * actually accepted this connection (such as a public or local interface).
+ *
+ * If your system has multiple interfaces (e.g. a WAN and a LAN interface),
+ * you can use this method to find out which interface was actually
+ * used for this connection.
+ *
+ * @return ?string local address (URI) or null if unknown
+ * @see self::getRemoteAddress()
+ */
+ public function getLocalAddress();
+}
diff --git a/assets/php/vendor/react/socket/src/Connector.php b/assets/php/vendor/react/socket/src/Connector.php
new file mode 100644
index 0000000..75276bc
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/Connector.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Config\Config;
+use React\Dns\Resolver\Factory;
+use React\Dns\Resolver\Resolver;
+use React\EventLoop\LoopInterface;
+use React\Promise;
+use RuntimeException;
+
+/**
+ * The `Connector` class is the main class in this package that implements the
+ * `ConnectorInterface` and allows you to create streaming connections.
+ *
+ * You can use this connector to create any kind of streaming connections, such
+ * as plaintext TCP/IP, secure TLS or local Unix connection streams.
+ *
+ * Under the hood, the `Connector` is implemented as a *higher-level facade*
+ * or the lower-level connectors implemented in this package. This means it
+ * also shares all of their features and implementation details.
+ * If you want to typehint in your higher-level protocol implementation, you SHOULD
+ * use the generic [`ConnectorInterface`](#connectorinterface) instead.
+ *
+ * @see ConnectorInterface for the base interface
+ */
+final class Connector implements ConnectorInterface
+{
+ private $connectors = array();
+
+ public function __construct(LoopInterface $loop, array $options = array())
+ {
+ // apply default options if not explicitly given
+ $options += array(
+ 'tcp' => true,
+ 'tls' => true,
+ 'unix' => true,
+
+ 'dns' => true,
+ 'timeout' => true,
+ );
+
+ if ($options['timeout'] === true) {
+ $options['timeout'] = (float)ini_get("default_socket_timeout");
+ }
+
+ if ($options['tcp'] instanceof ConnectorInterface) {
+ $tcp = $options['tcp'];
+ } else {
+ $tcp = new TcpConnector(
+ $loop,
+ is_array($options['tcp']) ? $options['tcp'] : array()
+ );
+ }
+
+ if ($options['dns'] !== false) {
+ if ($options['dns'] instanceof Resolver) {
+ $resolver = $options['dns'];
+ } else {
+ if ($options['dns'] !== true) {
+ $server = $options['dns'];
+ } else {
+ // try to load nameservers from system config or default to Google's public DNS
+ $config = Config::loadSystemConfigBlocking();
+ $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
+ }
+
+ $factory = new Factory();
+ $resolver = $factory->create(
+ $server,
+ $loop
+ );
+ }
+
+ $tcp = new DnsConnector($tcp, $resolver);
+ }
+
+ if ($options['tcp'] !== false) {
+ $options['tcp'] = $tcp;
+
+ if ($options['timeout'] !== false) {
+ $options['tcp'] = new TimeoutConnector(
+ $options['tcp'],
+ $options['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tcp'] = $options['tcp'];
+ }
+
+ if ($options['tls'] !== false) {
+ if (!$options['tls'] instanceof ConnectorInterface) {
+ $options['tls'] = new SecureConnector(
+ $tcp,
+ $loop,
+ is_array($options['tls']) ? $options['tls'] : array()
+ );
+ }
+
+ if ($options['timeout'] !== false) {
+ $options['tls'] = new TimeoutConnector(
+ $options['tls'],
+ $options['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tls'] = $options['tls'];
+ }
+
+ if ($options['unix'] !== false) {
+ if (!$options['unix'] instanceof ConnectorInterface) {
+ $options['unix'] = new UnixConnector($loop);
+ }
+ $this->connectors['unix'] = $options['unix'];
+ }
+ }
+
+ public function connect($uri)
+ {
+ $scheme = 'tcp';
+ if (strpos($uri, '://') !== false) {
+ $scheme = (string)substr($uri, 0, strpos($uri, '://'));
+ }
+
+ if (!isset($this->connectors[$scheme])) {
+ return Promise\reject(new RuntimeException(
+ 'No connector available for URI scheme "' . $scheme . '"'
+ ));
+ }
+
+ return $this->connectors[$scheme]->connect($uri);
+ }
+}
+
diff --git a/assets/php/vendor/react/socket/src/ConnectorInterface.php b/assets/php/vendor/react/socket/src/ConnectorInterface.php
new file mode 100644
index 0000000..196d01a
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/ConnectorInterface.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace React\Socket;
+
+/**
+ * The `ConnectorInterface` is responsible for providing an interface for
+ * establishing streaming connections, such as a normal TCP/IP connection.
+ *
+ * This is the main interface defined in this package and it is used throughout
+ * React's vast ecosystem.
+ *
+ * Most higher-level components (such as HTTP, database or other networking
+ * service clients) accept an instance implementing this interface to create their
+ * TCP/IP connection to the underlying networking service.
+ * This is usually done via dependency injection, so it's fairly simple to actually
+ * swap this implementation against any other implementation of this interface.
+ *
+ * The interface only offers a single `connect()` method.
+ *
+ * @see ConnectionInterface
+ */
+interface ConnectorInterface
+{
+ /**
+ * Creates a streaming connection to the given remote address
+ *
+ * If returns a Promise which either fulfills with a stream implementing
+ * `ConnectionInterface` on success or rejects with an `Exception` if the
+ * connection is not successful.
+ *
+ * ```php
+ * $connector->connect('google.com:443')->then(
+ * function (ConnectionInterface $connection) {
+ * // connection successfully established
+ * },
+ * function (Exception $error) {
+ * // failed to connect due to $error
+ * }
+ * );
+ * ```
+ *
+ * The returned Promise MUST be implemented in such a way that it can be
+ * cancelled when it is still pending. Cancelling a pending promise MUST
+ * reject its value with an Exception. It SHOULD clean up any underlying
+ * resources and references as applicable.
+ *
+ * ```php
+ * $promise = $connector->connect($uri);
+ *
+ * $promise->cancel();
+ * ```
+ *
+ * @param string $uri
+ * @return \React\Promise\PromiseInterface resolves with a stream implementing ConnectionInterface on success or rejects with an Exception on error
+ * @see ConnectionInterface
+ */
+ public function connect($uri);
+}
diff --git a/assets/php/vendor/react/socket/src/DnsConnector.php b/assets/php/vendor/react/socket/src/DnsConnector.php
new file mode 100644
index 0000000..90170e5
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/DnsConnector.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Resolver\Resolver;
+use React\Promise;
+use React\Promise\CancellablePromiseInterface;
+use InvalidArgumentException;
+use RuntimeException;
+
+final class DnsConnector implements ConnectorInterface
+{
+ private $connector;
+ private $resolver;
+
+ public function __construct(ConnectorInterface $connector, Resolver $resolver)
+ {
+ $this->connector = $connector;
+ $this->resolver = $resolver;
+ }
+
+ public function connect($uri)
+ {
+ if (strpos($uri, '://') === false) {
+ $parts = parse_url('tcp://' . $uri);
+ unset($parts['scheme']);
+ } else {
+ $parts = parse_url($uri);
+ }
+
+ if (!$parts || !isset($parts['host'])) {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $host = trim($parts['host'], '[]');
+ $connector = $this->connector;
+
+ return $this
+ ->resolveHostname($host)
+ ->then(function ($ip) use ($connector, $host, $parts) {
+ $uri = '';
+
+ // prepend original scheme if known
+ if (isset($parts['scheme'])) {
+ $uri .= $parts['scheme'] . '://';
+ }
+
+ if (strpos($ip, ':') !== false) {
+ // enclose IPv6 addresses in square brackets before appending port
+ $uri .= '[' . $ip . ']';
+ } else {
+ $uri .= $ip;
+ }
+
+ // append original port if known
+ if (isset($parts['port'])) {
+ $uri .= ':' . $parts['port'];
+ }
+
+ // append orignal path if known
+ if (isset($parts['path'])) {
+ $uri .= $parts['path'];
+ }
+
+ // append original query if known
+ if (isset($parts['query'])) {
+ $uri .= '?' . $parts['query'];
+ }
+
+ // append original hostname as query if resolved via DNS and if
+ // destination URI does not contain "hostname" query param already
+ $args = array();
+ parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
+ if ($host !== $ip && !isset($args['hostname'])) {
+ $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host);
+ }
+
+ // append original fragment if known
+ if (isset($parts['fragment'])) {
+ $uri .= '#' . $parts['fragment'];
+ }
+
+ return $connector->connect($uri);
+ });
+ }
+
+ private function resolveHostname($host)
+ {
+ if (false !== filter_var($host, FILTER_VALIDATE_IP)) {
+ return Promise\resolve($host);
+ }
+
+ $promise = $this->resolver->resolve($host);
+
+ return new Promise\Promise(
+ function ($resolve, $reject) use ($promise) {
+ // resolve/reject with result of DNS lookup
+ $promise->then($resolve, $reject);
+ },
+ function ($_, $reject) use ($promise) {
+ // cancellation should reject connection attempt
+ $reject(new RuntimeException('Connection attempt cancelled during DNS lookup'));
+
+ // (try to) cancel pending DNS lookup
+ if ($promise instanceof CancellablePromiseInterface) {
+ $promise->cancel();
+ }
+ }
+ );
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/FixedUriConnector.php b/assets/php/vendor/react/socket/src/FixedUriConnector.php
new file mode 100644
index 0000000..057bcdf
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/FixedUriConnector.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace React\Socket;
+
+/**
+ * Decorates an existing Connector to always use a fixed, preconfigured URI
+ *
+ * This can be useful for consumers that do not support certain URIs, such as
+ * when you want to explicitly connect to a Unix domain socket (UDS) path
+ * instead of connecting to a default address assumed by an higher-level API:
+ *
+ * ```php
+ * $connector = new FixedUriConnector(
+ * 'unix:///var/run/docker.sock',
+ * new UnixConnector($loop)
+ * );
+ *
+ * // destination will be ignored, actually connects to Unix domain socket
+ * $promise = $connector->connect('localhost:80');
+ * ```
+ */
+class FixedUriConnector implements ConnectorInterface
+{
+ private $uri;
+ private $connector;
+
+ /**
+ * @param string $uri
+ * @param ConnectorInterface $connector
+ */
+ public function __construct($uri, ConnectorInterface $connector)
+ {
+ $this->uri = $uri;
+ $this->connector = $connector;
+ }
+
+ public function connect($_)
+ {
+ return $this->connector->connect($this->uri);
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/LimitingServer.php b/assets/php/vendor/react/socket/src/LimitingServer.php
new file mode 100644
index 0000000..c7874ee
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/LimitingServer.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use Exception;
+use OverflowException;
+
+/**
+ * The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible
+ * for limiting and keeping track of open connections to this server instance.
+ *
+ * Whenever the underlying server emits a `connection` event, it will check its
+ * limits and then either
+ * - keep track of this connection by adding it to the list of
+ * open connections and then forward the `connection` event
+ * - or reject (close) the connection when its limits are exceeded and will
+ * forward an `error` event instead.
+ *
+ * Whenever a connection closes, it will remove this connection from the list of
+ * open connections.
+ *
+ * ```php
+ * $server = new LimitingServer($server, 100);
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+class LimitingServer extends EventEmitter implements ServerInterface
+{
+ private $connections = array();
+ private $server;
+ private $limit;
+
+ private $pauseOnLimit = false;
+ private $autoPaused = false;
+ private $manuPaused = false;
+
+ /**
+ * Instantiates a new LimitingServer.
+ *
+ * You have to pass a maximum number of open connections to ensure
+ * the server will automatically reject (close) connections once this limit
+ * is exceeded. In this case, it will emit an `error` event to inform about
+ * this and no `connection` event will be emitted.
+ *
+ * ```php
+ * $server = new LimitingServer($server, 100);
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * You MAY pass a `null` limit in order to put no limit on the number of
+ * open connections and keep accepting new connection until you run out of
+ * operating system resources (such as open file handles). This may be
+ * useful if you do not want to take care of applying a limit but still want
+ * to use the `getConnections()` method.
+ *
+ * You can optionally configure the server to pause accepting new
+ * connections once the connection limit is reached. In this case, it will
+ * pause the underlying server and no longer process any new connections at
+ * all, thus also no longer closing any excessive connections.
+ * The underlying operating system is responsible for keeping a backlog of
+ * pending connections until its limit is reached, at which point it will
+ * start rejecting further connections.
+ * Once the server is below the connection limit, it will continue consuming
+ * connections from the backlog and will process any outstanding data on
+ * each connection.
+ * This mode may be useful for some protocols that are designed to wait for
+ * a response message (such as HTTP), but may be less useful for other
+ * protocols that demand immediate responses (such as a "welcome" message in
+ * an interactive chat).
+ *
+ * ```php
+ * $server = new LimitingServer($server, 100, true);
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * @param ServerInterface $server
+ * @param int|null $connectionLimit
+ * @param bool $pauseOnLimit
+ */
+ public function __construct(ServerInterface $server, $connectionLimit, $pauseOnLimit = false)
+ {
+ $this->server = $server;
+ $this->limit = $connectionLimit;
+ if ($connectionLimit !== null) {
+ $this->pauseOnLimit = $pauseOnLimit;
+ }
+
+ $this->server->on('connection', array($this, 'handleConnection'));
+ $this->server->on('error', array($this, 'handleError'));
+ }
+
+ /**
+ * Returns an array with all currently active connections
+ *
+ * ```php
+ * foreach ($server->getConnection() as $connection) {
+ * $connection->write('Hi!');
+ * }
+ * ```
+ *
+ * @return ConnectionInterface[]
+ */
+ public function getConnections()
+ {
+ return $this->connections;
+ }
+
+ public function getAddress()
+ {
+ return $this->server->getAddress();
+ }
+
+ public function pause()
+ {
+ if (!$this->manuPaused) {
+ $this->manuPaused = true;
+
+ if (!$this->autoPaused) {
+ $this->server->pause();
+ }
+ }
+ }
+
+ public function resume()
+ {
+ if ($this->manuPaused) {
+ $this->manuPaused = false;
+
+ if (!$this->autoPaused) {
+ $this->server->resume();
+ }
+ }
+ }
+
+ public function close()
+ {
+ $this->server->close();
+ }
+
+ /** @internal */
+ public function handleConnection(ConnectionInterface $connection)
+ {
+ // close connection if limit exceeded
+ if ($this->limit !== null && count($this->connections) >= $this->limit) {
+ $this->handleError(new OverflowException('Connection closed because server reached connection limit'));
+ $connection->close();
+ return;
+ }
+
+ $this->connections[] = $connection;
+ $that = $this;
+ $connection->on('close', function () use ($that, $connection) {
+ $that->handleDisconnection($connection);
+ });
+
+ // pause accepting new connections if limit exceeded
+ if ($this->pauseOnLimit && !$this->autoPaused && count($this->connections) >= $this->limit) {
+ $this->autoPaused = true;
+
+ if (!$this->manuPaused) {
+ $this->server->pause();
+ }
+ }
+
+ $this->emit('connection', array($connection));
+ }
+
+ /** @internal */
+ public function handleDisconnection(ConnectionInterface $connection)
+ {
+ unset($this->connections[array_search($connection, $this->connections)]);
+
+ // continue accepting new connection if below limit
+ if ($this->autoPaused && count($this->connections) < $this->limit) {
+ $this->autoPaused = false;
+
+ if (!$this->manuPaused) {
+ $this->server->resume();
+ }
+ }
+ }
+
+ /** @internal */
+ public function handleError(Exception $error)
+ {
+ $this->emit('error', array($error));
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/SecureConnector.php b/assets/php/vendor/react/socket/src/SecureConnector.php
new file mode 100644
index 0000000..f04183d
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/SecureConnector.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\LoopInterface;
+use React\Promise;
+use BadMethodCallException;
+use InvalidArgumentException;
+use UnexpectedValueException;
+
+final class SecureConnector implements ConnectorInterface
+{
+ private $connector;
+ private $streamEncryption;
+ private $context;
+
+ public function __construct(ConnectorInterface $connector, LoopInterface $loop, array $context = array())
+ {
+ $this->connector = $connector;
+ $this->streamEncryption = new StreamEncryption($loop, false);
+ $this->context = $context;
+ }
+
+ public function connect($uri)
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ return Promise\reject(new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)')); // @codeCoverageIgnore
+ }
+
+ if (strpos($uri, '://') === false) {
+ $uri = 'tls://' . $uri;
+ }
+
+ $parts = parse_url($uri);
+ if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $uri = str_replace('tls://', '', $uri);
+ $context = $this->context;
+
+ $encryption = $this->streamEncryption;
+ return $this->connector->connect($uri)->then(function (ConnectionInterface $connection) use ($context, $encryption) {
+ // (unencrypted) TCP/IP connection succeeded
+
+ if (!$connection instanceof Connection) {
+ $connection->close();
+ throw new UnexpectedValueException('Base connector does not use internal Connection class exposing stream resource');
+ }
+
+ // set required SSL/TLS context options
+ foreach ($context as $name => $value) {
+ stream_context_set_option($connection->stream, 'ssl', $name, $value);
+ }
+
+ // try to enable encryption
+ return $encryption->enable($connection)->then(null, function ($error) use ($connection) {
+ // establishing encryption failed => close invalid connection and return error
+ $connection->close();
+ throw $error;
+ });
+ });
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/SecureServer.php b/assets/php/vendor/react/socket/src/SecureServer.php
new file mode 100644
index 0000000..302ae93
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/SecureServer.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+use BadMethodCallException;
+use UnexpectedValueException;
+
+/**
+ * The `SecureServer` class implements the `ServerInterface` and is responsible
+ * for providing a secure TLS (formerly known as SSL) server.
+ *
+ * It does so by wrapping a `TcpServer` instance which waits for plaintext
+ * TCP/IP connections and then performs a TLS handshake for each connection.
+ *
+ * ```php
+ * $server = new TcpServer(8000, $loop);
+ * $server = new SecureServer($server, $loop, array(
+ * // tls context options here…
+ * ));
+ * ```
+ *
+ * Whenever a client completes the TLS handshake, it will emit a `connection` event
+ * with a connection instance implementing [`ConnectionInterface`](#connectioninterface):
+ *
+ * ```php
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL;
+ *
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * Whenever a client fails to perform a successful TLS handshake, it will emit an
+ * `error` event and then close the underlying TCP/IP connection:
+ *
+ * ```php
+ * $server->on('error', function (Exception $e) {
+ * echo 'Error' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * Note that the `SecureServer` class is a concrete implementation for TLS sockets.
+ * If you want to typehint in your higher-level protocol implementation, you SHOULD
+ * use the generic `ServerInterface` instead.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class SecureServer extends EventEmitter implements ServerInterface
+{
+ private $tcp;
+ private $encryption;
+ private $context;
+
+ /**
+ * Creates a secure TLS server and starts waiting for incoming connections
+ *
+ * It does so by wrapping a `TcpServer` instance which waits for plaintext
+ * TCP/IP connections and then performs a TLS handshake for each connection.
+ * It thus requires valid [TLS context options],
+ * which in its most basic form may look something like this if you're using a
+ * PEM encoded certificate file:
+ *
+ * ```php
+ * $server = new TcpServer(8000, $loop);
+ * $server = new SecureServer($server, $loop, array(
+ * 'local_cert' => 'server.pem'
+ * ));
+ * ```
+ *
+ * Note that the certificate file will not be loaded on instantiation but when an
+ * incoming connection initializes its TLS context.
+ * This implies that any invalid certificate file paths or contents will only cause
+ * an `error` event at a later time.
+ *
+ * If your private key is encrypted with a passphrase, you have to specify it
+ * like this:
+ *
+ * ```php
+ * $server = new TcpServer(8000, $loop);
+ * $server = new SecureServer($server, $loop, array(
+ * 'local_cert' => 'server.pem',
+ * 'passphrase' => 'secret'
+ * ));
+ * ```
+ *
+ * Note that available [TLS context options],
+ * their defaults and effects of changing these may vary depending on your system
+ * and/or PHP version.
+ * Passing unknown context options has no effect.
+ *
+ * Advanced usage: Despite allowing any `ServerInterface` as first parameter,
+ * you SHOULD pass a `TcpServer` instance as first parameter, unless you
+ * know what you're doing.
+ * Internally, the `SecureServer` has to set the required TLS context options on
+ * the underlying stream resources.
+ * These resources are not exposed through any of the interfaces defined in this
+ * package, but only through the internal `Connection` class.
+ * The `TcpServer` class is guaranteed to emit connections that implement
+ * the `ConnectionInterface` and uses the internal `Connection` class in order to
+ * expose these underlying resources.
+ * If you use a custom `ServerInterface` and its `connection` event does not
+ * meet this requirement, the `SecureServer` will emit an `error` event and
+ * then close the underlying connection.
+ *
+ * @param ServerInterface|TcpServer $tcp
+ * @param LoopInterface $loop
+ * @param array $context
+ * @throws BadMethodCallException for legacy HHVM < 3.8 due to lack of support
+ * @see TcpServer
+ * @link http://php.net/manual/en/context.ssl.php for TLS context options
+ */
+ public function __construct(ServerInterface $tcp, LoopInterface $loop, array $context)
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ throw new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore
+ }
+
+ // default to empty passphrase to suppress blocking passphrase prompt
+ $context += array(
+ 'passphrase' => ''
+ );
+
+ $this->tcp = $tcp;
+ $this->encryption = new StreamEncryption($loop);
+ $this->context = $context;
+
+ $that = $this;
+ $this->tcp->on('connection', function ($connection) use ($that) {
+ $that->handleConnection($connection);
+ });
+ $this->tcp->on('error', function ($error) use ($that) {
+ $that->emit('error', array($error));
+ });
+ }
+
+ public function getAddress()
+ {
+ $address = $this->tcp->getAddress();
+ if ($address === null) {
+ return null;
+ }
+
+ return str_replace('tcp://' , 'tls://', $address);
+ }
+
+ public function pause()
+ {
+ $this->tcp->pause();
+ }
+
+ public function resume()
+ {
+ $this->tcp->resume();
+ }
+
+ public function close()
+ {
+ return $this->tcp->close();
+ }
+
+ /** @internal */
+ public function handleConnection(ConnectionInterface $connection)
+ {
+ if (!$connection instanceof Connection) {
+ $this->emit('error', array(new UnexpectedValueException('Base server does not use internal Connection class exposing stream resource')));
+ $connection->end();
+ return;
+ }
+
+ foreach ($this->context as $name => $value) {
+ stream_context_set_option($connection->stream, 'ssl', $name, $value);
+ }
+
+ $that = $this;
+
+ $this->encryption->enable($connection)->then(
+ function ($conn) use ($that) {
+ $that->emit('connection', array($conn));
+ },
+ function ($error) use ($that, $connection) {
+ $that->emit('error', array($error));
+ $connection->end();
+ }
+ );
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/Server.php b/assets/php/vendor/react/socket/src/Server.php
new file mode 100644
index 0000000..72712e4
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/Server.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+use Exception;
+
+final class Server extends EventEmitter implements ServerInterface
+{
+ private $server;
+
+ public function __construct($uri, LoopInterface $loop, array $context = array())
+ {
+ // sanitize TCP context options if not properly wrapped
+ if ($context && (!isset($context['tcp']) && !isset($context['tls']) && !isset($context['unix']))) {
+ $context = array('tcp' => $context);
+ }
+
+ // apply default options if not explicitly given
+ $context += array(
+ 'tcp' => array(),
+ 'tls' => array(),
+ 'unix' => array()
+ );
+
+ $scheme = 'tcp';
+ $pos = strpos($uri, '://');
+ if ($pos !== false) {
+ $scheme = substr($uri, 0, $pos);
+ }
+
+ if ($scheme === 'unix') {
+ $server = new UnixServer($uri, $loop, $context['unix']);
+ } else {
+ $server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);
+
+ if ($scheme === 'tls') {
+ $server = new SecureServer($server, $loop, $context['tls']);
+ }
+ }
+
+ $this->server = $server;
+
+ $that = $this;
+ $server->on('connection', function (ConnectionInterface $conn) use ($that) {
+ $that->emit('connection', array($conn));
+ });
+ $server->on('error', function (Exception $error) use ($that) {
+ $that->emit('error', array($error));
+ });
+ }
+
+ public function getAddress()
+ {
+ return $this->server->getAddress();
+ }
+
+ public function pause()
+ {
+ $this->server->pause();
+ }
+
+ public function resume()
+ {
+ $this->server->resume();
+ }
+
+ public function close()
+ {
+ $this->server->close();
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/ServerInterface.php b/assets/php/vendor/react/socket/src/ServerInterface.php
new file mode 100644
index 0000000..5319678
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/ServerInterface.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitterInterface;
+
+/**
+ * The `ServerInterface` is responsible for providing an interface for accepting
+ * incoming streaming connections, such as a normal TCP/IP connection.
+ *
+ * Most higher-level components (such as a HTTP server) accept an instance
+ * implementing this interface to accept incoming streaming connections.
+ * This is usually done via dependency injection, so it's fairly simple to actually
+ * swap this implementation against any other implementation of this interface.
+ * This means that you SHOULD typehint against this interface instead of a concrete
+ * implementation of this interface.
+ *
+ * Besides defining a few methods, this interface also implements the
+ * `EventEmitterInterface` which allows you to react to certain events:
+ *
+ * connection event:
+ * The `connection` event will be emitted whenever a new connection has been
+ * established, i.e. a new client connects to this server socket:
+ *
+ * ```php
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * echo 'new connection' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also the `ConnectionInterface` for more details about handling the
+ * incoming connection.
+ *
+ * error event:
+ * The `error` event will be emitted whenever there's an error accepting a new
+ * connection from a client.
+ *
+ * ```php
+ * $server->on('error', function (Exception $e) {
+ * echo 'error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * Note that this is not a fatal error event, i.e. the server keeps listening for
+ * new connections even after this event.
+ *
+ * @see ConnectionInterface
+ */
+interface ServerInterface extends EventEmitterInterface
+{
+ /**
+ * Returns the full address (URI) this server is currently listening on
+ *
+ * ```php
+ * $address = $server->getAddress();
+ * echo 'Server listening on ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the address can not be determined or is unknown at this time (such as
+ * after the socket has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * If this is a TCP/IP based server and you only want the local port, you may
+ * use something like this:
+ *
+ * ```php
+ * $address = $server->getAddress();
+ * $port = parse_url($address, PHP_URL_PORT);
+ * echo 'Server listening on port ' . $port . PHP_EOL;
+ * ```
+ *
+ * @return ?string the full listening address (URI) or NULL if it is unknown (not applicable to this server socket or already closed)
+ */
+ public function getAddress();
+
+ /**
+ * Pauses accepting new incoming connections.
+ *
+ * Removes the socket resource from the EventLoop and thus stop accepting
+ * new connections. Note that the listening socket stays active and is not
+ * closed.
+ *
+ * This means that new incoming connections will stay pending in the
+ * operating system backlog until its configurable backlog is filled.
+ * Once the backlog is filled, the operating system may reject further
+ * incoming connections until the backlog is drained again by resuming
+ * to accept new connections.
+ *
+ * Once the server is paused, no futher `connection` events SHOULD
+ * be emitted.
+ *
+ * ```php
+ * $server->pause();
+ *
+ * $server->on('connection', assertShouldNeverCalled());
+ * ```
+ *
+ * This method is advisory-only, though generally not recommended, the
+ * server MAY continue emitting `connection` events.
+ *
+ * Unless otherwise noted, a successfully opened server SHOULD NOT start
+ * in paused state.
+ *
+ * You can continue processing events by calling `resume()` again.
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `pause()` more than once SHOULD NOT have any effect.
+ * Similarly, calling this after `close()` is a NO-OP.
+ *
+ * @see self::resume()
+ * @return void
+ */
+ public function pause();
+
+ /**
+ * Resumes accepting new incoming connections.
+ *
+ * Re-attach the socket resource to the EventLoop after a previous `pause()`.
+ *
+ * ```php
+ * $server->pause();
+ *
+ * $loop->addTimer(1.0, function () use ($server) {
+ * $server->resume();
+ * });
+ * ```
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+ * Similarly, calling this after `close()` is a NO-OP.
+ *
+ * @see self::pause()
+ * @return void
+ */
+ public function resume();
+
+ /**
+ * Shuts down this listening socket
+ *
+ * This will stop listening for new incoming connections on this socket.
+ *
+ * Calling this method more than once on the same instance is a NO-OP.
+ *
+ * @return void
+ */
+ public function close();
+}
diff --git a/assets/php/vendor/react/socket/src/StreamEncryption.php b/assets/php/vendor/react/socket/src/StreamEncryption.php
new file mode 100644
index 0000000..ba5d472
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/StreamEncryption.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\LoopInterface;
+use React\Promise\Deferred;
+use RuntimeException;
+use UnexpectedValueException;
+
+/**
+ * This class is considered internal and its API should not be relied upon
+ * outside of Socket.
+ *
+ * @internal
+ */
+class StreamEncryption
+{
+ private $loop;
+ private $method;
+ private $server;
+
+ private $errstr;
+ private $errno;
+
+ public function __construct(LoopInterface $loop, $server = true)
+ {
+ $this->loop = $loop;
+ $this->server = $server;
+
+ // support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3.
+ // PHP 5.6+ supports bitmasks, legacy PHP only supports predefined
+ // constants, so apply accordingly below.
+ // Also, since PHP 5.6.7 up until before PHP 7.2.0 the main constant did
+ // only support TLSv1.0, so we explicitly apply all versions.
+ // @link http://php.net/manual/en/migration56.openssl.php#migration56.openssl.crypto-method
+ // @link https://3v4l.org/plbFn
+ if ($server) {
+ $this->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
+
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_SERVER;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_SERVER;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
+ }
+ } else {
+ $this->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+ }
+ }
+ }
+
+ public function enable(Connection $stream)
+ {
+ return $this->toggle($stream, true);
+ }
+
+ public function disable(Connection $stream)
+ {
+ return $this->toggle($stream, false);
+ }
+
+ public function toggle(Connection $stream, $toggle)
+ {
+ // pause actual stream instance to continue operation on raw stream socket
+ $stream->pause();
+
+ // TODO: add write() event to make sure we're not sending any excessive data
+
+ $deferred = new Deferred(function ($_, $reject) use ($toggle) {
+ // cancelling this leaves this stream in an inconsistent state…
+ $reject(new RuntimeException('Cancelled toggling encryption ' . $toggle ? 'on' : 'off'));
+ });
+
+ // get actual stream socket from stream instance
+ $socket = $stream->stream;
+
+ // get crypto method from context options or use global setting from constructor
+ $method = $this->method;
+ $context = stream_context_get_options($socket);
+ if (isset($context['ssl']['crypto_method'])) {
+ $method = $context['ssl']['crypto_method'];
+ }
+
+ $that = $this;
+ $toggleCrypto = function () use ($socket, $deferred, $toggle, $method, $that) {
+ $that->toggleCrypto($socket, $deferred, $toggle, $method);
+ };
+
+ $this->loop->addReadStream($socket, $toggleCrypto);
+
+ if (!$this->server) {
+ $toggleCrypto();
+ }
+
+ $loop = $this->loop;
+
+ return $deferred->promise()->then(function () use ($stream, $socket, $loop, $toggle) {
+ $loop->removeReadStream($socket);
+
+ $stream->encryptionEnabled = $toggle;
+ $stream->resume();
+
+ return $stream;
+ }, function($error) use ($stream, $socket, $loop) {
+ $loop->removeReadStream($socket);
+ $stream->resume();
+ throw $error;
+ });
+ }
+
+ public function toggleCrypto($socket, Deferred $deferred, $toggle, $method)
+ {
+ set_error_handler(array($this, 'handleError'));
+ $result = stream_socket_enable_crypto($socket, $toggle, $method);
+ restore_error_handler();
+
+ if (true === $result) {
+ $deferred->resolve();
+ } else if (false === $result) {
+ $deferred->reject(new UnexpectedValueException(
+ sprintf("Unable to complete SSL/TLS handshake: %s", $this->errstr),
+ $this->errno
+ ));
+ } else {
+ // need more data, will retry
+ }
+ }
+
+ public function handleError($errno, $errstr)
+ {
+ $this->errstr = str_replace(array("\r", "\n"), ' ', $errstr);
+ $this->errno = $errno;
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/TcpConnector.php b/assets/php/vendor/react/socket/src/TcpConnector.php
new file mode 100644
index 0000000..90d7df1
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/TcpConnector.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\LoopInterface;
+use React\Promise;
+use InvalidArgumentException;
+use RuntimeException;
+
+final class TcpConnector implements ConnectorInterface
+{
+ private $loop;
+ private $context;
+
+ public function __construct(LoopInterface $loop, array $context = array())
+ {
+ $this->loop = $loop;
+ $this->context = $context;
+ }
+
+ public function connect($uri)
+ {
+ if (strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ }
+
+ $parts = parse_url($uri);
+ if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $ip = trim($parts['host'], '[]');
+ if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP'));
+ }
+
+ // use context given in constructor
+ $context = array(
+ 'socket' => $this->context
+ );
+
+ // parse arguments from query component of URI
+ $args = array();
+ if (isset($parts['query'])) {
+ parse_str($parts['query'], $args);
+ }
+
+ // If an original hostname has been given, use this for TLS setup.
+ // This can happen due to layers of nested connectors, such as a
+ // DnsConnector reporting its original hostname.
+ // These context options are here in case TLS is enabled later on this stream.
+ // If TLS is not enabled later, this doesn't hurt either.
+ if (isset($args['hostname'])) {
+ $context['ssl'] = array(
+ 'SNI_enabled' => true,
+ 'peer_name' => $args['hostname']
+ );
+
+ // Legacy PHP < 5.6 ignores peer_name and requires legacy context options instead.
+ // The SNI_server_name context option has to be set here during construction,
+ // as legacy PHP ignores any values set later.
+ if (PHP_VERSION_ID < 50600) {
+ $context['ssl'] += array(
+ 'SNI_server_name' => $args['hostname'],
+ 'CN_match' => $args['hostname']
+ );
+ }
+ }
+
+ // latest versions of PHP no longer accept any other URI components and
+ // HHVM fails to parse URIs with a query but no path, so let's simplify our URI here
+ $remote = 'tcp://' . $parts['host'] . ':' . $parts['port'];
+
+ $socket = @stream_socket_client(
+ $remote,
+ $errno,
+ $errstr,
+ 0,
+ STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT,
+ stream_context_create($context)
+ );
+
+ if (false === $socket) {
+ return Promise\reject(new RuntimeException(
+ sprintf("Connection to %s failed: %s", $uri, $errstr),
+ $errno
+ ));
+ }
+
+ stream_set_blocking($socket, 0);
+
+ // wait for connection
+
+ return $this->waitForStreamOnce($socket);
+ }
+
+ private function waitForStreamOnce($stream)
+ {
+ $loop = $this->loop;
+
+ return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream) {
+ $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) {
+ $loop->removeWriteStream($stream);
+
+ // The following hack looks like the only way to
+ // detect connection refused errors with PHP's stream sockets.
+ if (false === stream_socket_get_name($stream, true)) {
+ fclose($stream);
+
+ $reject(new RuntimeException('Connection refused'));
+ } else {
+ $resolve(new Connection($stream, $loop));
+ }
+ });
+ }, function () use ($loop, $stream) {
+ $loop->removeWriteStream($stream);
+ fclose($stream);
+
+ throw new RuntimeException('Cancelled while waiting for TCP/IP connection to be established');
+ });
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/TcpServer.php b/assets/php/vendor/react/socket/src/TcpServer.php
new file mode 100644
index 0000000..119e177
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/TcpServer.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * The `TcpServer` class implements the `ServerInterface` and
+ * is responsible for accepting plaintext TCP/IP connections.
+ *
+ * ```php
+ * $server = new TcpServer(8080, $loop);
+ * ```
+ *
+ * Whenever a client connects, it will emit a `connection` event with a connection
+ * instance implementing `ConnectionInterface`:
+ *
+ * ```php
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class TcpServer extends EventEmitter implements ServerInterface
+{
+ private $master;
+ private $loop;
+ private $listening = false;
+
+ /**
+ * Creates a plaintext TCP/IP socket server and starts listening on the given address
+ *
+ * This starts accepting new incoming connections on the given address.
+ * See also the `connection event` documented in the `ServerInterface`
+ * for more details.
+ *
+ * ```php
+ * $server = new TcpServer(8080, $loop);
+ * ```
+ *
+ * As above, the `$uri` parameter can consist of only a port, in which case the
+ * server will default to listening on the localhost address `127.0.0.1`,
+ * which means it will not be reachable from outside of this system.
+ *
+ * In order to use a random port assignment, you can use the port `0`:
+ *
+ * ```php
+ * $server = new TcpServer(0, $loop);
+ * $address = $server->getAddress();
+ * ```
+ *
+ * In order to change the host the socket is listening on, you can provide an IP
+ * address through the first parameter provided to the constructor, optionally
+ * preceded by the `tcp://` scheme:
+ *
+ * ```php
+ * $server = new TcpServer('192.168.0.1:8080', $loop);
+ * ```
+ *
+ * If you want to listen on an IPv6 address, you MUST enclose the host in square
+ * brackets:
+ *
+ * ```php
+ * $server = new TcpServer('[::1]:8080', $loop);
+ * ```
+ *
+ * If the given URI is invalid, does not contain a port, any other scheme or if it
+ * contains a hostname, it will throw an `InvalidArgumentException`:
+ *
+ * ```php
+ * // throws InvalidArgumentException due to missing port
+ * $server = new TcpServer('127.0.0.1', $loop);
+ * ```
+ *
+ * If the given URI appears to be valid, but listening on it fails (such as if port
+ * is already in use or port below 1024 may require root access etc.), it will
+ * throw a `RuntimeException`:
+ *
+ * ```php
+ * $first = new TcpServer(8080, $loop);
+ *
+ * // throws RuntimeException because port is already in use
+ * $second = new TcpServer(8080, $loop);
+ * ```
+ *
+ * Note that these error conditions may vary depending on your system and/or
+ * configuration.
+ * See the exception message and code for more details about the actual error
+ * condition.
+ *
+ * Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php)
+ * for the underlying stream socket resource like this:
+ *
+ * ```php
+ * $server = new TcpServer('[::1]:8080', $loop, array(
+ * 'backlog' => 200,
+ * 'so_reuseport' => true,
+ * 'ipv6_v6only' => true
+ * ));
+ * ```
+ *
+ * Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+ * their defaults and effects of changing these may vary depending on your system
+ * and/or PHP version.
+ * Passing unknown context options has no effect.
+ *
+ * @param string|int $uri
+ * @param LoopInterface $loop
+ * @param array $context
+ * @throws InvalidArgumentException if the listening address is invalid
+ * @throws RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($uri, LoopInterface $loop, array $context = array())
+ {
+ $this->loop = $loop;
+
+ // a single port has been given => assume localhost
+ if ((string)(int)$uri === (string)$uri) {
+ $uri = '127.0.0.1:' . $uri;
+ }
+
+ // assume default scheme if none has been given
+ if (strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ }
+
+ // parse_url() does not accept null ports (random port assignment) => manually remove
+ if (substr($uri, -2) === ':0') {
+ $parts = parse_url(substr($uri, 0, -2));
+ if ($parts) {
+ $parts['port'] = 0;
+ }
+ } else {
+ $parts = parse_url($uri);
+ }
+
+ // ensure URI contains TCP scheme, host and port
+ if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
+ throw new InvalidArgumentException('Invalid URI "' . $uri . '" given');
+ }
+
+ if (false === filter_var(trim($parts['host'], '[]'), FILTER_VALIDATE_IP)) {
+ throw new InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP');
+ }
+
+ $this->master = @stream_socket_server(
+ $uri,
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ stream_context_create(array('socket' => $context))
+ );
+ if (false === $this->master) {
+ throw new RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno);
+ }
+ stream_set_blocking($this->master, 0);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!is_resource($this->master)) {
+ return null;
+ }
+
+ $address = stream_socket_get_name($this->master, false);
+
+ // check if this is an IPv6 address which includes multiple colons but no square brackets
+ $pos = strrpos($address, ':');
+ if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
+ $port = substr($address, $pos + 1);
+ $address = '[' . substr($address, 0, $pos) . ']:' . $port;
+ }
+
+ return 'tcp://' . $address;
+ }
+
+ public function pause()
+ {
+ if (!$this->listening) {
+ return;
+ }
+
+ $this->loop->removeReadStream($this->master);
+ $this->listening = false;
+ }
+
+ public function resume()
+ {
+ if ($this->listening || !is_resource($this->master)) {
+ return;
+ }
+
+ $that = $this;
+ $this->loop->addReadStream($this->master, function ($master) use ($that) {
+ $newSocket = @stream_socket_accept($master);
+ if (false === $newSocket) {
+ $that->emit('error', array(new RuntimeException('Error accepting new connection')));
+
+ return;
+ }
+ $that->handleConnection($newSocket);
+ });
+ $this->listening = true;
+ }
+
+ public function close()
+ {
+ if (!is_resource($this->master)) {
+ return;
+ }
+
+ $this->pause();
+ fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleConnection($socket)
+ {
+ $this->emit('connection', array(
+ new Connection($socket, $this->loop)
+ ));
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/TimeoutConnector.php b/assets/php/vendor/react/socket/src/TimeoutConnector.php
new file mode 100644
index 0000000..d4eba2e
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/TimeoutConnector.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\LoopInterface;
+use React\Promise\Timer;
+
+final class TimeoutConnector implements ConnectorInterface
+{
+ private $connector;
+ private $timeout;
+ private $loop;
+
+ public function __construct(ConnectorInterface $connector, $timeout, LoopInterface $loop)
+ {
+ $this->connector = $connector;
+ $this->timeout = $timeout;
+ $this->loop = $loop;
+ }
+
+ public function connect($uri)
+ {
+ return Timer\timeout($this->connector->connect($uri), $this->timeout, $this->loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/UnixConnector.php b/assets/php/vendor/react/socket/src/UnixConnector.php
new file mode 100644
index 0000000..9b84ab0
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/UnixConnector.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\LoopInterface;
+use React\Promise;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * Unix domain socket connector
+ *
+ * Unix domain sockets use atomic operations, so we can as well emulate
+ * async behavior.
+ */
+final class UnixConnector implements ConnectorInterface
+{
+ private $loop;
+
+ public function __construct(LoopInterface $loop)
+ {
+ $this->loop = $loop;
+ }
+
+ public function connect($path)
+ {
+ if (strpos($path, '://') === false) {
+ $path = 'unix://' . $path;
+ } elseif (substr($path, 0, 7) !== 'unix://') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $path . '" is invalid'));
+ }
+
+ $resource = @stream_socket_client($path, $errno, $errstr, 1.0);
+
+ if (!$resource) {
+ return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));
+ }
+
+ $connection = new Connection($resource, $this->loop);
+ $connection->unix = true;
+
+ return Promise\resolve($connection);
+ }
+}
diff --git a/assets/php/vendor/react/socket/src/UnixServer.php b/assets/php/vendor/react/socket/src/UnixServer.php
new file mode 100644
index 0000000..8f1ed98
--- /dev/null
+++ b/assets/php/vendor/react/socket/src/UnixServer.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * The `UnixServer` class implements the `ServerInterface` and
+ * is responsible for accepting plaintext connections on unix domain sockets.
+ *
+ * ```php
+ * $server = new UnixServer('unix:///tmp/app.sock', $loop);
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class UnixServer extends EventEmitter implements ServerInterface
+{
+ private $master;
+ private $loop;
+ private $listening = false;
+
+ /**
+ * Creates a plaintext socket server and starts listening on the given unix socket
+ *
+ * This starts accepting new incoming connections on the given address.
+ * See also the `connection event` documented in the `ServerInterface`
+ * for more details.
+ *
+ * ```php
+ * $server = new UnixServer('unix:///tmp/app.sock', $loop);
+ * ```
+ *
+ * @param string $path
+ * @param LoopInterface $loop
+ * @param array $context
+ * @throws InvalidArgumentException if the listening address is invalid
+ * @throws RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($path, LoopInterface $loop, array $context = array())
+ {
+ $this->loop = $loop;
+
+ if (strpos($path, '://') === false) {
+ $path = 'unix://' . $path;
+ } elseif (substr($path, 0, 7) !== 'unix://') {
+ throw new InvalidArgumentException('Given URI "' . $path . '" is invalid');
+ }
+
+ $this->master = @stream_socket_server(
+ $path,
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ stream_context_create(array('socket' => $context))
+ );
+ if (false === $this->master) {
+ throw new RuntimeException('Failed to listen on unix domain socket "' . $path . '": ' . $errstr, $errno);
+ }
+ stream_set_blocking($this->master, 0);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!is_resource($this->master)) {
+ return null;
+ }
+
+ return 'unix://' . stream_socket_get_name($this->master, false);
+ }
+
+ public function pause()
+ {
+ if (!$this->listening) {
+ return;
+ }
+
+ $this->loop->removeReadStream($this->master);
+ $this->listening = false;
+ }
+
+ public function resume()
+ {
+ if ($this->listening || !is_resource($this->master)) {
+ return;
+ }
+
+ $that = $this;
+ $this->loop->addReadStream($this->master, function ($master) use ($that) {
+ $newSocket = @stream_socket_accept($master);
+ if (false === $newSocket) {
+ $that->emit('error', array(new RuntimeException('Error accepting new connection')));
+
+ return;
+ }
+ $that->handleConnection($newSocket);
+ });
+ $this->listening = true;
+ }
+
+ public function close()
+ {
+ if (!is_resource($this->master)) {
+ return;
+ }
+
+ $this->pause();
+ fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleConnection($socket)
+ {
+ $connection = new Connection($socket, $this->loop);
+ $connection->unix = true;
+
+ $this->emit('connection', array(
+ $connection
+ ));
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/ConnectionTest.php b/assets/php/vendor/react/socket/tests/ConnectionTest.php
new file mode 100644
index 0000000..d3563df
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/ConnectionTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\Connection;
+
+class ConnectionTest extends TestCase
+{
+ public function testCloseConnectionWillCloseSocketResource()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not support socket operation on test memory stream');
+ }
+
+ $resource = fopen('php://memory', 'r+');
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connection = new Connection($resource, $loop);
+ $connection->close();
+
+ $this->assertFalse(is_resource($resource));
+ }
+
+ public function testCloseConnectionWillRemoveResourceFromLoopBeforeClosingResource()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not support socket operation on test memory stream');
+ }
+
+ $resource = fopen('php://memory', 'r+');
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addWriteStream')->with($resource);
+
+ $onRemove = null;
+ $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($param) use (&$onRemove) {
+ $onRemove = is_resource($param);
+ return true;
+ }));
+
+ $connection = new Connection($resource, $loop);
+ $connection->write('test');
+ $connection->close();
+
+ $this->assertTrue($onRemove);
+ $this->assertFalse(is_resource($resource));
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/ConnectorTest.php b/assets/php/vendor/react/socket/tests/ConnectorTest.php
new file mode 100644
index 0000000..c8eb19b
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/ConnectorTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\Connector;
+use React\Promise\Promise;
+
+class ConnectorTest extends TestCase
+{
+ public function testConnectorUsesTcpAsDefaultScheme()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp
+ ));
+
+ $connector->connect('127.0.0.1:80');
+ }
+
+ public function testConnectorPassedThroughHostnameIfDnsIsDisabled()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('tcp://google.com:80')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'dns' => false
+ ));
+
+ $connector->connect('tcp://google.com:80');
+ }
+
+ public function testConnectorWithUnknownSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop);
+
+ $promise = $connector->connect('unknown://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTcpDefaultSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tcp' => false
+ ));
+
+ $promise = $connector->connect('google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTcpSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tcp' => false
+ ));
+
+ $promise = $connector->connect('tcp://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTlsSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tls' => false
+ ));
+
+ $promise = $connector->connect('tls://google.com:443');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledUnixSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'unix' => false
+ ));
+
+ $promise = $connector->connect('unix://demo.sock');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorUsesGivenResolverInstance()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+ $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'dns' => $resolver
+ ));
+
+ $connector->connect('google.com:80');
+ }
+
+ public function testConnectorUsesResolvedHostnameIfDnsIsUsed()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function ($resolve) { $resolve('127.0.0.1'); });
+ $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+ $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'dns' => $resolver
+ ));
+
+ $connector->connect('tcp://google.com:80');
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/DnsConnectorTest.php b/assets/php/vendor/react/socket/tests/DnsConnectorTest.php
new file mode 100644
index 0000000..3c94c39
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/DnsConnectorTest.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\DnsConnector;
+use React\Promise;
+
+class DnsConnectorTest extends TestCase
+{
+ private $tcp;
+ private $resolver;
+ private $connector;
+
+ public function setUp()
+ {
+ $this->tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $this->resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+
+ $this->connector = new DnsConnector($this->tcp, $this->resolver);
+ }
+
+ public function testPassByResolverIfGivenIp()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('127.0.0.1:80'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('127.0.0.1:80');
+ }
+
+ public function testPassThroughResolverIfGivenHost()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=google.com'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('google.com:80');
+ }
+
+ public function testPassThroughResolverIfGivenHostWhichResolvesToIpv6()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('::1')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('[::1]:80?hostname=google.com'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('google.com:80');
+ }
+
+ public function testPassByResolverIfGivenCompleteUri()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://127.0.0.1:80/path?query#fragment'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://127.0.0.1:80/path?query#fragment');
+ }
+
+ public function testPassThroughResolverIfGivenCompleteUri()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/path?query&hostname=google.com#fragment'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://google.com:80/path?query#fragment');
+ }
+
+ public function testPassThroughResolverIfGivenExplicitHost()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/?hostname=google.de'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://google.com:80/?hostname=google.de');
+ }
+
+ public function testRejectsImmediatelyIfUriIsInvalid()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('////');
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testSkipConnectionIfDnsFails()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.invalid'))->will($this->returnValue(Promise\reject()));
+ $this->tcp->expects($this->never())->method('connect');
+
+ $this->connector->connect('example.invalid:80');
+ }
+
+ public function testCancelDuringDnsCancelsDnsAndDoesNotStartTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, $this->expectCallableOnce());
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue($pending));
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testCancelDuringTcpConnectionCancelsTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=example.com'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/FixedUriConnectorTest.php b/assets/php/vendor/react/socket/tests/FixedUriConnectorTest.php
new file mode 100644
index 0000000..f42d74f
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/FixedUriConnectorTest.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\FixedUriConnector;
+use React\Tests\Socket\TestCase;
+
+class FixedUriConnectorTest extends TestCase
+{
+ public function testWillInvokeGivenConnector()
+ {
+ $base = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $base->expects($this->once())->method('connect')->with('test')->willReturn('ret');
+
+ $connector = new FixedUriConnector('test', $base);
+
+ $this->assertEquals('ret', $connector->connect('ignored'));
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/FunctionalConnectorTest.php b/assets/php/vendor/react/socket/tests/FunctionalConnectorTest.php
new file mode 100644
index 0000000..6611352
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/FunctionalConnectorTest.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use Clue\React\Block;
+use React\EventLoop\Factory;
+use React\Socket\Connector;
+use React\Socket\TcpServer;
+
+class FunctionalConnectorTest extends TestCase
+{
+ const TIMEOUT = 1.0;
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithLocalhost()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9998, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new Connector($loop);
+
+ $connection = Block\await($connector->connect('localhost:9998'), $loop, self::TIMEOUT);
+
+ $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
+
+ $connection->close();
+ $server->close();
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php b/assets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php
new file mode 100644
index 0000000..78a59d0
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php
@@ -0,0 +1,438 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\EventLoop\Factory;
+use React\Socket\SecureServer;
+use React\Socket\ConnectionInterface;
+use React\Socket\TcpServer;
+use React\Socket\TcpConnector;
+use React\Socket\SecureConnector;
+use Clue\React\Block;
+
+class FunctionalSecureServerTest extends TestCase
+{
+ const TIMEOUT = 0.5;
+
+ public function setUp()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+ }
+
+ public function testEmitsConnectionForNewConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testWritesDataToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write('foo');
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local ConnectionInterface */
+
+ $local->on('data', $this->expectCallableOnceWith('foo'));
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testWritesDataInMultipleChunksToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write(str_repeat('*', 400000));
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ public function testWritesMoreDataInMultipleChunksToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write(str_repeat('*', 2000000));
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(2000000, $received);
+ }
+
+ public function testEmitsDataFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $once = $this->expectCallableOnceWith('foo');
+ $server->on('connection', function (ConnectionInterface $conn) use ($once) {
+ $conn->on('data', $once);
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $local->write("foo");
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsDataInMultipleChunksFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $received = 0;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$received) {
+ $conn->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $local->write(str_repeat('*', 400000));
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ public function testPipesDataBackInMultipleChunksFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) use (&$received) {
+ $conn->pipe($conn);
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ $local->write(str_repeat('*', 400000));
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ /**
+ * @requires PHP 5.6
+ */
+ public function testEmitsConnectionForNewTlsv11Connection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false,
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ /**
+ * @requires PHP 5.6
+ */
+ public function testEmitsErrorForClientWithTlsVersionMismatch()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER|STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false,
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsConnectionForNewConnectionWithEncryptedCertificate()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem',
+ 'passphrase' => 'swordfish'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithInvalidCertificate()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'invalid.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithEncryptedCertificateMissingPassphrase()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithEncryptedCertificateWithInvalidPassphrase()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem',
+ 'passphrase' => 'nope'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForConnectionWithPeerVerification()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => true
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then(null, $this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsErrorIfConnectionIsCancelled()
+ {
+ if (PHP_OS !== 'Linux') {
+ $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $promise->then(null, $this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsNothingIfConnectionIsIdle()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableNever());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect(str_replace('tls://', '', $server->getAddress()));
+
+ $promise->then($this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsErrorIfConnectionIsNotSecureHandshake()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect(str_replace('tls://', '', $server->getAddress()));
+
+ $promise->then(function (ConnectionInterface $stream) {
+ $stream->write("GET / HTTP/1.0\r\n\r\n");
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php b/assets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php
new file mode 100644
index 0000000..ec7855e
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php
@@ -0,0 +1,324 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\EventLoop\Factory;
+use React\Socket\TcpServer;
+use React\Socket\ConnectionInterface;
+use React\Socket\TcpConnector;
+use Clue\React\Block;
+
+class FunctionalTcpServerTest extends TestCase
+{
+ public function testEmitsConnectionForNewConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsNoConnectionForNewConnectionWhenPaused()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableNever());
+ $server->pause();
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionForNewConnectionWhenResumedAfterPause()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->pause();
+ $server->resume();
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionWithRemoteIp()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $peer);
+ }
+
+ public function testEmitsConnectionWithLocalIp()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $local);
+ $this->assertEquals($server->getAddress(), $local);
+ }
+
+ public function testEmitsConnectionWithLocalIpDespiteListeningOnAll()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer('0.0.0.0:0', $loop);
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $local);
+ }
+
+ public function testEmitsConnectionWithRemoteIpAfterConnectionIsClosedByPeer()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $conn->on('close', function () use ($conn, &$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $client = Block\await($promise, $loop, 0.1);
+ $client->end();
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $peer);
+ }
+
+ public function testEmitsConnectionWithRemoteNullAddressAfterConnectionIsClosedLocally()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $conn->close();
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertNull($peer);
+ }
+
+ public function testEmitsConnectionEvenIfConnectionIsCancelled()
+ {
+ if (PHP_OS !== 'Linux') {
+ $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $promise->then(null, $this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionForNewIpv6Connection()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionWithRemoteIpv6()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('[::1]:', $peer);
+ }
+
+ public function testEmitsConnectionWithLocalIpv6()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('[::1]:', $local);
+ $this->assertEquals($server->getAddress(), $local);
+ }
+
+ public function testEmitsConnectionWithInheritedContextOptions()
+ {
+ if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) {
+ // https://3v4l.org/hB4Tc
+ $this->markTestSkipped('Not supported on legacy HHVM < 3.13');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop, array(
+ 'backlog' => 4
+ ));
+
+ $all = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$all) {
+ $all = stream_context_get_options($conn->stream);
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array('socket' => array('backlog' => 4)), $all);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnInvalidUri()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('///', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWithoutPort()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('127.0.0.1', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWithWrongScheme()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('udp://127.0.0.1:0', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWIthHostname()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('localhost:8080', $loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/IntegrationTest.php b/assets/php/vendor/react/socket/tests/IntegrationTest.php
new file mode 100644
index 0000000..24dbe37
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/IntegrationTest.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use Clue\React\Block;
+use React\Dns\Resolver\Factory as ResolverFactory;
+use React\EventLoop\Factory;
+use React\Socket\Connector;
+use React\Socket\DnsConnector;
+use React\Socket\SecureConnector;
+use React\Socket\TcpConnector;
+
+/** @group internet */
+class IntegrationTest extends TestCase
+{
+ const TIMEOUT = 5.0;
+
+ /** @test */
+ public function gettingStuffFromGoogleShouldWork()
+ {
+ $loop = Factory::create();
+ $connector = new Connector($loop);
+
+ $conn = Block\await($connector->connect('google.com:80'), $loop);
+
+ $this->assertContains(':80', $conn->getRemoteAddress());
+ $this->assertNotEquals('google.com:80', $conn->getRemoteAddress());
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingEncryptedStuffFromGoogleShouldWork()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+ $secureConnector = new Connector($loop);
+
+ $conn = Block\await($secureConnector->connect('tls://google.com:443'), $loop);
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingEncryptedStuffFromGoogleShouldWorkIfHostIsResolvedFirst()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $factory = new ResolverFactory();
+ $dns = $factory->create('8.8.8.8', $loop);
+
+ $connector = new DnsConnector(
+ new SecureConnector(
+ new TcpConnector($loop),
+ $loop
+ ),
+ $dns
+ );
+
+ $conn = Block\await($connector->connect('google.com:443'), $loop);
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingPlaintextStuffFromEncryptedGoogleShouldNotWork()
+ {
+ $loop = Factory::create();
+ $connector = new Connector($loop);
+
+ $conn = Block\await($connector->connect('google.com:443'), $loop);
+
+ $this->assertContains(':443', $conn->getRemoteAddress());
+ $this->assertNotEquals('google.com:443', $conn->getRemoteAddress());
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertNotRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ public function testConnectingFailsIfDnsUsesInvalidResolver()
+ {
+ $loop = Factory::create();
+
+ $factory = new ResolverFactory();
+ $dns = $factory->create('demo.invalid', $loop);
+
+ $connector = new Connector($loop, array(
+ 'dns' => $dns
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
+ }
+
+ public function testConnectingFailsIfTimeoutIsTooSmall()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'timeout' => 0.001
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
+ }
+
+ public function testSelfSignedRejectsIfVerificationIsEnabled()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'verify_peer' => true
+ )
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT);
+ }
+
+ public function testSelfSignedResolvesIfVerificationIsDisabled()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'verify_peer' => false
+ )
+ ));
+
+ $conn = Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT);
+ $conn->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/LimitingServerTest.php b/assets/php/vendor/react/socket/tests/LimitingServerTest.php
new file mode 100644
index 0000000..2cc9a58
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/LimitingServerTest.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\LimitingServer;
+use React\Socket\TcpServer;
+use React\EventLoop\Factory;
+use Clue\React\Block;
+
+class LimitingServerTest extends TestCase
+{
+ public function testGetAddressWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn('127.0.0.1:1234');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $this->assertEquals('127.0.0.1:1234', $server->getAddress());
+ }
+
+ public function testPauseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ }
+
+ public function testPauseTwiceWillBePassedThroughToTcpServerOnce()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testResumeWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->resume();
+ }
+
+ public function testResumeTwiceWillBePassedThroughToTcpServerOnce()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->resume();
+ $server->resume();
+ }
+
+ public function testCloseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('close');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->close();
+ }
+
+ public function testSocketErrorWillBeForwarded()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('error', array(new \RuntimeException('test')));
+ }
+
+ public function testSocketConnectionWillBeForwarded()
+ {
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 100);
+ $server->on('connection', $this->expectCallableOnceWith($connection));
+ $server->on('error', $this->expectCallableNever());
+
+ $tcp->emit('connection', array($connection));
+
+ $this->assertEquals(array($connection), $server->getConnections());
+ }
+
+ public function testSocketConnectionWillBeClosedOnceLimitIsReached()
+ {
+ $first = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $first->expects($this->never())->method('close');
+ $second = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $second->expects($this->once())->method('close');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 1);
+ $server->on('connection', $this->expectCallableOnceWith($first));
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('connection', array($first));
+ $tcp->emit('connection', array($second));
+ }
+
+ public function testPausingServerWillBePausedOnceLimitIsReached()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $tcp = new TcpServer(0, $loop);
+
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+
+ $server = new LimitingServer($tcp, 1, true);
+
+ $tcp->emit('connection', array($connection));
+ }
+
+ public function testSocketDisconnectionWillRemoveFromList()
+ {
+ $loop = Factory::create();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $socket = stream_socket_client($tcp->getAddress());
+ fclose($socket);
+
+ $server = new LimitingServer($tcp, 100);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('error', $this->expectCallableNever());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array(), $server->getConnections());
+ }
+
+ public function testPausingServerWillEmitOnlyOneButAcceptTwoConnectionsDueToOperatingSystem()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new LimitingServer($server, 1, true);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('error', $this->expectCallableNever());
+
+ $first = stream_socket_client($server->getAddress());
+ $second = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ fclose($first);
+ fclose($second);
+ }
+
+ public function testPausingServerWillEmitTwoConnectionsFromBacklog()
+ {
+ $loop = Factory::create();
+
+ $twice = $this->createCallableMock();
+ $twice->expects($this->exactly(2))->method('__invoke');
+
+ $server = new TcpServer(0, $loop);
+ $server = new LimitingServer($server, 1, true);
+ $server->on('connection', $twice);
+ $server->on('error', $this->expectCallableNever());
+
+ $first = stream_socket_client($server->getAddress());
+ fclose($first);
+ $second = stream_socket_client($server->getAddress());
+ fclose($second);
+
+ Block\sleep(0.1, $loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/SecureConnectorTest.php b/assets/php/vendor/react/socket/tests/SecureConnectorTest.php
new file mode 100644
index 0000000..0b3a702
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/SecureConnectorTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Promise;
+use React\Socket\SecureConnector;
+
+class SecureConnectorTest extends TestCase
+{
+ private $loop;
+ private $tcp;
+ private $connector;
+
+ public function setUp()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $this->tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $this->connector = new SecureConnector($this->tcp, $this->loop);
+ }
+
+ public function testConnectionWillWaitForTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+
+ $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
+ }
+
+ public function testConnectionWithCompleteUriWillBePassedThroughExpectForScheme()
+ {
+ $pending = new Promise\Promise(function () { });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80/path?query#fragment'))->will($this->returnValue($pending));
+
+ $this->connector->connect('tls://example.com:80/path?query#fragment');
+ }
+
+ public function testConnectionToInvalidSchemeWillReject()
+ {
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('tcp://example.com:80');
+
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testCancelDuringTcpConnectionCancelsTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testConnectionWillBeClosedAndRejectedIfConnectioIsNoStream()
+ {
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $connection->expects($this->once())->method('close');
+
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->willReturn(Promise\resolve($connection));
+
+ $promise = $this->connector->connect('example.com:80');
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/SecureIntegrationTest.php b/assets/php/vendor/react/socket/tests/SecureIntegrationTest.php
new file mode 100644
index 0000000..8c9ba14
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/SecureIntegrationTest.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\EventLoop\Factory as LoopFactory;
+use React\Socket\TcpServer;
+use React\Socket\SecureServer;
+use React\Socket\TcpConnector;
+use React\Socket\SecureConnector;
+use Clue\React\Block;
+use React\Promise\Promise;
+use Evenement\EventEmitterInterface;
+use React\Promise\Deferred;
+use React\Socket\ConnectionInterface;
+
+class SecureIntegrationTest extends TestCase
+{
+ const TIMEOUT = 0.5;
+
+ private $loop;
+ private $server;
+ private $connector;
+ private $address;
+
+ public function setUp()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $this->loop = LoopFactory::create();
+ $this->server = new TcpServer(0, $this->loop);
+ $this->server = new SecureServer($this->server, $this->loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $this->address = $this->server->getAddress();
+ $this->connector = new SecureConnector(new TcpConnector($this->loop), $this->loop, array('verify_peer' => false));
+ }
+
+ public function tearDown()
+ {
+ if ($this->server !== null) {
+ $this->server->close();
+ $this->server = null;
+ }
+ }
+
+ public function testConnectToServer()
+ {
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testConnectToServerEmitsConnection()
+ {
+ $promiseServer = $this->createPromiseForEvent($this->server, 'connection', $this->expectCallableOnce());
+
+ $promiseClient = $this->connector->connect($this->address);
+
+ list($_, $client) = Block\awaitAll(array($promiseServer, $promiseClient), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->close();
+ }
+
+ public function testSendSmallDataToServerReceivesOneChunk()
+ {
+ // server expects one connection which emits one data event
+ $received = new Deferred();
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($received) {
+ $peer->on('data', function ($chunk) use ($received) {
+ $received->resolve($chunk);
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->write('hello');
+
+ // await server to report one "data" event
+ $data = Block\await($received->promise(), $this->loop, self::TIMEOUT);
+
+ $client->close();
+
+ $this->assertEquals('hello', $data);
+ }
+
+ public function testSendDataWithEndToServerReceivesAllData()
+ {
+ $disconnected = new Deferred();
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($disconnected) {
+ $received = '';
+ $peer->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ $peer->on('close', function () use (&$received, $disconnected) {
+ $disconnected->resolve($received);
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $data = str_repeat('a', 200000);
+ $client->end($data);
+
+ // await server to report connection "close" event
+ $received = Block\await($disconnected->promise(), $this->loop, self::TIMEOUT);
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testSendDataWithoutEndingToServerReceivesAllData()
+ {
+ $received = '';
+ $this->server->on('connection', function (ConnectionInterface $peer) use (&$received) {
+ $peer->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $data = str_repeat('d', 200000);
+ $client->write($data);
+
+ // buffer incoming data for 0.1s (should be plenty of time)
+ Block\sleep(0.1, $this->loop);
+
+ $client->close();
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testConnectToServerWhichSendsSmallDataReceivesOneChunk()
+ {
+ $this->server->on('connection', function (ConnectionInterface $peer) {
+ $peer->write('hello');
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // await client to report one "data" event
+ $receive = $this->createPromiseForEvent($client, 'data', $this->expectCallableOnceWith('hello'));
+ Block\await($receive, $this->loop, self::TIMEOUT);
+
+ $client->close();
+ }
+
+ public function testConnectToServerWhichSendsDataWithEndReceivesAllData()
+ {
+ $data = str_repeat('b', 100000);
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($data) {
+ $peer->end($data);
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // await data from client until it closes
+ $received = $this->buffer($client, $this->loop, self::TIMEOUT);
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testConnectToServerWhichSendsDataWithoutEndingReceivesAllData()
+ {
+ $data = str_repeat('c', 100000);
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($data) {
+ $peer->write($data);
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // buffer incoming data for 0.1s (should be plenty of time)
+ $received = '';
+ $client->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ Block\sleep(0.1, $this->loop);
+
+ $client->close();
+
+ $this->assertEquals($data, $received);
+ }
+
+ private function createPromiseForEvent(EventEmitterInterface $emitter, $event, $fn)
+ {
+ return new Promise(function ($resolve) use ($emitter, $event, $fn) {
+ $emitter->on($event, function () use ($resolve, $fn) {
+ $resolve(call_user_func_array($fn, func_get_args()));
+ });
+ });
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/SecureServerTest.php b/assets/php/vendor/react/socket/tests/SecureServerTest.php
new file mode 100644
index 0000000..92c641f
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/SecureServerTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\SecureServer;
+use React\Socket\TcpServer;
+
+class SecureServerTest extends TestCase
+{
+ public function setUp()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+ }
+
+ public function testGetAddressWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn('tcp://127.0.0.1:1234');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $this->assertEquals('tls://127.0.0.1:1234', $server->getAddress());
+ }
+
+ public function testGetAddressWillReturnNullIfTcpServerReturnsNull()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn(null);
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $this->assertNull($server->getAddress());
+ }
+
+ public function testPauseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->pause();
+ }
+
+ public function testResumeWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->resume();
+ }
+
+ public function testCloseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('close');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->close();
+ }
+
+ public function testConnectionWillBeEndedWithErrorIfItIsNotAStream()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $connection->expects($this->once())->method('end');
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('connection', array($connection));
+ }
+
+ public function testSocketErrorWillBeForwarded()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('error', array(new \RuntimeException('test')));
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/ServerTest.php b/assets/php/vendor/react/socket/tests/ServerTest.php
new file mode 100644
index 0000000..14fdb2c
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/ServerTest.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\EventLoop\Factory;
+use React\Socket\Server;
+use React\Socket\TcpConnector;
+use React\Socket\UnixConnector;
+use Clue\React\Block;
+use React\Socket\ConnectionInterface;
+
+class ServerTest extends TestCase
+{
+ const TIMEOUT = 0.1;
+
+ public function testCreateServerWithZeroPortAssignsRandomPort()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $this->assertNotEquals(0, $server->getAddress());
+ $server->close();
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsForInvalidUri()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new Server('invalid URI', $loop);
+ }
+
+ public function testConstructorCreatesExpectedTcpServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+
+ $connector = new TcpConnector($loop);
+ $connector->connect($server->getAddress())
+ ->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);
+
+ $connection->close();
+ $server->close();
+ }
+
+ public function testConstructorCreatesExpectedUnixServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server($this->getRandomSocketUri(), $loop);
+
+ $connector = new UnixConnector($loop);
+ $connector->connect($server->getAddress())
+ ->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);
+
+ $connection->close();
+ $server->close();
+ }
+
+ public function testEmitsConnectionForNewConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesNotEmitConnectionForNewConnectionToPausedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->pause();
+ $server->on('connection', $this->expectCallableNever());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesEmitConnectionForNewConnectionToResumedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->pause();
+ $server->on('connection', $this->expectCallableOnce());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ $server->resume();
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesNotAllowConnectionToClosedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->on('connection', $this->expectCallableNever());
+ $address = $server->getAddress();
+ $server->close();
+
+ $client = @stream_socket_client($address);
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertFalse($client);
+ }
+
+ public function testEmitsConnectionWithInheritedContextOptions()
+ {
+ if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) {
+ // https://3v4l.org/hB4Tc
+ $this->markTestSkipped('Not supported on legacy HHVM < 3.13');
+ }
+
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop, array(
+ 'backlog' => 4
+ ));
+
+ $all = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$all) {
+ $all = stream_context_get_options($conn->stream);
+ });
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array('socket' => array('backlog' => 4)), $all);
+ }
+
+ public function testDoesNotEmitSecureConnectionForNewPlainConnection()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $server = new Server('tls://127.0.0.1:0', $loop, array(
+ 'tls' => array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ )
+ ));
+ $server->on('connection', $this->expectCallableNever());
+
+ $client = stream_socket_client(str_replace('tls://', '', $server->getAddress()));
+
+ Block\sleep(0.1, $loop);
+ }
+
+ private function getRandomSocketUri()
+ {
+ return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/Stub/CallableStub.php b/assets/php/vendor/react/socket/tests/Stub/CallableStub.php
new file mode 100644
index 0000000..1b197eb
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/Stub/CallableStub.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace React\Tests\Socket\Stub;
+
+class CallableStub
+{
+ public function __invoke()
+ {
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/Stub/ConnectionStub.php b/assets/php/vendor/react/socket/tests/Stub/ConnectionStub.php
new file mode 100644
index 0000000..844b2ad
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/Stub/ConnectionStub.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace React\Tests\Socket\Stub;
+
+use Evenement\EventEmitter;
+use React\Socket\ConnectionInterface;
+use React\Stream\WritableStreamInterface;
+use React\Stream\Util;
+
+class ConnectionStub extends EventEmitter implements ConnectionInterface
+{
+ private $data = '';
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function isWritable()
+ {
+ return true;
+ }
+
+ public function pause()
+ {
+ }
+
+ public function resume()
+ {
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function write($data)
+ {
+ $this->data .= $data;
+
+ return true;
+ }
+
+ public function end($data = null)
+ {
+ }
+
+ public function close()
+ {
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function getRemoteAddress()
+ {
+ return '127.0.0.1';
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/Stub/ServerStub.php b/assets/php/vendor/react/socket/tests/Stub/ServerStub.php
new file mode 100644
index 0000000..d9e74f4
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/Stub/ServerStub.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace React\Tests\Socket\Stub;
+
+use Evenement\EventEmitter;
+use React\Socket\ServerInterface;
+
+class ServerStub extends EventEmitter implements ServerInterface
+{
+ public function getAddress()
+ {
+ return '127.0.0.1:80';
+ }
+
+ public function close()
+ {
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/TcpConnectorTest.php b/assets/php/vendor/react/socket/tests/TcpConnectorTest.php
new file mode 100644
index 0000000..e3575a7
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/TcpConnectorTest.php
@@ -0,0 +1,255 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use Clue\React\Block;
+use React\EventLoop\Factory;
+use React\Socket\ConnectionInterface;
+use React\Socket\TcpConnector;
+use React\Socket\TcpServer;
+
+class TcpConnectorTest extends TestCase
+{
+ const TIMEOUT = 0.1;
+
+ /** @test */
+ public function connectionToEmptyPortShouldFail()
+ {
+ $loop = Factory::create();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('127.0.0.1:9999')
+ ->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldAddResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $valid = false;
+ $loop->expects($this->once())->method('addWriteStream')->with($this->callback(function ($arg) use (&$valid) {
+ $valid = is_resource($arg);
+ return true;
+ }));
+ $connector->connect($server->getAddress());
+
+ $this->assertTrue($valid);
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceed()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+
+ $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithRemoteAdressSameAsTarget()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertEquals('tcp://127.0.0.1:9999', $connection->getRemoteAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithLocalAdressOnLocalhost()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertContains('tcp://127.0.0.1:', $connection->getLocalAddress());
+ $this->assertNotEquals('tcp://127.0.0.1:9999', $connection->getLocalAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithNullAddressesAfterConnectionClosed()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $connection->close();
+
+ $this->assertNull($connection->getRemoteAddress());
+ $this->assertNull($connection->getLocalAddress());
+ }
+
+ /** @test */
+ public function connectionToTcpServerWillCloseWhenOtherSideCloses()
+ {
+ $loop = Factory::create();
+
+ // immediately close connection and server once connection is in
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', function (ConnectionInterface $conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $once = $this->expectCallableOnce();
+ $connector = new TcpConnector($loop);
+ $connector->connect($server->getAddress())->then(function (ConnectionInterface $conn) use ($once) {
+ $conn->write('hello');
+ $conn->on('close', $once);
+ });
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToEmptyIp6PortShouldFail()
+ {
+ $loop = Factory::create();
+
+ $connector = new TcpConnector($loop);
+ $connector
+ ->connect('[::1]:9999')
+ ->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToIp6TcpServerShouldSucceed()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:9999', $loop);
+ } catch (\Exception $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (IPv6 not supported on this system?)');
+ }
+
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('[::1]:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertEquals('tcp://[::1]:9999', $connection->getRemoteAddress());
+
+ $this->assertContains('tcp://[::1]:', $connection->getLocalAddress());
+ $this->assertNotEquals('tcp://[::1]:9999', $connection->getLocalAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToHostnameShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('www.google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function connectionToInvalidPortShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('255.255.255.255:12345678')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function connectionToInvalidSchemeShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('tls://google.com:443')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function cancellingConnectionShouldRemoveResourceFromLoopAndCloseResource()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $loop->expects($this->once())->method('addWriteStream');
+ $promise = $connector->connect($server->getAddress());
+
+ $resource = null;
+ $valid = false;
+ $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($arg) use (&$resource, &$valid) {
+ $resource = $arg;
+ $valid = is_resource($arg);
+ return true;
+ }));
+ $promise->cancel();
+
+ $this->assertTrue($valid);
+ $this->assertFalse(is_resource($resource));
+ }
+
+ /** @test */
+ public function cancellingConnectionShouldRejectPromise()
+ {
+ $loop = Factory::create();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $this->setExpectedException('RuntimeException', 'Cancelled');
+ Block\await($promise, $loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/TcpServerTest.php b/assets/php/vendor/react/socket/tests/TcpServerTest.php
new file mode 100644
index 0000000..72b3c28
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/TcpServerTest.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use Clue\React\Block;
+use React\EventLoop\Factory;
+use React\Socket\TcpServer;
+use React\Stream\DuplexResourceStream;
+
+class TcpServerTest extends TestCase
+{
+ private $loop;
+ private $server;
+ private $port;
+
+ private function createLoop()
+ {
+ return Factory::create();
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::__construct
+ * @covers React\Socket\TcpServer::getAddress
+ */
+ public function setUp()
+ {
+ $this->loop = $this->createLoop();
+ $this->server = new TcpServer(0, $this->loop);
+
+ $this->port = parse_url($this->server->getAddress(), PHP_URL_PORT);
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::handleConnection
+ */
+ public function testConnection()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $this->server->on('connection', $this->expectCallableOnce());
+
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::handleConnection
+ */
+ public function testConnectionWithManyClients()
+ {
+ $client1 = stream_socket_client('tcp://localhost:'.$this->port);
+ $client2 = stream_socket_client('tcp://localhost:'.$this->port);
+ $client3 = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $this->server->on('connection', $this->expectCallableExactly(3));
+ $this->tick();
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataEventWillNotBeEmittedWhenClientSendsNoData()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedWithDataClientSends()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ fwrite($client, "foo\n");
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+ fwrite($client, "foo\n");
+ stream_socket_shutdown($client, STREAM_SHUT_WR);
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testLoopWillEndWhenServerIsClosed()
+ {
+ // explicitly unset server because we already call close()
+ $this->server->close();
+ $this->server = null;
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testCloseTwiceIsNoOp()
+ {
+ $this->server->close();
+ $this->server->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testGetAddressAfterCloseReturnsNull()
+ {
+ $this->server->close();
+ $this->assertNull($this->server->getAddress());
+ }
+
+ public function testLoopWillEndWhenServerIsClosedAfterSingleConnection()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $server->on('connection', function ($conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+ $stream = new DuplexResourceStream($client, $this->loop);
+
+ $bytes = 1024 * 1024;
+ $stream->end(str_repeat('*', $bytes));
+
+ $mock = $this->expectCallableOnce();
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $received = 0;
+ $server->on('connection', function ($conn) use ($mock, &$received, $server) {
+ // count number of bytes received
+ $conn->on('data', function ($data) use (&$received) {
+ $received += strlen($data);
+ });
+
+ $conn->on('end', $mock);
+
+ // do not await any further connections in order to let the loop terminate
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ $this->assertEquals($bytes, $received);
+ }
+
+ public function testConnectionDoesNotEndWhenClientDoesNotClose()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\Connection::end
+ */
+ public function testConnectionDoesEndWhenClientCloses()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ fclose($client);
+
+ $mock = $this->expectCallableOnce();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testCtorAddsResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new TcpServer(0, $loop);
+ }
+
+ public function testResumeWithoutPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->resume();
+ }
+
+ public function testPauseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->pause();
+ }
+
+ public function testPauseAfterPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testCloseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->close();
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testListenOnBusyPortThrows()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Windows supports listening on same port multiple times');
+ }
+
+ $another = new TcpServer($this->port, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::close
+ */
+ public function tearDown()
+ {
+ if ($this->server) {
+ $this->server->close();
+ }
+ }
+
+ private function tick()
+ {
+ Block\sleep(0, $this->loop);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/TestCase.php b/assets/php/vendor/react/socket/tests/TestCase.php
new file mode 100644
index 0000000..e87fc2f
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/TestCase.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Stream\ReadableStreamInterface;
+use React\EventLoop\LoopInterface;
+use Clue\React\Block;
+use React\Promise\Promise;
+use PHPUnit\Framework\TestCase as BaseTestCase;
+
+class TestCase extends BaseTestCase
+{
+ protected function expectCallableExactly($amount)
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnceWith($value)
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($value);
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Socket\Stub\CallableStub')->getMock();
+ }
+
+ protected function buffer(ReadableStreamInterface $stream, LoopInterface $loop, $timeout)
+ {
+ if (!$stream->isReadable()) {
+ return '';
+ }
+
+ return Block\await(new Promise(
+ function ($resolve, $reject) use ($stream) {
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $reject);
+
+ $stream->on('close', function () use (&$buffer, $resolve) {
+ $resolve($buffer);
+ });
+ },
+ function () use ($stream) {
+ $stream->close();
+ throw new \RuntimeException();
+ }
+ ), $loop, $timeout);
+ }
+
+ public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
+ {
+ if (method_exists($this, 'expectException')) {
+ // PHPUnit 5+
+ $this->expectException($exception);
+ if ($exceptionMessage !== '') {
+ $this->expectExceptionMessage($exceptionMessage);
+ }
+ if ($exceptionCode !== null) {
+ $this->expectExceptionCode($exceptionCode);
+ }
+ } else {
+ // legacy PHPUnit 4
+ parent::setExpectedException($exception, $exceptionMessage, $exceptionCode);
+ }
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/TimeoutConnectorTest.php b/assets/php/vendor/react/socket/tests/TimeoutConnectorTest.php
new file mode 100644
index 0000000..64787d9
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/TimeoutConnectorTest.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\TimeoutConnector;
+use React\Promise;
+use React\EventLoop\Factory;
+
+class TimeoutConnectorTest extends TestCase
+{
+ public function testRejectsOnTimeout()
+ {
+ $promise = new Promise\Promise(function () { });
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testRejectsWhenConnectorRejects()
+ {
+ $promise = Promise\reject(new \RuntimeException());
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 5.0, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testResolvesWhenConnectorResolves()
+ {
+ $promise = Promise\resolve();
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 5.0, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableOnce(),
+ $this->expectCallableNever()
+ );
+
+ $loop->run();
+ }
+
+ public function testRejectsAndCancelsPendingPromiseOnTimeout()
+ {
+ $promise = new Promise\Promise(function () { }, $this->expectCallableOnce());
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testCancelsPendingPromiseOnCancel()
+ {
+ $promise = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $out = $timeout->connect('google.com:80');
+ $out->cancel();
+
+ $out->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/UnixConnectorTest.php b/assets/php/vendor/react/socket/tests/UnixConnectorTest.php
new file mode 100644
index 0000000..1564064
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/UnixConnectorTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use React\Socket\ConnectionInterface;
+use React\Socket\UnixConnector;
+
+class UnixConnectorTest extends TestCase
+{
+ private $loop;
+ private $connector;
+
+ public function setUp()
+ {
+ $this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $this->connector = new UnixConnector($this->loop);
+ }
+
+ public function testInvalid()
+ {
+ $promise = $this->connector->connect('google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testInvalidScheme()
+ {
+ $promise = $this->connector->connect('tcp://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testValid()
+ {
+ // random unix domain socket path
+ $path = sys_get_temp_dir() . '/test' . uniqid() . '.sock';
+
+ // temporarily create unix domain socket server to connect to
+ $server = stream_socket_server('unix://' . $path, $errno, $errstr);
+
+ // skip test if we can not create a test server (Windows etc.)
+ if (!$server) {
+ $this->markTestSkipped('Unable to create socket "' . $path . '": ' . $errstr . '(' . $errno .')');
+ return;
+ }
+
+ // tests succeeds if we get notified of successful connection
+ $promise = $this->connector->connect($path);
+ $promise->then($this->expectCallableOnce());
+
+ // remember remote and local address of this connection and close again
+ $remote = $local = false;
+ $promise->then(function(ConnectionInterface $conn) use (&$remote, &$local) {
+ $remote = $conn->getRemoteAddress();
+ $local = $conn->getLocalAddress();
+ $conn->close();
+ });
+
+ // clean up server
+ fclose($server);
+ unlink($path);
+
+ $this->assertNull($local);
+ $this->assertEquals('unix://' . $path, $remote);
+ }
+}
diff --git a/assets/php/vendor/react/socket/tests/UnixServerTest.php b/assets/php/vendor/react/socket/tests/UnixServerTest.php
new file mode 100644
index 0000000..10f7e4f
--- /dev/null
+++ b/assets/php/vendor/react/socket/tests/UnixServerTest.php
@@ -0,0 +1,283 @@
+<?php
+
+namespace React\Tests\Socket;
+
+use Clue\React\Block;
+use React\EventLoop\Factory;
+use React\Socket\UnixServer;
+use React\Stream\DuplexResourceStream;
+
+class UnixServerTest extends TestCase
+{
+ private $loop;
+ private $server;
+ private $uds;
+
+ /**
+ * @covers React\Socket\UnixServer::__construct
+ * @covers React\Socket\UnixServer::getAddress
+ */
+ public function setUp()
+ {
+ $this->loop = Factory::create();
+ $this->uds = $this->getRandomSocketUri();
+ $this->server = new UnixServer($this->uds, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::handleConnection
+ */
+ public function testConnection()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $this->server->on('connection', $this->expectCallableOnce());
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::handleConnection
+ */
+ public function testConnectionWithManyClients()
+ {
+ $client1 = stream_socket_client($this->uds);
+ $client2 = stream_socket_client($this->uds);
+ $client3 = stream_socket_client($this->uds);
+
+ $this->server->on('connection', $this->expectCallableExactly(3));
+ $this->tick();
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataEventWillNotBeEmittedWhenClientSendsNoData()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedWithDataClientSends()
+ {
+ $client = stream_socket_client($this->uds);
+
+ fwrite($client, "foo\n");
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending()
+ {
+ $client = stream_socket_client($this->uds);
+ fwrite($client, "foo\n");
+ stream_socket_shutdown($client, STREAM_SHUT_WR);
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testLoopWillEndWhenServerIsClosed()
+ {
+ // explicitly unset server because we already call close()
+ $this->server->close();
+ $this->server = null;
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testCloseTwiceIsNoOp()
+ {
+ $this->server->close();
+ $this->server->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testGetAddressAfterCloseReturnsNull()
+ {
+ $this->server->close();
+ $this->assertNull($this->server->getAddress());
+ }
+
+ public function testLoopWillEndWhenServerIsClosedAfterSingleConnection()
+ {
+ $client = stream_socket_client($this->uds);
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $server->on('connection', function ($conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts()
+ {
+ $client = stream_socket_client($this->uds);
+ $stream = new DuplexResourceStream($client, $this->loop);
+
+ $bytes = 1024 * 1024;
+ $stream->end(str_repeat('*', $bytes));
+
+ $mock = $this->expectCallableOnce();
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $received = 0;
+ $server->on('connection', function ($conn) use ($mock, &$received, $server) {
+ // count number of bytes received
+ $conn->on('data', function ($data) use (&$received) {
+ $received += strlen($data);
+ });
+
+ $conn->on('end', $mock);
+
+ // do not await any further connections in order to let the loop terminate
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ $this->assertEquals($bytes, $received);
+ }
+
+ public function testConnectionDoesNotEndWhenClientDoesNotClose()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\Connection::end
+ */
+ public function testConnectionDoesEndWhenClientCloses()
+ {
+ $client = stream_socket_client($this->uds);
+
+ fclose($client);
+
+ $mock = $this->expectCallableOnce();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testCtorAddsResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ }
+
+ public function testResumeWithoutPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->resume();
+ }
+
+ public function testPauseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->pause();
+ }
+
+ public function testPauseAfterPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testCloseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->close();
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testListenOnBusyPortThrows()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Windows supports listening on same port multiple times');
+ }
+
+ $another = new UnixServer($this->uds, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::close
+ */
+ public function tearDown()
+ {
+ if ($this->server) {
+ $this->server->close();
+ }
+ }
+
+ private function getRandomSocketUri()
+ {
+ return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
+ }
+
+ private function tick()
+ {
+ Block\sleep(0, $this->loop);
+ }
+}