Merge branch 'refs/heads/master' into 0.4
This commit is contained in:
commit
b6ec4aa904
@ -6,9 +6,6 @@ php:
|
||||
- 5.5
|
||||
- hhvm
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
before_script:
|
||||
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "session.serialize_handler = php" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
|
||||
- composer install --dev --prefer-source
|
||||
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -8,6 +8,23 @@ CHANGELOG
|
||||
|
||||
---
|
||||
|
||||
* 0.3.2 (2014-06-08)
|
||||
|
||||
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
||||
* BF: Fixed accidental BC break from v0.3.1
|
||||
* Added autoDelete parameter to Topic to destroy when empty of connections
|
||||
* Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App)
|
||||
* Normalized Exceptions in WAMP
|
||||
|
||||
* 0.3.1 (2014-05-26)
|
||||
|
||||
* Added query parameter support to Router, set in HTTP request (ws://server?hello=world)
|
||||
* HHVM compatibility
|
||||
* BF: React/0.4 support; CPU starvation bug fixes
|
||||
* BF: Allow App::route to ignore Host header
|
||||
* Added expected filters to WAMP Topic broadcast method
|
||||
* Resource cleanup in WAMP TopicManager
|
||||
|
||||
* 0.3.0 (2013-10-14)
|
||||
|
||||
* Added the `App` class to help making Ratchet so easy to use it's silly
|
||||
|
@ -9,7 +9,7 @@ Build up your application through simple interfaces and re-use your application
|
||||
##WebSocket Compliance
|
||||
|
||||
* Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time)
|
||||
* Tested on Chrome 13 - 31, Firefox 6 - 26, Safari 5.0.1 - 6.1, iOS 4.2 - 7
|
||||
* Tested on Chrome 13+, Firefox 6+, Safari 5+, iOS 4.2+, IE 8+
|
||||
* Ratchet [passes](http://socketo.me/reports/ab/) the [Autobahn Testsuite](http://autobahn.ws/testsuite) (non-binary messages)
|
||||
|
||||
##Requirements
|
||||
@ -19,7 +19,7 @@ To avoid proxy/firewall blockage it's recommended WebSockets are requested on po
|
||||
In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines.
|
||||
You can find more details in the [server conf docs](http://socketo.me/docs/deploy#serverconfiguration).
|
||||
|
||||
PHP 5.3.9 (or higher) is required. If you have access, PHP 5.4 is *highly* recommended for its performance improvements.
|
||||
PHP 5.3.9 (or higher) is required. If you have access, PHP 5.4 (or higher) is *highly* recommended for its performance improvements.
|
||||
|
||||
### Documentation
|
||||
|
||||
|
@ -107,7 +107,9 @@ class App {
|
||||
$decorated = $controller;
|
||||
}
|
||||
|
||||
$httpHost = $httpHost ?: $this->httpHost;
|
||||
if ($httpHost === null) {
|
||||
$httpHost = $this->httpHost;
|
||||
}
|
||||
|
||||
$allowedOrigins = array_values($allowedOrigins);
|
||||
if (0 === count($allowedOrigins)) {
|
||||
|
@ -5,7 +5,7 @@ namespace Ratchet;
|
||||
* The version of Ratchet being used
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = 'Ratchet/0.3';
|
||||
const VERSION = 'Ratchet/0.3.2';
|
||||
|
||||
/**
|
||||
* A proxy object representing a connection to the application
|
||||
|
@ -72,6 +72,18 @@ class FlashPolicy implements MessageComponentInterface {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all domains from the allowed access list.
|
||||
*
|
||||
* @return \Ratchet\Server\FlashPolicy
|
||||
*/
|
||||
public function clearAllowedAccess() {
|
||||
$this->_access = array();
|
||||
$this->_cacheValid = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
|
||||
* domain policy files other than the master policy file located in the target domain's root and named
|
||||
|
@ -27,6 +27,12 @@ class IoServer {
|
||||
*/
|
||||
protected $handlers;
|
||||
|
||||
/**
|
||||
* The socket server the Ratchet Application is run off of
|
||||
* @var \React\Socket\ServerInterface
|
||||
*/
|
||||
public $socket;
|
||||
|
||||
/**
|
||||
* @param \Ratchet\MessageComponentInterface $app The Ratchet application stack to host
|
||||
* @param \React\Socket\ServerInterface $socket The React socket server to run the Ratchet application off of
|
||||
@ -42,6 +48,7 @@ class IoServer {
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->app = $app;
|
||||
$this->socket = $socket;
|
||||
|
||||
$socket->on('connection', array($this, 'handleConnect'));
|
||||
|
||||
|
@ -57,14 +57,7 @@ class SessionProvider implements HttpServerInterface {
|
||||
$this->setOptions($options);
|
||||
|
||||
if (null === $serializer) {
|
||||
// Temporarily fixing HHVM issue w/ reading ini values
|
||||
$handler_name = ini_get('session.serialize_handler');
|
||||
if ('' === $handler_name) {
|
||||
trigger_error('ini value session.seralize_handler was empty, assuming "php" - tmp hack/fix, bad things might happen', E_USER_WARNING);
|
||||
$handler_name = 'php';
|
||||
}
|
||||
|
||||
$serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase($handler_name)}Handler"; // awesome/terrible hack, eh?
|
||||
$serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
|
||||
if (!class_exists($serialClass)) {
|
||||
throw new \RuntimeException('Unable to parse session serialize handler');
|
||||
}
|
||||
|
@ -79,8 +79,8 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \Exception
|
||||
* @throws JsonException
|
||||
* @throws \Ratchet\Wamp\Exception
|
||||
* @throws \Ratchet\Wamp\JsonException
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
$from = $this->connections[$from];
|
||||
@ -90,7 +90,7 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||
}
|
||||
|
||||
if (!is_array($json) || $json !== array_values($json)) {
|
||||
throw new \UnexpectedValueException("Invalid WAMP message format");
|
||||
throw new Exception("Invalid WAMP message format");
|
||||
}
|
||||
|
||||
switch ($json[0]) {
|
||||
@ -134,7 +134,7 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Invalid message type');
|
||||
throw new Exception('Invalid WAMP message type');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,13 @@ use Ratchet\ConnectionInterface;
|
||||
* A topic/channel containing connections that have subscribed to it
|
||||
*/
|
||||
class Topic implements \IteratorAggregate, \Countable {
|
||||
/**
|
||||
* If true the TopicManager will destroy this object if it's ever empty of connections
|
||||
* @deprecated in v0.4
|
||||
* @type bool
|
||||
*/
|
||||
public $autoDelete = false;
|
||||
|
||||
private $id;
|
||||
|
||||
private $subscribers;
|
||||
|
@ -54,13 +54,12 @@ class TopicManager implements WsServerInterface, WampServerInterface {
|
||||
public function onUnsubscribe(ConnectionInterface $conn, $topic) {
|
||||
$topicObj = $this->getTopic($topic);
|
||||
|
||||
if ($conn->WAMP->subscriptions->contains($topicObj)) {
|
||||
$conn->WAMP->subscriptions->detach($topicObj);
|
||||
} else {
|
||||
if (!$conn->WAMP->subscriptions->contains($topicObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->topicLookup[$topic]->remove($conn);
|
||||
$this->cleanTopic($topicObj, $conn);
|
||||
|
||||
$this->app->onUnsubscribe($conn, $topicObj);
|
||||
}
|
||||
|
||||
@ -77,11 +76,8 @@ class TopicManager implements WsServerInterface, WampServerInterface {
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->app->onClose($conn);
|
||||
|
||||
foreach ($this->topicLookup as $topic => $storage) {
|
||||
$storage->remove($conn);
|
||||
if (0 === $storage->count()) {
|
||||
unset($this->topicLookup[$topic]);
|
||||
}
|
||||
foreach ($this->topicLookup as $topic) {
|
||||
$this->cleanTopic($topic, $conn);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,4 +110,16 @@ class TopicManager implements WsServerInterface, WampServerInterface {
|
||||
|
||||
return $this->topicLookup[$topic];
|
||||
}
|
||||
|
||||
protected function cleanTopic(Topic $topic, ConnectionInterface $conn) {
|
||||
if ($conn->WAMP->subscriptions->contains($topic)) {
|
||||
$conn->WAMP->subscriptions->detach($topic);
|
||||
}
|
||||
|
||||
$this->topicLookup[$topic->getId()]->remove($conn);
|
||||
|
||||
if ($topic->autoDelete && 0 === $topic->count()) {
|
||||
unset($this->topicLookup[$topic->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,9 @@ class WampServer implements MessageComponentInterface, WsServerInterface {
|
||||
public function onMessage(ConnectionInterface $conn, $msg) {
|
||||
try {
|
||||
$this->wampProtocol->onMessage($conn, $msg);
|
||||
} catch (JsonException $je) {
|
||||
} catch (Exception $we) {
|
||||
$conn->close(1007);
|
||||
} catch (\UnexpectedValueException $uve) {
|
||||
} catch (JsonException $je) {
|
||||
$conn->close(1007);
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,19 @@ use Ratchet\AbstractConnectionDecorator;
|
||||
*/
|
||||
class Connection extends AbstractConnectionDecorator {
|
||||
public function send($msg) {
|
||||
if (!$this->WebSocket->closing) {
|
||||
$this->getConnection()->send(chr(0) . $msg . chr(255));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function close() {
|
||||
if (!$this->WebSocket->closing) {
|
||||
$this->getConnection()->send(chr(255));
|
||||
$this->getConnection()->close();
|
||||
|
||||
$this->WebSocket->closing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,17 @@ use Ratchet\WebSocket\Version\DataInterface;
|
||||
* @property \StdClass $WebSocket
|
||||
*/
|
||||
class Connection extends AbstractConnectionDecorator {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function send($msg) {
|
||||
if (!$this->WebSocket->closing) {
|
||||
if (!($msg instanceof DataInterface)) {
|
||||
$msg = new Frame($msg);
|
||||
}
|
||||
|
||||
$this->getConnection()->send($msg->getContents());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -22,6 +27,10 @@ class Connection extends AbstractConnectionDecorator {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close($code = 1000) {
|
||||
if ($this->WebSocket->closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($code instanceof DataInterface) {
|
||||
$this->send($code);
|
||||
} else {
|
||||
@ -29,5 +38,7 @@ class Connection extends AbstractConnectionDecorator {
|
||||
}
|
||||
|
||||
$this->getConnection()->close();
|
||||
|
||||
$this->WebSocket->closing = true;
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ class WsServer implements HttpServerInterface {
|
||||
$conn->WebSocket = new \StdClass;
|
||||
$conn->WebSocket->request = $request;
|
||||
$conn->WebSocket->established = false;
|
||||
$conn->WebSocket->closing = false;
|
||||
|
||||
$this->attemptUpgrade($conn);
|
||||
}
|
||||
@ -87,6 +88,10 @@ class WsServer implements HttpServerInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
if ($from->WebSocket->closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (true === $from->WebSocket->established) {
|
||||
return $from->WebSocket->version->onMessage($this->connections[$from], $msg);
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase {
|
||||
* @dataProvider badFormatProvider
|
||||
*/
|
||||
public function testValidJsonButInvalidProtocol($message) {
|
||||
$this->setExpectedException('\UnexpectedValueException');
|
||||
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||
|
||||
$conn = $this->newConn();
|
||||
$this->_comp->onOpen($conn);
|
||||
|
@ -159,9 +159,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase {
|
||||
$this->mngr->onClose($this->conn);
|
||||
}
|
||||
|
||||
public function testConnIsRemovedFromTopicOnClose() {
|
||||
$name = 'State testing';
|
||||
|
||||
protected function topicProvider($name) {
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
@ -171,14 +169,42 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||
|
||||
return array($topic, $attribute);
|
||||
}
|
||||
|
||||
public function testConnIsRemovedFromTopicOnClose() {
|
||||
$name = 'State Testing';
|
||||
list($topic, $attribute) = $this->topicProvider($name);
|
||||
|
||||
$this->assertCount(1, $attribute->getValue($this->mngr));
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
$this->mngr->onClose($this->conn);
|
||||
|
||||
$this->assertFalse($topic->has($this->conn));
|
||||
}
|
||||
|
||||
$this->assertCount(0, $attribute->getValue($this->mngr));
|
||||
public static function topicConnExpectationProvider() {
|
||||
return array(
|
||||
array(true, 'onClose', 0)
|
||||
, array(true, 'onUnsubscribe', 0)
|
||||
, array(false, 'onClose', 1)
|
||||
, array(false, 'onUnsubscribe', 1)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider topicConnExpectationProvider
|
||||
*/
|
||||
public function testTopicRetentionFromLeavingConnections($autoDelete, $methodCall, $expectation) {
|
||||
$topicName = 'checkTopic';
|
||||
list($topic, $attribute) = $this->topicProvider($topicName);
|
||||
$topic->autoDelete = $autoDelete;
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $topicName);
|
||||
call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName));
|
||||
|
||||
$this->assertCount($expectation, $attribute->getValue($this->mngr));
|
||||
}
|
||||
|
||||
public function testOnErrorBubbles() {
|
||||
|
Loading…
Reference in New Issue
Block a user