Merge branch 'refs/heads/tickets' into wamp

This commit is contained in:
Chris Boden 2012-04-26 19:49:58 -04:00
commit 56768b5963
21 changed files with 60 additions and 161 deletions

View File

@ -1,11 +1,11 @@
[![Build Status](https://secure.travis-ci.org/cboden/Ratchet.png)](http://travis-ci.org/cboden/Ratchet) [![Build Status](https://secure.travis-ci.org/cboden/Ratchet.png?branch=master)](http://travis-ci.org/cboden/Ratchet)
#Ratchet #Ratchet
A PHP 5.3 (PSR-0 compliant) component library for serving sockets and building socket based applications. A PHP 5.3 (PSR-0 compliant) component library for serving sockets and building socket based applications.
Build up your application through simple interfaces using the decorator and command patterns. Build up your application through simple interfaces using the decorator and command patterns.
Re-use your application without changing any of its code just by combining different components. Re-use your application without changing any of its code just by combining different components.
Ratchet's primary intention is to be used as a WebSocket server (and a client in 0.6). Ratchet's primary intention is to be used as a WebSocket server.
##WebSockets ##WebSockets
@ -14,21 +14,21 @@ Ratchet's primary intention is to be used as a WebSocket server (and a client in
##Requirements ##Requirements
Shell access is required and a dedicated (virtual) machine with root access is recommended. Shell access is required and a dedicated machine with root access is recommended.
To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access. To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access.
Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine (for now). Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine (for now).
Cookies from your domain will be passed to the socket server, allowing you to identify users. Cookies from your domain will be passed to the socket server, allowing you to identify users.
Accessing your website's session data in Ratchet is a [feature in the works](https://github.com/cboden/Ratchet/tree/symfony/sessions). Accessing your website's session data in Ratchet is a [feature in the works](https://github.com/cboden/Ratchet/tree/symfony/sessions).
See https://github.com/cboden/socket-demos for some out-of-the-box working demos using Ratchet. See https://github.com/cboden/Ratchet-examples for some out-of-the-box working demos using Ratchet.
###Future considerations ###Future considerations
Ideally, soon, web servers will start supporting WebSockets to some capacity and PHP will no longer need to run its self from the command line. Ideally, soon, web servers will start supporting WebSockets to some capacity and PHP will no longer need to run its self from the command line.
In theory, the server (like Nginx) would recognize the HTTP handshake request to upgrade the protocol to WebSockets and run/pass data through to a user In theory, the server (like Nginx) would recognize the HTTP handshake request to upgrade the protocol to WebSockets and run/pass data through to a user
configured PHP file. When this happens, you can keep your script the same, just remove the Server Application wrapper and maybe eventually the configured PHP file. When this happens, you can keep your script the same, just remove the IOServerComponent wrapper and maybe eventually the
WebSocket Application wrapper if the servers recognize the protocol message framing. WebSocketComponent wrapper if the servers recognize the protocol message framing.
--- ---
@ -39,9 +39,8 @@ WebSocket Application wrapper if the servers recognize the protocol message fram
namespace MyApps; namespace MyApps;
use Ratchet\Component\MessageComponentInterface; use Ratchet\Component\MessageComponentInterface;
use Ratchet\Resource\ConnectionInterface; use Ratchet\Resource\ConnectionInterface;
use Ratchet\Resource\Socket\BSDSocket as Socket; use Ratchet\Component\Server\IOServerComponent;
use Ratchet\Component\Server\IOServerComponent as Server; use Ratchet\Component\WebSocket\WebSocketComponent;
use Ratchet\Component\WebSocket\WebSocketComponent as WebSocket;
use Ratchet\Resource\Command\Composite as Cmds; use Ratchet\Resource\Command\Composite as Cmds;
use Ratchet\Resource\Command\Action\SendMessage; use Ratchet\Resource\Command\Action\SendMessage;
use Ratchet\Resource\Command\Action\CloseConnection; use Ratchet\Resource\Command\Action\CloseConnection;
@ -84,9 +83,10 @@ class Chat implements MessageComponentInterface {
return new CloseConnection($conn); return new CloseConnection($conn);
} }
} }
// Run the server application through the WebSocket protocol // Run the server application through the WebSocket protocol
$server = new Server(new WebSocket(new Chat)); $server = new IOServerComponent(new WebSocketComponent(new Chat));
$server->run(new Socket, '0.0.0.0', 80); $server->run(80);
``` ```
# php chat.php # php chat.php

View File

@ -2,7 +2,8 @@
"name": "cboden/Ratchet" "name": "cboden/Ratchet"
, "type": "library" , "type": "library"
, "description": "PHP WebSocket server component library" , "description": "PHP WebSocket server component library"
, "keywords": ["WebSocket"] , "keywords": ["WebSockets", "Server", "Ratchet", "Sockets"]
, "website": "http://socketo.me"
, "repository": "https://github.com/cboden/Ratchet" , "repository": "https://github.com/cboden/Ratchet"
, "license": "MIT" , "license": "MIT"
, "authors": [ , "authors": [
@ -18,28 +19,8 @@
, "Ratchet": "src" , "Ratchet": "src"
} }
} }
, "repositories": {
"guzzle": {
"type": "package"
, "package": {
"name": "guzzle"
, "type": "library"
, "version": "2.0.2"
, "autoload": {
"psr-0": {
"Guzzle": "src"
}
}
, "source": {
"url": "https://github.com/guzzle/guzzle.git"
, "type": "git"
, "reference": "ac64abc2c05b921efc4623379c1674a282475ae5"
}
}
}
}
, "require": { , "require": {
"php": ">=5.3.2" "php": ">=5.3.2"
, "guzzle": "2.0.2" , "guzzle/guzzle": "v2.0.2"
} }
} }

20
composer.lock generated
View File

@ -1,14 +1,24 @@
{ {
"hash": "be8f67a3964cca0e4ffe4b98a7527daa", "hash": "bd52a853cdf4e34ae75e805f32ed97ae",
"packages": [ "packages": [
{ {
"package": "guzzle", "package": "doctrine/common",
"version": "2.0.2" "version": "2.2.x-dev",
"source-reference": "1e0aa60d109c630d19543d999f12e2852ef8f932"
}, },
{ {
"package": "symfony/http-foundation", "package": "guzzle/guzzle",
"version": "v2.0.2"
},
{
"package": "symfony/event-dispatcher",
"version": "dev-master", "version": "dev-master",
"source-reference": "b9aceabb83f3d03fe451cdd867d987e863e7a25e" "source-reference": "b98d68d3b8513c62d35504570f09e9d3dc33d083"
},
{
"package": "symfony/validator",
"version": "dev-master",
"source-reference": "53ec97264f909d29ae01665e8345f8fb763b94f3"
} }
], ],
"packages-dev": null, "packages-dev": null,

View File

@ -2,6 +2,7 @@
namespace Ratchet\Component\Server; namespace Ratchet\Component\Server;
use Ratchet\Component\MessageComponentInterface; use Ratchet\Component\MessageComponentInterface;
use Ratchet\Resource\Socket\SocketInterface; use Ratchet\Resource\Socket\SocketInterface;
use Ratchet\Resource\Socket\BSDSocket;
use Ratchet\Resource\ConnectionInterface; use Ratchet\Resource\ConnectionInterface;
use Ratchet\Resource\Connection; use Ratchet\Resource\Connection;
use Ratchet\Resource\Command\CommandInterface; use Ratchet\Resource\Command\CommandInterface;
@ -63,12 +64,17 @@ class IOServerComponent implements MessageComponentInterface {
/* /*
* Run the server infinitely * Run the server infinitely
* @param Ratchet\Resource\Socket\SocketInterface
* @param mixed The address to listen for incoming connections on. "0.0.0.0" to listen from anywhere
* @param int The port to listen to connections on (make sure to run as root if < 1000) * @param int The port to listen to connections on (make sure to run as root if < 1000)
* @param mixed The address to listen for incoming connections on. "0.0.0.0" to listen from anywhere
* @param Ratchet\Resource\Socket\SocketInterface
* @throws Ratchet\Exception * @throws Ratchet\Exception
*/ */
public function run(SocketInterface $host, $address = '127.0.0.1', $port = 1025) { public function run($port, $address = '0.0.0.0', SocketInterface $host = null) {
if (null === $host) {
$host = new BSDSocket;
$host->set_option(SOL_SOCKET, SO_REUSEADDR, 1);
}
$this->_connections[$host->getResource()] = new Connection($host); $this->_connections[$host->getResource()] = new Connection($host);
$this->_resources[] = $host->getResource(); $this->_resources[] = $host->getResource();
@ -148,13 +154,16 @@ class IOServerComponent implements MessageComponentInterface {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function onOpen(ConnectionInterface $conn) { public function onOpen(ConnectionInterface $conn) {
$new_socket = clone $conn->getSocket(); $new_socket = clone $conn->getSocket();
$new_socket->set_nonblock(); $new_socket->set_nonblock();
$new_connection = new Connection($new_socket); $new_connection = new Connection($new_socket);
$new_connection->remoteAddress = $new_socket->getRemoteAddress();
$new_connection->resourceId = (int)substr((string)$new_socket->getResource(), strrpos((string)$new_socket->getResource(), '#') + 1);
$this->_resources[] = $new_connection->getSocket()->getResource(); $this->_resources[] = $new_connection->getSocket()->getResource();
$this->_connections[$new_connection->getSocket()->getResource()] = $new_connection; $this->_connections[$new_connection->getSocket()->getResource()] = $new_connection;
@ -162,14 +171,14 @@ class IOServerComponent implements MessageComponentInterface {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function onMessage(ConnectionInterface $from, $msg) { public function onMessage(ConnectionInterface $from, $msg) {
return $this->_decorating->onMessage($from, $msg); return $this->_decorating->onMessage($from, $msg);
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function onClose(ConnectionInterface $conn) { public function onClose(ConnectionInterface $conn) {
$resource = $conn->getSocket()->getResource(); $resource = $conn->getSocket()->getResource();
@ -182,7 +191,7 @@ class IOServerComponent implements MessageComponentInterface {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function onError(ConnectionInterface $conn, \Exception $e) { public function onError(ConnectionInterface $conn, \Exception $e) {
return $this->_decorating->onError($conn, $e); return $this->_decorating->onError($conn, $e);

View File

@ -52,7 +52,7 @@ class WebSocketComponent implements MessageComponentInterface {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function onOpen(ConnectionInterface $conn) { public function onOpen(ConnectionInterface $conn) {
$conn->WebSocket = new \stdClass; $conn->WebSocket = new \stdClass;

View File

@ -21,7 +21,7 @@ class Runtime extends ActionTemplate {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function execute(ComponentInterface $scope = null) { public function execute(ComponentInterface $scope = null) {
$cmd = $this->_command; $cmd = $this->_command;

View File

@ -30,7 +30,7 @@ class SendMessage extends ActionTemplate {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
* @throws \UnexpectedValueException if a message was not set with setMessage() * @throws \UnexpectedValueException if a message was not set with setMessage()
*/ */
public function execute(ComponentInterface $scope = null) { public function execute(ComponentInterface $scope = null) {

View File

@ -23,7 +23,7 @@ class Composite extends \SplQueue implements CommandInterface {
} }
/** /**
* @{inheritdoc} * {@inheritdoc}
*/ */
public function execute(ComponentInterface $scope = null) { public function execute(ComponentInterface $scope = null) {
$this->setIteratorMode(static::IT_MODE_DELETE); $this->setIteratorMode(static::IT_MODE_DELETE);

View File

@ -7,8 +7,6 @@ use Ratchet\Resource\Socket\SocketInterface;
* This acts as a container to storm data (in memory) about the connection * This acts as a container to storm data (in memory) about the connection
*/ */
class Connection implements ConnectionInterface { class Connection implements ConnectionInterface {
protected $_data = array();
/** /**
* @var Ratchet\Resource\Socket\SocketInterface * @var Ratchet\Resource\Socket\SocketInterface
*/ */
@ -18,13 +16,6 @@ class Connection implements ConnectionInterface {
$this->_socket = $socket; $this->_socket = $socket;
} }
/**
* @return int
*/
public function getID() {
return (int)(string)$this->_socket;
}
/** /**
* This is here because I couldn't figure out a better/easier way to tie a connection and socket together for the server and commands * This is here because I couldn't figure out a better/easier way to tie a connection and socket together for the server and commands
* Anyway, if you're here, it's not recommended you use this/directly interact with the socket in your App... * Anyway, if you're here, it's not recommended you use this/directly interact with the socket in your App...
@ -35,40 +26,4 @@ class Connection implements ConnectionInterface {
public function getSocket() { public function getSocket() {
return $this->_socket; return $this->_socket;
} }
/**
* @{inheritdoc}
*/
public function __set($name, $value) {
$this->_data[$name] = $value;
}
/**
* @{inheritdoc}
*/
public function __get($name) {
if (!$this->__isset($name)) {
throw new \InvalidArgumentException("Attribute '{$name}' not found in Connection {$this->getID()}");
}
if (is_callable($this->_data[$name])) {
return $this->_data[$name]($this);
} else {
return $this->_data[$name];
}
}
/**
* @{inheritdoc}
*/
public function __isset($name) {
return isset($this->_data[$name]);
}
/**
* @{inheritdoc}
*/
public function __unset($name) {
unset($this->_data[$name]);
}
} }

View File

@ -2,34 +2,4 @@
namespace Ratchet\Resource; namespace Ratchet\Resource;
interface ConnectionInterface { interface ConnectionInterface {
/**
* @return int
*/
function getId();
/**
* Set an attribute to the connection
* @param mixed
* @param mixed
*/
function __set($name, $value);
/**
* Get a previously set attribute bound to the connection
* @return mixed
* @throws \InvalidArgumentException
*/
function __get($name);
/**
* @param mixed
* @return bool
*/
function __isset($name);
/**
* @param mixed
*/
function __unset($name);
} }

View File

@ -237,7 +237,6 @@ class BSDSocket implements SocketInterface {
$return = array(); $return = array();
foreach ($collection as $key => $socket) { foreach ($collection as $key => $socket) {
die("Checking if sock is instance of this: " . (int)($socket instanceof $this) . "\n");
$return[$key] = ($socket instanceof $this ? $socket->getResource() : $socket); $return[$key] = ($socket instanceof $this ? $socket->getResource() : $socket);
} }

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\Server; namespace Ratchet\Tests\Component\Server;
use Ratchet\Component\Server\IOServerComponent; use Ratchet\Component\Server\IOServerComponent;
use Ratchet\Tests\Mock\FakeSocket as Socket; use Ratchet\Tests\Mock\FakeSocket as Socket;
use Ratchet\Tests\Mock\Component as TestApp; use Ratchet\Tests\Mock\Component as TestApp;
@ -37,17 +37,17 @@ class IOServerComponentTest extends \PHPUnit_Framework_TestCase {
} }
public function testOnOpenPassesClonedSocket() { public function testOnOpenPassesClonedSocket() {
$this->_server->run($this->_catalyst); $this->_server->run(1025, '127.0.0.1', $this->_catalyst);
$master = $this->getMasterConnection(); $master = $this->getMasterConnection();
$this->_server->onOpen($master); $this->_server->onOpen($master);
$clone = $this->_decorated->_conn_open; $clone = $this->_decorated->_conn_open;
$this->assertEquals($master->getID() + 1, $clone->getID()); $this->assertEquals($master->resourceId + 1, $clone->resourceId);
} }
public function testOnMessageSendsToApp() { public function testOnMessageSendsToApp() {
$this->_server->run($this->_catalyst); $this->_server->run(1025, '127.0.0.1', $this->_catalyst);
$master = $this->getMasterConnection(); $master = $this->getMasterConnection();
// todo, make FakeSocket better, set data in select, recv to pass data when called, then do this check // todo, make FakeSocket better, set data in select, recv to pass data when called, then do this check

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\WebSocket\Version; namespace Ratchet\Tests\Component\WebSocket\Version;
use Ratchet\Component\WebSocket\Version\Hixie76; use Ratchet\Component\WebSocket\Version\Hixie76;
/** /**

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\WebSocket\Version; namespace Ratchet\Tests\Component\WebSocket\Version;
use Ratchet\Component\WebSocket\Version\HyBi10; use Ratchet\Component\WebSocket\Version\HyBi10;
use Ratchet\Component\WebSocket\Version\RFC6455\Frame; use Ratchet\Component\WebSocket\Version\RFC6455\Frame;

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\WebSocket\Version\RFC6455; namespace Ratchet\Tests\Component\WebSocket\Version\RFC6455;
use Ratchet\Component\WebSocket\Version\RFC6455\Frame; use Ratchet\Component\WebSocket\Version\RFC6455\Frame;
/** /**

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\WebSocket\Version\RFC6455; namespace Ratchet\Tests\Component\WebSocket\Version\RFC6455;
use Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier; use Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier;
/** /**

View File

@ -1,5 +1,5 @@
<?php <?php
namespace Ratchet\Tests\Application\WebSocket\Version; namespace Ratchet\Tests\Component\WebSocket\Version;
use Ratchet\Component\WebSocket\Version\RFC6455; use Ratchet\Component\WebSocket\Version\RFC6455;
use Ratchet\Component\WebSocket\Version\RFC6455\Frame; use Ratchet\Component\WebSocket\Version\RFC6455\Frame;
use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Message\RequestFactory;

View File

@ -12,7 +12,7 @@ class FakeSocket implements SocketInterface {
public $_last = array(); public $_last = array();
public function getResource() { public function getResource() {
return null; return "#{$this->_id}";
} }
public function __toString() { public function __toString() {

View File

@ -43,31 +43,6 @@ class ConnectionTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals($val, $this->_c->{$key}); $this->assertEquals($val, $this->_c->{$key});
} }
public function testExceptionThrownOnInvalidGet() {
$this->setExpectedException('InvalidArgumentException');
$ret = $this->_c->faked;
}
public static function lambdaProvider() {
return array(
array('hello', 'world')
, array('obj', new \stdClass)
, array('arr', array())
);
}
/**
* @dataProvider lambdaProvider
*/
public function testLambdaReturnValueOnGet($key, $val) {
$fn = function() use ($val) {
return $val;
};
$this->_c->{$key} = $fn;
$this->assertSame($val, $this->_c->{$key});
}
/** /**
* @dataProvider keyAndValProvider * @dataProvider keyAndValProvider
*/ */

View File

@ -2,4 +2,4 @@
error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
require_once dirname(__DIR__) . '/vendor/.composer/autoload.php'; require_once dirname(__DIR__) . '/vendor/autoload.php';