summaryrefslogtreecommitdiffstats
path: root/vendor/react/socket/src/TcpServer.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/react/socket/src/TcpServer.php')
-rw-r--r--vendor/react/socket/src/TcpServer.php258
1 files changed, 258 insertions, 0 deletions
diff --git a/vendor/react/socket/src/TcpServer.php b/vendor/react/socket/src/TcpServer.php
new file mode 100644
index 0000000..442af70
--- /dev/null
+++ b/vendor/react/socket/src/TcpServer.php
@@ -0,0 +1,258 @@
+<?php
+
+namespace React\Socket;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * The `TcpServer` class implements the `ServerInterface` and
+ * is responsible for accepting plaintext TCP/IP connections.
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer(8080);
+ * ```
+ *
+ * Whenever a client connects, it will emit a `connection` event with a connection
+ * instance implementing `ConnectionInterface`:
+ *
+ * ```php
+ * $server->on('connection', function (React\Socket\ConnectionInterface $connection) {
+ * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class TcpServer extends EventEmitter implements ServerInterface
+{
+ private $master;
+ private $loop;
+ private $listening = false;
+
+ /**
+ * Creates a plaintext TCP/IP socket server and starts listening on the given address
+ *
+ * This starts accepting new incoming connections on the given address.
+ * See also the `connection event` documented in the `ServerInterface`
+ * for more details.
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer(8080);
+ * ```
+ *
+ * As above, the `$uri` parameter can consist of only a port, in which case the
+ * server will default to listening on the localhost address `127.0.0.1`,
+ * which means it will not be reachable from outside of this system.
+ *
+ * In order to use a random port assignment, you can use the port `0`:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer(0);
+ * $address = $server->getAddress();
+ * ```
+ *
+ * In order to change the host the socket is listening on, you can provide an IP
+ * address through the first parameter provided to the constructor, optionally
+ * preceded by the `tcp://` scheme:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer('192.168.0.1:8080');
+ * ```
+ *
+ * If you want to listen on an IPv6 address, you MUST enclose the host in square
+ * brackets:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer('[::1]:8080');
+ * ```
+ *
+ * If the given URI is invalid, does not contain a port, any other scheme or if it
+ * contains a hostname, it will throw an `InvalidArgumentException`:
+ *
+ * ```php
+ * // throws InvalidArgumentException due to missing port
+ * $server = new React\Socket\TcpServer('127.0.0.1');
+ * ```
+ *
+ * If the given URI appears to be valid, but listening on it fails (such as if port
+ * is already in use or port below 1024 may require root access etc.), it will
+ * throw a `RuntimeException`:
+ *
+ * ```php
+ * $first = new React\Socket\TcpServer(8080);
+ *
+ * // throws RuntimeException because port is already in use
+ * $second = new React\Socket\TcpServer(8080);
+ * ```
+ *
+ * Note that these error conditions may vary depending on your system and/or
+ * configuration.
+ * See the exception message and code for more details about the actual error
+ * condition.
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php)
+ * for the underlying stream socket resource like this:
+ *
+ * ```php
+ * $server = new React\Socket\TcpServer('[::1]:8080', null, array(
+ * 'backlog' => 200,
+ * 'so_reuseport' => true,
+ * 'ipv6_v6only' => true
+ * ));
+ * ```
+ *
+ * Note that available [socket context options](https://www.php.net/manual/en/context.socket.php),
+ * their defaults and effects of changing these may vary depending on your system
+ * and/or PHP version.
+ * Passing unknown context options has no effect.
+ * The `backlog` context option defaults to `511` unless given explicitly.
+ *
+ * @param string|int $uri
+ * @param ?LoopInterface $loop
+ * @param array $context
+ * @throws InvalidArgumentException if the listening address is invalid
+ * @throws RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($uri, LoopInterface $loop = null, array $context = array())
+ {
+ $this->loop = $loop ?: Loop::get();
+
+ // a single port has been given => assume localhost
+ if ((string)(int)$uri === (string)$uri) {
+ $uri = '127.0.0.1:' . $uri;
+ }
+
+ // assume default scheme if none has been given
+ if (\strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ }
+
+ // parse_url() does not accept null ports (random port assignment) => manually remove
+ if (\substr($uri, -2) === ':0') {
+ $parts = \parse_url(\substr($uri, 0, -2));
+ if ($parts) {
+ $parts['port'] = 0;
+ }
+ } else {
+ $parts = \parse_url($uri);
+ }
+
+ // ensure URI contains TCP scheme, host and port
+ if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
+ throw new \InvalidArgumentException(
+ 'Invalid URI "' . $uri . '" given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ if (@\inet_pton(\trim($parts['host'], '[]')) === false) {
+ throw new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
+ }
+
+ $this->master = @\stream_socket_server(
+ $uri,
+ $errno,
+ $errstr,
+ \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
+ \stream_context_create(array('socket' => $context + array('backlog' => 511)))
+ );
+ if (false === $this->master) {
+ if ($errno === 0) {
+ // PHP does not seem to report errno, so match errno from errstr
+ // @link https://3v4l.org/3qOBl
+ $errno = SocketServer::errno($errstr);
+ }
+
+ throw new \RuntimeException(
+ 'Failed to listen on "' . $uri . '": ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
+ }
+ \stream_set_blocking($this->master, false);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!\is_resource($this->master)) {
+ return null;
+ }
+
+ $address = \stream_socket_get_name($this->master, false);
+
+ // check if this is an IPv6 address which includes multiple colons but no square brackets
+ $pos = \strrpos($address, ':');
+ if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') {
+ $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore
+ }
+
+ return 'tcp://' . $address;
+ }
+
+ public function pause()
+ {
+ if (!$this->listening) {
+ return;
+ }
+
+ $this->loop->removeReadStream($this->master);
+ $this->listening = false;
+ }
+
+ public function resume()
+ {
+ if ($this->listening || !\is_resource($this->master)) {
+ return;
+ }
+
+ $that = $this;
+ $this->loop->addReadStream($this->master, function ($master) use ($that) {
+ try {
+ $newSocket = SocketServer::accept($master);
+ } catch (\RuntimeException $e) {
+ $that->emit('error', array($e));
+ return;
+ }
+ $that->handleConnection($newSocket);
+ });
+ $this->listening = true;
+ }
+
+ public function close()
+ {
+ if (!\is_resource($this->master)) {
+ return;
+ }
+
+ $this->pause();
+ \fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleConnection($socket)
+ {
+ $this->emit('connection', array(
+ new Connection($socket, $this->loop)
+ ));
+ }
+}