diff --git a/README.md b/README.md index 37ead8d..8e83e37 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,15 @@ Connection sockets will use IPv6 by default where available. If you need to forc $connection->setOption('force-ipv4', true); ``` +### allow-self-signed + +By default all ssl connections only accept officially signed certificates. Sometimes you need to connect to a irc server that uses a self signed certificate. If you need to allow connections to servers using a self signed certificate, set this option to `true`. + +```php +setOption('allow-self-signed', true); +``` + ### transport By default, a standard TCP socket is used. For IRC servers that support TLS or SSL, specify an [appropriate transport](http://www.php.net/manual/en/transports.inet.php). diff --git a/composer.json b/composer.json index 23deec9..e4d8dae 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "phergie/phergie-irc-connection": "~2.0", "react/event-loop": "~0.4.0", "react/stream": "~0.4.2", - "react/socket-client": "~0.4.2", + "react/socket-client": "^0.5", "react/dns": "~0.4.0", "react/promise": "~2.0", "psr/log": "~1.0", diff --git a/composer.lock b/composer.lock index 007af17..77e7ab7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c38446c0b5dece82b3ad6cb9ec5931c2", - "content-hash": "6f01fbb3e6e17957ea27377241ee2b05", + "content-hash": "0c7fd26e27ee86a153ae480d255ba20c", "packages": [ { "name": "evenement/evenement", @@ -51,7 +50,7 @@ "event-dispatcher", "event-emitter" ], - "time": "2012-11-02 14:49:47" + "time": "2012-11-02T14:49:47+00:00" }, { "name": "monolog/monolog", @@ -128,7 +127,7 @@ "logging", "psr-3" ], - "time": "2015-10-14 12:51:02" + "time": "2015-10-14T12:51:02+00:00" }, { "name": "phergie/phergie-irc-connection", @@ -167,7 +166,7 @@ "irc", "server" ], - "time": "2015-05-26 15:14:31" + "time": "2015-05-26T15:14:31+00:00" }, { "name": "phergie/phergie-irc-generator", @@ -204,7 +203,7 @@ "generator", "irc" ], - "time": "2015-07-16 16:26:11" + "time": "2015-07-16T16:26:11+00:00" }, { "name": "phergie/phergie-irc-parser", @@ -241,7 +240,7 @@ "irc", "parser" ], - "time": "2015-07-16 16:26:41" + "time": "2015-07-16T16:26:41+00:00" }, { "name": "psr/log", @@ -279,7 +278,7 @@ "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2012-12-21T11:40:51+00:00" }, { "name": "react/cache", @@ -318,7 +317,7 @@ "keywords": [ "cache" ], - "time": "2014-02-02 01:11:26" + "time": "2014-02-02T01:11:26+00:00" }, { "name": "react/dns", @@ -360,7 +359,7 @@ "dns", "dns-resolver" ], - "time": "2014-04-12 14:09:10" + "time": "2014-04-12T14:09:10+00:00" }, { "name": "react/event-loop", @@ -403,7 +402,7 @@ "keywords": [ "event-loop" ], - "time": "2014-02-26 17:36:58" + "time": "2014-02-26T17:36:58+00:00" }, { "name": "react/promise", @@ -447,7 +446,57 @@ } ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2015-07-03 13:48:55" + "time": "2015-07-03T13:48:55+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "ddedc67bfd7f579fc83e66ff67e3564b179297dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/ddedc67bfd7f579fc83e66ff67e3564b179297dd", + "reference": "ddedc67bfd7f579fc83e66ff67e3564b179297dd", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "~0.4.0|~0.3.0", + "react/promise": "~2.1|~1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Trivial timeout implementation for Promises", + "homepage": "https://github.com/react/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "time": "2016-12-27T08:12:19+00:00" }, { "name": "react/socket", @@ -488,35 +537,34 @@ "keywords": [ "Socket" ], - "time": "2014-05-25 17:02:16" + "time": "2014-05-25T17:02:16+00:00" }, { "name": "react/socket-client", - "version": "v0.4.4", + "version": "v0.5.1", "source": { "type": "git", "url": "https://github.com/reactphp/socket-client.git", - "reference": "3ca814d7e03e2a5bffeaa5773423107957a8aece" + "reference": "3cb406a14008c092207d95a63ebe7739f74843bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket-client/zipball/3ca814d7e03e2a5bffeaa5773423107957a8aece", - "reference": "3ca814d7e03e2a5bffeaa5773423107957a8aece", + "url": "https://api.github.com/repos/reactphp/socket-client/zipball/3cb406a14008c092207d95a63ebe7739f74843bf", + "reference": "3cb406a14008c092207d95a63ebe7739f74843bf", "shasum": "" }, "require": { - "php": ">=5.4.0", - "react/dns": "0.4.*", - "react/event-loop": "0.4.*", - "react/promise": "~2.0", - "react/stream": "0.4.*" + "php": ">=5.3.0", + "react/dns": "0.4.*|0.3.*", + "react/event-loop": "0.4.*|0.3.*", + "react/promise": "^2.1 || ^1.2", + "react/promise-timer": "~1.0", + "react/stream": "0.4.*|0.3.*" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } + "require-dev": { + "clue/block-react": "~1.0" }, + "type": "library", "autoload": { "psr-4": { "React\\SocketClient\\": "src" @@ -530,7 +578,7 @@ "keywords": [ "Socket" ], - "time": "2015-09-23 22:40:04" + "time": "2016-11-20T00:11:24+00:00" }, { "name": "react/stream", @@ -578,7 +626,7 @@ "pipe", "stream" ], - "time": "2015-10-07 18:32:58" + "time": "2015-10-07T18:32:58+00:00" } ], "packages-dev": [ @@ -638,7 +686,7 @@ "codeclimate", "coverage" ], - "time": "2015-09-08 14:22:33" + "time": "2015-09-08T14:22:33+00:00" }, { "name": "doctrine/instantiator", @@ -692,7 +740,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "guzzle/guzzle", @@ -787,7 +835,8 @@ "rest", "web service" ], - "time": "2015-03-18 18:23:50" + "abandoned": "guzzlehttp/guzzle", + "time": "2015-03-18T18:23:50+00:00" }, { "name": "phake/phake", @@ -845,7 +894,7 @@ "mock", "testing" ], - "time": "2015-11-30 17:02:04" + "time": "2015-11-30T17:02:04+00:00" }, { "name": "phergie/phergie-irc-bot-react-development", @@ -882,7 +931,7 @@ "irc", "react" ], - "time": "2015-11-10 00:38:33" + "time": "2015-11-10T00:38:33+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -931,7 +980,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2015-02-03 12:10:50" + "time": "2015-02-03T12:10:50+00:00" }, { "name": "phpspec/prophecy", @@ -991,7 +1040,7 @@ "spy", "stub" ], - "time": "2015-08-13 10:07:40" + "time": "2015-08-13T10:07:40+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1053,7 +1102,7 @@ "testing", "xunit" ], - "time": "2015-10-06 15:47:00" + "time": "2015-10-06T15:47:00+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1100,7 +1149,7 @@ "filesystem", "iterator" ], - "time": "2015-06-21 13:08:43" + "time": "2015-06-21T13:08:43+00:00" }, { "name": "phpunit/php-text-template", @@ -1141,7 +1190,7 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", @@ -1182,7 +1231,7 @@ "keywords": [ "timer" ], - "time": "2015-06-21 08:01:12" + "time": "2015-06-21T08:01:12+00:00" }, { "name": "phpunit/php-token-stream", @@ -1231,7 +1280,7 @@ "keywords": [ "tokenizer" ], - "time": "2015-09-15 10:49:45" + "time": "2015-09-15T10:49:45+00:00" }, { "name": "phpunit/phpunit", @@ -1303,7 +1352,7 @@ "testing", "xunit" ], - "time": "2015-12-12 07:45:58" + "time": "2015-12-12T07:45:58+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1359,7 +1408,7 @@ "mock", "xunit" ], - "time": "2015-10-02 06:51:40" + "time": "2015-10-02T06:51:40+00:00" }, { "name": "satooshi/php-coveralls", @@ -1427,7 +1476,7 @@ "github", "test" ], - "time": "2013-05-04 08:07:33" + "time": "2013-05-04T08:07:33+00:00" }, { "name": "sebastian/comparator", @@ -1491,7 +1540,7 @@ "compare", "equality" ], - "time": "2015-07-26 15:48:44" + "time": "2015-07-26T15:48:44+00:00" }, { "name": "sebastian/diff", @@ -1543,7 +1592,7 @@ "keywords": [ "diff" ], - "time": "2015-12-08 07:14:41" + "time": "2015-12-08T07:14:41+00:00" }, { "name": "sebastian/environment", @@ -1593,7 +1642,7 @@ "environment", "hhvm" ], - "time": "2015-12-02 08:37:27" + "time": "2015-12-02T08:37:27+00:00" }, { "name": "sebastian/exporter", @@ -1659,7 +1708,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2015-06-21T07:55:53+00:00" }, { "name": "sebastian/global-state", @@ -1710,7 +1759,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12 03:26:01" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/recursion-context", @@ -1763,7 +1812,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-11-11 19:50:13" + "time": "2015-11-11T19:50:13+00:00" }, { "name": "sebastian/version", @@ -1798,7 +1847,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" + "time": "2015-06-21T13:59:46+00:00" }, { "name": "symfony/config", @@ -1848,7 +1897,7 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2015-11-23 20:38:01" + "time": "2015-11-23T20:38:01+00:00" }, { "name": "symfony/console", @@ -1908,7 +1957,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-11-30 12:35:10" + "time": "2015-11-30T12:35:10+00:00" }, { "name": "symfony/event-dispatcher", @@ -1968,7 +2017,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2015-10-30T20:15:42+00:00" }, { "name": "symfony/filesystem", @@ -2017,7 +2066,7 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-11-23 10:19:46" + "time": "2015-11-23T10:19:46+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2073,7 +2122,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/stopwatch", @@ -2122,7 +2171,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2015-10-30T20:15:42+00:00" }, { "name": "symfony/yaml", @@ -2171,7 +2220,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-11-30 12:36:17" + "time": "2015-11-30T12:36:17+00:00" } ], "aliases": [], diff --git a/src/Client.php b/src/Client.php old mode 100644 new mode 100755 index 5209787..f0a72c2 --- a/src/Client.php +++ b/src/Client.php @@ -259,6 +259,17 @@ protected function getForceIpv4Flag(ConnectionInterface $connection) return (boolean) $connection->getOption('force-ipv4') ?: false; } + /** + * Extracts the value of the allow-self-signed option from a given connection. + * + * @param \Phergie\Irc\ConnectionInterface $connection + * @return boolean TRUE to allow self signed certificates, FALSE otherwise + */ + protected function getAllowSelfSignedFlag(ConnectionInterface $connection) + { + return (boolean) $connection->getOption('allow-self-signed') ?: false; + } + /** * Derives a remote for a given connection. * @@ -315,6 +326,24 @@ protected function getContext(ConnectionInterface $connection) return $context; } + /** + * Derives a set of socket context options for a given secure connection. + * + * @param \Phergie\Irc\ConnectionInterface $connection + * @return array Associative array of context option key-value pairs + */ + protected function getSecureContext(ConnectionInterface $connection) + { + $context = array(); + if ($this->getForceIpv4Flag($connection)) { + $context['bindto'] = '0.0.0.0:0'; + } + if ($this->getAllowSelfSignedFlag($connection)) { + $context['allow_self_signed'] = true; + } + return $context; + } + /** * Returns a stream for a socket connection. * @@ -611,12 +640,12 @@ function($error) use($connection) { * * @return \React\SocketClient\SecureConnector */ - protected function getSecureConnector() + protected function getSecureConnector($connection) { if (!$this->secureConnector) { $loop = $this->getLoop(); $connector = new \React\SocketClient\Connector($loop, $this->getResolver()); - $this->secureConnector = new \React\SocketClient\SecureConnector($connector, $loop); + $this->secureConnector = new \React\SocketClient\SecureConnector($connector, $loop, $this->getSecureContext($connection)); } return $this->secureConnector; } @@ -630,18 +659,10 @@ protected function getSecureConnector() */ protected function addSecureConnection(ConnectionInterface $connection) { - // @see https://github.com/reactphp/socket-client/issues/4 - if ($this->getForceIpv4Flag($connection)) { - throw new Exception( - 'Using the SSL transport and IPv4 together is not currently supported', - Exception::ERR_CONNECTION_STATE_UNSUPPORTED - ); - } - $hostname = $connection->getServerHostname(); $port = $connection->getServerPort(); - $this->getSecureConnector() + $this->getSecureConnector($connection) ->create($hostname, $port) ->then( function(DuplexStreamInterface $stream) use ($connection) { diff --git a/tests/ClientTest.php b/tests/ClientTest.php old mode 100644 new mode 100755 index 92d4a28..c5d165f --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -332,23 +332,21 @@ public function testAddConnectionWithConcreteWriteStream() /** * Tests that adding a connection configured to use the SSL transport and - * force use of IPv4 throws an exception. + * force use of IPv4 does not throw an exception * * @see https://github.com/reactphp/socket-client/issues/4 */ - public function testAddConnectionWithSslTransportAndForceIpv4ThrowsException() + public function testAddConnectionWithSslTransportAndForceIpv4() { $connection = $this->getMockConnectionForAddConnection(); Phake::when($connection)->getOption('transport')->thenReturn('ssl'); Phake::when($connection)->getOption('force-ipv4')->thenReturn(true); - $this->client->setResolver($this->getMockResolver()); + $writeStream = $this->getMockWriteStream(); + Phake::when($this->client)->getWriteStream($connection)->thenReturn($writeStream); - try { - $this->client->addConnection($connection); - $this->fail('Expected exception was not thrown'); - } catch (Exception $e) { - $this->assertSame(Exception::ERR_CONNECTION_STATE_UNSUPPORTED, $e->getCode()); - } + $this->client->setLogger($this->getMockLogger()); + $this->client->setResolver($this->getMockResolver()); + $this->client->addConnection($connection); } /** @@ -366,7 +364,7 @@ public function testAddConnectionWithStreamInitializationError() $exception = new Exception('message'); Phake::when($this->client)->identifyUser($connection, $writeStream)->thenThrow($exception); - Phake::when($this->client)->getSecureConnector()->thenReturn($this->getMockSecureConnector()); + Phake::when($this->client)->getSecureConnector($connection)->thenReturn($this->getMockSecureConnector()); $logger = $this->getMockLogger();