summaryrefslogtreecommitdiffstats
path: root/vendor/gipfl/socket
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gipfl/socket')
-rw-r--r--vendor/gipfl/socket/composer.json29
-rw-r--r--vendor/gipfl/socket/src/ConnectionList.php87
-rw-r--r--vendor/gipfl/socket/src/UnixSocketInspection.php89
-rw-r--r--vendor/gipfl/socket/src/UnixSocketPeer.php102
4 files changed, 307 insertions, 0 deletions
diff --git a/vendor/gipfl/socket/composer.json b/vendor/gipfl/socket/composer.json
new file mode 100644
index 0000000..cc19780
--- /dev/null
+++ b/vendor/gipfl/socket/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "gipfl/socket",
+ "description": "Helpful ReactPHP socket utility classes",
+ "type": "library",
+ "license": "MIT",
+ "autoload": {
+ "psr-4": {
+ "gipfl\\Socket\\": "src/"
+ }
+ },
+ "authors": [
+ {
+ "name": "Thomas Gelf",
+ "email": "thomas@gelf.net"
+ }
+ ],
+ "config": {
+ "sort-packages": true
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "ext-posix": "*",
+ "ext-sockets": "*",
+ "evenement/evenement": ">=2.0",
+ "gipfl/json": ">=0.1",
+ "react/event-loop": ">=1.0",
+ "react/socket": ">=1.0"
+ }
+}
diff --git a/vendor/gipfl/socket/src/ConnectionList.php b/vendor/gipfl/socket/src/ConnectionList.php
new file mode 100644
index 0000000..47ea489
--- /dev/null
+++ b/vendor/gipfl/socket/src/ConnectionList.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace gipfl\Socket;
+
+use Evenement\EventEmitterInterface;
+use Evenement\EventEmitterTrait;
+use InvalidArgumentException;
+use React\EventLoop\LoopInterface;
+use React\Promise\Deferred;
+use React\Socket\ConnectionInterface;
+use SplObjectStorage;
+use function React\Promise\all;
+
+/**
+ * @method ConnectionInterface current
+ */
+class ConnectionList extends SplObjectStorage implements EventEmitterInterface
+{
+ use EventEmitterTrait;
+
+ const ON_ATTACHED = 'attached';
+ const ON_DETACHED = 'detached';
+
+ /** @var LoopInterface */
+ protected $loop;
+
+ public function __construct(LoopInterface $loop)
+ {
+ $this->loop = $loop;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function attach($object, $info = null)
+ {
+ if (! $object instanceof ConnectionInterface || $info !== null) {
+ throw new InvalidArgumentException(sprintf(
+ 'Can attach only %s instances',
+ ConnectionInterface::class
+ ));
+ }
+ $object->on('close', function () use ($object) {
+ $this->detach($object);
+ });
+
+ parent::attach($object, $info);
+ $this->emit(self::ON_ATTACHED, [$object]);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function detach($object)
+ {
+ parent::detach($object);
+ $this->emit(self::ON_DETACHED, [$object]);
+ }
+
+ public function close()
+ {
+ $pending = [];
+ foreach ($this as $connection) {
+ $pending[] = $this->closeConnection($connection, $this->loop);
+ }
+
+ return all($pending);
+ }
+
+ public static function closeConnection(ConnectionInterface $connection, LoopInterface $loop, $timeout = 5)
+ {
+ $deferred = new Deferred();
+ $connection->end();
+ if ($connection->isWritable() || $connection->isReadable()) {
+ $timer = $loop->addTimer($timeout, function () use ($connection, $deferred) {
+ $connection->close();
+ });
+
+ $connection->on('close', function () use ($deferred, $timer) {
+ $this->loop->cancelTimer($timer);
+ $deferred->resolve();
+ });
+ } else {
+ $loop->futureTick(function () use ($deferred) {
+ $deferred->resolve();
+ });
+ }
+
+ return $deferred->promise();
+ }
+}
diff --git a/vendor/gipfl/socket/src/UnixSocketInspection.php b/vendor/gipfl/socket/src/UnixSocketInspection.php
new file mode 100644
index 0000000..e1e9819
--- /dev/null
+++ b/vendor/gipfl/socket/src/UnixSocketInspection.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace gipfl\Socket;
+
+use RuntimeException;
+use React\Socket\ConnectionInterface;
+use function array_shift;
+use function file_exists;
+use function is_int;
+use function posix_getgrgid;
+use function posix_getpwuid;
+use function preg_split;
+use function socket_get_option;
+use function socket_import_stream;
+use function stat;
+use function strlen;
+use function trim;
+
+class UnixSocketInspection
+{
+ /**
+ * @param ConnectionInterface $connection
+ * @return UnixSocketPeer
+ */
+ public static function getPeer(ConnectionInterface $connection)
+ {
+ $socket = socket_import_stream($connection->stream);
+ $remotePid = static::getRemotePidFromSocket($socket);
+ $stat = static::statProcFile($remotePid);
+ $uid = $stat['uid'];
+ $gid = $stat['gid'];
+ $userInfo = static::getUserInfo($uid);
+ $gecosParts = preg_split('/,/', $userInfo['gecos']);
+ $fullName = trim(array_shift($gecosParts));
+ $groupInfo = static::getGroupInfo($gid);
+
+ return new UnixSocketPeer(
+ $remotePid,
+ $uid,
+ $gid,
+ $userInfo['name'],
+ strlen($fullName) ? $fullName : null,
+ $groupInfo['name']
+ );
+ }
+
+ protected static function getRemotePidFromSocket($socket)
+ {
+ // SO_PEERCRED = 17
+ $remotePid = socket_get_option($socket, SOL_SOCKET, 17);
+ if (! is_int($remotePid) || ! $remotePid > 0) {
+ throw new RuntimeException("Remote PID expected, got " . var_export($remotePid));
+ }
+
+ return $remotePid;
+ }
+
+ protected static function statProcFile($pid)
+ {
+ $procDir = "/proc/$pid";
+ if (file_exists($procDir)) {
+ return stat($procDir);
+ } else {
+ throw new RuntimeException("Got no proc dir ($procDir) for remote node");
+ }
+ }
+
+ protected static function getUserInfo($uid)
+ {
+ $userInfo = posix_getpwuid($uid);
+
+ if ($userInfo === false) {
+ throw new RuntimeException("Unable to resolve remote UID '$uid'");
+ }
+
+ return $userInfo;
+ }
+
+ protected static function getGroupInfo($gid)
+ {
+ $groupInfo = posix_getgrgid($gid);
+
+ if ($groupInfo === false) {
+ throw new RuntimeException("Unable to resolve remote GID '$gid'");
+ }
+
+ return $groupInfo;
+ }
+}
diff --git a/vendor/gipfl/socket/src/UnixSocketPeer.php b/vendor/gipfl/socket/src/UnixSocketPeer.php
new file mode 100644
index 0000000..409487b
--- /dev/null
+++ b/vendor/gipfl/socket/src/UnixSocketPeer.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace gipfl\Socket;
+
+use gipfl\Json\JsonSerialization;
+
+class UnixSocketPeer implements JsonSerialization
+{
+ /** @var int */
+ protected $pid;
+
+ /** @var int */
+ protected $uid;
+
+ /** @var int */
+ protected $gid;
+
+ /** @var string */
+ protected $username;
+
+ /** @var ?string */
+ protected $fullName;
+
+ /** @var string */
+ protected $groupName;
+
+ public function __construct($pid, $uid, $gid, $username, $fullName, $groupName)
+ {
+ $this->pid = $pid;
+ $this->uid = $uid;
+ $this->gid = $gid;
+ $this->username = $username;
+ $this->fullName = $fullName;
+ $this->groupName = $groupName;
+ }
+
+ /**
+ * @return int
+ */
+ public function getPid()
+ {
+ return $this->pid;
+ }
+
+ /**
+ * @return int
+ */
+ public function getUid()
+ {
+ return $this->uid;
+ }
+
+ /**
+ * @return int
+ */
+ public function getGid()
+ {
+ return $this->gid;
+ }
+
+ /**
+ * @return string
+ */
+ public function getUsername()
+ {
+ return $this->username;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getFullName()
+ {
+ return $this->fullName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getGroupName()
+ {
+ return $this->groupName;
+ }
+
+ public static function fromSerialization($any)
+ {
+ return new static($any->pid, $any->uid, $any->gid, $any->username, $any->fullName, $any->groupName);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return (object) [
+ 'pid' => $this->pid,
+ 'uid' => $this->uid,
+ 'gid' => $this->gid,
+ 'username' => $this->username,
+ 'fullName' => $this->fullName,
+ 'groupName' => $this->groupName,
+ ];
+ }
+}