From 55af22ce25c5e5a77df9dc70856b8c466f656e71 Mon Sep 17 00:00:00 2001 From: khelle Date: Sun, 25 Jun 2017 01:43:42 +0200 Subject: [PATCH] Renamed library from Kraken to Dazzle --- .gitattributes | 1 + .gitignore | 4 + .noninteractive | 0 .scrutinizer.yml | 119 +++++ .travis.yml | 24 + CHANGELOG.md | 3 + CONTRIBUTING.md | 19 + LICENSE | 19 + README.md | 53 +- composer.json | 38 +- phpunit.xml | 33 ++ src/Socket/Socket.php | 656 ------------------------- src/Socket/SocketInterface.php | 97 ---- src/Socket/SocketListener.php | 599 ---------------------- src/Socket/SocketListenerInterface.php | 71 --- src/Zmq/ZmqBuffer.php | 6 +- src/Zmq/ZmqContext.php | 4 +- src/Zmq/ZmqSocket.php | 6 +- 18 files changed, 275 insertions(+), 1477 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .noninteractive create mode 100644 .scrutinizer.yml create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 phpunit.xml delete mode 100644 src/Socket/Socket.php delete mode 100644 src/Socket/SocketInterface.php delete mode 100644 src/Socket/SocketListener.php delete mode 100644 src/Socket/SocketListenerInterface.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51cd273 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +vendor +composer.lock +composer.phar \ No newline at end of file diff --git a/.noninteractive b/.noninteractive new file mode 100644 index 0000000..e69de29 diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..4e1a7ea --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,119 @@ +filter: + excluded_paths: + - 'test/*' + - 'vendor/*' +checks: + php: + return_doc_comments: true + remove_extra_empty_lines: true + +coding_style: + php: + indentation: + general: + use_tabs: false + size: 4 + switch: + indent_case: true + spaces: + general: + linefeed_character: newline + before_parentheses: + function_declaration: false + closure_definition: false + function_call: false + if: true + for: true + while: true + switch: true + catch: true + array_initializer: false + around_operators: + assignment: true + logical: true + equality: true + relational: true + bitwise: true + additive: true + multiplicative: true + shift: true + unary_additive: false + concatenation: true + negation: false + before_left_brace: + class: true + function: true + if: true + else: true + for: true + while: true + do: true + switch: true + try: true + catch: true + finally: true + before_keywords: + else: true + while: true + catch: true + finally: true + within: + brackets: false + array_initializer: false + grouping: false + function_call: false + function_declaration: false + if: false + for: false + while: false + switch: false + catch: false + type_cast: false + ternary_operator: + before_condition: true + after_condition: true + before_alternative: true + after_alternative: true + in_short_version: false + other: + before_comma: false + after_comma: true + before_semicolon: false + after_semicolon: true + after_type_cast: true + braces: + classes_functions: + class: new-line + function: new-line + closure: end-of-line + if: + opening: new-line + always: false + else_on_new_line: true + for: + opening: new-line + always: true + while: + opening: new-line + always: true + do_while: + opening: new-line + always: true + while_on_new_line: true + switch: + opening: new-line + try: + opening: new-line + catch_on_new_line: true + finally_on_new_line: true + upper_lower_casing: + keywords: + general: lower + constants: + true_false_null: lower + +tools: + external_code_coverage: + timeout: 1800 + runs: 1 + php_code_coverage: false \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..aca9235 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: php + +dist: trusty +sudo: required + +php: + - 5.6 + - 7.0 + - 7.1 + +before_install: + - export PHP_MAJOR="$(echo $TRAVIS_PHP_VERSION | cut -d '.' -f 1,2)" + +install: + - travis_retry composer self-update + - travis_retry composer install --prefer-source --no-interaction --ignore-platform-reqs + - php -m + +script: + - vendor/bin/phpunit -d memory_limit=1024M --coverage-text --coverage-clover=coverage.clover + +after_script: + - if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi + - if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2a138d1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release Notes + +This changelog references the relevant changes, bug and security fixes done. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ea7a9d7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing + +Contributions are **welcome** and accepted via **Pull Requests** on [GitHub](https://github.com/dazzle-php/throwable). + +## Pull Requests + +- **Naming convention** - all pull requests fixing a problem should match "Fix #issue Message" pattern, the new features and non-fix changes should match "Resolve #issue Message", the rest should contain only "Message". +- **Follow our template of code** - all contributions have to follow [PSR-2 coding standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) with an exception of control structures, which have to have opening parenthesis always placed in the next line instead of the same. +- **Add tests** - the contribution won't be accepted if it doesn't have tests. +- **Document any change in behaviour** - make sure the `README.md` is kept up to date. +- **Create feature branches** - don't create pull requests from your master branch. +- **One pull request per feature** - for multiple things that you want to do, send also multiple pull requests. +- **Keep coherent history** - make sure each individual commit in your pull request is meaningful. If you had to make multiple commits during development cycle, please squash them before submitting. + +## Running Tests + +``` +$> vendor/bin/phpunit +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdde115 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2017 Kamil Jamróz + +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/README.md b/README.md index 25a2f0f..22bc8e7 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,29 @@ -# Kraken Ipc Component +# Dazzle Async ZMQ -[![Build Status](https://travis-ci.org/kraken-php/framework.svg)](https://travis-ci.org/kraken-php/framework) -[![Total Downloads](https://poser.pugx.org/kraken-php/ipc/downloads)](https://packagist.org/packages/kraken-php/ipc) -[![Latest Stable Version](https://poser.pugx.org/kraken-php/ipc/v/stable)](https://packagist.org/packages/kraken-php/ipc) -[![Latest Unstable Version](https://poser.pugx.org/kraken-php/ipc/v/unstable)](https://packagist.org/packages/kraken-php/ipc) -[![License](https://poser.pugx.org/kraken-php/framework/license)](https://packagist.org/packages/kraken-php/framework) -[![Kraken Compatible](https://img.shields.io/badge/kraken-compatible-6b02af.svg)](https://github.com/kraken-php/framework) +[![Build Status](https://travis-ci.org/dazzle-php/zmq.svg)](https://travis-ci.org/dazzle-php/zmq) +[![Code Coverage](https://scrutinizer-ci.com/g/dazzle-php/zmq/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/dazzle-php/zmq/?branch=master) +[![Code Quality](https://scrutinizer-ci.com/g/dazzle-php/zmq/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dazzle-php/zmq/?branch=master) +[![Latest Stable Version](https://poser.pugx.org/dazzle-php/zmq/v/stable)](https://packagist.org/packages/dazzle-php/zmq) +[![Latest Unstable Version](https://poser.pugx.org/dazzle-php/zmq/v/unstable)](https://packagist.org/packages/dazzle-php/zmq) +[![License](https://poser.pugx.org/dazzle-php/zmq/license)](https://packagist.org/packages/dazzle-php/zmq/license) -> **Note:** This repository is a part of [Kraken Framework][3], but **can be used freely as standalone library**. If you -are interested in more asynchronous components for PHP, check out the rest of [Kraken repository][5] or see our -[asynchronous application skeleton][4] example. +
+

+ +

## Description -Ipc is a component that provides various models for implementing inter-process communication via asynchronous -sockets or external services. +Dazzle Socket is a component that implements asynchronous ZMQ socket handling for PHP. ## Feature Highlights -Ipc features: +Dazzlee ZMQ features: * Asynchronous handling of incoming and outcoming messages, -* Support for TCP, UDP and Unix sockets, * Support for ZeroMQ extension and ZeroMQ protocols, -* Kraken Framework compatibility, * ...and more. -## Examples - -See more examples in [official documentation][2]. - ## Requirements * PHP-5.6 or PHP-7.0+, @@ -38,27 +32,22 @@ See more examples in [official documentation][2]. ## Installation ``` -composer require kraken-php/ipc +$> composer require dazzle-php/zmq ``` ## Tests -Tests are provided within our write-only [Framework repository][3]. - -## Documentation - -Documentation for this module can be found in the [official documentation][2]. +``` +$> vendor/bin/phpunit -d memory_limit=1024M +``` ## Contributing -This library is read-only subtree split of Kraken Framework. To make contributions, please go to [Framework repository][3]. +Thank you for considering contributing to this repository! The contribution guide can be found in the [contribution tips][1]. ## License -This library licensed under the MIT license, see more information in [Kraken Framework][3] license section. +Dazzle Framework is open-sourced software licensed under the [MIT license][2]. -[1]: http://kraken-php.com -[2]: http://kraken-php.com/docs/api-ipc -[3]: https://github.com/kraken-php/framework -[4]: https://github.com/kraken-php/kraken -[5]: https://github.com/kraken-php +[1]: https://github.com/dazzle-php/zmq/blob/master/CONTRIBUTING.md +[2]: http://opensource.org/licenses/MIT diff --git a/composer.json b/composer.json index 80a2af1..c0eb7e5 100644 --- a/composer.json +++ b/composer.json @@ -1,31 +1,41 @@ { - "name": "kraken-php/ipc", - "description": "Kraken Framework Ipc Component.", + "name": "dazzle-php/zmq", + "description": "Dazzle Asynchronous ZMQ.", "keywords": [ - "kraken", "kraken-php", "ipc", "message-driven", "agent-model", "soa", "service-oriented", "zmq", "zeromq" + "dazzle", "dazzle-php", "ipc", "inter", "inter-process", "message-driven", "sock", "socket", "zmq", "zeromq" ], "license": "MIT", - "homepage": "http://kraken-php.com", "support": { - "issues": "https://github.com/kraken-php/framework/issues", - "source": "https://github.com/kraken-php/framework" + "issues": "https://github.com/dazzle-php/zmq/issues", + "source": "https://github.com/dazzle-php/zmq" }, - "authors": [{ - "name": "Kamil Jamroz" - }], + "authors": [ + { + "name": "Kamil Jamroz", + "homepage": "https://github.com/khelle" + }, + { + "name": "The contributors", + "homepage": "http://github.com/dazzle-php/zmq/contributors" + } + ], "require": { "php": ">=5.6.7", - "kraken-php/event": "0.4.*", - "kraken-php/loop": "0.4.*", - "kraken-php/stream": "0.4.*", - "kraken-php/throwable": "0.4.*" + "dazzle-php/event": "0.5.*", + "dazzle-php/loop": "0.5.*", + "dazzle-php/stream": "0.5.*", + "dazzle-php/throwable": "0.5.*" + }, + "require-dev": { + "phpunit/phpunit": ">=4.8.0 <5.4.0" }, "suggest": { "ext-zmq": "*" }, "autoload": { "psr-4": { - "Kraken\\Ipc\\": "src" + "Dazzle\\Zmq\\": "src/Socket", + "Dazzle\\Zmq\\Test\\": "test" } }, "extra": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..d445af9 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + + test/TModule + + + + test/TUnit + + + + + + src + + + diff --git a/src/Socket/Socket.php b/src/Socket/Socket.php deleted file mode 100644 index 637af23..0000000 --- a/src/Socket/Socket.php +++ /dev/null @@ -1,656 +0,0 @@ -configure($config); - - if (!is_resource($endpointOrResource)) - { - $endpointOrResource = $this->createClient($endpointOrResource, $config); - } - - parent::__construct($endpointOrResource, $loop); - - $this->read(); - } - catch (Error $ex) - { - throw new InstantiationException('SocketClient could not be created.', $ex); - } - catch (Exception $ex) - { - throw new InstantiationException('SocketClient could not be created.', $ex); - } - } - - /** - * @override - * @inheritDoc - */ - public function stop() - { - $this->close(); - } - - /** - * @override - * @inheritDoc - */ - public function getLocalEndpoint() - { - return $this->parseEndpoint(false); - } - - /** - * @override - * @inheritDoc - */ - public function getRemoteEndpoint() - { - return $this->parseEndpoint(true); - } - - /** - * @override - * @inheritDoc - */ - public function getLocalAddress() - { - $endpoint = explode('://', $this->getLocalEndpoint(), 2); - - return isset($endpoint[1]) ? $endpoint[1] : $endpoint[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalHost() - { - $address = explode(':', $this->getLocalAddress(), 2); - - return $address[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalPort() - { - $address = explode(':', $this->getLocalAddress(), 2); - - return isset($address[1]) ? $address[1] : ''; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalProtocol() - { - $endpoint = explode('://', $this->getLocalEndpoint(), 2); - - return isset($endpoint[0]) ? $endpoint[0] : ''; - } - - /** - * @override - * @inheritDoc - */ - public function getRemoteAddress() - { - $endpoint = explode('://', $this->getRemoteEndpoint(), 2); - - return isset($endpoint[1]) ? $endpoint[1] : $endpoint[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getRemoteHost() - { - $address = explode(':', $this->getRemoteAddress(), 2); - - return $address[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getRemotePort() - { - $address = explode(':', $this->getRemoteAddress(), 2); - - return isset($address[1]) ? $address[1] : ''; - } - - /** - * @override - * @inheritDoc - */ - public function getRemoteProtocol() - { - $endpoint = explode('://', $this->getRemoteEndpoint(), 2); - - return isset($endpoint[0]) ? $endpoint[0] : ''; - } - - /** - * @override - * @inheritDoc - */ - public function isEncrypted() - { - return $this->crypto !== 0; - } - - /** - * Create the client resource. - * - * This method creates client resource for socket connections. - * - * @param string $endpoint - * @param mixed[] $config - * @return resource - * @throws LogicException - */ - protected function createClient($endpoint, $config = []) - { - $ssl = $this->config['ssl']; - $name = $this->config['ssl_name']; - $verifySign = $this->config['ssl_verify_sign']; - $verifyPeer = $this->config['ssl_verify_peer']; - $verifyDepth = $this->config['ssl_verify_depth']; - - $context['socket'] = [ - 'connect' => $endpoint - ]; - - $context['ssl'] = [ - 'capture_peer_cert' => true, - 'capture_peer_chain' => true, - 'capture_peer_cert_chain' => true, - 'allow_self_signed' => !$verifySign, - 'verify_peer' => $verifyPeer, - 'verify_peer_name' => $verifyPeer, - 'verify_depth' => $verifyDepth, - 'SNI_enabled' => $name !== '', - 'SNI_server_name' => $name, - 'peer_name' => $name, - 'disable_compression' => true, - 'honor_cipher_order' => true, - ]; - - if ($ssl && isset($config['ssl_cafile'])) - { - $context['ssl']['cafile'] = $config['ssl_cafile']; - } - - if ($ssl && isset($config['ssl_capath'])) - { - $context['ssl']['capath'] = $config['ssl_capath']; - } - - $context = stream_context_create($context); - // Error reporting suppressed since stream_socket_client() emits an E_WARNING on failure. - $socket = @stream_socket_client( - $endpoint, - $errno, - $errstr, - 0, // Timeout does not apply for async connect. - STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT, - $context - ); - - if (!$socket || $errno) - { - throw new LogicException( - sprintf('Could not connect socket [%s] because of error [%d; %s]', $endpoint, $errno, $errstr) - ); - } - - stream_set_blocking($socket, false); - - return $socket; - } - - /** - * Handle socket encryption. - * - * @internal - */ - public function handleEncrypt() - { - $ex = null; - - try - { - if ($this->isEncrypted()) - { - return; - } - - $this->encrypt($this->config['ssl_method']); - - if ($this->isEncrypted()) - { - $this->pause(); - $this->resume(); - } - } - catch (Error $ex) - {} - catch (Exception $ex) - {} - - if ($ex !== null) - { - $this->close(); - $this->emit('error', [ $this, $ex ]); - } - } - - /** - * @internal - * @override - * @inheritDoc - */ - public function handleRead() - { - $ex = null; - - try - { - $data = fread($this->resource, $this->bufferSize); - - if ($data !== '' && $data !== false) - { - $this->emit('data', [ $this, $data ]); - $this->emit('end', [ $this ]); - } - - if ($data === '' || $data === false || !is_resource($this->resource) || feof($this->resource)) - { - $this->close(); - } - } - catch (Error $ex) - {} - catch (Exception $ex) - {} - - if ($ex !== null) - { - $this->emit('error', [ $this, $ex ]); - } - } - - /** - * @internal - * @override - * @inheritDoc - */ - public function handleWrite() - { - $ex = null; - - try - { - parent::handleWrite(); - } - catch (Error $ex) - {} - catch (Exception $ex) - {} - - if ($ex !== null) - { - $this->emit('error', [ $this, $ex ]); - } - } - - /** - * @internal - * @override - * @inheritDoc - */ - public function handleClose() - { - $this->pause(); - - if (is_resource($this->resource)) - { - // http://chat.stackoverflow.com/transcript/message/7727858#7727858 - stream_socket_shutdown($this->resource, STREAM_SHUT_RDWR); - stream_set_blocking($this->resource, 0); - fclose($this->resource); - } - } - - /** - * Get function that should be invoked on read event. - * - * @return callable - */ - protected function getHandleReadFunction() - { - return $this->config['ssl'] === true && !$this->isEncrypted() - ? [ $this, 'handleEncrypt' ] - : [ $this, 'handleRead' ]; - } - - /** - * Get function that should be invoked on write event. - * - * @return callable - */ - protected function getHandleWriteFunction() - { - return $this->config['ssl'] === true && !$this->isEncrypted() - ? [ $this, 'handleEncrypt' ] - : [ $this, 'handleWrite' ]; - } - - /** - * Configure socket. - * - * @param string[] $config - */ - private function configure($config = []) - { - $this->config = $config; - - $this->configureVariable('ssl'); - $this->configureVariable('ssl_method'); - $this->configureVariable('ssl_name'); - $this->configureVariable('ssl_verify_sign'); - $this->configureVariable('ssl_verify_peer'); - $this->configureVariable('ssl_verify_depth'); - } - - /** - * Configure config variable. - * - * @param $configKey - */ - private function configureVariable($configKey) - { - $configStaticKey = 'CONFIG_DEFAULT_' . strtoupper($configKey); - $this->config[$configKey] = isset($this->config[$configKey]) ? $this->config[$configKey] : constant("static::$configStaticKey"); - } - - /** - * @param bool $wantPeer - * @return string - */ - private function parseEndpoint($wantPeer = false) - { - $wantIndex = (int)$wantPeer; - - if (isset($this->cachedEndpoint[$wantIndex])) - { - return $this->cachedEndpoint[$wantIndex]; - } - - if (get_resource_type($this->resource) === Socket::TYPE_UNKNOWN) - { - return ''; - } - - $name = stream_socket_get_name($this->resource, $wantPeer); - $type = $this->getStreamType(); - - switch ($type) - { - case Socket::TYPE_UNIX: - $transport = 'unix://'; - $endpoint = $transport . $name; - break; - - case Socket::TYPE_TCP: - $transport = 'tcp://'; - if (substr_count($name, ':') > 1) - { - $parts = explode(':', $name); - $count = count($parts); - $port = $parts[$count - 1]; - unset($parts[$count - 1]); - $endpoint = $transport.'[' . implode(':', $parts) . ']:' . $port; - } - else - { - $endpoint = $transport . $name; - } - break; - - case Socket::TYPE_UDP: - $transport = 'udp://'; - $endpoint = $transport . $name; - break; - - default: - $endpoint = ''; - } - - $this->cachedEndpoint[$wantIndex] = $endpoint; - - return $endpoint; - } - - /** - * @override - * @inheritDoc - */ - private function encrypt($method) - { - $type = $this->selectCryptoType($method); - - if ($type === self::CRYPTO_TYPE_UNKNOWN) - { - throw new ExecutionException('Socket encryption method is invalid!'); - } - - $resource = $this->getResource(); - - if ($type === self::CRYPTO_TYPE_SERVER && $this->cryptoMethod === 0) - { - $raw = @stream_socket_recvfrom($resource, 11, STREAM_PEEK); - - if ($raw === '') - { - return; - } - - if (11 > strlen($raw)) - { - throw new ExecutionException('Failed to read crypto handshake.'); - } - - $data = unpack('ctype/nversion/nlength/Nembed/nmax-version', $raw); - if (0x16 !== $data['type']) - { - throw new ExecutionException('Invalid crypto handshake.'); - } - - // Check if version was available in $method. - $version = $this->selectCryptoVersion($data['max-version']); - if ($method & $version) - { - $method = $version; - } - } - - $this->cryptoMethod = $method; - $result = @stream_socket_enable_crypto($resource, true, $this->cryptoMethod); - - if ($result === 0) - { - return; - } - else if (!$result) - { - $message = 'Socket encryption failed.'; - if ($error = error_get_last()) - { - $message .= sprintf(' Errno: %d; %s', $error['type'], $error['message']); - } - throw new ExecutionException($message); - } - else - { - $this->crypto = $this->cryptoMethod; - } - } - - /** - * Checks type of crypto. - * - * @param int $version - * @return int - */ - private function selectCryptoType($version) - { - switch ($version) - { - case STREAM_CRYPTO_METHOD_SSLv3_SERVER: - case STREAM_CRYPTO_METHOD_TLSv1_0_SERVER: - case STREAM_CRYPTO_METHOD_TLSv1_1_SERVER: - case STREAM_CRYPTO_METHOD_TLSv1_2_SERVER: - return self::CRYPTO_TYPE_SERVER; - - case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: - case STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT: - case STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT: - case STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT: - return self::CRYPTO_TYPE_CLIENT; - - default: - return self::CRYPTO_TYPE_UNKNOWN; - } - } - - /** - * Returns highest supported crypto method constant based on protocol version identifier. - * - * @param int $version - * @return int - */ - private function selectCryptoVersion($version) - { - switch ($version) - { - case 0x300: return STREAM_CRYPTO_METHOD_SSLv3_SERVER; - case 0x301: return STREAM_CRYPTO_METHOD_TLSv1_0_SERVER; - case 0x302: return STREAM_CRYPTO_METHOD_TLSv1_1_SERVER; - default: return STREAM_CRYPTO_METHOD_TLSv1_2_SERVER; - } - } -} diff --git a/src/Socket/SocketInterface.php b/src/Socket/SocketInterface.php deleted file mode 100644 index 91c055f..0000000 --- a/src/Socket/SocketInterface.php +++ /dev/null @@ -1,97 +0,0 @@ -configure($config); - $this->endpoint = $endpointOrResource; - $this->socket = null; - $this->loop = $loop; - $this->started = false; - $this->paused = true; - } - - /** - * - */ - public function __destruct() - { - $this->handleClose(); - - parent::__destruct(); - - unset($this->socket); - unset($this->loop); - unset($this->started); - unset($this->paused); - unset($this->config); - unset($this->endpoint); - } - - /** - * @override - * @inheritDoc - */ - public function start() - { - if ($this->isOpen()) - { - return; - } - - try - { - if (!is_resource($this->endpoint)) - { - $this->socket = $this->createServer($this->endpoint, $this->config); - } - else - { - $this->socket = &$this->endpoint; - } - - $this->resume(); - $this->started = true; - } - catch (Error $ex) - { - throw new InstantiationException('SocketListener could not be created.', $ex); - } - catch (Exception $ex) - { - throw new InstantiationException('SocketListener could not be created.', $ex); - } - } - - /** - * @override - * @inheritDoc - */ - public function stop() - { - $this->close(); - } - - /** - * @override - * @inheritDoc - */ - public function getLocalEndpoint() - { - return $this->parseEndpoint(); - } - - /** - * @override - * @inheritDoc - */ - public function getLocalAddress() - { - $endpoint = explode('://', $this->getLocalEndpoint(), 2); - - return isset($endpoint[1]) ? $endpoint[1] : $endpoint[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalHost() - { - $address = explode(':', $this->getLocalAddress(), 2); - - return $address[0]; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalPort() - { - $address = explode(':', $this->getLocalAddress(), 2); - - return isset($address[1]) ? $address[1] : ''; - } - - /** - * @override - * @inheritDoc - */ - public function getLocalProtocol() - { - $endpoint = explode('://', $this->getLocalEndpoint(), 2); - - return isset($endpoint[0]) ? $endpoint[0]:''; - } - - /** - * @override - * @inheritDoc - */ - public function getResource() - { - return $this->socket; - } - - /** - * @override - * @inheritDoc - */ - public function getResourceId() - { - return (int) $this->socket; - } - - /** - * @override - * @inheritDoc - */ - public function getMetadata() - { - if ($this->isOpen()) - { - return stream_get_meta_data($this->socket); - } - return []; - } - - /** - * @override - * @inheritDoc - */ - public function getStreamType() - { - $data = $this->getMetadata(); - - return isset($data['stream_type']) ? $data['stream_type'] : 'undefined'; - } - - /** - * @override - * @inheritDoc - */ - public function getWrapperType() - { - $data = $this->getMetadata(); - - return isset($data['wrapper_type']) ? $data['wrapper_type'] : 'undefined'; - } - - /** - * @override - * @inheritDoc - */ - public function isOpen() - { - return $this->started; - } - - /** - * @override - * @inheritDoc - */ - public function isPaused() - { - return $this->paused; - } - - /** - * @override - * @inheritDoc - */ - public function isEncrypted() - { - return isset($this->config['ssl']) && $this->config['ssl'] === true; - } - - /** - * @override - * @inheritDoc - */ - public function close() - { - if (!$this->isOpen()) - { - return; - } - - $this->started = false; - - $this->emit('close', [ $this ]); - $this->handleClose(); - $this->emit('done', [ $this ]); - } - - /** - * @override - * @inheritDoc - */ - public function pause() - { - if (!$this->paused) - { - $this->paused = true; - - if (isset($this->loop)) - { - $this->loop->removeReadStream($this->socket); - } - } - } - - /** - * @override - * @inheritDoc - */ - public function resume() - { - if ($this->paused) - { - $this->paused = false; - - if (isset($this->loop)) - { - $this->loop->addReadStream($this->socket, [ $this, 'handleConnect' ]); - } - } - } - - /** - * Create the server resource. - * - * This method creates server resource for socket connections. - * - * @param string $endpoint - * @param mixed[] $config - * @return resource - * @throws LogicException - */ - protected function createServer($endpoint, $config = []) - { - if (stripos($endpoint, 'unix://') !== false) - { - if ($endpoint[7] === DIRECTORY_SEPARATOR) - { - $path = substr($endpoint, 7); - } - else - { - $path = getcwd() . DIRECTORY_SEPARATOR . substr($endpoint, 7); - $endpoint = 'unix://' . $path; - } - - if (file_exists($path)) - { - unlink($path); - } - } - - $ssl = $this->config['ssl']; - $name = $this->config['ssl_name']; - $verifySign = $this->config['ssl_verify_sign']; - $verifyPeer = $this->config['ssl_verify_peer']; - $verifyDepth = $this->config['ssl_verify_depth']; - - $backlog = (int) (isset($config['backlog']) ? $config['backlog'] : self::DEFAULT_BACKLOG); - $reuseaddr = (bool) (isset($config['reuseaddr']) ? $config['reuseaddr'] : false); - $reuseport = (bool) (isset($config['reuseport']) ? $config['reuseport'] : false); - - $context['socket'] = [ - 'bindto' => $endpoint, - 'backlog' => $backlog, - 'ipv6_v6only' => true, - 'so_reuseaddr' => $reuseaddr, - 'so_reuseport' => $reuseport, - ]; - - $context['ssl'] = [ - 'allow_self_signed' => !$verifySign, - 'verify_peer' => $verifyPeer, - 'verify_peer_name' => $verifyPeer, - 'verify_depth' => $verifyDepth, - 'disable_compression' => true, - 'SNI_enabled' => $name !== '', - 'SNI_server_name' => $name, - 'peer_name' => $name, - ]; - - if ($ssl && isset($config['ssl_cert'])) - { - $context['ssl']['local_cert'] = $config['ssl_cert']; - } - - if ($ssl && isset($config['ssl_key'])) - { - $context['ssl']['local_pk'] = $config['ssl_key']; - } - - if ($ssl && isset($config['ssl_secret'])) - { - $context['ssl']['passphrase'] = $config['ssl_secret']; - } - - $bitmask = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; - - $context = stream_context_create($context); - - // Error reporting suppressed since stream_socket_server() emits an E_WARNING on failure. - $socket = stream_socket_server( - $endpoint, - $errno, - $errstr, - $bitmask, - $context - ); - - if (!$socket || $errno) - { - throw new LogicException( - sprintf('Could not bind socket [%s] because of error [%d; %s]', $endpoint, $errno, $errstr) - ); - } - - return $socket; - } - - /** - * Create the client resource. - * - * This method creates client resource for socket connections. - * - * @param resource $resource - * @param string[] $config - * @return SocketInterface - */ - protected function createClient($resource, $config = []) - { - return new Socket($resource, $this->loop, $config); - } - - /** - * Handle the new connection. - * - * @internal - */ - public function handleConnect() - { - $socket = @stream_socket_accept($this->socket); - - if ($socket === false) - { - $this->emit('error', [ $this, new ReadException('Socket could not accept new connection.') ]); - return; - } - - $client = null; - $ex = null; - - try - { - $client = $this->createClient($socket, [ - 'ssl' => $this->config['ssl'], - 'ssl_method' => $this->config['ssl_method'], - ]); - - $this->emit('connect', [ $this, $client ]); - } - catch (Error $ex) - {} - catch (Exception $ex) - {} - - if ($ex !== null) - { - $this->handleDisconnect($socket); - $this->emit('error', [ $this, new ReadException('Socket could not wrap new connection!') ]); - } - } - - private function handleDisconnect($resource) - { - if (is_resource($resource)) - { - // http://chat.stackoverflow.com/transcript/message/7727858#7727858 - stream_socket_shutdown($resource, STREAM_SHUT_RDWR); - stream_set_blocking($resource, 0); - fclose($resource); - } - } - - /** - * Handle closing event. - * - * @internal - */ - public function handleClose() - { - $this->pause(); - - if (is_resource($this->socket)) - { - if ($this->getStreamType() === Socket::TYPE_UNIX) - { - $path = substr($this->parseEndpoint(), 7); - unlink($path); - } - fclose($this->socket); - } - } - - /** - * Configure socket. - * - * @param string[] $config - */ - private function configure($config = []) - { - $this->config = $config; - - $this->configureVariable('ssl'); - $this->configureVariable('ssl_method'); - $this->configureVariable('ssl_name'); - $this->configureVariable('ssl_verify_sign'); - $this->configureVariable('ssl_verify_peer'); - $this->configureVariable('ssl_verify_depth'); - } - - /** - * Configure static key - * - * @param $configKey - */ - private function configureVariable($configKey) - { - $configStaticKey = 'CONFIG_DEFAULT_' . strtoupper($configKey); - $this->config[$configKey] = isset($this->config[$configKey]) ? $this->config[$configKey] : constant("static::$configStaticKey"); - } - - /** - * @return string - */ - private function parseEndpoint() - { - if ($this->isOpen()) - { - $name = stream_socket_get_name($this->socket, false); - $type = $this->getStreamType(); - - switch ($type) - { - case Socket::TYPE_UNIX: - $transport = 'unix://'; - $endpoint = $transport . $name; - break; - - case Socket::TYPE_TCP: - $transport = 'tcp://'; - if (substr_count($name, ':') > 1) - { - $parts = explode(':', $name); - $count = count($parts); - $port = $parts[$count - 1]; - unset($parts[$count - 1]); - $endpoint = $transport.'[' . implode(':', $parts) . ']:' . $port; - } - else - { - $endpoint = $transport . $name; - } - break; - - case Socket::TYPE_UDP: - $transport = 'udp://'; - $endpoint = $transport . $name; - break; - - default: - $endpoint = ''; - } - - return $endpoint; - } - else - { - return is_string($this->endpoint) ? $this->endpoint : ''; - } - } -} diff --git a/src/Socket/SocketListenerInterface.php b/src/Socket/SocketListenerInterface.php deleted file mode 100644 index f15efa9..0000000 --- a/src/Socket/SocketListenerInterface.php +++ /dev/null @@ -1,71 +0,0 @@ -