summaryrefslogtreecommitdiffstats
path: root/vendor/react/socket/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:38:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:38:42 +0000
commitc3ca98e1b35123f226c7f4c596b5dee78caa4223 (patch)
tree9b6eb109283da55e7d9064baa9fac795a40264cb /vendor/react/socket/src
parentInitial commit. (diff)
downloadicinga-php-thirdparty-upstream.tar.xz
icinga-php-thirdparty-upstream.zip
Adding upstream version 0.11.0.upstream/0.11.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/react/socket/src')
-rw-r--r--vendor/react/socket/src/Connection.php187
-rw-r--r--vendor/react/socket/src/ConnectionInterface.php119
-rw-r--r--vendor/react/socket/src/Connector.php236
-rw-r--r--vendor/react/socket/src/ConnectorInterface.php58
-rw-r--r--vendor/react/socket/src/DnsConnector.php117
-rw-r--r--vendor/react/socket/src/FdServer.php212
-rw-r--r--vendor/react/socket/src/FixedUriConnector.php41
-rw-r--r--vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php333
-rw-r--r--vendor/react/socket/src/HappyEyeBallsConnector.php69
-rw-r--r--vendor/react/socket/src/LimitingServer.php203
-rw-r--r--vendor/react/socket/src/SecureConnector.php122
-rw-r--r--vendor/react/socket/src/SecureServer.php206
-rw-r--r--vendor/react/socket/src/Server.php114
-rw-r--r--vendor/react/socket/src/ServerInterface.php151
-rw-r--r--vendor/react/socket/src/SocketServer.php187
-rw-r--r--vendor/react/socket/src/StreamEncryption.php141
-rw-r--r--vendor/react/socket/src/TcpConnector.php159
-rw-r--r--vendor/react/socket/src/TcpServer.php258
-rw-r--r--vendor/react/socket/src/TimeoutConnector.php51
-rw-r--r--vendor/react/socket/src/UnixConnector.php51
-rw-r--r--vendor/react/socket/src/UnixServer.php154
21 files changed, 3169 insertions, 0 deletions
diff --git a/vendor/react/socket/src/Connection.php b/vendor/react/socket/src/Connection.php
new file mode 100644
index 0000000..5e3b00d
--- /dev/null
+++ b/vendor/react/socket/src/Connection.php
@@ -0,0 +1,187 @@
+<?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 < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might
+ // block with 100% CPU usage on fragmented TLS records.
+ // We try to work around this by always consuming the complete receive
+ // buffer at once to avoid stale data in TLS buffers. This is known to
+ // work around high CPU usage for well-behaving peers, but this may
+ // cause very large data chunks for high throughput scenarios. The buggy
+ // behavior can still be triggered due to network I/O buffers or
+ // malicious peers on affected versions, upgrading is highly recommended.
+ // @link https://bugs.php.net/bug.php?id=77390
+ $clearCompleteBuffer = \PHP_VERSION_ID < 70215 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70303);
+
+ // 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()
+ {
+ if (!\is_resource($this->stream)) {
+ return null;
+ }
+
+ return $this->parseAddress(\stream_socket_get_name($this->stream, true));
+ }
+
+ public function getLocalAddress()
+ {
+ if (!\is_resource($this->stream)) {
+ return null;
+ }
+
+ 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); // @codeCoverageIgnore
+ }
+
+ // 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; // @codeCoverageIgnore
+ }
+
+ 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) !== '[') {
+ $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore
+ }
+
+ return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address;
+ }
+}
diff --git a/vendor/react/socket/src/ConnectionInterface.php b/vendor/react/socket/src/ConnectionInterface.php
new file mode 100644
index 0000000..64613b5
--- /dev/null
+++ b/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/vendor/react/socket/src/Connector.php b/vendor/react/socket/src/Connector.php
new file mode 100644
index 0000000..93477bd
--- /dev/null
+++ b/vendor/react/socket/src/Connector.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Config\Config as DnsConfig;
+use React\Dns\Resolver\Factory as DnsFactory;
+use React\Dns\Resolver\ResolverInterface;
+use React\EventLoop\LoopInterface;
+
+/**
+ * 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*
+ * 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.
+ *
+ * @see ConnectorInterface for the base interface
+ */
+final class Connector implements ConnectorInterface
+{
+ private $connectors = array();
+
+ /**
+ * Instantiate new `Connector`
+ *
+ * ```php
+ * $connector = new React\Socket\Connector();
+ * ```
+ *
+ * This class takes two optional arguments for more advanced usage:
+ *
+ * ```php
+ * // constructor signature as of v1.9.0
+ * $connector = new React\Socket\Connector(array $context = [], ?LoopInterface $loop = null);
+ *
+ * // legacy constructor signature before v1.9.0
+ * $connector = new React\Socket\Connector(?LoopInterface $loop = null, array $context = []);
+ * ```
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * @param array|LoopInterface|null $context
+ * @param null|LoopInterface|array $loop
+ * @throws \InvalidArgumentException for invalid arguments
+ */
+ public function __construct($context = array(), $loop = null)
+ {
+ // swap arguments for legacy constructor signature
+ if (($context instanceof LoopInterface || $context === null) && (\func_num_args() <= 1 || \is_array($loop))) {
+ $swap = $loop === null ? array(): $loop;
+ $loop = $context;
+ $context = $swap;
+ }
+
+ if (!\is_array($context) || ($loop !== null && !$loop instanceof LoopInterface)) {
+ throw new \InvalidArgumentException('Expected "array $context" and "?LoopInterface $loop" arguments');
+ }
+
+ // apply default options if not explicitly given
+ $context += array(
+ 'tcp' => true,
+ 'tls' => true,
+ 'unix' => true,
+
+ 'dns' => true,
+ 'timeout' => true,
+ 'happy_eyeballs' => true,
+ );
+
+ if ($context['timeout'] === true) {
+ $context['timeout'] = (float)\ini_get("default_socket_timeout");
+ }
+
+ if ($context['tcp'] instanceof ConnectorInterface) {
+ $tcp = $context['tcp'];
+ } else {
+ $tcp = new TcpConnector(
+ $loop,
+ \is_array($context['tcp']) ? $context['tcp'] : array()
+ );
+ }
+
+ if ($context['dns'] !== false) {
+ if ($context['dns'] instanceof ResolverInterface) {
+ $resolver = $context['dns'];
+ } else {
+ if ($context['dns'] !== true) {
+ $config = $context['dns'];
+ } else {
+ // try to load nameservers from system config or default to Google's public DNS
+ $config = DnsConfig::loadSystemConfigBlocking();
+ if (!$config->nameservers) {
+ $config->nameservers[] = '8.8.8.8'; // @codeCoverageIgnore
+ }
+ }
+
+ $factory = new DnsFactory();
+ $resolver = $factory->createCached(
+ $config,
+ $loop
+ );
+ }
+
+ if ($context['happy_eyeballs'] === true) {
+ $tcp = new HappyEyeBallsConnector($loop, $tcp, $resolver);
+ } else {
+ $tcp = new DnsConnector($tcp, $resolver);
+ }
+ }
+
+ if ($context['tcp'] !== false) {
+ $context['tcp'] = $tcp;
+
+ if ($context['timeout'] !== false) {
+ $context['tcp'] = new TimeoutConnector(
+ $context['tcp'],
+ $context['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tcp'] = $context['tcp'];
+ }
+
+ if ($context['tls'] !== false) {
+ if (!$context['tls'] instanceof ConnectorInterface) {
+ $context['tls'] = new SecureConnector(
+ $tcp,
+ $loop,
+ \is_array($context['tls']) ? $context['tls'] : array()
+ );
+ }
+
+ if ($context['timeout'] !== false) {
+ $context['tls'] = new TimeoutConnector(
+ $context['tls'],
+ $context['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tls'] = $context['tls'];
+ }
+
+ if ($context['unix'] !== false) {
+ if (!$context['unix'] instanceof ConnectorInterface) {
+ $context['unix'] = new UnixConnector($loop);
+ }
+ $this->connectors['unix'] = $context['unix'];
+ }
+ }
+
+ public function connect($uri)
+ {
+ $scheme = 'tcp';
+ if (\strpos($uri, '://') !== false) {
+ $scheme = (string)\substr($uri, 0, \strpos($uri, '://'));
+ }
+
+ if (!isset($this->connectors[$scheme])) {
+ return \React\Promise\reject(new \RuntimeException(
+ 'No connector available for URI scheme "' . $scheme . '" (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ return $this->connectors[$scheme]->connect($uri);
+ }
+
+
+ /**
+ * [internal] Builds on URI from the given URI parts and ip address with original hostname as query
+ *
+ * @param array $parts
+ * @param string $host
+ * @param string $ip
+ * @return string
+ * @internal
+ */
+ public static function uri(array $parts, $host, $ip)
+ {
+ $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 $uri;
+ }
+}
diff --git a/vendor/react/socket/src/ConnectorInterface.php b/vendor/react/socket/src/ConnectorInterface.php
new file mode 100644
index 0000000..3dd78f1
--- /dev/null
+++ b/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 (React\Socket\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/vendor/react/socket/src/DnsConnector.php b/vendor/react/socket/src/DnsConnector.php
new file mode 100644
index 0000000..27fc8f8
--- /dev/null
+++ b/vendor/react/socket/src/DnsConnector.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Resolver\ResolverInterface;
+use React\Promise;
+use React\Promise\CancellablePromiseInterface;
+
+final class DnsConnector implements ConnectorInterface
+{
+ private $connector;
+ private $resolver;
+
+ public function __construct(ConnectorInterface $connector, ResolverInterface $resolver)
+ {
+ $this->connector = $connector;
+ $this->resolver = $resolver;
+ }
+
+ public function connect($uri)
+ {
+ $original = $uri;
+ if (\strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ $parts = \parse_url($uri);
+ if (isset($parts['scheme'])) {
+ unset($parts['scheme']);
+ }
+ } else {
+ $parts = \parse_url($uri);
+ }
+
+ if (!$parts || !isset($parts['host'])) {
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $original . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ $host = \trim($parts['host'], '[]');
+ $connector = $this->connector;
+
+ // skip DNS lookup / URI manipulation if this URI already contains an IP
+ if (@\inet_pton($host) !== false) {
+ return $connector->connect($original);
+ }
+
+ $promise = $this->resolver->resolve($host);
+ $resolved = null;
+
+ return new Promise\Promise(
+ function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host, $parts) {
+ // resolve/reject with result of DNS lookup
+ $promise->then(function ($ip) use (&$promise, &$resolved, $uri, $connector, $host, $parts) {
+ $resolved = $ip;
+
+ return $promise = $connector->connect(
+ Connector::uri($parts, $host, $ip)
+ )->then(null, function (\Exception $e) use ($uri) {
+ if ($e instanceof \RuntimeException) {
+ $message = \preg_replace('/^(Connection to [^ ]+)[&?]hostname=[^ &]+/', '$1', $e->getMessage());
+ $e = new \RuntimeException(
+ 'Connection to ' . $uri . ' failed: ' . $message,
+ $e->getCode(),
+ $e
+ );
+
+ // avoid garbage references by replacing all closures in call stack.
+ // what a lovely piece of code!
+ $r = new \ReflectionProperty('Exception', 'trace');
+ $r->setAccessible(true);
+ $trace = $r->getValue($e);
+
+ // Exception trace arguments are not available on some PHP 7.4 installs
+ // @codeCoverageIgnoreStart
+ foreach ($trace as &$one) {
+ if (isset($one['args'])) {
+ foreach ($one['args'] as &$arg) {
+ if ($arg instanceof \Closure) {
+ $arg = 'Object(' . \get_class($arg) . ')';
+ }
+ }
+ }
+ }
+ // @codeCoverageIgnoreEnd
+ $r->setValue($e, $trace);
+ }
+
+ throw $e;
+ });
+ }, function ($e) use ($uri, $reject) {
+ $reject(new \RuntimeException('Connection to ' . $uri .' failed during DNS lookup: ' . $e->getMessage(), 0, $e));
+ })->then($resolve, $reject);
+ },
+ function ($_, $reject) use (&$promise, &$resolved, $uri) {
+ // cancellation should reject connection attempt
+ // reject DNS resolution with custom reason, otherwise rely on connection cancellation below
+ if ($resolved === null) {
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during DNS lookup (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
+ }
+
+ // (try to) cancel pending DNS lookup / connection attempt
+ if ($promise instanceof CancellablePromiseInterface) {
+ // overwrite callback arguments for PHP7+ only, so they do not show
+ // up in the Exception trace and do not cause a possible cyclic reference.
+ $_ = $reject = null;
+
+ $promise->cancel();
+ $promise = null;
+ }
+ }
+ );
+ }
+}
diff --git a/vendor/react/socket/src/FdServer.php b/vendor/react/socket/src/FdServer.php
new file mode 100644
index 0000000..2c7a6c4
--- /dev/null
+++ b/vendor/react/socket/src/FdServer.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+
+/**
+ * [Internal] The `FdServer` class implements the `ServerInterface` and
+ * is responsible for accepting connections from an existing file descriptor.
+ *
+ * ```php
+ * $socket = new React\Socket\FdServer(3);
+ * ```
+ *
+ * Whenever a client connects, it will emit a `connection` event with a connection
+ * instance implementing `ConnectionInterface`:
+ *
+ * ```php
+ * $socket->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
+ * @internal
+ */
+final class FdServer extends EventEmitter implements ServerInterface
+{
+ private $master;
+ private $loop;
+ private $unix = false;
+ private $listening = false;
+
+ /**
+ * Creates a socket server and starts listening on the given file descriptor
+ *
+ * This starts accepting new incoming connections on the given file descriptor.
+ * See also the `connection event` documented in the `ServerInterface`
+ * for more details.
+ *
+ * ```php
+ * $socket = new React\Socket\FdServer(3);
+ * ```
+ *
+ * If the given FD is invalid or out of range, it will throw an `InvalidArgumentException`:
+ *
+ * ```php
+ * // throws InvalidArgumentException
+ * $socket = new React\Socket\FdServer(-1);
+ * ```
+ *
+ * If the given FD appears to be valid, but listening on it fails (such as
+ * if the FD does not exist or does not refer to a socket server), it will
+ * throw a `RuntimeException`:
+ *
+ * ```php
+ * // throws RuntimeException because FD does not reference a socket server
+ * $socket = new React\Socket\FdServer(0, $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.
+ *
+ * @param int|string $fd FD number such as `3` or as URL in the form of `php://fd/3`
+ * @param ?LoopInterface $loop
+ * @throws \InvalidArgumentException if the listening address is invalid
+ * @throws \RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($fd, LoopInterface $loop = null)
+ {
+ if (\preg_match('#^php://fd/(\d+)$#', $fd, $m)) {
+ $fd = (int) $m[1];
+ }
+ if (!\is_int($fd) || $fd < 0 || $fd >= \PHP_INT_MAX) {
+ throw new \InvalidArgumentException(
+ 'Invalid FD number given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ $this->loop = $loop ?: Loop::get();
+
+ $this->master = @\fopen('php://fd/' . $fd, 'r+');
+ if (false === $this->master) {
+ // Match errstr from PHP's warning message.
+ // fopen(php://fd/3): Failed to open stream: Error duping file descriptor 3; possibly it doesn't exist: [9]: Bad file descriptor
+ $error = \error_get_last();
+ \preg_match('/\[(\d+)\]: (.*)/', $error['message'], $m);
+ $errno = isset($m[1]) ? (int) $m[1] : 0;
+ $errstr = isset($m[2]) ? $m[2] : $error['message'];
+
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
+ }
+
+ $meta = \stream_get_meta_data($this->master);
+ if (!isset($meta['stream_type']) || $meta['stream_type'] !== 'tcp_socket') {
+ \fclose($this->master);
+
+ $errno = \defined('SOCKET_ENOTSOCK') ? \SOCKET_ENOTSOCK : 88;
+ $errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Not a socket';
+
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (ENOTSOCK)',
+ $errno
+ );
+ }
+
+ // Socket should not have a peer address if this is a listening socket.
+ // Looks like this work-around is the closest we can get because PHP doesn't expose SO_ACCEPTCONN even with ext-sockets.
+ if (\stream_socket_get_name($this->master, true) !== false) {
+ \fclose($this->master);
+
+ $errno = \defined('SOCKET_EISCONN') ? \SOCKET_EISCONN : 106;
+ $errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Socket is connected';
+
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (EISCONN)',
+ $errno
+ );
+ }
+
+ // Assume this is a Unix domain socket (UDS) when its listening address doesn't parse as a valid URL with a port.
+ // Looks like this work-around is the closest we can get because PHP doesn't expose SO_DOMAIN even with ext-sockets.
+ $this->unix = \parse_url($this->getAddress(), \PHP_URL_PORT) === false;
+
+ \stream_set_blocking($this->master, false);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!\is_resource($this->master)) {
+ return null;
+ }
+
+ $address = \stream_socket_get_name($this->master, false);
+
+ if ($this->unix === true) {
+ 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) !== '[') {
+ $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore
+ }
+
+ 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) {
+ try {
+ $newSocket = SocketServer::accept($master);
+ } catch (\RuntimeException $e) {
+ $that->emit('error', array($e));
+ 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 = $this->unix;
+
+ $this->emit('connection', array($connection));
+ }
+}
diff --git a/vendor/react/socket/src/FixedUriConnector.php b/vendor/react/socket/src/FixedUriConnector.php
new file mode 100644
index 0000000..f83241d
--- /dev/null
+++ b/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 React\Socket\FixedUriConnector(
+ * 'unix:///var/run/docker.sock',
+ * new React\Socket\UnixConnector()
+ * );
+ *
+ * // 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/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php b/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php
new file mode 100644
index 0000000..6bd0716
--- /dev/null
+++ b/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php
@@ -0,0 +1,333 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Model\Message;
+use React\Dns\Resolver\ResolverInterface;
+use React\EventLoop\LoopInterface;
+use React\EventLoop\TimerInterface;
+use React\Promise;
+use React\Promise\CancellablePromiseInterface;
+
+/**
+ * @internal
+ */
+final class HappyEyeBallsConnectionBuilder
+{
+ /**
+ * As long as we haven't connected yet keep popping an IP address of the connect queue until one of them
+ * succeeds or they all fail. We will wait 100ms between connection attempts as per RFC.
+ *
+ * @link https://tools.ietf.org/html/rfc8305#section-5
+ */
+ const CONNECTION_ATTEMPT_DELAY = 0.1;
+
+ /**
+ * Delay `A` lookup by 50ms sending out connection to IPv4 addresses when IPv6 records haven't
+ * resolved yet as per RFC.
+ *
+ * @link https://tools.ietf.org/html/rfc8305#section-3
+ */
+ const RESOLUTION_DELAY = 0.05;
+
+ public $loop;
+ public $connector;
+ public $resolver;
+ public $uri;
+ public $host;
+ public $resolved = array(
+ Message::TYPE_A => false,
+ Message::TYPE_AAAA => false,
+ );
+ public $resolverPromises = array();
+ public $connectionPromises = array();
+ public $connectQueue = array();
+ public $nextAttemptTimer;
+ public $parts;
+ public $ipsCount = 0;
+ public $failureCount = 0;
+ public $resolve;
+ public $reject;
+
+ public $lastErrorFamily;
+ public $lastError6;
+ public $lastError4;
+
+ public function __construct(LoopInterface $loop, ConnectorInterface $connector, ResolverInterface $resolver, $uri, $host, $parts)
+ {
+ $this->loop = $loop;
+ $this->connector = $connector;
+ $this->resolver = $resolver;
+ $this->uri = $uri;
+ $this->host = $host;
+ $this->parts = $parts;
+ }
+
+ public function connect()
+ {
+ $timer = null;
+ $that = $this;
+ return new Promise\Promise(function ($resolve, $reject) use ($that, &$timer) {
+ $lookupResolve = function ($type) use ($that, $resolve, $reject) {
+ return function (array $ips) use ($that, $type, $resolve, $reject) {
+ unset($that->resolverPromises[$type]);
+ $that->resolved[$type] = true;
+
+ $that->mixIpsIntoConnectQueue($ips);
+
+ // start next connection attempt if not already awaiting next
+ if ($that->nextAttemptTimer === null && $that->connectQueue) {
+ $that->check($resolve, $reject);
+ }
+ };
+ };
+
+ $that->resolverPromises[Message::TYPE_AAAA] = $that->resolve(Message::TYPE_AAAA, $reject)->then($lookupResolve(Message::TYPE_AAAA));
+ $that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function (array $ips) use ($that, &$timer) {
+ // happy path: IPv6 has resolved already (or could not resolve), continue with IPv4 addresses
+ if ($that->resolved[Message::TYPE_AAAA] === true || !$ips) {
+ return $ips;
+ }
+
+ // Otherwise delay processing IPv4 lookup until short timer passes or IPv6 resolves in the meantime
+ $deferred = new Promise\Deferred();
+ $timer = $that->loop->addTimer($that::RESOLUTION_DELAY, function () use ($deferred, $ips) {
+ $deferred->resolve($ips);
+ });
+
+ $that->resolverPromises[Message::TYPE_AAAA]->then(function () use ($that, $timer, $deferred, $ips) {
+ $that->loop->cancelTimer($timer);
+ $deferred->resolve($ips);
+ });
+
+ return $deferred->promise();
+ })->then($lookupResolve(Message::TYPE_A));
+ }, function ($_, $reject) use ($that, &$timer) {
+ $reject(new \RuntimeException(
+ 'Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '') . ' (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
+ $_ = $reject = null;
+
+ $that->cleanUp();
+ if ($timer instanceof TimerInterface) {
+ $that->loop->cancelTimer($timer);
+ }
+ });
+ }
+
+ /**
+ * @internal
+ * @param int $type DNS query type
+ * @param callable $reject
+ * @return \React\Promise\PromiseInterface<string[]> Returns a promise that
+ * always resolves with a list of IP addresses on success or an empty
+ * list on error.
+ */
+ public function resolve($type, $reject)
+ {
+ $that = $this;
+ return $that->resolver->resolveAll($that->host, $type)->then(null, function (\Exception $e) use ($type, $reject, $that) {
+ unset($that->resolverPromises[$type]);
+ $that->resolved[$type] = true;
+
+ if ($type === Message::TYPE_A) {
+ $that->lastError4 = $e->getMessage();
+ $that->lastErrorFamily = 4;
+ } else {
+ $that->lastError6 = $e->getMessage();
+ $that->lastErrorFamily = 6;
+ }
+
+ // cancel next attempt timer when there are no more IPs to connect to anymore
+ if ($that->nextAttemptTimer !== null && !$that->connectQueue) {
+ $that->loop->cancelTimer($that->nextAttemptTimer);
+ $that->nextAttemptTimer = null;
+ }
+
+ if ($that->hasBeenResolved() && $that->ipsCount === 0) {
+ $reject(new \RuntimeException(
+ $that->error(),
+ 0,
+ $e
+ ));
+ }
+
+ // Exception already handled above, so don't throw an unhandled rejection here
+ return array();
+ });
+ }
+
+ /**
+ * @internal
+ */
+ public function check($resolve, $reject)
+ {
+ $ip = \array_shift($this->connectQueue);
+
+ // start connection attempt and remember array position to later unset again
+ $this->connectionPromises[] = $this->attemptConnection($ip);
+ \end($this->connectionPromises);
+ $index = \key($this->connectionPromises);
+
+ $that = $this;
+ $that->connectionPromises[$index]->then(function ($connection) use ($that, $index, $resolve) {
+ unset($that->connectionPromises[$index]);
+
+ $that->cleanUp();
+
+ $resolve($connection);
+ }, function (\Exception $e) use ($that, $index, $ip, $resolve, $reject) {
+ unset($that->connectionPromises[$index]);
+
+ $that->failureCount++;
+
+ $message = \preg_replace('/^(Connection to [^ ]+)[&?]hostname=[^ &]+/', '$1', $e->getMessage());
+ if (\strpos($ip, ':') === false) {
+ $that->lastError4 = $message;
+ $that->lastErrorFamily = 4;
+ } else {
+ $that->lastError6 = $message;
+ $that->lastErrorFamily = 6;
+ }
+
+ // start next connection attempt immediately on error
+ if ($that->connectQueue) {
+ if ($that->nextAttemptTimer !== null) {
+ $that->loop->cancelTimer($that->nextAttemptTimer);
+ $that->nextAttemptTimer = null;
+ }
+
+ $that->check($resolve, $reject);
+ }
+
+ if ($that->hasBeenResolved() === false) {
+ return;
+ }
+
+ if ($that->ipsCount === $that->failureCount) {
+ $that->cleanUp();
+
+ $reject(new \RuntimeException(
+ $that->error(),
+ $e->getCode(),
+ $e
+ ));
+ }
+ });
+
+ // Allow next connection attempt in 100ms: https://tools.ietf.org/html/rfc8305#section-5
+ // Only start timer when more IPs are queued or when DNS query is still pending (might add more IPs)
+ if ($this->nextAttemptTimer === null && (\count($this->connectQueue) > 0 || $this->resolved[Message::TYPE_A] === false || $this->resolved[Message::TYPE_AAAA] === false)) {
+ $this->nextAttemptTimer = $this->loop->addTimer(self::CONNECTION_ATTEMPT_DELAY, function () use ($that, $resolve, $reject) {
+ $that->nextAttemptTimer = null;
+
+ if ($that->connectQueue) {
+ $that->check($resolve, $reject);
+ }
+ });
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function attemptConnection($ip)
+ {
+ $uri = Connector::uri($this->parts, $this->host, $ip);
+
+ return $this->connector->connect($uri);
+ }
+
+ /**
+ * @internal
+ */
+ public function cleanUp()
+ {
+ // clear list of outstanding IPs to avoid creating new connections
+ $this->connectQueue = array();
+
+ foreach ($this->connectionPromises as $connectionPromise) {
+ if ($connectionPromise instanceof CancellablePromiseInterface) {
+ $connectionPromise->cancel();
+ }
+ }
+
+ foreach ($this->resolverPromises as $resolverPromise) {
+ if ($resolverPromise instanceof CancellablePromiseInterface) {
+ $resolverPromise->cancel();
+ }
+ }
+
+ if ($this->nextAttemptTimer instanceof TimerInterface) {
+ $this->loop->cancelTimer($this->nextAttemptTimer);
+ $this->nextAttemptTimer = null;
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function hasBeenResolved()
+ {
+ foreach ($this->resolved as $typeHasBeenResolved) {
+ if ($typeHasBeenResolved === false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Mixes an array of IP addresses into the connect queue in such a way they alternate when attempting to connect.
+ * The goal behind it is first attempt to connect to IPv6, then to IPv4, then to IPv6 again until one of those
+ * attempts succeeds.
+ *
+ * @link https://tools.ietf.org/html/rfc8305#section-4
+ *
+ * @internal
+ */
+ public function mixIpsIntoConnectQueue(array $ips)
+ {
+ \shuffle($ips);
+ $this->ipsCount += \count($ips);
+ $connectQueueStash = $this->connectQueue;
+ $this->connectQueue = array();
+ while (\count($connectQueueStash) > 0 || \count($ips) > 0) {
+ if (\count($ips) > 0) {
+ $this->connectQueue[] = \array_shift($ips);
+ }
+ if (\count($connectQueueStash) > 0) {
+ $this->connectQueue[] = \array_shift($connectQueueStash);
+ }
+ }
+ }
+
+ /**
+ * @internal
+ * @return string
+ */
+ public function error()
+ {
+ if ($this->lastError4 === $this->lastError6) {
+ $message = $this->lastError6;
+ } elseif ($this->lastErrorFamily === 6) {
+ $message = 'Last error for IPv6: ' . $this->lastError6 . '. Previous error for IPv4: ' . $this->lastError4;
+ } else {
+ $message = 'Last error for IPv4: ' . $this->lastError4 . '. Previous error for IPv6: ' . $this->lastError6;
+ }
+
+ if ($this->hasBeenResolved() && $this->ipsCount === 0) {
+ if ($this->lastError6 === $this->lastError4) {
+ $message = ' during DNS lookup: ' . $this->lastError6;
+ } else {
+ $message = ' during DNS lookup. ' . $message;
+ }
+ } else {
+ $message = ': ' . $message;
+ }
+
+ return 'Connection to ' . $this->uri . ' failed' . $message;
+ }
+}
diff --git a/vendor/react/socket/src/HappyEyeBallsConnector.php b/vendor/react/socket/src/HappyEyeBallsConnector.php
new file mode 100644
index 0000000..4b04f77
--- /dev/null
+++ b/vendor/react/socket/src/HappyEyeBallsConnector.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace React\Socket;
+
+use React\Dns\Resolver\ResolverInterface;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use React\Promise;
+
+final class HappyEyeBallsConnector implements ConnectorInterface
+{
+ private $loop;
+ private $connector;
+ private $resolver;
+
+ public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, ResolverInterface $resolver = null)
+ {
+ // $connector and $resolver arguments are actually required, marked
+ // optional for technical reasons only. Nullable $loop without default
+ // requires PHP 7.1, null default is also supported in legacy PHP
+ // versions, but required parameters are not allowed after arguments
+ // with null default. Mark all parameters optional and check accordingly.
+ if ($connector === null || $resolver === null) {
+ throw new \InvalidArgumentException('Missing required $connector or $resolver argument');
+ }
+
+ $this->loop = $loop ?: Loop::get();
+ $this->connector = $connector;
+ $this->resolver = $resolver;
+ }
+
+ public function connect($uri)
+ {
+ $original = $uri;
+ if (\strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ $parts = \parse_url($uri);
+ if (isset($parts['scheme'])) {
+ unset($parts['scheme']);
+ }
+ } else {
+ $parts = \parse_url($uri);
+ }
+
+ if (!$parts || !isset($parts['host'])) {
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $original . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ $host = \trim($parts['host'], '[]');
+
+ // skip DNS lookup / URI manipulation if this URI already contains an IP
+ if (@\inet_pton($host) !== false) {
+ return $this->connector->connect($original);
+ }
+
+ $builder = new HappyEyeBallsConnectionBuilder(
+ $this->loop,
+ $this->connector,
+ $this->resolver,
+ $uri,
+ $host,
+ $parts
+ );
+ return $builder->connect();
+ }
+}
diff --git a/vendor/react/socket/src/LimitingServer.php b/vendor/react/socket/src/LimitingServer.php
new file mode 100644
index 0000000..d19000b
--- /dev/null
+++ b/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 React\Socket\LimitingServer($server, 100);
+ * $server->on('connection', function (React\Socket\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 React\Socket\LimitingServer($server, 100);
+ * $server->on('connection', function (React\Socket\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 React\Socket\LimitingServer($server, 100, true);
+ * $server->on('connection', function (React\Socket\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/vendor/react/socket/src/SecureConnector.php b/vendor/react/socket/src/SecureConnector.php
new file mode 100644
index 0000000..03c6e36
--- /dev/null
+++ b/vendor/react/socket/src/SecureConnector.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\Loop;
+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 = null, array $context = array())
+ {
+ $this->connector = $connector;
+ $this->streamEncryption = new StreamEncryption($loop ?: Loop::get(), 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 (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ $context = $this->context;
+ $encryption = $this->streamEncryption;
+ $connected = false;
+ $promise = $this->connector->connect(
+ \str_replace('tls://', '', $uri)
+ )->then(function (ConnectionInterface $connection) use ($context, $encryption, $uri, &$promise, &$connected) {
+ // (unencrypted) TCP/IP connection succeeded
+ $connected = true;
+
+ 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 $promise = $encryption->enable($connection)->then(null, function ($error) use ($connection, $uri) {
+ // establishing encryption failed => close invalid connection and return error
+ $connection->close();
+
+ throw new \RuntimeException(
+ 'Connection to ' . $uri . ' failed during TLS handshake: ' . $error->getMessage(),
+ $error->getCode()
+ );
+ });
+ }, function (\Exception $e) use ($uri) {
+ if ($e instanceof \RuntimeException) {
+ $message = \preg_replace('/^Connection to [^ ]+/', '', $e->getMessage());
+ $e = new \RuntimeException(
+ 'Connection to ' . $uri . $message,
+ $e->getCode(),
+ $e
+ );
+
+ // avoid garbage references by replacing all closures in call stack.
+ // what a lovely piece of code!
+ $r = new \ReflectionProperty('Exception', 'trace');
+ $r->setAccessible(true);
+ $trace = $r->getValue($e);
+
+ // Exception trace arguments are not available on some PHP 7.4 installs
+ // @codeCoverageIgnoreStart
+ foreach ($trace as &$one) {
+ if (isset($one['args'])) {
+ foreach ($one['args'] as &$arg) {
+ if ($arg instanceof \Closure) {
+ $arg = 'Object(' . \get_class($arg) . ')';
+ }
+ }
+ }
+ }
+ // @codeCoverageIgnoreEnd
+ $r->setValue($e, $trace);
+ }
+
+ throw $e;
+ });
+
+ return new \React\Promise\Promise(
+ function ($resolve, $reject) use ($promise) {
+ $promise->then($resolve, $reject);
+ },
+ function ($_, $reject) use (&$promise, $uri, &$connected) {
+ if ($connected) {
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during TLS handshake (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
+ }
+
+ $promise->cancel();
+ $promise = null;
+ }
+ );
+ }
+}
diff --git a/vendor/react/socket/src/SecureServer.php b/vendor/react/socket/src/SecureServer.php
new file mode 100644
index 0000000..d0525c9
--- /dev/null
+++ b/vendor/react/socket/src/SecureServer.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+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 React\Socket\TcpServer(8000);
+ * $server = new React\Socket\SecureServer($server, null, 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 (React\Socket\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 React\Socket\TcpServer(8000);
+ * $server = new React\Socket\SecureServer($server, null, 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 React\Socket\TcpServer(8000);
+ * $server = new React\Socket\SecureServer($server, null, 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.
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * 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 https://www.php.net/manual/en/context.ssl.php for TLS context options
+ */
+ public function __construct(ServerInterface $tcp, LoopInterface $loop = null, array $context = array())
+ {
+ 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 ?: Loop::get());
+ $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->close();
+ return;
+ }
+
+ foreach ($this->context as $name => $value) {
+ \stream_context_set_option($connection->stream, 'ssl', $name, $value);
+ }
+
+ // get remote address before starting TLS handshake in case connection closes during handshake
+ $remote = $connection->getRemoteAddress();
+ $that = $this;
+
+ $this->encryption->enable($connection)->then(
+ function ($conn) use ($that) {
+ $that->emit('connection', array($conn));
+ },
+ function ($error) use ($that, $connection, $remote) {
+ $error = new \RuntimeException(
+ 'Connection from ' . $remote . ' failed during TLS handshake: ' . $error->getMessage(),
+ $error->getCode()
+ );
+
+ $that->emit('error', array($error));
+ $connection->close();
+ }
+ );
+ }
+}
diff --git a/vendor/react/socket/src/Server.php b/vendor/react/socket/src/Server.php
new file mode 100644
index 0000000..7d4111e
--- /dev/null
+++ b/vendor/react/socket/src/Server.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use Exception;
+
+/**
+ * @deprecated 1.9.0 See `SocketServer` instead
+ * @see SocketServer
+ */
+final class Server extends EventEmitter implements ServerInterface
+{
+ private $server;
+
+ /**
+ * [Deprecated] `Server`
+ *
+ * This class exists for BC reasons only and should not be used anymore.
+ *
+ * ```php
+ * // deprecated
+ * $socket = new React\Socket\Server(0);
+ * $socket = new React\Socket\Server('127.0.0.1:8000');
+ * $socket = new React\Socket\Server('127.0.0.1:8000', null, $context);
+ * $socket = new React\Socket\Server('127.0.0.1:8000', $loop, $context);
+ *
+ * // new
+ * $socket = new React\Socket\SocketServer('127.0.0.1:0');
+ * $socket = new React\Socket\SocketServer('127.0.0.1:8000');
+ * $socket = new React\Socket\SocketServer('127.0.0.1:8000', $context);
+ * $socket = new React\Socket\SocketServer('127.0.0.1:8000', $context, $loop);
+ * ```
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * 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.
+ *
+ * @param string|int $uri
+ * @param LoopInterface $loop
+ * @param array $context
+ * @deprecated 1.9.0 See `SocketServer` instead
+ * @see SocketServer
+ */
+ public function __construct($uri, LoopInterface $loop = null, array $context = array())
+ {
+ $loop = $loop ?: Loop::get();
+
+ // 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/vendor/react/socket/src/ServerInterface.php b/vendor/react/socket/src/ServerInterface.php
new file mode 100644
index 0000000..aa79fa1
--- /dev/null
+++ b/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
+ * $socket->on('connection', function (React\Socket\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
+ * $socket->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 = $socket->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 = $socket->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
+ * $socket->pause();
+ *
+ * $socket->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
+ * $socket->pause();
+ *
+ * Loop::addTimer(1.0, function () use ($socket) {
+ * $socket->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/vendor/react/socket/src/SocketServer.php b/vendor/react/socket/src/SocketServer.php
new file mode 100644
index 0000000..2ea03ba
--- /dev/null
+++ b/vendor/react/socket/src/SocketServer.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\LoopInterface;
+
+final class SocketServer extends EventEmitter implements ServerInterface
+{
+ private $server;
+
+ /**
+ * The `SocketServer` class is 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.
+ *
+ * ```php
+ * $socket = new React\Socket\SocketServer('127.0.0.1:0');
+ * $socket = new React\Socket\SocketServer('127.0.0.1:8000');
+ * $socket = new React\Socket\SocketServer('127.0.0.1:8000', $context);
+ * ```
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * @param string $uri
+ * @param array $context
+ * @param ?LoopInterface $loop
+ * @throws \InvalidArgumentException if the listening address is invalid
+ * @throws \RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($uri, array $context = array(), LoopInterface $loop = null)
+ {
+ // 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']);
+ } elseif ($scheme === 'php') {
+ $server = new FdServer($uri, $loop);
+ } else {
+ if (preg_match('#^(?:\w+://)?\d+$#', $uri)) {
+ throw new \InvalidArgumentException(
+ 'Invalid URI given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ $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();
+ }
+
+ /**
+ * [internal] Internal helper method to accept new connection from given server socket
+ *
+ * @param resource $socket server socket to accept connection from
+ * @return resource new client socket if any
+ * @throws \RuntimeException if accepting fails
+ * @internal
+ */
+ public static function accept($socket)
+ {
+ $newSocket = @\stream_socket_accept($socket, 0);
+
+ if (false === $newSocket) {
+ // Match errstr from PHP's warning message.
+ // stream_socket_accept(): accept failed: Connection timed out
+ $error = \error_get_last();
+ $errstr = \preg_replace('#.*: #', '', $error['message']);
+ $errno = self::errno($errstr);
+
+ throw new \RuntimeException(
+ 'Unable to accept new connection: ' . $errstr . self::errconst($errno),
+ $errno
+ );
+ }
+
+ return $newSocket;
+ }
+
+ /**
+ * [Internal] Returns errno value for given errstr
+ *
+ * The errno and errstr values describes the type of error that has been
+ * encountered. This method tries to look up the given errstr and find a
+ * matching errno value which can be useful to provide more context to error
+ * messages. It goes through the list of known errno constants when
+ * ext-sockets is available to find an errno matching the given errstr.
+ *
+ * @param string $errstr
+ * @return int errno value (e.g. value of `SOCKET_ECONNREFUSED`) or 0 if not found
+ * @internal
+ * @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
+ * @codeCoverageIgnore
+ */
+ public static function errno($errstr)
+ {
+ if (\function_exists('socket_strerror')) {
+ foreach (\get_defined_constants(false) as $name => $value) {
+ if (\strpos($name, 'SOCKET_E') === 0 && \socket_strerror($value) === $errstr) {
+ return $value;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * [Internal] Returns errno constant name for given errno value
+ *
+ * The errno value describes the type of error that has been encountered.
+ * This method tries to look up the given errno value and find a matching
+ * errno constant name which can be useful to provide more context and more
+ * descriptive error messages. It goes through the list of known errno
+ * constants when ext-sockets is available to find the matching errno
+ * constant name.
+ *
+ * Because this method is used to append more context to error messages, the
+ * constant name will be prefixed with a space and put between parenthesis
+ * when found.
+ *
+ * @param int $errno
+ * @return string e.g. ` (ECONNREFUSED)` or empty string if no matching const for the given errno could be found
+ * @internal
+ * @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
+ * @codeCoverageIgnore
+ */
+ public static function errconst($errno)
+ {
+ if (\function_exists('socket_strerror')) {
+ foreach (\get_defined_constants(false) as $name => $value) {
+ if ($value === $errno && \strpos($name, 'SOCKET_E') === 0) {
+ return ' (' . \substr($name, 7) . ')';
+ }
+ }
+ }
+
+ return '';
+ }
+}
diff --git a/vendor/react/socket/src/StreamEncryption.php b/vendor/react/socket/src/StreamEncryption.php
new file mode 100644
index 0000000..4aa7fca
--- /dev/null
+++ b/vendor/react/socket/src/StreamEncryption.php
@@ -0,0 +1,141 @@
+<?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;
+
+ public function __construct(LoopInterface $loop, $server = true)
+ {
+ $this->loop = $loop;
+ $this->server = $server;
+
+ // support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3.
+ // As of PHP 7.2+ the main crypto method constant includes all TLS versions.
+ // As of PHP 5.6+ the crypto method is a bitmask, so we explicitly include all TLS versions.
+ // For legacy PHP < 5.6 the crypto method is a single value only and this constant includes all TLS versions.
+ // @link https://3v4l.org/9PSST
+ if ($server) {
+ $this->method = \STREAM_CRYPTO_METHOD_TLS_SERVER;
+
+ if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) {
+ $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER; // @codeCoverageIgnore
+ }
+ } else {
+ $this->method = \STREAM_CRYPTO_METHOD_TLS_CLIENT;
+
+ if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) {
+ $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; // @codeCoverageIgnore
+ }
+ }
+ }
+
+ public function enable(Connection $stream)
+ {
+ return $this->toggle($stream, true);
+ }
+
+ 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
+
+ // cancelling this leaves this stream in an inconsistent state…
+ $deferred = new Deferred(function () {
+ throw new \RuntimeException();
+ });
+
+ // 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)
+ {
+ $error = null;
+ \set_error_handler(function ($_, $errstr) use (&$error) {
+ $error = \str_replace(array("\r", "\n"), ' ', $errstr);
+
+ // remove useless function name from error message
+ if (($pos = \strpos($error, "): ")) !== false) {
+ $error = \substr($error, $pos + 3);
+ }
+ });
+
+ $result = \stream_socket_enable_crypto($socket, $toggle, $method);
+
+ \restore_error_handler();
+
+ if (true === $result) {
+ $deferred->resolve();
+ } else if (false === $result) {
+ // overwrite callback arguments for PHP7+ only, so they do not show
+ // up in the Exception trace and do not cause a possible cyclic reference.
+ $d = $deferred;
+ $deferred = null;
+
+ if (\feof($socket) || $error === null) {
+ // EOF or failed without error => connection closed during handshake
+ $d->reject(new \UnexpectedValueException(
+ 'Connection lost during TLS handshake (ECONNRESET)',
+ \defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 104
+ ));
+ } else {
+ // handshake failed with error message
+ $d->reject(new \UnexpectedValueException(
+ $error
+ ));
+ }
+ } else {
+ // need more data, will retry
+ }
+ }
+}
diff --git a/vendor/react/socket/src/TcpConnector.php b/vendor/react/socket/src/TcpConnector.php
new file mode 100644
index 0000000..a4d3b5b
--- /dev/null
+++ b/vendor/react/socket/src/TcpConnector.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\Loop;
+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 = null, array $context = array())
+ {
+ $this->loop = $loop ?: Loop::get();
+ $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 (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ $ip = \trim($parts['host'], '[]');
+ if (@\inet_pton($ip) === false) {
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ // 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.
+ // @codeCoverageIgnoreStart
+ if (\PHP_VERSION_ID < 50600) {
+ $context['ssl'] += array(
+ 'SNI_server_name' => $args['hostname'],
+ 'CN_match' => $args['hostname']
+ );
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ // 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'];
+
+ $stream = @\stream_socket_client(
+ $remote,
+ $errno,
+ $errstr,
+ 0,
+ \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT,
+ \stream_context_create($context)
+ );
+
+ if (false === $stream) {
+ return Promise\reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ ));
+ }
+
+ // wait for connection
+ $loop = $this->loop;
+ return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream, $uri) {
+ $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject, $uri) {
+ $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)) {
+ // If we reach this point, we know the connection is dead, but we don't know the underlying error condition.
+ // @codeCoverageIgnoreStart
+ if (\function_exists('socket_import_stream')) {
+ // actual socket errno and errstr can be retrieved with ext-sockets on PHP 5.4+
+ $socket = \socket_import_stream($stream);
+ $errno = \socket_get_option($socket, \SOL_SOCKET, \SO_ERROR);
+ $errstr = \socket_strerror($errno);
+ } elseif (\PHP_OS === 'Linux') {
+ // Linux reports socket errno and errstr again when trying to write to the dead socket.
+ // Suppress error reporting to get error message below and close dead socket before rejecting.
+ // This is only known to work on Linux, Mac and Windows are known to not support this.
+ @\fwrite($stream, \PHP_EOL);
+ $error = \error_get_last();
+
+ // fwrite(): send of 2 bytes failed with errno=111 Connection refused
+ \preg_match('/errno=(\d+) (.+)/', $error['message'], $m);
+ $errno = isset($m[1]) ? (int) $m[1] : 0;
+ $errstr = isset($m[2]) ? $m[2] : $error['message'];
+ } else {
+ // Not on Linux and ext-sockets not available? Too bad.
+ $errno = \defined('SOCKET_ECONNREFUSED') ? \SOCKET_ECONNREFUSED : 111;
+ $errstr = 'Connection refused?';
+ }
+ // @codeCoverageIgnoreEnd
+
+ \fclose($stream);
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ ));
+ } else {
+ $resolve(new Connection($stream, $loop));
+ }
+ });
+ }, function () use ($loop, $stream, $uri) {
+ $loop->removeWriteStream($stream);
+ \fclose($stream);
+
+ // @codeCoverageIgnoreStart
+ // legacy PHP 5.3 sometimes requires a second close call (see tests)
+ if (\PHP_VERSION_ID < 50400 && \is_resource($stream)) {
+ \fclose($stream);
+ }
+ // @codeCoverageIgnoreEnd
+
+ throw new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during TCP/IP handshake (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ );
+ });
+ }
+}
diff --git a/vendor/react/socket/src/TcpServer.php b/vendor/react/socket/src/TcpServer.php
new file mode 100644
index 0000000..442af70
--- /dev/null
+++ b/vendor/react/socket/src/TcpServer.php
@@ -0,0 +1,258 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+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 React\Socket\TcpServer(8080);
+ * ```
+ *
+ * Whenever a client connects, it will emit a `connection` event with a connection
+ * instance implementing `ConnectionInterface`:
+ *
+ * ```php
+ * $server->on('connection', function (React\Socket\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 React\Socket\TcpServer(8080);
+ * ```
+ *
+ * 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 React\Socket\TcpServer(0);
+ * $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 React\Socket\TcpServer('192.168.0.1:8080');
+ * ```
+ *
+ * If you want to listen on an IPv6 address, you MUST enclose the host in square
+ * brackets:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer('[::1]:8080');
+ * ```
+ *
+ * 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 React\Socket\TcpServer('127.0.0.1');
+ * ```
+ *
+ * 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 React\Socket\TcpServer(8080);
+ *
+ * // throws RuntimeException because port is already in use
+ * $second = new React\Socket\TcpServer(8080);
+ * ```
+ *
+ * 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.
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php)
+ * for the underlying stream socket resource like this:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer('[::1]:8080', null, array(
+ * 'backlog' => 200,
+ * 'so_reuseport' => true,
+ * 'ipv6_v6only' => true
+ * ));
+ * ```
+ *
+ * Note that available [socket context options](https://www.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.
+ * The `backlog` context option defaults to `511` unless given explicitly.
+ *
+ * @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 = null, array $context = array())
+ {
+ $this->loop = $loop ?: Loop::get();
+
+ // 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 (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ if (@\inet_pton(\trim($parts['host'], '[]')) === false) {
+ throw new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ $this->master = @\stream_socket_server(
+ $uri,
+ $errno,
+ $errstr,
+ \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
+ \stream_context_create(array('socket' => $context + array('backlog' => 511)))
+ );
+ if (false === $this->master) {
+ if ($errno === 0) {
+ // PHP does not seem to report errno, so match errno from errstr
+ // @link https://3v4l.org/3qOBl
+ $errno = SocketServer::errno($errstr);
+ }
+
+ throw new \RuntimeException(
+ 'Failed to listen on "' . $uri . '": ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
+ }
+ \stream_set_blocking($this->master, false);
+
+ $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) !== '[') {
+ $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore
+ }
+
+ 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) {
+ try {
+ $newSocket = SocketServer::accept($master);
+ } catch (\RuntimeException $e) {
+ $that->emit('error', array($e));
+ 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/vendor/react/socket/src/TimeoutConnector.php b/vendor/react/socket/src/TimeoutConnector.php
new file mode 100644
index 0000000..332369f
--- /dev/null
+++ b/vendor/react/socket/src/TimeoutConnector.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use React\Promise\Timer;
+use React\Promise\Timer\TimeoutException;
+
+final class TimeoutConnector implements ConnectorInterface
+{
+ private $connector;
+ private $timeout;
+ private $loop;
+
+ public function __construct(ConnectorInterface $connector, $timeout, LoopInterface $loop = null)
+ {
+ $this->connector = $connector;
+ $this->timeout = $timeout;
+ $this->loop = $loop ?: Loop::get();
+ }
+
+ public function connect($uri)
+ {
+ return Timer\timeout($this->connector->connect($uri), $this->timeout, $this->loop)->then(null, self::handler($uri));
+ }
+
+ /**
+ * Creates a static rejection handler that reports a proper error message in case of a timeout.
+ *
+ * This uses a private static helper method to ensure this closure is not
+ * bound to this instance and the exception trace does not include a
+ * reference to this instance and its connector stack as a result.
+ *
+ * @param string $uri
+ * @return callable
+ */
+ private static function handler($uri)
+ {
+ return function (\Exception $e) use ($uri) {
+ if ($e instanceof TimeoutException) {
+ throw new \RuntimeException(
+ 'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds (ETIMEDOUT)',
+ \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110
+ );
+ }
+
+ throw $e;
+ };
+ }
+}
diff --git a/vendor/react/socket/src/UnixConnector.php b/vendor/react/socket/src/UnixConnector.php
new file mode 100644
index 0000000..513fb51
--- /dev/null
+++ b/vendor/react/socket/src/UnixConnector.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace React\Socket;
+
+use React\EventLoop\Loop;
+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 = null)
+ {
+ $this->loop = $loop ?: Loop::get();
+ }
+
+ 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 (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
+ }
+
+ $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 . SocketServer::errconst($errno),
+ $errno
+ ));
+ }
+
+ $connection = new Connection($resource, $this->loop);
+ $connection->unix = true;
+
+ return Promise\resolve($connection);
+ }
+}
diff --git a/vendor/react/socket/src/UnixServer.php b/vendor/react/socket/src/UnixServer.php
new file mode 100644
index 0000000..668e8cb
--- /dev/null
+++ b/vendor/react/socket/src/UnixServer.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+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 React\Socket\UnixServer('unix:///tmp/app.sock');
+ * ```
+ *
+ * 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 React\Socket\UnixServer('unix:///tmp/app.sock');
+ * ```
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * @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 = null, array $context = array())
+ {
+ $this->loop = $loop ?: Loop::get();
+
+ if (\strpos($path, '://') === false) {
+ $path = 'unix://' . $path;
+ } elseif (\substr($path, 0, 7) !== 'unix://') {
+ throw new \InvalidArgumentException(
+ 'Given URI "' . $path . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ $this->master = @\stream_socket_server(
+ $path,
+ $errno,
+ $errstr,
+ \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
+ \stream_context_create(array('socket' => $context))
+ );
+ if (false === $this->master) {
+ // PHP does not seem to report errno/errstr for Unix domain sockets (UDS) right now.
+ // This only applies to UDS server sockets, see also https://3v4l.org/NAhpr.
+ // Parse PHP warning message containing unknown error, HHVM reports proper info at least.
+ if ($errno === 0 && $errstr === '') {
+ $error = \error_get_last();
+ if (\preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error['message'], $match)) {
+ $errstr = isset($match[3]) ? $match['3'] : $match[1];
+ $errno = isset($match[2]) ? (int)$match[2] : 0;
+ }
+ }
+
+ throw new \RuntimeException(
+ 'Failed to listen on Unix domain socket "' . $path . '": ' . $errstr . SocketServer::errconst($errno),
+ $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) {
+ try {
+ $newSocket = SocketServer::accept($master);
+ } catch (\RuntimeException $e) {
+ $that->emit('error', array($e));
+ 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
+ ));
+ }
+}