diff options
Diffstat (limited to 'vendor/clue/connection-manager-extra')
11 files changed, 457 insertions, 0 deletions
diff --git a/vendor/clue/connection-manager-extra/LICENSE b/vendor/clue/connection-manager-extra/LICENSE new file mode 100644 index 0000000..da15612 --- /dev/null +++ b/vendor/clue/connection-manager-extra/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Christian Lück + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/clue/connection-manager-extra/composer.json b/vendor/clue/connection-manager-extra/composer.json new file mode 100644 index 0000000..7534f80 --- /dev/null +++ b/vendor/clue/connection-manager-extra/composer.json @@ -0,0 +1,29 @@ +{ + "name": "clue/connection-manager-extra", + "description": "Extra decorators for creating async TCP/IP connections, built on top of ReactPHP's Socket component", + "keywords": ["Socket", "network", "connection", "timeout", "delay", "reject", "repeat", "retry", "random", "acl", "firewall", "ReactPHP"], + "homepage": "https://github.com/clue/reactphp-connection-manager-extra", + "license": "MIT", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "autoload": { + "psr-4": { "ConnectionManager\\Extra\\": "src" } + }, + "autoload-dev": { + "psr-4": { "ConnectionManager\\Tests\\Extra\\": "tests/" } + }, + "require": { + "php": ">=5.3", + "react/socket": "^1.0 || ^0.8 || ^0.7", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/promise": "^2.1 || ^1.2.1", + "react/promise-timer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8" + } +} diff --git a/vendor/clue/connection-manager-extra/src/ConnectionManagerDelay.php b/vendor/clue/connection-manager-extra/src/ConnectionManagerDelay.php new file mode 100644 index 0000000..b5112c6 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/ConnectionManagerDelay.php @@ -0,0 +1,30 @@ +<?php + +namespace ConnectionManager\Extra; + +use React\Socket\ConnectorInterface; +use React\EventLoop\LoopInterface; +use React\Promise\Timer; + +class ConnectionManagerDelay implements ConnectorInterface +{ + private $connectionManager; + private $delay; + private $loop; + + public function __construct(ConnectorInterface $connectionManager, $delay, LoopInterface $loop) + { + $this->connectionManager = $connectionManager; + $this->delay = $delay; + $this->loop = $loop; + } + + public function connect($uri) + { + $connectionManager = $this->connectionManager; + + return Timer\resolve($this->delay, $this->loop)->then(function () use ($connectionManager, $uri) { + return $connectionManager->connect($uri); + }); + } +} diff --git a/vendor/clue/connection-manager-extra/src/ConnectionManagerReject.php b/vendor/clue/connection-manager-extra/src/ConnectionManagerReject.php new file mode 100644 index 0000000..1222c83 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/ConnectionManagerReject.php @@ -0,0 +1,41 @@ +<?php + +namespace ConnectionManager\Extra; + +use React\Socket\ConnectorInterface; +use React\Promise; +use Exception; + +// a simple connection manager that rejects every single connection attempt +class ConnectionManagerReject implements ConnectorInterface +{ + private $reason = 'Connection rejected'; + + /** + * @param null|string|callable $reason + */ + public function __construct($reason = null) + { + if ($reason !== null) { + $this->reason = $reason; + } + } + + public function connect($uri) + { + $reason = $this->reason; + if (!is_string($reason)) { + try { + $reason = $reason($uri); + } catch (\Exception $e) { + $reason = $e; + } + } + + if (!$reason instanceof \Exception) { + $reason = new Exception($reason); + } + + return Promise\reject($reason); + } +} diff --git a/vendor/clue/connection-manager-extra/src/ConnectionManagerRepeat.php b/vendor/clue/connection-manager-extra/src/ConnectionManagerRepeat.php new file mode 100644 index 0000000..10f3f5a --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/ConnectionManagerRepeat.php @@ -0,0 +1,52 @@ +<?php + +namespace ConnectionManager\Extra; + +use React\Socket\ConnectorInterface; +use InvalidArgumentException; +use Exception; +use React\Promise\Promise; +use React\Promise\CancellablePromiseInterface; + +class ConnectionManagerRepeat implements ConnectorInterface +{ + protected $connectionManager; + protected $maximumTries; + + public function __construct(ConnectorInterface $connectionManager, $maximumTries) + { + if ($maximumTries < 1) { + throw new InvalidArgumentException('Maximum number of tries must be >= 1'); + } + $this->connectionManager = $connectionManager; + $this->maximumTries = $maximumTries; + } + + public function connect($uri) + { + $tries = $this->maximumTries; + $connector = $this->connectionManager; + + return new Promise(function ($resolve, $reject) use ($uri, &$pending, &$tries, $connector) { + $try = function ($error = null) use (&$try, &$pending, &$tries, $uri, $connector, $resolve, $reject) { + if ($tries > 0) { + --$tries; + $pending = $connector->connect($uri); + $pending->then($resolve, $try); + } else { + $reject(new Exception('Connection still fails even after retrying', 0, $error)); + } + }; + + $try(); + }, function ($_, $reject) use (&$pending, &$tries) { + // stop retrying, reject results and cancel pending attempt + $tries = 0; + $reject(new \RuntimeException('Cancelled')); + + if ($pending instanceof CancellablePromiseInterface) { + $pending->cancel(); + } + }); + } +} diff --git a/vendor/clue/connection-manager-extra/src/ConnectionManagerSwappable.php b/vendor/clue/connection-manager-extra/src/ConnectionManagerSwappable.php new file mode 100644 index 0000000..d133225 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/ConnectionManagerSwappable.php @@ -0,0 +1,26 @@ +<?php + +namespace ConnectionManager\Extra; + +use React\Socket\ConnectorInterface; + +// connection manager decorator which simplifies exchanging the actual connection manager during runtime +class ConnectionManagerSwappable implements ConnectorInterface +{ + protected $connectionManager; + + public function __construct(ConnectorInterface $connectionManager) + { + $this->connectionManager = $connectionManager; + } + + public function connect($uri) + { + return $this->connectionManager->connect($uri); + } + + public function setConnectionManager(ConnectorInterface $connectionManager) + { + $this->connectionManager = $connectionManager; + } +} diff --git a/vendor/clue/connection-manager-extra/src/ConnectionManagerTimeout.php b/vendor/clue/connection-manager-extra/src/ConnectionManagerTimeout.php new file mode 100644 index 0000000..5ec0872 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/ConnectionManagerTimeout.php @@ -0,0 +1,35 @@ +<?php + +namespace ConnectionManager\Extra; + +use React\Socket\ConnectorInterface; +use React\EventLoop\LoopInterface; +use React\Promise\Timer; + +class ConnectionManagerTimeout implements ConnectorInterface +{ + private $connectionManager; + private $timeout; + private $loop; + + public function __construct(ConnectorInterface $connectionManager, $timeout, LoopInterface $loop) + { + $this->connectionManager = $connectionManager; + $this->timeout = $timeout; + $this->loop = $loop; + } + + public function connect($uri) + { + $promise = $this->connectionManager->connect($uri); + + return Timer\timeout($promise, $this->timeout, $this->loop)->then(null, function ($e) use ($promise) { + // connection successfully established but timeout already expired => close successful connection + $promise->then(function ($connection) { + $connection->end(); + }); + + throw $e; + }); + } +} diff --git a/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConcurrent.php b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConcurrent.php new file mode 100644 index 0000000..c1eb9cf --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConcurrent.php @@ -0,0 +1,36 @@ +<?php + +namespace ConnectionManager\Extra\Multiple; + +use ConnectionManager\Extra\Multiple\ConnectionManagerConsecutive; +use React\Promise; +use React\Promise\CancellablePromiseInterface; + +class ConnectionManagerConcurrent extends ConnectionManagerConsecutive +{ + public function connect($uri) + { + $all = array(); + foreach ($this->managers as $connector) { + /* @var $connection Connector */ + $all []= $connector->connect($uri); + } + return Promise\any($all)->then(function ($conn) use ($all) { + // a connection attempt succeeded + // => cancel all pending connection attempts + foreach ($all as $promise) { + if ($promise instanceof CancellablePromiseInterface) { + $promise->cancel(); + } + + // if promise resolves despite cancellation, immediately close stream + $promise->then(function ($stream) use ($conn) { + if ($stream !== $conn) { + $stream->close(); + } + }); + } + return $conn; + }); + } +} diff --git a/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConsecutive.php b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConsecutive.php new file mode 100644 index 0000000..1474b85 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConsecutive.php @@ -0,0 +1,62 @@ +<?php + +namespace ConnectionManager\Extra\Multiple; + +use React\Socket\ConnectorInterface; +use React\Promise; +use UnderflowException; +use React\Promise\CancellablePromiseInterface; + +class ConnectionManagerConsecutive implements ConnectorInterface +{ + protected $managers; + + /** + * + * @param ConnectorInterface[] $managers + */ + public function __construct(array $managers) + { + if (!$managers) { + throw new \InvalidArgumentException('List of connectors must not be empty'); + } + $this->managers = $managers; + } + + public function connect($uri) + { + return $this->tryConnection($this->managers, $uri); + } + + /** + * + * @param ConnectorInterface[] $managers + * @param string $uri + * @return Promise + * @internal + */ + public function tryConnection(array $managers, $uri) + { + return new Promise\Promise(function ($resolve, $reject) use (&$managers, &$pending, $uri) { + $try = function () use (&$try, &$managers, $uri, $resolve, $reject, &$pending) { + if (!$managers) { + return $reject(new UnderflowException('No more managers to try to connect through')); + } + + $manager = array_shift($managers); + $pending = $manager->connect($uri); + $pending->then($resolve, $try); + }; + + $try(); + }, function ($_, $reject) use (&$managers, &$pending) { + // stop retrying, reject results and cancel pending attempt + $managers = array(); + $reject(new \RuntimeException('Cancelled')); + + if ($pending instanceof CancellablePromiseInterface) { + $pending->cancel(); + } + }); + } +} diff --git a/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerRandom.php b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerRandom.php new file mode 100644 index 0000000..88d1fd6 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerRandom.php @@ -0,0 +1,14 @@ +<?php + +namespace ConnectionManager\Extra\Multiple; + +class ConnectionManagerRandom extends ConnectionManagerConsecutive +{ + public function connect($uri) + { + $managers = $this->managers; + shuffle($managers); + + return $this->tryConnection($managers, $uri); + } +} diff --git a/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerSelective.php b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerSelective.php new file mode 100644 index 0000000..859ea90 --- /dev/null +++ b/vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerSelective.php @@ -0,0 +1,111 @@ +<?php + +namespace ConnectionManager\Extra\Multiple; + +use React\Socket\ConnectorInterface; +use React\Promise; +use UnderflowException; +use InvalidArgumentException; + +class ConnectionManagerSelective implements ConnectorInterface +{ + private $managers; + + /** + * + * @param ConnectorInterface[] $managers + */ + public function __construct(array $managers) + { + foreach ($managers as $filter => $manager) { + $host = $filter; + $portMin = 0; + $portMax = 65535; + + // search colon (either single one OR preceded by "]" due to IPv6) + $colon = strrpos($host, ':'); + if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) { + if (!isset($host[$colon + 1])) { + throw new InvalidArgumentException('Entry "' . $filter . '" has no port after colon'); + } + + $minus = strpos($host, '-', $colon); + if ($minus === false) { + $portMin = $portMax = (int)substr($host, $colon + 1); + + if (substr($host, $colon + 1) !== (string)$portMin) { + throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port after colon'); + } + } else { + $portMin = (int)substr($host, $colon + 1, ($minus - $colon)); + $portMax = (int)substr($host, $minus + 1); + + if (substr($host, $colon + 1) !== ($portMin . '-' . $portMax)) { + throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port range after colon'); + } + + if ($portMin > $portMax) { + throw new InvalidArgumentException('Entry "' . $filter . '" has port range mixed up'); + } + } + $host = substr($host, 0, $colon); + } + + if ($host === '') { + throw new InvalidArgumentException('Entry "' . $filter . '" has an empty host'); + } + + if (!$manager instanceof ConnectorInterface) { + throw new InvalidArgumentException('Entry "' . $filter . '" is not a valid connector'); + } + } + + $this->managers = $managers; + } + + public function connect($uri) + { + $parts = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri); + if (!isset($parts) || !isset($parts['scheme'], $parts['host'], $parts['port'])) { + return Promise\reject(new InvalidArgumentException('Invalid URI')); + } + + $connector = $this->getConnectorForTarget( + trim($parts['host'], '[]'), + $parts['port'] + ); + + if ($connector === null) { + return Promise\reject(new UnderflowException('No connector for given target found')); + } + + return $connector->connect($uri); + } + + private function getConnectorForTarget($targetHost, $targetPort) + { + foreach ($this->managers as $host => $connector) { + $portMin = 0; + $portMax = 65535; + + // search colon (either single one OR preceded by "]" due to IPv6) + $colon = strrpos($host, ':'); + if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) { + $minus = strpos($host, '-', $colon); + if ($minus === false) { + $portMin = $portMax = (int)substr($host, $colon + 1); + } else { + $portMin = (int)substr($host, $colon + 1, ($minus - $colon)); + $portMax = (int)substr($host, $minus + 1); + } + $host = trim(substr($host, 0, $colon), '[]'); + } + + if ($targetPort >= $portMin && $targetPort <= $portMax && fnmatch($host, $targetHost)) { + return $connector; + } + } + + return null; + } +} |