Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

183
vendor/react/socket/src/Connection.php vendored Executable file
View File

@@ -0,0 +1,183 @@
<?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. Underlying Stream implementation will take care
// of closing stream resource, so we otherwise keep this open here.
@\stream_socket_shutdown($this->stream, \STREAM_SHUT_RDWR);
}
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;
}
}

View File

@@ -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();
}

236
vendor/react/socket/src/Connector.php vendored Executable file
View File

@@ -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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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;
}
}

View File

@@ -0,0 +1,59 @@
<?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<ConnectionInterface>
* Resolves with a `ConnectionInterface` on success or rejects with an `Exception` on error.
* @see ConnectionInterface
*/
public function connect($uri);
}

117
vendor/react/socket/src/DnsConnector.php vendored Executable file
View File

@@ -0,0 +1,117 @@
<?php
namespace React\Socket;
use React\Dns\Resolver\ResolverInterface;
use React\Promise;
use React\Promise\PromiseInterface;
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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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 $ti => $one) {
if (isset($one['args'])) {
foreach ($one['args'] as $ai => $arg) {
if ($arg instanceof \Closure) {
$trace[$ti]['args'][$ai] = '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 PromiseInterface && \method_exists($promise, 'cancel')) {
// 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;
}
}
);
}
}

222
vendor/react/socket/src/FdServer.php vendored Executable file
View File

@@ -0,0 +1,222 @@
<?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, $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 : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
);
}
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
$this->loop = $loop ?: Loop::get();
$errno = 0;
$errstr = '';
\set_error_handler(function ($_, $error) use (&$errno, &$errstr) {
// 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
\preg_match('/\[(\d+)\]: (.*)/', $error, $m);
$errno = isset($m[1]) ? (int) $m[1] : 0;
$errstr = isset($m[2]) ? $m[2] : $error;
});
$this->master = \fopen('php://fd/' . $fd, 'r+');
\restore_error_handler();
if (false === $this->master) {
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));
}
}

41
vendor/react/socket/src/FixedUriConnector.php vendored Executable file
View File

@@ -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);
}
}

View File

@@ -0,0 +1,334 @@
<?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\PromiseInterface;
/**
* @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()
{
$that = $this;
return new Promise\Promise(function ($resolve, $reject) use ($that) {
$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) {
// 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(function () use (&$ips) {
// discard all IPv4 addresses if cancelled
$ips = array();
});
$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) {
$reject(new \RuntimeException(
'Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '') . ' (ECONNABORTED)',
\defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
));
$_ = $reject = null;
$that->cleanUp();
});
}
/**
* @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();
// cancel pending connection attempts
foreach ($this->connectionPromises as $connectionPromise) {
if ($connectionPromise instanceof PromiseInterface && \method_exists($connectionPromise, 'cancel')) {
$connectionPromise->cancel();
}
}
// cancel pending DNS resolution (cancel IPv4 first in case it is awaiting IPv6 resolution delay)
foreach (\array_reverse($this->resolverPromises) as $resolverPromise) {
if ($resolverPromise instanceof PromiseInterface && \method_exists($resolverPromise, 'cancel')) {
$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;
}
}

View File

@@ -0,0 +1,80 @@
<?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;
/**
* @param ?LoopInterface $loop
* @param ConnectorInterface $connector
* @param ResolverInterface $resolver
*/
public function __construct($loop = null, $connector = null, $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 ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}
if (!$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($connector) expected React\Socket\ConnectorInterface');
}
if (!$resolver instanceof ResolverInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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();
}
}

203
vendor/react/socket/src/LimitingServer.php vendored Executable file
View File

@@ -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));
}
}

132
vendor/react/socket/src/SecureConnector.php vendored Executable file
View File

@@ -0,0 +1,132 @@
<?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;
/**
* @param ConnectorInterface $connector
* @param ?LoopInterface $loop
* @param array $context
*/
public function __construct(ConnectorInterface $connector, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
));
}
$context = $this->context;
$encryption = $this->streamEncryption;
$connected = false;
/** @var \React\Promise\PromiseInterface<ConnectionInterface> $promise */
$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 $ti => $one) {
if (isset($one['args'])) {
foreach ($one['args'] as $ai => $arg) {
if ($arg instanceof \Closure) {
$trace[$ti]['args'][$ai] = '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;
}
);
}
}

210
vendor/react/socket/src/SecureServer.php vendored Executable file
View File

@@ -0,0 +1,210 @@
<?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, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
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();
}
);
}
}

118
vendor/react/socket/src/Server.php vendored Executable file
View File

@@ -0,0 +1,118 @@
<?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, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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();
}
}

151
vendor/react/socket/src/ServerInterface.php vendored Executable file
View File

@@ -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();
}

215
vendor/react/socket/src/SocketServer.php vendored Executable file
View File

@@ -0,0 +1,215 @@
<?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(), $loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
}
// 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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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)
{
$errno = 0;
$errstr = '';
\set_error_handler(function ($_, $error) use (&$errno, &$errstr) {
// Match errstr from PHP's warning message.
// stream_socket_accept(): accept failed: Connection timed out
$errstr = \preg_replace('#.*: #', '', $error);
$errno = SocketServer::errno($errstr);
});
$newSocket = \stream_socket_accept($socket, 0);
\restore_error_handler();
if (false === $newSocket) {
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 either
* `ext-sockets`, `ext-posix` or `ext-pcntl` 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) 2023 Christian Lück, taken from https://github.com/clue/errno with permission
* @codeCoverageIgnore
*/
public static function errno($errstr)
{
// PHP defines the required `strerror()` function through either `ext-sockets`, `ext-posix` or `ext-pcntl`
$strerror = \function_exists('socket_strerror') ? 'socket_strerror' : (\function_exists('posix_strerror') ? 'posix_strerror' : (\function_exists('pcntl_strerror') ? 'pcntl_strerror' : null));
if ($strerror !== null) {
assert(\is_string($strerror) && \is_callable($strerror));
// PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED`
// PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE`
// go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errstr`
foreach (\get_defined_constants(false) as $name => $value) {
if (\is_int($value) && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0) && $strerror($value) === $errstr) {
return $value;
}
}
// if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available)
// go through list of all possible errno values from 1 to `MAX_ERRNO` and see if they match the given `$errstr`
for ($errno = 1, $max = \defined('MAX_ERRNO') ? \MAX_ERRNO : 4095; $errno <= $max; ++$errno) {
if ($strerror($errno) === $errstr) {
return $errno;
}
}
}
// if we reach this, no matching errno value could be found (unlikely when either `ext-sockets`, `ext-posix` or `ext-pcntl` is available)
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 either `ext-sockets` or `ext-pcntl` 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) 2023 Christian Lück, taken from https://github.com/clue/errno with permission
* @codeCoverageIgnore
*/
public static function errconst($errno)
{
// PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED`
// PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE`
// go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errno`
foreach (\get_defined_constants(false) as $name => $value) {
if ($value === $errno && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0)) {
return ' (' . \substr($name, \strpos($name, '_') + 1) . ')';
}
}
// if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available)
return '';
}
}

158
vendor/react/socket/src/StreamEncryption.php vendored Executable file
View File

@@ -0,0 +1,158 @@
<?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
}
}
}
/**
* @param Connection $stream
* @return \React\Promise\PromiseInterface<Connection>
*/
public function enable(Connection $stream)
{
return $this->toggle($stream, true);
}
/**
* @param Connection $stream
* @param bool $toggle
* @return \React\Promise\PromiseInterface<Connection>
*/
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;
});
}
/**
* @internal
* @param resource $socket
* @param Deferred<null> $deferred
* @param bool $toggle
* @param int $method
* @return void
*/
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(null);
} 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
}
}
}

173
vendor/react/socket/src/TcpConnector.php vendored Executable file
View File

@@ -0,0 +1,173 @@
<?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;
/**
* @param ?LoopInterface $loop
* @param array $context
*/
public function __construct($loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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.
$errno = 0;
$errstr = '';
\set_error_handler(function ($_, $error) use (&$errno, &$errstr) {
// Match errstr from PHP's warning message.
// fwrite(): send of 1 bytes failed with errno=111 Connection refused
\preg_match('/errno=(\d+) (.+)/', $error, $m);
$errno = isset($m[1]) ? (int) $m[1] : 0;
$errstr = isset($m[2]) ? $m[2] : $error;
});
\fwrite($stream, \PHP_EOL);
\restore_error_handler();
} 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
);
});
}
}

262
vendor/react/socket/src/TcpServer.php vendored Executable file
View File

@@ -0,0 +1,262 @@
<?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, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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)
));
}
}

79
vendor/react/socket/src/TimeoutConnector.php vendored Executable file
View File

@@ -0,0 +1,79 @@
<?php
namespace React\Socket;
use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;
use React\Promise\Promise;
final class TimeoutConnector implements ConnectorInterface
{
private $connector;
private $timeout;
private $loop;
/**
* @param ConnectorInterface $connector
* @param float $timeout
* @param ?LoopInterface $loop
*/
public function __construct(ConnectorInterface $connector, $timeout, $loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
}
$this->connector = $connector;
$this->timeout = $timeout;
$this->loop = $loop ?: Loop::get();
}
public function connect($uri)
{
$promise = $this->connector->connect($uri);
$loop = $this->loop;
$time = $this->timeout;
return new Promise(function ($resolve, $reject) use ($loop, $time, $promise, $uri) {
$timer = null;
$promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) {
if ($timer) {
$loop->cancelTimer($timer);
}
$timer = false;
$resolve($v);
}, function ($v) use (&$timer, $loop, $reject) {
if ($timer) {
$loop->cancelTimer($timer);
}
$timer = false;
$reject($v);
});
// promise already resolved => no need to start timer
if ($timer === false) {
return;
}
// start timeout timer which will cancel the pending promise
$timer = $loop->addTimer($time, function () use ($time, &$promise, $reject, $uri) {
$reject(new \RuntimeException(
'Connection to ' . $uri . ' timed out after ' . $time . ' seconds (ETIMEDOUT)',
\defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110
));
// Cancel pending connection to clean up any underlying resources and references.
// Avoid garbage references in call stack by passing pending promise by reference.
assert(\method_exists($promise, 'cancel'));
$promise->cancel();
$promise = null;
});
}, function () use (&$promise) {
// Cancelling this promise will cancel the pending connection, thus triggering the rejection logic above.
// Avoid garbage references in call stack by passing pending promise by reference.
assert(\method_exists($promise, 'cancel'));
$promise->cancel();
$promise = null;
});
}
}

58
vendor/react/socket/src/UnixConnector.php vendored Executable file
View File

@@ -0,0 +1,58 @@
<?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;
/**
* @param ?LoopInterface $loop
*/
public function __construct($loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_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);
}
}

162
vendor/react/socket/src/UnixServer.php vendored Executable file
View File

@@ -0,0 +1,162 @@
<?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, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}
$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 : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
);
}
$errno = 0;
$errstr = '';
\set_error_handler(function ($_, $error) use (&$errno, &$errstr) {
// 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 (\preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error, $match)) {
$errstr = isset($match[3]) ? $match['3'] : $match[1];
$errno = isset($match[2]) ? (int)$match[2] : 0;
}
});
$this->master = \stream_socket_server(
$path,
$errno,
$errstr,
\STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
\stream_context_create(array('socket' => $context))
);
\restore_error_handler();
if (false === $this->master) {
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
));
}
}