commit
c6f089885b
20
LICENSE
20
LICENSE
@ -1,7 +1,19 @@
|
|||||||
Copyright (c) 2011 Chris Boden
|
Copyright (c) 2011-2012 Chris Boden
|
||||||
|
|
||||||
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:
|
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 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.
|
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.
|
||||||
|
52
README.md
52
README.md
@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
#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) library for serving WebSockets 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 and 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.
|
|
||||||
|
|
||||||
##WebSockets
|
##WebSockets
|
||||||
|
|
||||||
* Supports the RFC6455, HyBi-10, and Hixie76 protocol versions (at the same time)
|
* Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time)
|
||||||
* Tested on Chrome 18 - 16, Firefox 6 - 8, Safari 5, iOS 4.2, iOS 5
|
* Tested on Chrome 18 - 16, Firefox 6 - 9, Safari 5, iOS 4.2, iOS 5
|
||||||
|
|
||||||
##Requirements
|
##Requirements
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80,
|
|||||||
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 requires you to use [Symfony2 HttpFoundation Sessions](http://symfony.com/doc/master/components/http_foundation/sessions.html) on your website.
|
Accessing your website's session data in Ratchet requires you to use [Symfony2 Sessions](http://symfony.com/doc/master/components/http_foundation/sessions.html) on your website.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
@ -32,57 +31,46 @@ See https://github.com/cboden/Ratchet-examples for some out-of-the-box working d
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
namespace MyApps;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\Server\IoServer;
|
||||||
use Ratchet\Component\Server\IOServerComponent;
|
use Ratchet\WebSocket\WsServer;
|
||||||
use Ratchet\Component\WebSocket\WebSocketComponent;
|
|
||||||
use Ratchet\Resource\Command\Composite as Cmds;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Resource\Command\Action\CloseConnection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* chat.php
|
* chat.php
|
||||||
* Send any incoming messages to all connected clients (except sender)
|
* Send any incoming messages to all connected clients (except sender)
|
||||||
*/
|
*/
|
||||||
class Chat implements MessageComponentInterface {
|
class Chat implements MessageComponentInterface {
|
||||||
protected $_clients;
|
protected $clients;
|
||||||
|
|
||||||
public function __construct(MessageComponentInterface $app = null) {
|
public function __construct() {
|
||||||
$this->_clients = new \SplObjectStorage;
|
$this->clients = new \SplObjectStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onOpen(ConnectionInterface $conn) {
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
$this->_clients->attach($conn);
|
$this->clients->attach($conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onMessage(ConnectionInterface $from, $msg) {
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
$commands = new Cmds;
|
foreach ($this->clients as $client) {
|
||||||
|
|
||||||
foreach ($this->_clients as $client) {
|
|
||||||
if ($from != $client) {
|
if ($from != $client) {
|
||||||
$msg_cmd = new SendMessage($client);
|
$client->send($msg);
|
||||||
$msg_cmd->setMessage($msg);
|
|
||||||
|
|
||||||
$commands->enqueue($msg_cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $commands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onClose(ConnectionInterface $conn) {
|
public function onClose(ConnectionInterface $conn) {
|
||||||
$this->_clients->detach($conn);
|
$this->clients->detach($conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
return new CloseConnection($conn);
|
$conn->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the server application through the WebSocket protocol
|
// Run the server application through the WebSocket protocol on port 8000
|
||||||
$server = new IOServerComponent(new WebSocketComponent(new Chat));
|
$server = IoServer::factory(new WsServer(new Chat), 8000);
|
||||||
$server->run(8000);
|
$server->run();
|
||||||
```
|
```
|
||||||
|
|
||||||
# php chat.php
|
# php chat.php
|
@ -17,11 +17,14 @@
|
|||||||
"psr-0": {
|
"psr-0": {
|
||||||
"Ratchet\\Tests": "tests"
|
"Ratchet\\Tests": "tests"
|
||||||
, "Ratchet": "src"
|
, "Ratchet": "src"
|
||||||
|
, "React": "vendor/cboden/react/src"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
, "require": {
|
, "require": {
|
||||||
"php": ">=5.3.2"
|
"php": ">=5.3.2"
|
||||||
, "guzzle/guzzle": "v2.0.2"
|
, "guzzle/guzzle": "2.5.*"
|
||||||
, "symfony/http-foundation": "2.1.*"
|
, "symfony/http-foundation": "2.1.*"
|
||||||
|
, "react/event-loop": "dev-master"
|
||||||
|
, "react/socket": "dev-master"
|
||||||
}
|
}
|
||||||
}
|
}
|
34
composer.lock
generated
34
composer.lock
generated
@ -1,14 +1,31 @@
|
|||||||
{
|
{
|
||||||
"hash": "c4bc28d46c32e18713efab25a77428e6",
|
"hash": "b7d2ee4e3fd11f2e9c862b6b2ca2372f",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"package": "doctrine/common",
|
"package": "evenement/evenement",
|
||||||
"version": "2.2.x-dev",
|
"version": "dev-master",
|
||||||
"source-reference": "1e0aa60d109c630d19543d999f12e2852ef8f932"
|
"source-reference": "808e3aaea8d4f908e455b0e047cc1acc46b38d44"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "guzzle/guzzle",
|
"package": "guzzle/guzzle",
|
||||||
"version": "v2.0.2"
|
"version": "v2.5.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "react/event-loop",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source-reference": "e9850fe37b04a34cb4d6b59861d3a6c680fb1aa1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "react/socket",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source-reference": "a2b7d74eef48f9fec44bf727d19b0385101b62a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "symfony/event-dispatcher",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source-reference": "0c1ae4898196f5e96b79028d8d2f35de4b584659",
|
||||||
|
"alias-pretty-version": "2.1.x-dev",
|
||||||
|
"alias-version": "2.1.9999999.9999999-dev"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "symfony/event-dispatcher",
|
"package": "symfony/event-dispatcher",
|
||||||
@ -19,12 +36,13 @@
|
|||||||
"package": "symfony/http-foundation",
|
"package": "symfony/http-foundation",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source-reference": "54c22f4bf8625303503a117dcc68544d3f8ac876",
|
"source-reference": "54c22f4bf8625303503a117dcc68544d3f8ac876",
|
||||||
"alias": "2.1.9999999.9999999-dev"
|
"alias-pretty-version": "2.1.x-dev",
|
||||||
|
"alias-version": "2.1.9999999.9999999-dev"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "symfony/validator",
|
"package": "symfony/http-foundation",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source-reference": "704f655d060b14475d7bd2a0b6d653c70f88218a"
|
"source-reference": "54c22f4bf8625303503a117dcc68544d3f8ac876"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": null,
|
"packages-dev": null,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<phpunit
|
<phpunit
|
||||||
forceCoversAnnotation="true"
|
forceCoversAnnotation="true"
|
||||||
mapTestClassNameToCoveredClassName="true"
|
mapTestClassNameToCoveredClassName="true"
|
||||||
bootstrap="tests/bootstrap.php"
|
bootstrap="vendor/autoload.php"
|
||||||
colors="true"
|
colors="true"
|
||||||
backupGlobals="false"
|
backupGlobals="false"
|
||||||
backupStaticAttributes="false"
|
backupStaticAttributes="false"
|
||||||
|
40
src/Ratchet/AbstractConnectionDecorator.php
Normal file
40
src/Ratchet/AbstractConnectionDecorator.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps ConnectionInterface objects via the decorator pattern but allows
|
||||||
|
* parameters to bubble through with magic methods
|
||||||
|
*/
|
||||||
|
abstract class AbstractConnectionDecorator implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $wrappedConn;
|
||||||
|
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
$this->wrappedConn = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected function getConnection() {
|
||||||
|
return $this->wrappedConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($name, $value) {
|
||||||
|
$this->wrappedConn->$name = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name) {
|
||||||
|
return $this->wrappedConn->$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name) {
|
||||||
|
return isset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unset($name) {
|
||||||
|
unset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
}
|
@ -1,199 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\Server;
|
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
|
||||||
use Ratchet\Resource\Socket\SocketInterface;
|
|
||||||
use Ratchet\Resource\Socket\BSDSocket;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
use Ratchet\Resource\Connection;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an open-ended socket to listen on a port for incomming connections. Events are delegated through this to attached applications
|
|
||||||
*/
|
|
||||||
class IOServerComponent implements MessageComponentInterface {
|
|
||||||
/**
|
|
||||||
* @var array of Socket Resources
|
|
||||||
*/
|
|
||||||
protected $_resources = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array of resources/Connections
|
|
||||||
*/
|
|
||||||
protected $_connections = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The decorated application to send events to
|
|
||||||
* @var Ratchet\Component\ComponentInterface
|
|
||||||
*/
|
|
||||||
protected $_decorating;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bytes to read in the TCP buffer at a time
|
|
||||||
* Default is (currently) 4kb
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_buffer_size = 4096;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* After run() is called, the server will loop as long as this is true
|
|
||||||
* This is here for unit testing purposes
|
|
||||||
* @var bool
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
protected $_run = true;
|
|
||||||
|
|
||||||
public function __construct(MessageComponentInterface $component) {
|
|
||||||
$this->_decorating = $component;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the incoming buffer size in bytes
|
|
||||||
* @param int
|
|
||||||
* @return App
|
|
||||||
* @throws InvalidArgumentException If the parameter is less than 1
|
|
||||||
*/
|
|
||||||
public function setBufferSize($recv_bytes) {
|
|
||||||
if ((int)$recv_bytes < 1) {
|
|
||||||
throw new \InvalidArgumentException('Invalid number of bytes set, must be more than 0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_buffer_size = (int)$recv_bytes;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Run the server infinitely
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
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->_resources[] = $host->getResource();
|
|
||||||
|
|
||||||
gc_enable();
|
|
||||||
set_time_limit(0);
|
|
||||||
ob_implicit_flush();
|
|
||||||
|
|
||||||
declare(ticks = 1);
|
|
||||||
|
|
||||||
$host->set_option(SOL_SOCKET, SO_SNDBUF, $this->_buffer_size);
|
|
||||||
$host->set_nonblock()->bind($address, (int)$port)->listen();
|
|
||||||
|
|
||||||
do {
|
|
||||||
$this->loop($host);
|
|
||||||
} while ($this->_run);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function loop(SocketInterface $host) {
|
|
||||||
$changed = $this->_resources;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$write = $except = null;
|
|
||||||
|
|
||||||
$num_changed = $host->select($changed, $write, $except, null);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// master had a problem?...what to do?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($changed as $resource) {
|
|
||||||
try {
|
|
||||||
$conn = $this->_connections[$resource];
|
|
||||||
|
|
||||||
if ($host->getResource() === $resource) {
|
|
||||||
$res = $this->onOpen($conn);
|
|
||||||
} else {
|
|
||||||
$data = $buf = '';
|
|
||||||
$bytes = $conn->getSocket()->recv($buf, $this->_buffer_size, MSG_DONTWAIT);
|
|
||||||
if ($bytes > 0) {
|
|
||||||
$data = $buf;
|
|
||||||
|
|
||||||
// This idea works* but...
|
|
||||||
// 1) A single DDOS attack will block the entire application (I think)
|
|
||||||
// 2) What if the last message in the frame is equal to $recv_bytes? Would loop until another msg is sent
|
|
||||||
// 3) This failed...an intermediary can set their buffer lower and this still propagates a fragment
|
|
||||||
// Need to 1) proc_open the recv() calls. 2) ???
|
|
||||||
|
|
||||||
/*
|
|
||||||
while ($bytes === $recv_bytes) {
|
|
||||||
$bytes = $conn->recv($buf, $recv_bytes, 0);
|
|
||||||
$data .= $buf;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
$res = $this->onMessage($conn, $data);
|
|
||||||
} else {
|
|
||||||
$res = $this->onClose($conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$res = $this->onError($conn, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($res instanceof CommandInterface) {
|
|
||||||
try {
|
|
||||||
$new_res = $res->execute($this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
break;
|
|
||||||
// trigger new error
|
|
||||||
// $new_res = $this->onError($e->getSocket()); ???
|
|
||||||
// this is dangerous territory...could get in an infinte loop...Exception might not be Ratchet\Exception...$new_res could be ActionInterface|Composite|NULL...
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = $new_res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onOpen(ConnectionInterface $conn) {
|
|
||||||
$new_socket = clone $conn->getSocket();
|
|
||||||
$new_socket->set_nonblock();
|
|
||||||
$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->_connections[$new_connection->getSocket()->getResource()] = $new_connection;
|
|
||||||
|
|
||||||
return $this->_decorating->onOpen($new_connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onMessage(ConnectionInterface $from, $msg) {
|
|
||||||
return $this->_decorating->onMessage($from, $msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onClose(ConnectionInterface $conn) {
|
|
||||||
$resource = $conn->getSocket()->getResource();
|
|
||||||
|
|
||||||
$cmd = $this->_decorating->onClose($conn);
|
|
||||||
|
|
||||||
unset($this->_connections[$resource], $this->_resources[array_search($resource, $this->_resources)]);
|
|
||||||
|
|
||||||
return $cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
|
||||||
return $this->_decorating->onError($conn, $e);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent as WAMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Respond to a client RPC with an error
|
|
||||||
*/
|
|
||||||
class CallError extends SendMessage {
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_uri;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_desc = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_details;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string The unique ID given by the client to respond to
|
|
||||||
* @param string The URI given by the client ot respond to
|
|
||||||
* @param string A developer-oriented description of the error
|
|
||||||
* @param string|null An optional human readable detail message to send back
|
|
||||||
* @return CallError
|
|
||||||
*/
|
|
||||||
public function setError($callId, $uri, $desc = '', $details = null) {
|
|
||||||
$this->_id = $callId;
|
|
||||||
$this->_uri = $uri;
|
|
||||||
$this->_desc = $desc;
|
|
||||||
|
|
||||||
$data = array(WAMP::MSG_CALL_ERROR, $callId, $uri, $desc);
|
|
||||||
|
|
||||||
if (null !== $details) {
|
|
||||||
$data[] = $details;
|
|
||||||
$this->_details = $details;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->setMessage(json_encode($data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getId() {
|
|
||||||
return $this->_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getUri() {
|
|
||||||
return $this->_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDescription() {
|
|
||||||
return $this->_desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getDetails() {
|
|
||||||
return $this->_details;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent as WAMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Respond to a client RPC
|
|
||||||
*/
|
|
||||||
class CallResult extends SendMessage {
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string The unique ID given by the client to respond to
|
|
||||||
* @param array An array of data to return to the client
|
|
||||||
* @return CallResult
|
|
||||||
*/
|
|
||||||
public function setResult($callId, array $data = array()) {
|
|
||||||
$this->_id = $callId;
|
|
||||||
$this->_data = $data;
|
|
||||||
|
|
||||||
return $this->setMessage(json_encode(array(WAMP::MSG_CALL_RESULT, $callId, $data)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getId() {
|
|
||||||
return $this->_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
public function getData() {
|
|
||||||
return $this->_data;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent as WAMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an event in the context of a topicURI
|
|
||||||
* This event (message) is to be sent to all subscribers of $uri
|
|
||||||
*/
|
|
||||||
class Event extends SendMessage {
|
|
||||||
/**
|
|
||||||
* @param string The URI or CURIE to broadcast to
|
|
||||||
* @param mixed Data to send with the event. Anything that is json'able
|
|
||||||
* @return Event
|
|
||||||
*/
|
|
||||||
public function setEvent($uri, $msg) {
|
|
||||||
return $this->setMessage(json_encode(array(WAMP::MSG_EVENT, $uri, $msg)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent as WAMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a curie to uri mapping to the client
|
|
||||||
* Both sides will agree to send the curie, representing the uri,
|
|
||||||
* resulting in less data transfered
|
|
||||||
*/
|
|
||||||
class Prefix extends SendMessage {
|
|
||||||
protected $_curie;
|
|
||||||
protected $_uri;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @param string
|
|
||||||
* @return Prefix
|
|
||||||
*/
|
|
||||||
public function setPrefix($curie, $uri) {
|
|
||||||
$this->_curie = $curie;
|
|
||||||
$this->_uri = $uri;
|
|
||||||
|
|
||||||
return $this->setMessage(json_encode(array(WAMP::MSG_PREFIX, $curie, $uri)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getCurie() {
|
|
||||||
return $this->_curie;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUri() {
|
|
||||||
return $this->_uri;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent as WAMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send Welcome message to each new connecting client
|
|
||||||
*/
|
|
||||||
class Welcome extends SendMessage {
|
|
||||||
/**
|
|
||||||
* @param string The unique identifier to mark the client
|
|
||||||
* @param string The server application name/version
|
|
||||||
* @return Welcome
|
|
||||||
*/
|
|
||||||
public function setWelcome($sessionId, $serverIdent = '') {
|
|
||||||
return $this->setMessage(json_encode(array(WAMP::MSG_WELCOME, $sessionId, 1, $serverIdent)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WAMP;
|
|
||||||
use Ratchet\Component\WebSocket\WebSocketComponentInterface;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
use Ratchet\Resource\Command\Composite;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
use Ratchet\Resource\Command\Factory as CmdFactory;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\Prefix;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\Welcome;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebSocket Application Messaging Protocol
|
|
||||||
*
|
|
||||||
* @link http://wamp.ws/spec
|
|
||||||
* @link https://github.com/oberstet/AutobahnJS
|
|
||||||
*
|
|
||||||
* +--------------+----+------------------+
|
|
||||||
* | Message Type | ID | DIRECTION |
|
|
||||||
* |--------------+----+------------------+
|
|
||||||
* | WELCOME | 0 | Server-to-Client |
|
|
||||||
* | PREFIX | 1 | Bi-Directional |
|
|
||||||
* | CALL | 2 | Client-to-Server |
|
|
||||||
* | CALL RESULT | 3 | Server-to-Client |
|
|
||||||
* | CALL ERROR | 4 | Server-to-Client |
|
|
||||||
* | SUBSCRIBE | 5 | Client-to-Server |
|
|
||||||
* | UNSUBSCRIBE | 6 | Client-to-Server |
|
|
||||||
* | PUBLISH | 7 | Client-to-Server |
|
|
||||||
* | EVENT | 8 | Server-to-Client |
|
|
||||||
* +--------------+----+------------------+
|
|
||||||
*/
|
|
||||||
class WAMPServerComponent implements WebSocketComponentInterface {
|
|
||||||
const MSG_WELCOME = 0;
|
|
||||||
const MSG_PREFIX = 1;
|
|
||||||
const MSG_CALL = 2;
|
|
||||||
const MSG_CALL_RESULT = 3;
|
|
||||||
const MSG_CALL_ERROR = 4;
|
|
||||||
const MSG_SUBSCRIBE = 5;
|
|
||||||
const MSG_UNSUBSCRIBE = 6;
|
|
||||||
const MSG_PUBLISH = 7;
|
|
||||||
const MSG_EVENT = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var WAMPServerComponentInterface
|
|
||||||
*/
|
|
||||||
protected $_decorating;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any server to client prefixes are stored here
|
|
||||||
* They're taxied along with the next outgoing message
|
|
||||||
* @var Ratchet\Resource\Command\Composite
|
|
||||||
*/
|
|
||||||
protected $_msg_buffer = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getSubProtocol() {
|
|
||||||
return 'wamp';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo WAMP spec does not say what to do when there is an error with PREFIX...
|
|
||||||
*/
|
|
||||||
public function addPrefix(ConnectionInterface $conn, $curie, $uri, $from_server = false) {
|
|
||||||
// validate uri
|
|
||||||
// validate curie
|
|
||||||
|
|
||||||
// make sure the curie is shorter than the uri
|
|
||||||
|
|
||||||
$conn->WAMP->prefixes[$curie] = $uri;
|
|
||||||
|
|
||||||
if ($from_server) {
|
|
||||||
$prefix = new Prefix($conn);
|
|
||||||
$prefix->setPrefix($curie, $uri);
|
|
||||||
|
|
||||||
$this->_msg_buffer->enqueue($prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onOpen(ConnectionInterface $conn) {
|
|
||||||
$conn->WAMP = new \StdClass;
|
|
||||||
$conn->WAMP->sessionId = uniqid();
|
|
||||||
$conn->WAMP->prefixes = array();
|
|
||||||
|
|
||||||
$wamp = $this;
|
|
||||||
$conn->WAMP->addPrefix = function($curie, $uri) use ($wamp, $conn) {
|
|
||||||
$wamp->addPrefix($conn, $curie, $uri, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
$welcome = new Welcome($conn);
|
|
||||||
$welcome->setWelcome($conn->WAMP->sessionId, \Ratchet\Resource\VERSION);
|
|
||||||
$this->_msg_buffer->enqueue($welcome);
|
|
||||||
|
|
||||||
return $this->attachStack($this->_decorating->onOpen($conn));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @{inheritdoc}
|
|
||||||
* @throws Exception
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
public function onMessage(ConnectionInterface $from, $msg) {
|
|
||||||
if (null === ($json = @json_decode($msg, true))) {
|
|
||||||
throw new JSONException;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($json[0]) {
|
|
||||||
case static::MSG_PREFIX:
|
|
||||||
$ret = $this->addPrefix($from, $json[1], $json[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case static::MSG_CALL:
|
|
||||||
array_shift($json);
|
|
||||||
$callID = array_shift($json);
|
|
||||||
$procURI = array_shift($json);
|
|
||||||
|
|
||||||
if (count($json) == 1 && is_array($json[0])) {
|
|
||||||
$json = $json[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$ret = $this->_decorating->onCall($from, $callID, $procURI, $json);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case static::MSG_SUBSCRIBE:
|
|
||||||
$ret = $this->_decorating->onSubscribe($from, $this->getUri($from, $json[1]));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case static::MSG_UNSUBSCRIBE:
|
|
||||||
$ret = $this->_decorating->onUnSubscribe($from, $this->getUri($from, $json[1]));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case static::MSG_PUBLISH:
|
|
||||||
$ret = $this->_decorating->onPublish($from, $this->getUri($from, $json[1]), $json[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Exception('Invalid message type');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->attachStack($ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the full request URI from the connection object if a prefix has been established for it
|
|
||||||
* @param Ratchet\Resource\Connection
|
|
||||||
* @param ...
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getUri(ConnectionInterface $conn, $uri) {
|
|
||||||
return (isset($conn->WAMP->prefixes[$uri]) ? $conn->WAMP->prefixes[$uri] : $uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the developer's application as set some server-to-client prefixes to be set,
|
|
||||||
* this method ensures those are taxied to the next outgoing message
|
|
||||||
* @param Ratchet\Resource\Command\CommandInterface|NULL
|
|
||||||
* @return Ratchet\Resource\Command\Composite
|
|
||||||
*/
|
|
||||||
protected function attachStack(CommandInterface $command = null) {
|
|
||||||
$stack = $this->_msg_buffer;
|
|
||||||
$stack->enqueue($command);
|
|
||||||
|
|
||||||
$this->_msg_buffer = new Composite;
|
|
||||||
|
|
||||||
return $stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param WAMPServerComponentInterface An class to propagate calls through
|
|
||||||
*/
|
|
||||||
public function __construct(WAMPServerComponentInterface $server_component) {
|
|
||||||
CmdFactory::registerActionPath(__NAMESPACE__ . '\\Command\\Action');
|
|
||||||
|
|
||||||
$this->_decorating = $server_component;
|
|
||||||
$this->_msg_buffer = new Composite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onClose(ConnectionInterface $conn) {
|
|
||||||
return $this->_decorating->onClose($conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
|
||||||
return $this->_decorating->onError($conn, $e);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WebSocket\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not yet implemented/completed
|
|
||||||
*/
|
|
||||||
class Disconnect extends SendMessage {
|
|
||||||
protected $_code = 1000;
|
|
||||||
|
|
||||||
public function setStatusCode($code) {
|
|
||||||
$this->_code = (int)$code;
|
|
||||||
|
|
||||||
// re-do message based on code
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
parent::execute();
|
|
||||||
$this->_socket->close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WebSocket\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\ActionTemplate;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not yet implemented/completed
|
|
||||||
*/
|
|
||||||
class Ping extends ActionTemplate {
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WebSocket\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\ActionTemplate;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not yet implemented/completed
|
|
||||||
*/
|
|
||||||
class Pong extends ActionTemplate {
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Component\WebSocket\Guzzle\Http\Message;
|
|
||||||
use Guzzle\Http\Message\RequestFactory as gReqFac;
|
|
||||||
use Guzzle\Http\Url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just slighly changing the Guzzle fromMessage() method to always return an EntityEnclosingRequest instance instead of Request
|
|
||||||
*/
|
|
||||||
class RequestFactory extends gReqFac {
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @return Guzzle\Http\Message\RequestInterface
|
|
||||||
*/
|
|
||||||
public static function fromRequest($message) {
|
|
||||||
$parsed = static::parseMessage($message);
|
|
||||||
|
|
||||||
if (!$parsed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::fromRequestParts(
|
|
||||||
$parsed['method'],
|
|
||||||
$parsed['parts'],
|
|
||||||
$parsed['headers'],
|
|
||||||
$parsed['body'],
|
|
||||||
$parsed['protocol'],
|
|
||||||
$parsed['protocol_version']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function fromRequestParts($method, array $parts, $headers = null, $body = null, $protocol = 'HTTP', $protocolVersion = '1.1') {
|
|
||||||
return self::requestCreate($method, Url::buildUrl($parts, true), $headers, $body)
|
|
||||||
->setProtocolVersion($protocolVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function requestCreate($method, $url, $headers = null, $body = null) {
|
|
||||||
$c = static::$entityEnclosingRequestClass;
|
|
||||||
$request = new $c($method, $url, $headers);
|
|
||||||
$request->setBody($body);
|
|
||||||
|
|
||||||
return $request;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component;
|
namespace Ratchet;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the interface to build a Ratchet application with
|
* This is the interface to build a Ratchet application with
|
||||||
@ -9,16 +9,14 @@ use Ratchet\Resource\ConnectionInterface;
|
|||||||
interface ComponentInterface {
|
interface ComponentInterface {
|
||||||
/**
|
/**
|
||||||
* When a new connection is opened it will be passed to this method
|
* When a new connection is opened it will be passed to this method
|
||||||
* @param Ratchet\Resource\Connection The socket/connection that just connected to your application
|
* @param Ratchet\Connection The socket/connection that just connected to your application
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
function onOpen(ConnectionInterface $conn);
|
function onOpen(ConnectionInterface $conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed.
|
* This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed.
|
||||||
* @param Ratchet\Resource\Connection The socket/connection that is closing/closed
|
* @param Ratchet\Connection The socket/connection that is closing/closed
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
function onClose(ConnectionInterface $conn);
|
function onClose(ConnectionInterface $conn);
|
||||||
@ -26,9 +24,8 @@ interface ComponentInterface {
|
|||||||
/**
|
/**
|
||||||
* If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown,
|
* If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown,
|
||||||
* the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method
|
* the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method
|
||||||
* @param Ratchet\Resource\Connection
|
* @param Ratchet\Connection
|
||||||
* @param \Exception
|
* @param \Exception
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
function onError(ConnectionInterface $conn, \Exception $e);
|
function onError(ConnectionInterface $conn, \Exception $e);
|
21
src/Ratchet/ConnectionInterface.php
Normal file
21
src/Ratchet/ConnectionInterface.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
const VERSION = 'Ratchet/0.1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy object representing a connection to the application
|
||||||
|
* This acts as a container to storm data (in memory) about the connection
|
||||||
|
*/
|
||||||
|
interface ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* Send data to the connection
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
function send($data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection
|
||||||
|
*/
|
||||||
|
function close();
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component;
|
namespace Ratchet;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
interface MessageComponentInterface extends ComponentInterface {
|
interface MessageComponentInterface extends ComponentInterface {
|
||||||
/**
|
/**
|
||||||
* Triggered when a client sends data through the socket
|
* Triggered when a client sends data through the socket
|
||||||
* @param Ratchet\Resource\ConnectionInterface The socket/connection that sent the message to your application
|
* @param Ratchet\ConnectionInterface The socket/connection that sent the message to your application
|
||||||
* @param string The message received
|
* @param string The message received
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
function onMessage(ConnectionInterface $from, $msg);
|
function onMessage(ConnectionInterface $from, $msg);
|
@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A single command tied to 1 socket connection
|
|
||||||
*/
|
|
||||||
interface ActionInterface extends CommandInterface {
|
|
||||||
/**
|
|
||||||
* Pass the Sockets to execute the command on
|
|
||||||
* @param Ratchet\Resource\Connection
|
|
||||||
*/
|
|
||||||
function __construct(ConnectionInterface $conn);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Ratchet\Command\Connection
|
|
||||||
*/
|
|
||||||
function getConnection();
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
|
|
||||||
abstract class ActionTemplate implements ActionInterface {
|
|
||||||
/**
|
|
||||||
* @var Ratchet\Resource\Connection
|
|
||||||
*/
|
|
||||||
protected $_conn;
|
|
||||||
|
|
||||||
public function __construct(ConnectionInterface $conn) {
|
|
||||||
$this->_conn = $conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConnection() {
|
|
||||||
return $this->_conn;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
use Ratchet\Resource\Command\Composite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the connection to the sockets passed in the constructor
|
|
||||||
*/
|
|
||||||
class CloseConnection extends ActionTemplate {
|
|
||||||
function execute(ComponentInterface $scope = null) {
|
|
||||||
// All this code allows an application to have its onClose method executed before the socket is actually closed
|
|
||||||
$ret = $scope->onClose($this->getConnection());
|
|
||||||
|
|
||||||
if ($ret instanceof CommandInterface) {
|
|
||||||
$comp = new Composite;
|
|
||||||
$comp->enqueue($ret);
|
|
||||||
|
|
||||||
$rt = new Runtime($this->getConnection());
|
|
||||||
$rt->setCommand(function(ConnectionInterface $conn, ComponentInterface $scope) {
|
|
||||||
$conn->getSocket()->close();
|
|
||||||
});
|
|
||||||
$comp->enqueue($rt);
|
|
||||||
|
|
||||||
return $comp;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->getConnection()->getSocket()->close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Null pattern - execution does nothing, used when something needs to be passed back
|
|
||||||
*/
|
|
||||||
class Null extends ActionTemplate {
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This allows you to create a run-time command by using a closure
|
|
||||||
*/
|
|
||||||
class Runtime extends ActionTemplate {
|
|
||||||
/**
|
|
||||||
* The stored closure command to execude
|
|
||||||
* @var Closure
|
|
||||||
*/
|
|
||||||
protected $_command = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Your closure should accept two parameters (\Ratchet\Resource\Connection, \Ratchet\Component\ComponentInterface) parameter and return a CommandInterface or NULL
|
|
||||||
* @param Closure Your closure/lambda to execute when the time comes
|
|
||||||
*/
|
|
||||||
public function setCommand(\Closure $callback) {
|
|
||||||
$this->_command = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
$cmd = $this->_command;
|
|
||||||
|
|
||||||
return $cmd($this->getConnection(), $scope);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command\Action;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send text back to the client end of the socket(s)
|
|
||||||
*/
|
|
||||||
class SendMessage extends ActionTemplate {
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_message = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The message to send to the socket(s)
|
|
||||||
* @param string
|
|
||||||
* @return SendMessage Fluid interface
|
|
||||||
*/
|
|
||||||
public function setMessage($msg) {
|
|
||||||
$this->_message = (string)$msg;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the message from setMessage()
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMessage() {
|
|
||||||
return $this->_message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
* @throws \UnexpectedValueException if a message was not set with setMessage()
|
|
||||||
*/
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
if (empty($this->_message)) {
|
|
||||||
throw new \UnexpectedValueException("Message is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->getConnection()->getSocket()->deliver($this->_message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Socket implementation of the Command Pattern
|
|
||||||
* User created applications are to return a Command to the server for execution
|
|
||||||
*/
|
|
||||||
interface CommandInterface {
|
|
||||||
/**
|
|
||||||
* The Server class will call the execution
|
|
||||||
* @param Ratchet\ComponentInterface Scope to execute the command under
|
|
||||||
* @return CommandInterface|NULL
|
|
||||||
*/
|
|
||||||
function execute(ComponentInterface $scope = null);
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command;
|
|
||||||
use Ratchet\Component\ComponentInterface;
|
|
||||||
|
|
||||||
class Composite extends \SplQueue implements CommandInterface {
|
|
||||||
/**
|
|
||||||
* Add another Command to the stack
|
|
||||||
* Unlike a true composite the enqueue flattens a composite parameter into leafs
|
|
||||||
* @param CommandInterface|null
|
|
||||||
*/
|
|
||||||
public function enqueue($command) {
|
|
||||||
if (null === $command) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!($command instanceof CommandInterface)) {
|
|
||||||
throw new \InvalidArgumentException("Parameter MUST implement Ratchet.Component.CommandInterface");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($command instanceof self) {
|
|
||||||
foreach ($command as $cmd) {
|
|
||||||
$this->enqueue($cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::enqueue($command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function execute(ComponentInterface $scope = null) {
|
|
||||||
$this->setIteratorMode(static::IT_MODE_DELETE);
|
|
||||||
|
|
||||||
$recursive = new self;
|
|
||||||
|
|
||||||
foreach ($this as $command) {
|
|
||||||
$recursive->enqueue($command->execute($scope));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($recursive) > 0) {
|
|
||||||
return $recursive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Command;
|
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory pattern class to easily create all the things in the Ratchet\Resource\Command interface
|
|
||||||
*/
|
|
||||||
class Factory {
|
|
||||||
protected $_paths = array();
|
|
||||||
|
|
||||||
protected $_mapped_commands = array();
|
|
||||||
|
|
||||||
protected static $globalPaths = array();
|
|
||||||
|
|
||||||
protected $_ignoreGlobals = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool If set to TRUE this will ignore all the statically registered namespaces
|
|
||||||
*/
|
|
||||||
public function __construct($ignoreGlobals = false) {
|
|
||||||
$this->addActionPath(__NAMESPACE__ . '\\Action');
|
|
||||||
$this->_ignoreGlobals = (boolean)$ignoreGlobals;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new namespace of which CommandInterfaces reside under to autoload with $this->newCommand()
|
|
||||||
* @param string
|
|
||||||
*/
|
|
||||||
public function addActionPath($namespace) {
|
|
||||||
$this->_paths[] = $this->slashIt($namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function registerActionPath($namespace) {
|
|
||||||
static::$globalPaths[$namespace] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Composite
|
|
||||||
*/
|
|
||||||
public function newComposite() {
|
|
||||||
return new Composite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @return CommandInterface
|
|
||||||
* @throws UnexpectedValueException
|
|
||||||
*/
|
|
||||||
public function newCommand($name, ConnectionInterface $conn) {
|
|
||||||
if (isset($this->_mapped_commands[$name])) {
|
|
||||||
$cmd = $this->_mapped_commands[$name];
|
|
||||||
return new $cmd($conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->_paths as $path) {
|
|
||||||
if (class_exists($path . $name)) {
|
|
||||||
$this->_mapped_commands[$name] = $path . $name;
|
|
||||||
return $this->newCommand($name, $conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === $this->_ignoreGlobals) {
|
|
||||||
foreach (static::$globalPaths as $path => $one) {
|
|
||||||
$path = $this->slashIt($path);
|
|
||||||
if (class_exists($path . $name)) {
|
|
||||||
$this->_mapped_commands[$name] = $path . $name;
|
|
||||||
return $this->newCommand($name, $conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \UnexepctedValueException("Command {$name} not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function slashIt($ns) {
|
|
||||||
return (substr($ns, -1) == '\\' ? $ns : $ns . '\\');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource;
|
|
||||||
use Ratchet\Resource\Socket\SocketInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A proxy object representing a connection to the application
|
|
||||||
* This acts as a container to storm data (in memory) about the connection
|
|
||||||
*/
|
|
||||||
class Connection implements ConnectionInterface {
|
|
||||||
/**
|
|
||||||
* @var Ratchet\Resource\Socket\SocketInterface
|
|
||||||
*/
|
|
||||||
protected $_socket;
|
|
||||||
|
|
||||||
public function __construct(SocketInterface $socket) {
|
|
||||||
$this->_socket = $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
|
|
||||||
* Anyway, if you're here, it's not recommended you use this/directly interact with the socket in your App...
|
|
||||||
* The command pattern (which is fully flexible, see Runtime) is the safest, desired way to interact with the socket(s).
|
|
||||||
* @return Ratchet\SocketInterface
|
|
||||||
* @todo Figure out a better way to match Socket/Connection in Application and Commands
|
|
||||||
*/
|
|
||||||
public function getSocket() {
|
|
||||||
return $this->_socket;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource;
|
|
||||||
|
|
||||||
const VERSION = 'Ratchet/0.1';
|
|
||||||
|
|
||||||
interface ConnectionInterface {
|
|
||||||
}
|
|
@ -1,245 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Socket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper for the PHP socket_ functions
|
|
||||||
* @author Chris Boden <shout at chrisboden dot ca>
|
|
||||||
* @link http://ca2.php.net/manual/en/book.sockets.php
|
|
||||||
*/
|
|
||||||
class BSDSocket implements SocketInterface {
|
|
||||||
/**
|
|
||||||
* @type resource
|
|
||||||
*/
|
|
||||||
protected $_resource;
|
|
||||||
|
|
||||||
public static $_defaults = array(
|
|
||||||
'domain' => AF_INET
|
|
||||||
, 'type' => SOCK_STREAM
|
|
||||||
, 'protocol' => SOL_TCP
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int Specifies the protocol family to be used by the socket.
|
|
||||||
* @param int The type of communication to be used by the socket
|
|
||||||
* @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket
|
|
||||||
* @throws BSDSocketException
|
|
||||||
*/
|
|
||||||
public function __construct($domain = null, $type = null, $protocol = null) {
|
|
||||||
list($domain, $type, $protocol) = static::getConfig($domain, $type, $protocol);
|
|
||||||
|
|
||||||
$this->_resource = @socket_create($domain, $type, $protocol);
|
|
||||||
|
|
||||||
if (!is_resource($this->_resource)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __destruct() {
|
|
||||||
@socket_close($this->_resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() {
|
|
||||||
$id = (string)$this->getResource();
|
|
||||||
return (string)substr($id, strrpos($id, '#') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return resource (Socket)
|
|
||||||
*/
|
|
||||||
public function getResource() {
|
|
||||||
return $this->_resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __clone() {
|
|
||||||
$this->_resource = @socket_accept($this->_resource);
|
|
||||||
|
|
||||||
if (false === $this->_resource) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deliver($message) {
|
|
||||||
$len = strlen($message);
|
|
||||||
|
|
||||||
do {
|
|
||||||
$sent = $this->write($message, $len);
|
|
||||||
$len -= $sent;
|
|
||||||
$message = substr($message, $sent);
|
|
||||||
} while ($len > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bind($address, $port = 0) {
|
|
||||||
if (false === @socket_bind($this->getResource(), $address, $port)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function close() {
|
|
||||||
@socket_close($this->getResource());
|
|
||||||
unset($this->_resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connect($address, $port = 0) {
|
|
||||||
if (false === @socket_connect($this->getResource(), $address, $port)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemoteAddress() {
|
|
||||||
$address = $port = '';
|
|
||||||
if (false === @socket_getpeername($this->getResource(), $address, $port)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_option($level, $optname) {
|
|
||||||
if (false === ($res = @socket_get_option($this->getResource(), $level, $optname))) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listen($backlog = 0) {
|
|
||||||
if (false === @socket_listen($this->getResource(), $backlog)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function read($length, $type = PHP_BINARY_READ) {
|
|
||||||
if (false === ($res = @socket_read($this->getResource(), $length, $type))) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see http://ca3.php.net/manual/en/function.socket-recv.php
|
|
||||||
* @param string Variable to write data to
|
|
||||||
* @param int Number of bytes to read
|
|
||||||
* @param int
|
|
||||||
* @return int Number of bytes received
|
|
||||||
* @throws BSDSocketException
|
|
||||||
*/
|
|
||||||
public function recv(&$buf, $len, $flags) {
|
|
||||||
if (false === ($bytes = @socket_recv($this->_resource, $buf, $len, $flags))) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Since PHP is retarded and their golden hammer, the array, doesn't implement any interfaces I have to hackishly overload socket_select
|
|
||||||
* @see http://ca3.php.net/manual/en/function.socket-select.php
|
|
||||||
* @param Iterator|array|NULL The sockets listed in the read array will be watched to see if characters become available for reading (more precisely, to see if a read will not block - in particular, a socket resource is also ready on end-of-file, in which case a socket_read() will return a zero length string).
|
|
||||||
* @param Iterator|array|NULL The sockets listed in the write array will be watched to see if a write will not block.
|
|
||||||
* @param Iterator|array|NULL The sockets listed in the except array will be watched for exceptions.
|
|
||||||
* @param int The tv_sec and tv_usec together form the timeout parameter. The timeout is an upper bound on the amount of time elapsed before socket_select() return. tv_sec may be zero , causing socket_select() to return immediately. This is useful for polling. If tv_sec is NULL (no timeout), socket_select() can block indefinitely.
|
|
||||||
* @param int
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
* @throws BSDSocketException
|
|
||||||
*/
|
|
||||||
public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) {
|
|
||||||
$read = static::mungForSelect($read);
|
|
||||||
$write = static::mungForSelect($write);
|
|
||||||
$except = static::mungForSelect($except);
|
|
||||||
|
|
||||||
$num = socket_select($read, $write, $except, $tv_sec, $tv_usec);
|
|
||||||
|
|
||||||
if (false === $num) {
|
|
||||||
throw new BSDException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_block() {
|
|
||||||
if (false === @socket_set_block($this->getResource())) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_nonblock() {
|
|
||||||
if (false === @socket_set_nonblock($this->getResource())) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_option($level, $optname, $optval) {
|
|
||||||
if (false === @socket_set_option($this->getResource(), $level, $optname, $optval)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function shutdown($how = 2) {
|
|
||||||
if (false === @socket_shutdown($this->getResource(), $how)) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function write($buffer, $length = 0) {
|
|
||||||
if (false === ($res = @socket_write($this->getResource(), $buffer, $length))) {
|
|
||||||
throw new BSDSocketException($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @param int Specifies the protocol family to be used by the socket.
|
|
||||||
* @param int The type of communication to be used by the socket
|
|
||||||
* @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected static function getConfig($domain = null, $type = null, $protocol = null) {
|
|
||||||
foreach (static::$_defaults as $key => $val) {
|
|
||||||
if (null === $$key) {
|
|
||||||
$$key = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($domain, $type, $protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @param Iterator|array|NULL
|
|
||||||
* @return array|NULL
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
protected static function mungForSelect($collection) {
|
|
||||||
if (null === $collection || is_array($collection)) {
|
|
||||||
return $collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!($collection instanceof \Traversable)) {
|
|
||||||
throw new \InvalidArgumentException('Object pass is not traversable');
|
|
||||||
}
|
|
||||||
|
|
||||||
$return = array();
|
|
||||||
foreach ($collection as $key => $socket) {
|
|
||||||
$return[$key] = ($socket instanceof $this ? $socket->getResource() : $socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Socket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses internal php methods to fill an Exception class (no parameters required)
|
|
||||||
*/
|
|
||||||
class BSDSocketException extends \Exception {
|
|
||||||
/**
|
|
||||||
* @var BSDSocket
|
|
||||||
*/
|
|
||||||
protected $_socket;
|
|
||||||
|
|
||||||
public function __construct(BSDSocket $socket) {
|
|
||||||
$int = socket_last_error();
|
|
||||||
$msg = socket_strerror($int);
|
|
||||||
|
|
||||||
$this->_socket = $socket;
|
|
||||||
//@socket_clear_error($socket->getResource());
|
|
||||||
|
|
||||||
parent::__construct($msg, $int);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSocket() {
|
|
||||||
return $this->_socket;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,151 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Resource\Socket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object-oriented container for a single socket connection
|
|
||||||
* @todo Major refactor when socket streams are implemented against this interface
|
|
||||||
*/
|
|
||||||
interface SocketInterface {
|
|
||||||
/**
|
|
||||||
* @return resource
|
|
||||||
*/
|
|
||||||
function getResource();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the unique ID of this socket instance
|
|
||||||
*/
|
|
||||||
function __toString();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls socket_accept, duplicating its self
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function __clone();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message through the socket. This writes to the buffer until the entire message is delivered
|
|
||||||
* @param string Your message to send to the socket
|
|
||||||
* @return null
|
|
||||||
* @throws Exception
|
|
||||||
* @see write
|
|
||||||
*/
|
|
||||||
function deliver($message);
|
|
||||||
|
|
||||||
// Not sure if I'll implement this or leave it only in clone
|
|
||||||
// function accept();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind the socket instance to an address/port
|
|
||||||
* @param string
|
|
||||||
* @param int
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function bind($address, $port = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the open connection to the client/socket
|
|
||||||
*/
|
|
||||||
function close();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates a connection to a socket
|
|
||||||
* @param string
|
|
||||||
* @param int
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function connect($address, $port = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the address the socket connected from
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function getRemoteAddress();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int
|
|
||||||
* @param int
|
|
||||||
* @return mixed
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function get_option($level, $optname);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for incoming connections on this socket
|
|
||||||
* @param int
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function listen($backlog = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a maximum of length bytes from a socket
|
|
||||||
* @param int Number of bytes to read
|
|
||||||
* @param int Flags
|
|
||||||
* @return string Data read from the socket
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function read($length, $type = PHP_BINARY_READ);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the client sends data to the server through the socket
|
|
||||||
* @param string Variable to write data to
|
|
||||||
* @param int Number of bytes to read
|
|
||||||
* @param int
|
|
||||||
* @return int Number of bytes received
|
|
||||||
* @throws Exception
|
|
||||||
* @todo Change the pass by reference
|
|
||||||
*/
|
|
||||||
function recv(&$buf, $len, $flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array|Iterator
|
|
||||||
* @param array|Iterator
|
|
||||||
* @param array|Iterator
|
|
||||||
* @param int
|
|
||||||
* @param int
|
|
||||||
* @return int
|
|
||||||
* @throws Exception
|
|
||||||
* @todo Figure out how to break this out to not do pass by reference
|
|
||||||
*/
|
|
||||||
function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the blocking mode on the socket resource
|
|
||||||
* Wen an operation (receive, send, connect, accept, etc) is performed after set_block() the script will pause execution until the operation is completed
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function set_block();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets nonblocking mode for socket resource
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function set_nonblock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int
|
|
||||||
* @param int
|
|
||||||
* @param mixed
|
|
||||||
* @return SocketInterface
|
|
||||||
*/
|
|
||||||
function set_option($level, $optname, $optval);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int
|
|
||||||
* @return SocketInterface
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
function shutdown($how = 2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send text to the client on the other end of the socket
|
|
||||||
* @param string
|
|
||||||
* @param int
|
|
||||||
*/
|
|
||||||
function write($buffer, $length = 0);
|
|
||||||
}
|
|
@ -1,11 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Server;
|
namespace Ratchet\Server;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use Ratchet\Resource\Connection;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Resource\Command\Action\CloseConnection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An app to go on a server stack to pass a policy file to a Flash socket
|
* An app to go on a server stack to pass a policy file to a Flash socket
|
||||||
@ -17,7 +13,7 @@ use Ratchet\Resource\Command\Action\CloseConnection;
|
|||||||
* @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
|
* @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
|
||||||
* @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
|
* @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
|
||||||
*/
|
*/
|
||||||
class FlashPolicyComponent implements MessageComponentInterface {
|
class FlashPolicy implements MessageComponentInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the root policy node
|
* Contains the root policy node
|
||||||
@ -58,7 +54,7 @@ class FlashPolicyComponent implements MessageComponentInterface {
|
|||||||
* Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
|
* Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
|
||||||
* be used to allow all ports.
|
* be used to allow all ports.
|
||||||
* @param bool
|
* @param bool
|
||||||
* @return FlashPolicyComponent
|
* @return FlashPolicy
|
||||||
*/
|
*/
|
||||||
public function addAllowedAccess($domain, $ports = '*', $secure = false) {
|
public function addAllowedAccess($domain, $ports = '*', $secure = false) {
|
||||||
if (!$this->validateDomain($domain)) {
|
if (!$this->validateDomain($domain)) {
|
||||||
@ -81,7 +77,7 @@ class FlashPolicyComponent implements MessageComponentInterface {
|
|||||||
* crossdomain.xml.
|
* crossdomain.xml.
|
||||||
*
|
*
|
||||||
* @param string
|
* @param string
|
||||||
* @return FlashPolicyComponent
|
* @return FlashPolicy
|
||||||
*/
|
*/
|
||||||
public function setSiteControl($permittedCrossDomainPolicies = 'all') {
|
public function setSiteControl($permittedCrossDomainPolicies = 'all') {
|
||||||
if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
|
if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
|
||||||
@ -109,10 +105,7 @@ class FlashPolicyComponent implements MessageComponentInterface {
|
|||||||
$this->_cacheValid = true;
|
$this->_cacheValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cmd = new SendMessage($from);
|
$from->send($this->_cache . "\0");
|
||||||
$cmd->setMessage($this->_cache . "\0");
|
|
||||||
|
|
||||||
return $cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,7 +118,7 @@ class FlashPolicyComponent implements MessageComponentInterface {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
return new CloseConnection($conn);
|
$conn->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
39
src/Ratchet/Server/IoConnection.php
Normal file
39
src/Ratchet/Server/IoConnection.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use React\Socket\ConnectionInterface as ReactConn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
class IoConnection implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var Ratchet\Server\IOServer
|
||||||
|
*/
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var React\Socket\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $conn;
|
||||||
|
|
||||||
|
public function __construct(ReactConn $conn, IoServer $server) {
|
||||||
|
$this->conn = $conn;
|
||||||
|
$this->server = $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
return $this->conn->write($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close() {
|
||||||
|
$this->server->onClose($this);
|
||||||
|
$this->conn->end();
|
||||||
|
}
|
||||||
|
}
|
96
src/Ratchet/Server/IoServer.php
Normal file
96
src/Ratchet/Server/IoServer.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Socket\ServerInterface;
|
||||||
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
|
use React\Socket\Server as Reactor;
|
||||||
|
use React\EventLoop\StreamSelectLoop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an open-ended socket to listen on a port for incomming connections. Events are delegated through this to attached applications
|
||||||
|
*/
|
||||||
|
class IoServer {
|
||||||
|
/**
|
||||||
|
* @var React\EventLoop\LoopInterface
|
||||||
|
*/
|
||||||
|
public $loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
public $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of React event handlers
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $handlers = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Ratchet\MessageComponentInterface The Ratchet application stack to host
|
||||||
|
* @param React\Socket\ServerInterface The React socket server to run the Ratchet application off of
|
||||||
|
* @param React\EventLoop\LoopInterface The React looper to run the Ratchet application off of
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $app, ServerInterface $socket, LoopInterface $loop) {
|
||||||
|
$this->loop = $loop;
|
||||||
|
$this->app = $app;
|
||||||
|
|
||||||
|
$socket->on('connect', array($this, 'handleConnect'));
|
||||||
|
|
||||||
|
$this->handlers['data'] = array($this, 'handleData');
|
||||||
|
$this->handlers['end'] = array($this, 'handleEnd');
|
||||||
|
$this->handlers['error'] = array($this, 'handleError');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') {
|
||||||
|
// Enable this after we fix a bug with libevent
|
||||||
|
// $loop = LoopFactory::create();
|
||||||
|
|
||||||
|
$loop = new StreamSelectLoop;
|
||||||
|
|
||||||
|
$socket = new Reactor($loop);
|
||||||
|
$socket->listen($port, $address);
|
||||||
|
$server = new static($component, $socket, $loop);
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run() {
|
||||||
|
$this->loop->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleConnect($conn) {
|
||||||
|
$conn->decor = new IoConnection($conn, $this);
|
||||||
|
|
||||||
|
$conn->decor->resourceId = (int)$conn->socket;
|
||||||
|
$conn->decor->remoteAddress = '127.0.0.1'; // todo
|
||||||
|
|
||||||
|
$this->app->onOpen($conn->decor);
|
||||||
|
|
||||||
|
$conn->on('data', $this->handlers['data']);
|
||||||
|
$conn->on('end', $this->handlers['end']);
|
||||||
|
$conn->on('error', $this->handlers['error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleData($data, $conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onMessage($conn->decor, $data);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleEnd($conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onClose($conn->decor);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleError(\Exception $e, $conn) {
|
||||||
|
$this->app->onError($conn->decor, $e);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Server;
|
namespace Ratchet\Server;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use Ratchet\Resource\Command\Action\CloseConnection;
|
|
||||||
|
|
||||||
class IpBlackListComponent implements MessageComponentInterface {
|
class IpBlackList implements MessageComponentInterface {
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $_blacklist = array();
|
protected $_blacklist = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\MessageComponentInterface
|
* @var Ratchet\MessageComponentInterface
|
||||||
*/
|
*/
|
||||||
protected $_decorating;
|
protected $_decorating;
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ class IpBlackListComponent implements MessageComponentInterface {
|
|||||||
*/
|
*/
|
||||||
function onOpen(ConnectionInterface $conn) {
|
function onOpen(ConnectionInterface $conn) {
|
||||||
if ($this->isBlocked($conn->remoteAddress)) {
|
if ($this->isBlocked($conn->remoteAddress)) {
|
||||||
return new CloseConnection($conn);
|
return $conn->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_decorating->onOpen($conn);
|
return $this->_decorating->onOpen($conn);
|
||||||
@ -91,17 +90,17 @@ class IpBlackListComponent implements MessageComponentInterface {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
function onClose(ConnectionInterface $conn) {
|
function onClose(ConnectionInterface $conn) {
|
||||||
if ($this->isBlocked($conn->remoteAddress)) {
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
return null;
|
$this->_decorating->onClose($conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_decorating->onClose($conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
function onError(ConnectionInterface $conn, \Exception $e) {
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
return $this->_decorating->onError($conn, $e);
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
|
$this->_decorating->onError($conn, $e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session\Serialize;
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
interface HandlerInterface {
|
interface HandlerInterface {
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session\Serialize;
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
class PhpBinaryHandler implements HandlerInterface {
|
class PhpBinaryHandler implements HandlerInterface {
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session\Serialize;
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
class PhpHandler implements HandlerInterface {
|
class PhpHandler implements HandlerInterface {
|
||||||
/**
|
/**
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session;
|
namespace Ratchet\Session;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use Ratchet\Component\Session\Storage\VirtualSessionStorage;
|
use Ratchet\Session\Storage\VirtualSessionStorage;
|
||||||
use Ratchet\Component\Session\Serialize\HandlerInterface;
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||||
|
|
||||||
@ -13,9 +13,9 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
|||||||
* Your website must also use Symfony HttpFoundation Sessions to read your sites session data
|
* Your website must also use Symfony HttpFoundation Sessions to read your sites session data
|
||||||
* If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer)
|
* If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer)
|
||||||
*/
|
*/
|
||||||
class SessionComponent implements MessageComponentInterface {
|
class SessionProvider implements MessageComponentInterface {
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\MessageComponentInterface
|
* @var Ratchet\MessageComponentInterface
|
||||||
*/
|
*/
|
||||||
protected $_app;
|
protected $_app;
|
||||||
|
|
||||||
@ -32,15 +32,15 @@ class SessionComponent implements MessageComponentInterface {
|
|||||||
protected $_null;
|
protected $_null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\Session\Serialize\HandlerInterface
|
* @var Ratchet\Session\Serialize\HandlerInterface
|
||||||
*/
|
*/
|
||||||
protected $_serializer;
|
protected $_serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Ratchet\Component\MessageComponentInterface
|
* @param Ratchet\MessageComponentInterface
|
||||||
* @param SessionHandlerInterface
|
* @param SessionHandlerInterface
|
||||||
* @param array
|
* @param array
|
||||||
* @param Ratchet\Component\Session\Serialize\HandlerInterface
|
* @param Ratchet\Session\Serialize\HandlerInterface
|
||||||
* @throws RuntimeException If unable to match serialization methods
|
* @throws RuntimeException If unable to match serialization methods
|
||||||
*/
|
*/
|
||||||
public function __construct(MessageComponentInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
|
public function __construct(MessageComponentInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session\Storage\Proxy;
|
namespace Ratchet\Session\Storage\Proxy;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
|
|
||||||
class VirtualProxy extends SessionHandlerProxy {
|
class VirtualProxy extends SessionHandlerProxy {
|
@ -1,19 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\Session\Storage;
|
namespace Ratchet\Session\Storage;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||||
use Ratchet\Component\Session\Storage\Proxy\VirtualProxy;
|
use Ratchet\Session\Storage\Proxy\VirtualProxy;
|
||||||
use Ratchet\Component\Session\Serialize\HandlerInterface;
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
|
|
||||||
class VirtualSessionStorage extends NativeSessionStorage {
|
class VirtualSessionStorage extends NativeSessionStorage {
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\Session\Serialize\HandlerInterface
|
* @var Ratchet\Session\Serialize\HandlerInterface
|
||||||
*/
|
*/
|
||||||
protected $_serializer;
|
protected $_serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SessionHandlerInterface
|
* @param SessionHandlerInterface
|
||||||
* @param string The ID of the session to retreive
|
* @param string The ID of the session to retreive
|
||||||
* @param Ratchet\Component\Session\Serialize\HandlerInterface
|
* @param Ratchet\Session\Serialize\HandlerInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) {
|
public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) {
|
||||||
$this->setSaveHandler($handler);
|
$this->setSaveHandler($handler);
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WAMP;
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
class Exception extends \Exception {
|
class Exception extends \Exception {
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WAMP;
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
class JSONException extends Exception {
|
class JsonException extends Exception {
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$code = json_last_error();
|
$code = json_last_error();
|
||||||
|
|
86
src/Ratchet/Wamp/WampConnection.php
Normal file
86
src/Ratchet/Wamp/WampConnection.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
use Ratchet\Wamp\WampServer as WAMP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ConnectionInterface object wrapper that is passed to your WAMP application
|
||||||
|
* representing a client. Methods on this Connection are therefore different.
|
||||||
|
* @property stdClass $WAMP
|
||||||
|
*/
|
||||||
|
class WampConnection extends AbstractConnectionDecorator {
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
parent::__construct($conn);
|
||||||
|
|
||||||
|
$this->WAMP = new \StdClass;
|
||||||
|
$this->WAMP->sessionId = uniqid();
|
||||||
|
$this->WAMP->prefixes = array();
|
||||||
|
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string The unique ID given by the client to respond to
|
||||||
|
* @param array An array of data to return to the client
|
||||||
|
*/
|
||||||
|
public function callResult($id, array $data = array()) {
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string The unique ID given by the client to respond to
|
||||||
|
* @param string The URI given by the client ot respond to
|
||||||
|
* @param string A developer-oriented description of the error
|
||||||
|
* @param string|null An optional human readable detail message to send back
|
||||||
|
*/
|
||||||
|
public function callError($id, $uri, $desc = '', $details = null) {
|
||||||
|
$data = array(WAMP::MSG_CALL_ERROR, $id, $uri, $desc);
|
||||||
|
|
||||||
|
if (null !== $details) {
|
||||||
|
$data[] = $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->send(json_encode($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string The URI or CURIE to broadcast to
|
||||||
|
* @param mixed Data to send with the event. Anything that is json'able
|
||||||
|
*/
|
||||||
|
public function event($uri, $msg) {
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_EVENT, $uri, $msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
public function prefix($curie, $uri) {
|
||||||
|
$this->WAMP->prefixes[$curie] = $uri;
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, $uri)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full request URI from the connection object if a prefix has been established for it
|
||||||
|
* @param string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUri($uri) {
|
||||||
|
return (isset($this->WAMP->prefixes[$uri]) ? $this->WAMP->prefixes[$uri] : $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close() {
|
||||||
|
$this->getConnection()->close();
|
||||||
|
}
|
||||||
|
}
|
137
src/Ratchet/Wamp/WampServer.php
Normal file
137
src/Ratchet/Wamp/WampServer.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket Application Messaging Protocol
|
||||||
|
*
|
||||||
|
* @link http://wamp.ws/spec
|
||||||
|
* @link https://github.com/oberstet/AutobahnJS
|
||||||
|
*
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
* | Message Type | ID | DIRECTION |
|
||||||
|
* |--------------+----+------------------+
|
||||||
|
* | WELCOME | 0 | Server-to-Client |
|
||||||
|
* | PREFIX | 1 | Bi-Directional |
|
||||||
|
* | CALL | 2 | Client-to-Server |
|
||||||
|
* | CALL RESULT | 3 | Server-to-Client |
|
||||||
|
* | CALL ERROR | 4 | Server-to-Client |
|
||||||
|
* | SUBSCRIBE | 5 | Client-to-Server |
|
||||||
|
* | UNSUBSCRIBE | 6 | Client-to-Server |
|
||||||
|
* | PUBLISH | 7 | Client-to-Server |
|
||||||
|
* | EVENT | 8 | Server-to-Client |
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
*/
|
||||||
|
class WampServer implements WsServerInterface {
|
||||||
|
const MSG_WELCOME = 0;
|
||||||
|
const MSG_PREFIX = 1;
|
||||||
|
const MSG_CALL = 2;
|
||||||
|
const MSG_CALL_RESULT = 3;
|
||||||
|
const MSG_CALL_ERROR = 4;
|
||||||
|
const MSG_SUBSCRIBE = 5;
|
||||||
|
const MSG_UNSUBSCRIBE = 6;
|
||||||
|
const MSG_PUBLISH = 7;
|
||||||
|
const MSG_EVENT = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WampServerInterface
|
||||||
|
*/
|
||||||
|
protected $_decorating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SplObjectStorage
|
||||||
|
*/
|
||||||
|
protected $connections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampServerInterface An class to propagate calls through
|
||||||
|
*/
|
||||||
|
public function __construct(WampServerInterface $server_component) {
|
||||||
|
$this->_decorating = $server_component;
|
||||||
|
$this->connections = new \SplObjectStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocol() {
|
||||||
|
return 'wamp';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
$this->connections->attach($conn, $decor);
|
||||||
|
|
||||||
|
$this->_decorating->onOpen($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{inheritdoc}
|
||||||
|
* @throws Exception
|
||||||
|
* @throws JsonException
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from = $this->connections[$from];
|
||||||
|
|
||||||
|
if (null === ($json = @json_decode($msg, true))) {
|
||||||
|
throw new JsonException;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($json[0]) {
|
||||||
|
case static::MSG_PREFIX:
|
||||||
|
$from->WAMP->prefixes[$json[1]] = $json[2];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_CALL:
|
||||||
|
array_shift($json);
|
||||||
|
$callID = array_shift($json);
|
||||||
|
$procURI = array_shift($json);
|
||||||
|
|
||||||
|
if (count($json) == 1 && is_array($json[0])) {
|
||||||
|
$json = $json[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_decorating->onCall($from, $callID, $procURI, $json);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_SUBSCRIBE:
|
||||||
|
$this->_decorating->onSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_UNSUBSCRIBE:
|
||||||
|
$this->_decorating->onUnSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_PUBLISH:
|
||||||
|
$exclude = (array_key_exists(3, $json) ? $json[3] : null);
|
||||||
|
$eligible = (array_key_exists(4, $json) ? $json[4] : null);
|
||||||
|
|
||||||
|
$this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Invalid message type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$decor = $this->connections[$conn];
|
||||||
|
$this->connections->detach($conn);
|
||||||
|
|
||||||
|
$this->_decorating->onClose($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_decorating->onError($this->connections[$conn], $e);
|
||||||
|
}
|
||||||
|
}
|
@ -1,46 +1,42 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WAMP;
|
namespace Ratchet\Wamp;
|
||||||
use Ratchet\Component\ComponentInterface;
|
use Ratchet\ComponentInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A (not literal) extension of Ratchet\Component\ComponentInterface
|
* A (not literal) extension of Ratchet\ConnectionInterface
|
||||||
* onMessage is replaced by various types of messages for this protocol (pub/sub or rpc)
|
* onMessage is replaced by various types of messages for this protocol (pub/sub or rpc)
|
||||||
* @todo Thought: URI as class. Class has short and long version stored (if as prefix)
|
* @todo Thought: URI as class. Class has short and long version stored (if as prefix)
|
||||||
*/
|
*/
|
||||||
interface WAMPServerComponentInterface extends ComponentInterface {
|
interface WampServerInterface extends ComponentInterface {
|
||||||
/**
|
/**
|
||||||
* An RPC call has been received
|
* An RPC call has been received
|
||||||
* @param Ratchet\Resource\Connection
|
* @param Ratchet\Connection
|
||||||
* @param string
|
* @param string
|
||||||
* @param ...
|
* @param ...
|
||||||
* @param array Call parameters received from the client
|
* @param array Call parameters received from the client
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
*/
|
*/
|
||||||
function onCall(ConnectionInterface $conn, $id, $procURI, array $params);
|
function onCall(ConnectionInterface $conn, $id, $procURI, array $params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to subscribe to a URI has been made
|
* A request to subscribe to a URI has been made
|
||||||
* @param Ratchet\Resource\Connection
|
* @param Ratchet\Connection
|
||||||
* @param ...
|
* @param ...
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
*/
|
*/
|
||||||
function onSubscribe(ConnectionInterface $conn, $uri);
|
function onSubscribe(ConnectionInterface $conn, $uri);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to unsubscribe from a URI has been made
|
* A request to unsubscribe from a URI has been made
|
||||||
* @param Ratchet\Resource\Connection
|
* @param Ratchet\Connection
|
||||||
* @param ...
|
* @param ...
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
*/
|
*/
|
||||||
function onUnSubscribe(ConnectionInterface $conn, $uri);
|
function onUnSubscribe(ConnectionInterface $conn, $uri);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A client is attempting to publish content to a subscribed connections on a URI
|
* A client is attempting to publish content to a subscribed connections on a URI
|
||||||
* @param Ratchet\Resource\Connection
|
* @param Ratchet\Connection
|
||||||
* @param ...
|
* @param ...
|
||||||
* @param string
|
* @param string
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|null
|
|
||||||
*/
|
*/
|
||||||
function onPublish(ConnectionInterface $conn, $uri, $event);
|
function onPublish(ConnectionInterface $conn, $uri, $event, $exclude, $eligible);
|
||||||
}
|
}
|
19
src/Ratchet/WebSocket/Guzzle/Http/Message/RequestFactory.php
Normal file
19
src/Ratchet/WebSocket/Guzzle/Http/Message/RequestFactory.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket\Guzzle\Http\Message;
|
||||||
|
use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory;
|
||||||
|
use Guzzle\Http\EntityBody;
|
||||||
|
|
||||||
|
class RequestFactory extends GuzzleRequestFactory {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function create($method, $url, $headers = null, $body = null) {
|
||||||
|
$c = $this->entityEnclosingRequestClass;
|
||||||
|
$request = new $c($method, $url, $headers);
|
||||||
|
if ($body) {
|
||||||
|
$request->setBody(EntityBody::factory($body));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
|
|
||||||
interface FrameInterface {
|
interface FrameInterface {
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
use Guzzle\Http\Message\Response;
|
use Guzzle\Http\Message\Response;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class Hixie76 implements VersionInterface {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public static function isProtocol(RequestInterface $request) {
|
public static function isProtocol(RequestInterface $request) {
|
||||||
return !(null === $request->getHeader('Sec-WebSocket-Key2'));
|
return !(null === $request->getHeader('Sec-WebSocket-Key2', true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,13 +28,13 @@ class Hixie76 implements VersionInterface {
|
|||||||
* @return Guzzle\Http\Message\Response
|
* @return Guzzle\Http\Message\Response
|
||||||
*/
|
*/
|
||||||
public function handshake(RequestInterface $request) {
|
public function handshake(RequestInterface $request) {
|
||||||
$body = $this->sign($request->getHeader('Sec-WebSocket-Key1'), $request->getHeader('Sec-WebSocket-Key2'), $request->getBody());
|
$body = $this->sign($request->getHeader('Sec-WebSocket-Key1', true), $request->getHeader('Sec-WebSocket-Key2', true), (string)$request->getBody());
|
||||||
|
|
||||||
$headers = array(
|
$headers = array(
|
||||||
'Upgrade' => 'WebSocket'
|
'Upgrade' => 'WebSocket'
|
||||||
, 'Connection' => 'Upgrade'
|
, 'Connection' => 'Upgrade'
|
||||||
, 'Sec-WebSocket-Origin' => $request->getHeader('Origin')
|
, 'Sec-WebSocket-Origin' => $request->getHeader('Origin', true)
|
||||||
, 'Sec-WebSocket-Location' => 'ws://' . $request->getHeader('Host') . $request->getPath()
|
, 'Sec-WebSocket-Location' => 'ws://' . $request->getHeader('Host', true) . $request->getPath()
|
||||||
);
|
);
|
||||||
|
|
||||||
$response = new Response('101', $headers, $body);
|
$response = new Response('101', $headers, $body);
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version\Hixie76;
|
namespace Ratchet\WebSocket\Version\Hixie76;
|
||||||
use Ratchet\Component\WebSocket\Version\FrameInterface;
|
use Ratchet\WebSocket\Version\FrameInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This does not entirely follow the protocol to spec, but (mostly) works
|
* This does not entirely follow the protocol to spec, but (mostly) works
|
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version\Hixie76;
|
namespace Ratchet\WebSocket\Version\Hixie76;
|
||||||
use Ratchet\Component\WebSocket\Version\MessageInterface;
|
use Ratchet\WebSocket\Version\MessageInterface;
|
||||||
use Ratchet\Component\WebSocket\Version\FrameInterface;
|
use Ratchet\WebSocket\Version\FrameInterface;
|
||||||
|
|
||||||
class Message implements MessageInterface {
|
class Message implements MessageInterface {
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\WebSocket\Version\FrameInterface
|
* @var Ratchet\WebSocket\Version\FrameInterface
|
||||||
*/
|
*/
|
||||||
protected $_frame = null;
|
protected $_frame = null;
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
|
|
||||||
class HyBi10 extends RFC6455 {
|
class HyBi10 extends RFC6455 {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Consider making parent interface/composite for Message/Frame with (isCoalesced, getOpcdoe, getPayloadLength, getPayload)
|
* @todo Consider making parent interface/composite for Message/Frame with (isCoalesced, getOpcdoe, getPayloadLength, getPayload)
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier;
|
use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier;
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
use Guzzle\Http\Message\Response;
|
use Guzzle\Http\Message\Response;
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version\RFC6455;
|
namespace Ratchet\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\Component\WebSocket\Version\FrameInterface;
|
use Ratchet\WebSocket\Version\FrameInterface;
|
||||||
|
|
||||||
class Frame implements FrameInterface {
|
class Frame implements FrameInterface {
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version\RFC6455;
|
namespace Ratchet\WebSocket\Version\RFC6455;
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,17 +14,15 @@ class HandshakeVerifier {
|
|||||||
* @return bool TRUE if all headers are valid, FALSE if 1 or more were invalid
|
* @return bool TRUE if all headers are valid, FALSE if 1 or more were invalid
|
||||||
*/
|
*/
|
||||||
public function verifyAll(RequestInterface $request) {
|
public function verifyAll(RequestInterface $request) {
|
||||||
$headers = $request->getHeaders();
|
|
||||||
|
|
||||||
$passes = 0;
|
$passes = 0;
|
||||||
|
|
||||||
$passes += (int)$this->verifyMethod($request->getMethod());
|
$passes += (int)$this->verifyMethod($request->getMethod());
|
||||||
$passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion());
|
$passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion());
|
||||||
$passes += (int)$this->verifyRequestURI($request->getPath());
|
$passes += (int)$this->verifyRequestURI($request->getPath());
|
||||||
$passes += (int)$this->verifyHost($headers['Host']);
|
$passes += (int)$this->verifyHost($request->getHeader('Host', true));
|
||||||
$passes += (int)$this->verifyUpgradeRequest($headers['Upgrade']);
|
$passes += (int)$this->verifyUpgradeRequest($request->getHeader('Upgrade', true));
|
||||||
$passes += (int)$this->verifyConnection($headers['Connection']);
|
$passes += (int)$this->verifyConnection($request->getHeader('Connection', true));
|
||||||
$passes += (int)$this->verifyKey($headers['Sec-WebSocket-Key']);
|
$passes += (int)$this->verifyKey($request->getHeader('Sec-WebSocket-Key', true));
|
||||||
//$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality
|
//$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality
|
||||||
|
|
||||||
return (7 === $passes);
|
return (7 === $passes);
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version\RFC6455;
|
namespace Ratchet\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\Component\WebSocket\Version\MessageInterface;
|
use Ratchet\WebSocket\Version\MessageInterface;
|
||||||
use Ratchet\Component\WebSocket\Version\FrameInterface;
|
use Ratchet\WebSocket\Version\FrameInterface;
|
||||||
|
|
||||||
class Message implements MessageInterface {
|
class Message implements MessageInterface {
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
33
src/Ratchet/WebSocket/WsConnection.php
Normal file
33
src/Ratchet/WebSocket/WsConnection.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @property stdClass $WebSocket
|
||||||
|
*/
|
||||||
|
class WsConnection extends AbstractConnectionDecorator {
|
||||||
|
public function send($data) {
|
||||||
|
// need frame caching
|
||||||
|
|
||||||
|
$data = $this->WebSocket->version->frame($data, false);
|
||||||
|
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
// send close frame
|
||||||
|
|
||||||
|
// ???
|
||||||
|
|
||||||
|
// profit
|
||||||
|
|
||||||
|
$this->getConnection()->close(); // temporary
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ping() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pong() {
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket;
|
namespace Ratchet\WebSocket;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use Ratchet\Resource\Command\Factory;
|
|
||||||
use Ratchet\Resource\Command\CommandInterface;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Guzzle\Http\Message\RequestInterface;
|
use Guzzle\Http\Message\RequestInterface;
|
||||||
use Ratchet\Component\WebSocket\Guzzle\Http\Message\RequestFactory;
|
use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The adapter to handle WebSocket requests/responses
|
* The adapter to handle WebSocket requests/responses
|
||||||
* This is a mediator between the Server and your application to handle real-time messaging through a web browser
|
* This is a mediator between the Server and your application to handle real-time messaging through a web browser
|
||||||
|
* @todo Separate this class into a two classes: Component and a protocol handler
|
||||||
* @link http://ca.php.net/manual/en/ref.http.php
|
* @link http://ca.php.net/manual/en/ref.http.php
|
||||||
* @link http://dev.w3.org/html5/websockets/
|
* @link http://dev.w3.org/html5/websockets/
|
||||||
*/
|
*/
|
||||||
class WebSocketComponent implements MessageComponentInterface {
|
class WsServer implements MessageComponentInterface {
|
||||||
/**
|
/**
|
||||||
* Decorated component
|
* Decorated component
|
||||||
* @var Ratchet\Component\MessageComponentInterface
|
* @var Ratchet\MessageComponentInterface
|
||||||
*/
|
*/
|
||||||
protected $_decorating;
|
protected $_decorating;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates commands/composites instead of calling several classes manually
|
* @var SplObjectStorage
|
||||||
* @var Ratchet\Resource\Command\Factory
|
|
||||||
*/
|
*/
|
||||||
protected $_factory;
|
protected $connections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-entrant instances of protocol version classes
|
* Re-entrant instances of protocol version classes
|
||||||
@ -48,7 +45,7 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
|
|
||||||
public function __construct(MessageComponentInterface $component) {
|
public function __construct(MessageComponentInterface $component) {
|
||||||
$this->_decorating = $component;
|
$this->_decorating = $component;
|
||||||
$this->_factory = new Factory;
|
$this->connections = new \SplObjectStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,7 +69,7 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$headers = RequestFactory::fromRequest($from->WebSocket->headers);
|
$headers = RequestFactory::getInstance()->fromMessage($from->WebSocket->headers);
|
||||||
$from->WebSocket->version = $this->getVersion($headers);
|
$from->WebSocket->version = $this->getVersion($headers);
|
||||||
$from->WebSocket->headers = $headers;
|
$from->WebSocket->headers = $headers;
|
||||||
}
|
}
|
||||||
@ -93,14 +90,15 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
if (count($agreed_protocols) > 0) {
|
if (count($agreed_protocols) > 0) {
|
||||||
$response->setHeader('Sec-WebSocket-Protocol', implode(',', $agreed_protocols));
|
$response->setHeader('Sec-WebSocket-Protocol', implode(',', $agreed_protocols));
|
||||||
}
|
}
|
||||||
$response->setHeader('X-Powered-By', \Ratchet\Resource\VERSION);
|
$response->setHeader('X-Powered-By', \Ratchet\VERSION);
|
||||||
$header = (string)$response;
|
$header = (string)$response;
|
||||||
|
|
||||||
$comp = $this->_factory->newComposite();
|
$from->send($header);
|
||||||
$comp->enqueue($this->_factory->newCommand('SendMessage', $from)->setMessage($header));
|
|
||||||
$comp->enqueue($this->prepareCommand($this->_decorating->onOpen($from, $msg))); // Need to send headers/handshake to application, let it have the cookies, etc
|
|
||||||
|
|
||||||
return $comp;
|
$conn = new WsConnection($from);
|
||||||
|
$this->connections->attach($from, $conn);
|
||||||
|
|
||||||
|
return $this->_decorating->onOpen($conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($from->WebSocket->message)) {
|
if (!isset($from->WebSocket->message)) {
|
||||||
@ -115,6 +113,7 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
$from->WebSocket->frame->addBuffer($msg);
|
$from->WebSocket->frame->addBuffer($msg);
|
||||||
if ($from->WebSocket->frame->isCoalesced()) {
|
if ($from->WebSocket->frame->isCoalesced()) {
|
||||||
if ($from->WebSocket->frame->getOpcode() > 2) {
|
if ($from->WebSocket->frame->getOpcode() > 2) {
|
||||||
|
$from->end();
|
||||||
throw new \UnexpectedValueException('Control frame support coming soon!');
|
throw new \UnexpectedValueException('Control frame support coming soon!');
|
||||||
}
|
}
|
||||||
// Check frame
|
// Check frame
|
||||||
@ -127,10 +126,8 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($from->WebSocket->message->isCoalesced()) {
|
if ($from->WebSocket->message->isCoalesced()) {
|
||||||
$cmds = $this->prepareCommand($this->_decorating->onMessage($from, (string)$from->WebSocket->message));
|
$this->_decorating->onMessage($this->connections[$from], (string)$from->WebSocket->message);
|
||||||
unset($from->WebSocket->message);
|
unset($from->WebSocket->message);
|
||||||
|
|
||||||
return $cmds;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,57 +135,25 @@ class WebSocketComponent implements MessageComponentInterface {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onClose(ConnectionInterface $conn) {
|
public function onClose(ConnectionInterface $conn) {
|
||||||
return $this->prepareCommand($this->_decorating->onClose($conn));
|
// WS::onOpen is not called when the socket connects, it's call when the handshake is done
|
||||||
|
// The socket could close before WS calls onOpen, so we need to check if we've "opened" it for the developer yet
|
||||||
|
if ($this->connections->contains($conn)) {
|
||||||
|
$decor = $this->connections[$conn];
|
||||||
|
$this->connections->detach($conn);
|
||||||
|
|
||||||
|
$this->_decorating->onClose($decor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
* @todo Shouldn't I be using prepareCommand() on the return? look into this
|
|
||||||
*/
|
*/
|
||||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
return $this->_decorating->onError($conn, $e);
|
if ($this->connections->contains($conn)) {
|
||||||
|
$this->_decorating->onError($this->connections[$conn], $e);
|
||||||
|
} else {
|
||||||
|
$conn->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a return Command from your application is a message, if so encode it/them
|
|
||||||
* @param Ratchet\Resource\Command\CommandInterface|NULL
|
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|NULL
|
|
||||||
*/
|
|
||||||
protected function prepareCommand(CommandInterface $command = null) {
|
|
||||||
$cache = array();
|
|
||||||
return $this->mungCommand($command, $cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual work of prepareCommand
|
|
||||||
* Separated to pass the cache array by reference, so we're not framing the same stirng over and over
|
|
||||||
* @param Ratchet\Resource\Command\CommandInterface|NULL
|
|
||||||
* @param array
|
|
||||||
* @return Ratchet\Resource\Command\CommandInterface|NULL
|
|
||||||
*/
|
|
||||||
protected function mungCommand(CommandInterface $command = null, &$cache) {
|
|
||||||
if ($command instanceof SendMessage) {
|
|
||||||
if (!isset($command->getConnection()->WebSocket->version)) { // Client could close connection before handshake complete or invalid handshake
|
|
||||||
return $command;
|
|
||||||
}
|
|
||||||
|
|
||||||
$version = $command->getConnection()->WebSocket->version;
|
|
||||||
$hash = md5($command->getMessage()) . '-' . spl_object_hash($version);
|
|
||||||
|
|
||||||
if (!isset($cache[$hash])) {
|
|
||||||
$cache[$hash] = $version->frame($command->getMessage(), $this->_mask_payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $command->setMessage($cache[$hash]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($command instanceof \Traversable) {
|
|
||||||
foreach ($command as $cmd) {
|
|
||||||
$cmd = $this->mungCommand($cmd, $cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $command;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Component\WebSocket;
|
namespace Ratchet\WebSocket;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
|
|
||||||
interface WebSocketComponentInterface extends MessageComponentInterface {
|
interface WsServerInterface extends MessageComponentInterface {
|
||||||
/**
|
/**
|
||||||
* Currently instead of this, I'm setting header in the Connection object passed around...not sure which I like more
|
* Currently instead of this, I'm setting header in the Connection object passed around...not sure which I like more
|
||||||
* @param string
|
* @param string
|
147
tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php
Normal file
147
tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Tests;
|
||||||
|
use Ratchet\Tests\Mock\ConnectionDecorator;
|
||||||
|
use Ratchet\Tests\Mock\Connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\AbstractConnectionDecorator
|
||||||
|
*/
|
||||||
|
class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $mock;
|
||||||
|
protected $l1;
|
||||||
|
protected $l2;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = new Connection;
|
||||||
|
$this->l1 = new ConnectionDecorator($this->mock);
|
||||||
|
$this->l2 = new ConnectionDecorator($this->l1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGet() {
|
||||||
|
$var = 'hello';
|
||||||
|
$val = 'world';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->l1->$var);
|
||||||
|
$this->assertEquals($val, $this->l2->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSet() {
|
||||||
|
$var = 'Chris';
|
||||||
|
$val = 'Boden';
|
||||||
|
|
||||||
|
$this->l1->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetLevel2() {
|
||||||
|
$var = 'Try';
|
||||||
|
$val = 'Again';
|
||||||
|
|
||||||
|
$this->l2->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetTrue() {
|
||||||
|
$var = 'PHP';
|
||||||
|
$val = 'Ratchet';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertTrue(isset($this->l1->$var));
|
||||||
|
$this->assertTrue(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetFalse() {
|
||||||
|
$var = 'herp';
|
||||||
|
$val = 'derp';
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->l1->$var));
|
||||||
|
$this->assertFalse(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnset() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l1->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsetLevel2() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l2->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnection() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l1, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->mock, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnectionLevel2() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l2, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWrapperCanStoreSelfInDecorator() {
|
||||||
|
$this->mock->decorator = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursion() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursionLevel2() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l2;
|
||||||
|
|
||||||
|
$this->assertSame($this->l2, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn);
|
||||||
|
|
||||||
|
// just for fun
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothing() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->mock->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel1() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l1->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel2() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l2->nonExistant;
|
||||||
|
}
|
||||||
|
}
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Component\Server;
|
|
||||||
use Ratchet\Component\Server\IOServerComponent;
|
|
||||||
use Ratchet\Tests\Mock\FakeSocket as Socket;
|
|
||||||
use Ratchet\Tests\Mock\Component as TestApp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Component\Server\IOServerComponent
|
|
||||||
*/
|
|
||||||
class IOServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
protected $_catalyst;
|
|
||||||
protected $_server;
|
|
||||||
protected $_decorated;
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
$this->_catalyst = new Socket;
|
|
||||||
$this->_decorated = new TestApp;
|
|
||||||
$this->_server = new IOServerComponent($this->_decorated);
|
|
||||||
|
|
||||||
$ref = new \ReflectionClass('\\Ratchet\\Component\\Server\\IOServerComponent');
|
|
||||||
$prop = $ref->getProperty('_run');
|
|
||||||
$prop->setAccessible(true);
|
|
||||||
$prop->setValue($this->_server, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getPrivateProperty($class, $name) {
|
|
||||||
$reflectedClass = new \ReflectionClass($class);
|
|
||||||
$property = $reflectedClass->getProperty($name);
|
|
||||||
$property->setAccessible(true);
|
|
||||||
|
|
||||||
return $property->getValue($class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMasterConnection() {
|
|
||||||
$connections = $this->getPrivateProperty($this->_server, '_connections');
|
|
||||||
return array_pop($connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testOnOpenPassesClonedSocket() {
|
|
||||||
$this->_server->run(1025, '127.0.0.1', $this->_catalyst);
|
|
||||||
$master = $this->getMasterConnection();
|
|
||||||
|
|
||||||
$this->_server->onOpen($master);
|
|
||||||
$clone = $this->_decorated->last['onOpen'][0];
|
|
||||||
|
|
||||||
$this->assertEquals($master->resourceId + 1, $clone->resourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testOnMessageSendsToApp() {
|
|
||||||
$this->_server->run(1025, '127.0.0.1', $this->_catalyst);
|
|
||||||
$master = $this->getMasterConnection();
|
|
||||||
|
|
||||||
// todo, make FakeSocket better, set data in select, recv to pass data when called, then do this check
|
|
||||||
// that way can mimic the TCP fragmentation/buffer situation
|
|
||||||
|
|
||||||
$this->_server->onOpen($master);
|
|
||||||
$clone = $this->_decorated->last['onOpen'][0];
|
|
||||||
|
|
||||||
// $this->_server->run($this->_catalyst);
|
|
||||||
$msg = 'Hello World!';
|
|
||||||
$this->_server->onMessage($clone, $msg);
|
|
||||||
|
|
||||||
$this->assertEquals($msg, $this->_decorated->last['onMessage'][1]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\CallError;
|
|
||||||
use Ratchet\Tests\Mock\Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Component\WAMP\Command\Action\CallError
|
|
||||||
*/
|
|
||||||
class CallErrorTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
public function testCallError() {
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
|
|
||||||
$callId = uniqid();
|
|
||||||
$uri = 'http://example.com/end/point';
|
|
||||||
|
|
||||||
$error->setError($callId, $uri);
|
|
||||||
$resultString = $error->getMessage();
|
|
||||||
|
|
||||||
$this->assertEquals(array(4, $callId, $uri, ''), json_decode($resultString, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDetailedCallError() {
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
|
|
||||||
$callId = uniqid();
|
|
||||||
$uri = 'http://example.com/end/point';
|
|
||||||
$desc = 'beep boop beep';
|
|
||||||
$detail = 'Error: Too much awesome';
|
|
||||||
|
|
||||||
$error->setError($callId, $uri, $desc, $detail);
|
|
||||||
$resultString = $error->getMessage();
|
|
||||||
|
|
||||||
$this->assertEquals(array(4, $callId, $uri, $desc, $detail), json_decode($resultString, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetId() {
|
|
||||||
$id = uniqid();
|
|
||||||
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
$error->setError($id, 'http://example.com');
|
|
||||||
|
|
||||||
$this->assertEquals($id, $error->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetUri() {
|
|
||||||
$uri = 'http://example.com/end/point';
|
|
||||||
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
$error->setError(uniqid(), $uri);
|
|
||||||
|
|
||||||
$this->assertEquals($uri, $error->getUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDescription() {
|
|
||||||
$desc = uniqid();
|
|
||||||
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
$error->setError(uniqid(), 'curie', $desc);
|
|
||||||
|
|
||||||
$this->assertEquals($desc, $error->getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDetails() {
|
|
||||||
$detail = uniqid();
|
|
||||||
|
|
||||||
$error = new CallError(new Connection);
|
|
||||||
$this->assertNull($error->getDetails());
|
|
||||||
$error->setError(uniqid(), 'http://socketo.me', 'desc', $detail);
|
|
||||||
|
|
||||||
$this->assertEquals($detail, $error->getDetails());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Component\WAMP\Command\Action;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\CallResult;
|
|
||||||
use Ratchet\Tests\Mock\Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Component\WAMP\Command\Action\CallResult
|
|
||||||
*/
|
|
||||||
class CallResultTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
public function testGetMessage() {
|
|
||||||
$result = new CallResult(new Connection);
|
|
||||||
|
|
||||||
$callId = uniqid();
|
|
||||||
$data = array('hello' => 'world', 'herp' => 'derp');
|
|
||||||
|
|
||||||
$result->setResult($callId, $data);
|
|
||||||
$resultString = $result->getMessage();
|
|
||||||
|
|
||||||
$this->assertEquals(array(3, $callId, $data), json_decode($resultString, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetId() {
|
|
||||||
$id = uniqid();
|
|
||||||
|
|
||||||
$result = new CallResult(new Connection);
|
|
||||||
$result->setResult($id, array());
|
|
||||||
|
|
||||||
$this->assertEquals($id, $result->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetData() {
|
|
||||||
$data = array(
|
|
||||||
'hello' => 'world'
|
|
||||||
, 'recursive' => array(
|
|
||||||
'the' => 'quick'
|
|
||||||
, 'brown' => 'fox'
|
|
||||||
)
|
|
||||||
, 'jumps'
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = new CallResult(new Connection);
|
|
||||||
$result->setResult(uniqid(), $data);
|
|
||||||
|
|
||||||
$this->assertEquals($data, $result->getData());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Mock;
|
namespace Ratchet\Tests\Mock;
|
||||||
use Ratchet\Component\MessageComponentInterface;
|
use Ratchet\MessageComponentInterface;
|
||||||
use Ratchet\Tests\Mock\Socket as MockSocket;
|
use Ratchet\Tests\Mock\Socket as MockSocket;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Rename to MessageComponent
|
* @todo Rename to MessageComponent
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Mock;
|
namespace Ratchet\Tests\Mock;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
class Connection implements ConnectionInterface {
|
class Connection implements ConnectionInterface {
|
||||||
|
public $last = array(
|
||||||
|
'send' => ''
|
||||||
|
, 'close' => false
|
||||||
|
);
|
||||||
|
|
||||||
public $remoteAddress = '127.0.0.1';
|
public $remoteAddress = '127.0.0.1';
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
}
|
||||||
}
|
}
|
22
tests/Ratchet/Tests/Mock/ConnectionDecorator.php
Normal file
22
tests/Ratchet/Tests/Mock/ConnectionDecorator.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Tests\Mock;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
|
||||||
|
class ConnectionDecorator extends AbstractConnectionDecorator {
|
||||||
|
public $last = array(
|
||||||
|
'write' => ''
|
||||||
|
, 'end' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
|
||||||
|
$this->getConnection()->close();
|
||||||
|
}
|
||||||
|
}
|
@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Mock;
|
|
||||||
use Ratchet\Resource\Socket\SocketInterface;
|
|
||||||
use Ratchet\Resource\Socket\BSDSocket as RealSocket;
|
|
||||||
|
|
||||||
class FakeSocket implements SocketInterface {
|
|
||||||
public $_arguments = array();
|
|
||||||
public $_options = array();
|
|
||||||
|
|
||||||
protected $_id = 1;
|
|
||||||
|
|
||||||
public $_last = array();
|
|
||||||
|
|
||||||
public function getResource() {
|
|
||||||
return "#{$this->_id}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() {
|
|
||||||
return (string)$this->_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct($domain = null, $type = null, $protocol = null) {
|
|
||||||
list($this->_arguments['domain'], $this->_arguments['type'], $this->_arguments['protocol']) = array(1, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __clone() {
|
|
||||||
$this->_id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deliver($message) {
|
|
||||||
$this->write($message, strlen($message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bind($address, $port = 0) {
|
|
||||||
$this->_last['bind'] = array($address, $port);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connect($address, $port = 0) {
|
|
||||||
$this->_last['connect'] = array($address, $port = 0);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemoteAddress() {
|
|
||||||
return '127.0.0.1';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_option($level, $optname) {
|
|
||||||
return $this->_options[$level][$optname];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listen($backlog = 0) {
|
|
||||||
$this->_last['listen'] = array($backlog);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function read($length, $type = PHP_BINARY_READ) {
|
|
||||||
$this->_last['read'] = array($length, $type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function recv(&$buf, $len, $flags) {
|
|
||||||
$this->_last['recv'] = array($buf, $len, $flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) {
|
|
||||||
$this->_last['select'] = array($read, $write, $except, $tv_sec, $tv_usec);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_block() {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_nonblock() {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set_option($level, $optname, $optval) {
|
|
||||||
if (!isset($this->_options[$level])) {
|
|
||||||
$this->_options[$level] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_options[$level][$optname] = $optval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function shutdown($how = 2) {
|
|
||||||
$this->_last['shutdown'] = array($how);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function write($buffer, $length = 0) {
|
|
||||||
$this->_last['write'] = array($buffer, $length);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Mock;
|
namespace Ratchet\Tests\Mock;
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponentInterface;
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
use Ratchet\Resource\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
class WAMPComponent implements WAMPServerComponentInterface {
|
class WampComponent implements WampServerInterface {
|
||||||
public $last = array();
|
public $last = array();
|
||||||
|
|
||||||
public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
|
public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
|
||||||
@ -18,7 +18,7 @@ class WAMPComponent implements WAMPServerComponentInterface {
|
|||||||
$this->last[__FUNCTION__] = func_get_args();
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onPublish(ConnectionInterface $conn, $uri, $event) {
|
public function onPublish(ConnectionInterface $conn, $uri, $event, $exclude, $eligible) {
|
||||||
$this->last[__FUNCTION__] = func_get_args();
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Resource\Command\Action;
|
|
||||||
use Ratchet\Resource\Command\Action\SendMessage;
|
|
||||||
use Ratchet\Tests\Mock\Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Resource\Command\Action\SendMessage
|
|
||||||
*/
|
|
||||||
class SendMessageTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
public function testFluentInterface() {
|
|
||||||
$cmd = new SendMessage(new Connection);
|
|
||||||
$this->assertInstanceOf('\\Ratchet\\Resource\\Command\\Action\\SendMessage', $cmd->setMessage('Hello World!'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetMessageMatchesSet() {
|
|
||||||
$msg = 'The quick brown fox jumps over the lazy dog.';
|
|
||||||
$cmd = new SendMessage(new Connection);
|
|
||||||
$cmd->setMessage($msg);
|
|
||||||
|
|
||||||
$this->assertEquals($msg, $cmd->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Resource\Command;
|
|
||||||
use Ratchet\Resource\Command\Composite;
|
|
||||||
use Ratchet\Resource\Connection;
|
|
||||||
use Ratchet\Tests\Mock\FakeSocket;
|
|
||||||
use Ratchet\Resource\Command\Action\Null as NullAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Resource\Command\Composite
|
|
||||||
*/
|
|
||||||
class CompositeTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
protected $_comp;
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
$this->_comp = new Composite;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function newNull() {
|
|
||||||
return new NullAction(new Connection(new FakeSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanEnqueueNull() {
|
|
||||||
$count = $this->_comp->count();
|
|
||||||
|
|
||||||
$this->_comp->enqueue(null);
|
|
||||||
|
|
||||||
$this->assertEquals($count, $this->_comp->count());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testEnqueueCommand() {
|
|
||||||
$count = $this->_comp->count();
|
|
||||||
|
|
||||||
$this->_comp->enqueue($this->newNull());
|
|
||||||
|
|
||||||
$this->assertEquals($count + 1, $this->_comp->count());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function badEnqueueProviders() {
|
|
||||||
return array(
|
|
||||||
array(array())
|
|
||||||
, array('string')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider badEnqueueProviders
|
|
||||||
*/
|
|
||||||
public function testCanNotPassOtherThings($object) {
|
|
||||||
$this->setExpectedException('InvalidArgumentException');
|
|
||||||
|
|
||||||
$this->_comp->enqueue($object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCompositeComposite() {
|
|
||||||
$compTwo = new Composite;
|
|
||||||
$compTwo->enqueue($this->newNull());
|
|
||||||
$compTwo->enqueue($this->newNull());
|
|
||||||
|
|
||||||
$this->_comp->enqueue($this->newNull());
|
|
||||||
$this->_comp->enqueue($compTwo);
|
|
||||||
|
|
||||||
$this->assertEquals(3, $this->_comp->count());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Resource;
|
|
||||||
use Ratchet\Resource\Connection;
|
|
||||||
use Ratchet\Tests\Mock\FakeSocket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Resource\Connection
|
|
||||||
*/
|
|
||||||
class ConnectionTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
/**
|
|
||||||
* @var Ratchet\Tests\Mock\FakeSocket
|
|
||||||
*/
|
|
||||||
protected $_fs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Ratchet\Resource\Connection
|
|
||||||
*/
|
|
||||||
protected $_c;
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
$this->_fs = new FakeSocket;
|
|
||||||
$this->_c = new Connection($this->_fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function keyAndValProvider() {
|
|
||||||
return array(
|
|
||||||
array('hello', 'world')
|
|
||||||
, array('herp', 'derp')
|
|
||||||
, array('depth', array('hell', 'yes'))
|
|
||||||
, array('moar', array('hellz' => 'yes'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetSocketReturnsWhatIsSetInConstruct() {
|
|
||||||
$this->assertSame($this->_fs, $this->_c->getSocket());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider keyAndValProvider
|
|
||||||
*/
|
|
||||||
public function testCanGetWhatIsSet($key, $val) {
|
|
||||||
$this->_c->{$key} = $val;
|
|
||||||
$this->assertEquals($val, $this->_c->{$key});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider keyAndValProvider
|
|
||||||
*/
|
|
||||||
public function testIssetWorksOnOverloadedVariables($key, $val) {
|
|
||||||
$this->_c->{$key} = $val;
|
|
||||||
$this->assertTrue(isset($this->_c->{$key}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider keyAndValProvider
|
|
||||||
*/
|
|
||||||
public function testUnsetMakesIssetReturnFalse($key, $val) {
|
|
||||||
$this->_c->{$key} = $val;
|
|
||||||
unset($this->_c->{$key});
|
|
||||||
$this->assertFalse(isset($this->_c->{$key}));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Ratchet\Tests\Resource\Socket;
|
|
||||||
use Ratchet\Tests\Mock\FakeSocket as Socket;
|
|
||||||
use Ratchet\Resource\Socket\BSDSocket as RealSocket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Resource\Socket\BSDSocket
|
|
||||||
*/
|
|
||||||
class BSDSocketTest extends \PHPUnit_Framework_TestCase {
|
|
||||||
protected $_socket;
|
|
||||||
|
|
||||||
protected static function getMethod($name) {
|
|
||||||
$class = new \ReflectionClass('\\Ratchet\\Tests\\Mock\\FakeSocket');
|
|
||||||
$method = $class->getMethod($name);
|
|
||||||
$method->setAccessible(true);
|
|
||||||
|
|
||||||
return $method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
$this->_socket = new Socket();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (1): I may or may not re-enable this test (need to add code back to FakeSocket), not sure if I'll keep this feature at all
|
|
||||||
public function testGetDefaultConfigForConstruct() {
|
|
||||||
$ref_conf = static::getMethod('getConfig');
|
|
||||||
$config = $ref_conf->invokeArgs($this->_socket, array());
|
|
||||||
|
|
||||||
$this->assertEquals(array_values(Socket::$_defaults), $config);
|
|
||||||
}
|
|
||||||
/**/
|
|
||||||
|
|
||||||
public function testInvalidConstructorArguments() {
|
|
||||||
$this->setExpectedException('\\Ratchet\\Resource\\Socket\\BSDSocketException');
|
|
||||||
$socket = new RealSocket('invalid', 'param', 'derp');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testConstructAndCallByOpenAndClose() {
|
|
||||||
$socket = new RealSocket();
|
|
||||||
$socket->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asArrayProvider() {
|
|
||||||
return array(
|
|
||||||
array(array('hello' => 'world'), array('hello' => 'world'))
|
|
||||||
, array(null, null)
|
|
||||||
, array(array('hello' => 'world'), new \ArrayObject(array('hello' => 'world')))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* (1)
|
|
||||||
* @dataProvider asArrayProvider
|
|
||||||
* /
|
|
||||||
public function testMethodMungforselectReturnsExpectedValues($output, $input) {
|
|
||||||
$method = static::getMethod('mungForSelect');
|
|
||||||
$return = $method->invokeArgs($this->_socket, array($input));
|
|
||||||
|
|
||||||
$this->assertEquals($return, $output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function NOPEtestMethodMungforselectRejectsNonTraversable() {
|
|
||||||
$this->setExpectedException('\\InvalidArgumentException');
|
|
||||||
$method = static::getMethod('mungForSelect');
|
|
||||||
$method->invokeArgs($this->_socket, array('I am upset with PHP ATM'));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
@ -1,16 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Application\Server;
|
namespace Ratchet\Tests\Application\Server;
|
||||||
use Ratchet\Component\Server\FlashPolicyComponent;
|
use Ratchet\Server\FlashPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\Server\FlashPolicyComponent
|
* @covers Ratchet\Server\FlashPolicy
|
||||||
*/
|
*/
|
||||||
class FlashPolicyComponentTest extends \PHPUnit_Framework_TestCase {
|
class FlashPolicyTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
protected $_policy;
|
protected $_policy;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_policy = new FlashPolicyComponent();
|
$this->_policy = new FlashPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPolicyRender() {
|
public function testPolicyRender() {
|
@ -1,19 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\Server;
|
namespace Ratchet\Tests\Server;
|
||||||
use Ratchet\Component\Server\IpBlackListComponent;
|
use Ratchet\Server\IpBlackList;
|
||||||
use Ratchet\Tests\Mock\Connection;
|
use Ratchet\Tests\Mock\Connection;
|
||||||
use Ratchet\Tests\Mock\Component as MockComponent;
|
use Ratchet\Tests\Mock\Component as MockComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\Server\IpBlackListComponent
|
* @covers Ratchet\Server\IpBlackList
|
||||||
*/
|
*/
|
||||||
class IpBlackListComponentTest extends \PHPUnit_Framework_TestCase {
|
class IpBlackListTest extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_comp;
|
protected $_comp;
|
||||||
protected $_mock;
|
protected $_mock;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_mock = new MockComponent;
|
$this->_mock = new MockComponent;
|
||||||
$this->_comp = new IpBlackListComponent($this->_mock);
|
$this->_comp = new IpBlackList($this->_mock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBlockAndCloseOnOpen() {
|
public function testBlockAndCloseOnOpen() {
|
||||||
@ -23,7 +23,7 @@ class IpBlackListComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
$ret = $this->_comp->onOpen($conn);
|
$ret = $this->_comp->onOpen($conn);
|
||||||
|
|
||||||
$this->assertInstanceOf('\\Ratchet\\Resource\\Command\\Action\\CloseConnection', $ret);
|
$this->assertTrue($conn->last['close']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddAndRemoveWithFluentInterfaces() {
|
public function testAddAndRemoveWithFluentInterfaces() {
|
||||||
@ -86,6 +86,6 @@ class IpBlackListComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testUnblockingSilentlyFails() {
|
public function testUnblockingSilentlyFails() {
|
||||||
$this->assertInstanceOf('\\Ratchet\\Component\\Server\\IpBlackListComponent', $this->_comp->unblockAddress('localhost'));
|
$this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->_comp->unblockAddress('localhost'));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\Session\Serialize;
|
namespace Ratchet\Tests\Session\Serialize;
|
||||||
use Ratchet\Component\Session\Serialize\PhpHandler;
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\Session\Serialize\PhpHandler
|
* @covers Ratchet\Session\Serialize\PhpHandler
|
||||||
*/
|
*/
|
||||||
class PhpHandlerTest extends \PHPUnit_Framework_TestCase {
|
class PhpHandlerTest extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_handler;
|
protected $_handler;
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\Session;
|
namespace Ratchet\Tests\Session;
|
||||||
use Ratchet\Component\Session\SessionComponent;
|
use Ratchet\Session\SessionProvider;
|
||||||
use Ratchet\Tests\Mock\Component as MockComponent;
|
use Ratchet\Tests\Mock\Component as MockComponent;
|
||||||
use Ratchet\Tests\Mock\MemorySessionHandler;
|
use Ratchet\Tests\Mock\MemorySessionHandler;
|
||||||
use Ratchet\Tests\Mock\Connection;
|
use Ratchet\Tests\Mock\Connection;
|
||||||
@ -9,11 +9,11 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
|||||||
use Guzzle\Http\Message\Request;
|
use Guzzle\Http\Message\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\Session\SessionComponent
|
* @covers Ratchet\Session\SessionProvider
|
||||||
* @covers Ratchet\Component\Session\Storage\VirtualSessionStorage
|
* @covers Ratchet\Session\Storage\VirtualSessionStorage
|
||||||
* @covers Ratchet\Component\Session\Storage\Proxy\VirtualProxy
|
* @covers Ratchet\Session\Storage\Proxy\VirtualProxy
|
||||||
*/
|
*/
|
||||||
class SessionComponentTest extends \PHPUnit_Framework_TestCase {
|
class SessionProviderTest extends \PHPUnit_Framework_TestCase {
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
if (!class_exists('Symfony\\Component\\HttpFoundation\\Session\\Session')) {
|
if (!class_exists('Symfony\\Component\\HttpFoundation\\Session\\Session')) {
|
||||||
return $this->markTestSkipped('Dependency of Symfony HttpFoundation failed');
|
return $this->markTestSkipped('Dependency of Symfony HttpFoundation failed');
|
||||||
@ -31,11 +31,11 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
* @dataProvider classCaseProvider
|
* @dataProvider classCaseProvider
|
||||||
*/
|
*/
|
||||||
public function testToClassCase($in, $out) {
|
public function testToClassCase($in, $out) {
|
||||||
$ref = new \ReflectionClass('\\Ratchet\\Component\\Session\\SessionComponent');
|
$ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider');
|
||||||
$method = $ref->getMethod('toClassCase');
|
$method = $ref->getMethod('toClassCase');
|
||||||
$method->setAccessible(true);
|
$method->setAccessible(true);
|
||||||
|
|
||||||
$component = new SessionComponent(new MockComponent, new MemorySessionHandler);
|
$component = new SessionProvider(new MockComponent, new MemorySessionHandler);
|
||||||
$this->assertEquals($out, $method->invokeArgs($component, array($in)));
|
$this->assertEquals($out, $method->invokeArgs($component, array($in)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$pdo->exec(vsprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY, %s TEXT, %s INTEGER)", $dbOptions));
|
$pdo->exec(vsprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY, %s TEXT, %s INTEGER)", $dbOptions));
|
||||||
$pdo->prepare(vsprintf("INSERT INTO %s (%s, %s, %s) VALUES (?, ?, ?)", $dbOptions))->execute(array($sessionId, base64_encode('_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'), time()));
|
$pdo->prepare(vsprintf("INSERT INTO %s (%s, %s, %s) VALUES (?, ?, ?)", $dbOptions))->execute(array($sessionId, base64_encode('_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'), time()));
|
||||||
|
|
||||||
$component = new SessionComponent(new MockComponent, new PdoSessionHandler($pdo, $dbOptions), array('auto_start' => 1));
|
$component = new SessionProvider(new MockComponent, new PdoSessionHandler($pdo, $dbOptions), array('auto_start' => 1));
|
||||||
$connection = new Connection();
|
$connection = new Connection();
|
||||||
|
|
||||||
$headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array()));
|
$headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array()));
|
||||||
@ -83,7 +83,7 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mock = new MockComponent;
|
$mock = new MockComponent;
|
||||||
$comp = new SessionComponent($mock, new NullSessionHandler);
|
$comp = new SessionProvider($mock, new NullSessionHandler);
|
||||||
|
|
||||||
$comp->onOpen($conns[1]);
|
$comp->onOpen($conns[1]);
|
||||||
$comp->onOpen($conns[3]);
|
$comp->onOpen($conns[3]);
|
67
tests/Ratchet/Tests/Wamp/WampConnectionTest.php
Normal file
67
tests/Ratchet/Tests/Wamp/WampConnectionTest.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Tests\Wamp;
|
||||||
|
use Ratchet\Wamp\WampConnection;
|
||||||
|
use Ratchet\Tests\Mock\Connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\WampConnection
|
||||||
|
*/
|
||||||
|
class WampConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
public function testCallResult() {
|
||||||
|
$conn = new Connection;
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
|
||||||
|
$callId = uniqid();
|
||||||
|
$data = array('hello' => 'world', 'herp' => 'derp');
|
||||||
|
|
||||||
|
|
||||||
|
$decor->callResult($callId, $data);
|
||||||
|
$resultString = $conn->last['send'];
|
||||||
|
|
||||||
|
$this->assertEquals(array(3, $callId, $data), json_decode($resultString, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallError() {
|
||||||
|
$conn = new Connection;
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
|
||||||
|
$decor->callError($callId, $uri);
|
||||||
|
$resultString = $conn->last['send'];
|
||||||
|
|
||||||
|
$this->assertEquals(array(4, $callId, $uri, ''), json_decode($resultString, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetailedCallError() {
|
||||||
|
$conn = new Connection;
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
$desc = 'beep boop beep';
|
||||||
|
$detail = 'Error: Too much awesome';
|
||||||
|
|
||||||
|
$decor->callError($callId, $uri, $desc, $detail);
|
||||||
|
$resultString = $conn->last['send'];
|
||||||
|
|
||||||
|
$this->assertEquals(array(4, $callId, $uri, $desc, $detail), json_decode($resultString, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrefix() {
|
||||||
|
$conn = new WampConnection(new Connection);
|
||||||
|
|
||||||
|
$shortOut = 'outgoing';
|
||||||
|
$longOut = 'http://example.com/outoing';
|
||||||
|
|
||||||
|
$conn->prefix($shortOut, $longOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUriWhenNoCurieGiven() {
|
||||||
|
$conn = new WampConnection(new Connection);
|
||||||
|
$uri = 'http://example.com/noshort';
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $conn->getUri($uri));
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WAMP;
|
namespace Ratchet\Tests\Wamp;
|
||||||
use Ratchet\Component\WAMP\WAMPServerComponent;
|
use Ratchet\Wamp\WampServer;
|
||||||
use Ratchet\Resource\Connection;
|
use Ratchet\Wamp\WampConnection;
|
||||||
use Ratchet\Tests\Mock\FakeSocket;
|
use Ratchet\Tests\Mock\Connection;
|
||||||
use Ratchet\Tests\Mock\WAMPComponent as TestComponent;
|
use Ratchet\Tests\Mock\WampComponent as TestComponent;
|
||||||
use Ratchet\Component\WAMP\Command\Action\CallResult;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\CallError;
|
|
||||||
use Ratchet\Component\WAMP\Command\Action\Event;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WAMP\WAMPServerComponent
|
* @covers Ratchet\Wamp\WampServer
|
||||||
* @covers Ratchet\Component\WAMP\WAMPServerComponentInterface
|
* @covers Ratchet\Wamp\WampServerInterface
|
||||||
|
* @covers Ratchet\Wamp\WampConnection
|
||||||
*/
|
*/
|
||||||
class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
class WampServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_comp;
|
protected $_comp;
|
||||||
|
|
||||||
protected $_app;
|
protected $_app;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_app = new TestComponent;
|
$this->_app = new TestComponent;
|
||||||
$this->_comp = new WAMPServerComponent($this->_app);
|
$this->_comp = new WampServer($this->_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function newConn() {
|
protected function newConn() {
|
||||||
return new Connection(new FakeSocket);
|
return new Connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function invalidMessageProvider() {
|
public function invalidMessageProvider() {
|
||||||
@ -40,20 +38,19 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
* @dataProvider invalidMessageProvider
|
* @dataProvider invalidMessageProvider
|
||||||
*/
|
*/
|
||||||
public function testInvalidMessages($type) {
|
public function testInvalidMessages($type) {
|
||||||
$this->setExpectedException('\\Ratchet\\Component\\WAMP\\Exception');
|
$this->setExpectedException('\\Ratchet\\Wamp\\Exception');
|
||||||
|
|
||||||
$this->_comp->onMessage($this->newConn(), json_encode(array($type)));
|
$conn = $this->newConn();
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array($type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Component\WAMP\Command\Action\Welcome
|
|
||||||
*/
|
|
||||||
public function testWelcomeMessage() {
|
public function testWelcomeMessage() {
|
||||||
$conn = new Connection(new FakeSocket);
|
$conn = $this->newConn();
|
||||||
|
|
||||||
$return = $this->_comp->onOpen($conn);
|
$this->_comp->onOpen($conn);
|
||||||
$action = $return->pop();
|
|
||||||
$message = $action->getMessage();
|
$message = $conn->last['send'];
|
||||||
$json = json_decode($message);
|
$json = json_decode($message);
|
||||||
|
|
||||||
$this->assertEquals(4, count($json));
|
$this->assertEquals(4, count($json));
|
||||||
@ -66,7 +63,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$uri = 'http://example.com';
|
$uri = 'http://example.com';
|
||||||
$clientMessage = array(5, $uri);
|
$clientMessage = array(5, $uri);
|
||||||
|
|
||||||
$this->_comp->onMessage($this->newConn(), json_encode($clientMessage));
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
$this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
|
$this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
|
||||||
}
|
}
|
||||||
@ -75,7 +75,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$uri = 'http://example.com/endpoint';
|
$uri = 'http://example.com/endpoint';
|
||||||
$clientMessage = array(6, $uri);
|
$clientMessage = array(6, $uri);
|
||||||
|
|
||||||
$this->_comp->onMessage($this->newConn(), json_encode($clientMessage));
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
$this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
|
$this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
|
||||||
}
|
}
|
||||||
@ -104,7 +107,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$id = uniqid();
|
$id = uniqid();
|
||||||
$clientMessage = array_merge(array(2, $id, $uri), $args);
|
$clientMessage = array_merge(array(2, $id, $uri), $args);
|
||||||
|
|
||||||
$this->_comp->onMessage($this->newConn(), json_encode($clientMessage));
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
$this->assertEquals($id, $this->_app->last['onCall'][1]);
|
$this->assertEquals($id, $this->_app->last['onCall'][1]);
|
||||||
$this->assertEquals($uri, $this->_app->last['onCall'][2]);
|
$this->assertEquals($uri, $this->_app->last['onCall'][2]);
|
||||||
@ -133,70 +139,68 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider eventProvider
|
* @dataProvider eventProvider
|
||||||
* @covers Ratchet\Component\WAMP\Command\Action\Event
|
|
||||||
*/
|
*/
|
||||||
public function testEvent($topic, $payload) {
|
public function testEvent($topic, $payload) {
|
||||||
$event = new Event($this->newConn());
|
$conn = new WampConnection($this->newConn());
|
||||||
$event->setEvent($topic, $payload);
|
$conn->event($topic, $payload);
|
||||||
|
|
||||||
$eventString = $event->getMessage();
|
$eventString = $conn->last['send'];
|
||||||
|
|
||||||
$this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
|
$this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOnClosePropagation() {
|
public function testOnClosePropagation() {
|
||||||
$conn = $this->newConn();
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
$this->_comp->onClose($conn);
|
$this->_comp->onClose($conn);
|
||||||
|
|
||||||
$this->assertSame($conn, $this->_app->last['onClose'][0]);
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onClose'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOnErrorPropagation() {
|
public function testOnErrorPropagation() {
|
||||||
$conn = $this->newConn();
|
$conn = new Connection;
|
||||||
|
|
||||||
try {
|
$e = new \Exception('Nope');
|
||||||
throw new \Exception('Nope');
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
$this->_comp->onError($conn, $e);
|
$this->_comp->onError($conn, $e);
|
||||||
|
|
||||||
$this->assertSame($conn, $this->_app->last['onError'][0]);
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onError'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
$this->assertSame($e, $this->_app->last['onError'][1]);
|
$this->assertSame($e, $this->_app->last['onError'][1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers Ratchet\Component\WAMP\Command\Action\Prefix
|
|
||||||
*/
|
|
||||||
public function testPrefix() {
|
public function testPrefix() {
|
||||||
$conn = $this->newConn();
|
$conn = new WampConnection($this->newConn());
|
||||||
$this->_comp->onOpen($conn);
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
$shortOut = 'outgoing';
|
|
||||||
$longOut = 'http://example.com/outoing';
|
|
||||||
|
|
||||||
$shortIn = 'incoming';
|
$shortIn = 'incoming';
|
||||||
$shortIn = 'http://example.com/incoming/';
|
$longIn = 'http://example.com/incoming/';
|
||||||
|
|
||||||
$this->assertTrue(is_callable($conn->WAMP->addPrefix));
|
$this->_comp->onMessage($conn, json_encode(array(1, $shortIn, $longIn)));
|
||||||
|
|
||||||
$cb = $conn->WAMP->addPrefix;
|
$this->assertEquals($longIn, $conn->WAMP->prefixes[$shortIn]);
|
||||||
$cb($shortOut, $longOut);
|
$this->assertEquals($longIn, $conn->getUri($shortIn));
|
||||||
|
|
||||||
$return = $this->_comp->onMessage($conn, json_encode(array(1, $shortIn, $shortOut)));
|
|
||||||
$command = $return->pop();
|
|
||||||
|
|
||||||
$this->assertInstanceOf('Ratchet\\Component\\WAMP\\Command\\Action\\Prefix', $command);
|
|
||||||
$this->assertEquals($shortOut, $command->getCurie());
|
|
||||||
$this->assertEquals($longOut, $command->getUri());
|
|
||||||
|
|
||||||
$this->assertEquals(array(1, $shortOut, $longOut), json_decode($command->getMessage()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMessageMustBeJson() {
|
public function testMessageMustBeJson() {
|
||||||
$this->setExpectedException('\\Ratchet\\Component\\WAMP\\JsonException');
|
$this->setExpectedException('\\Ratchet\\Wamp\\JsonException');
|
||||||
|
|
||||||
$this->_comp->onMessage($this->newConn(), 'Hello World!');
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, 'Hello World!');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Tests\WebSocket\Guzzle\Http\Message;
|
||||||
|
use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory
|
||||||
|
*/
|
||||||
|
class RequestFactoryTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->factory = RequestFactory::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageProvider() {
|
||||||
|
return array(
|
||||||
|
'status' => 'GET / HTTP/1.1'
|
||||||
|
, 'headers' => array(
|
||||||
|
'Upgrade' => 'WebSocket'
|
||||||
|
, 'Connection' => 'Upgrade'
|
||||||
|
, 'Host' => 'localhost:8000'
|
||||||
|
, 'Sec-WebSocket-Key1' => '> b3lU Z0 fh f 3+83394 6 (zG4'
|
||||||
|
, 'Sec-WebSocket-Key2' => ',3Z0X0677 dV-d [159 Z*4'
|
||||||
|
)
|
||||||
|
, 'body' => "123456\r\n\r\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function combineMessage($status, array $headers, $body = '') {
|
||||||
|
$message = $status . "\r\n";
|
||||||
|
|
||||||
|
foreach ($headers as $key => $val) {
|
||||||
|
$message .= "{$key}: {$val}\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$message .= "\r\n{$body}";
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExpectedDataFromGuzzleHeaders() {
|
||||||
|
$parts = $this->testMessageProvider();
|
||||||
|
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||||
|
$object = $this->factory->fromMessage($message);
|
||||||
|
|
||||||
|
foreach ($parts['headers'] as $key => $val) {
|
||||||
|
$this->assertEquals($val, $object->getHeader($key, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExpectedDataFromNonGuzzleHeaders() {
|
||||||
|
$parts = $this->testMessageProvider();
|
||||||
|
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||||
|
$object = $this->factory->fromMessage($message);
|
||||||
|
|
||||||
|
$this->assertNull($object->getHeader('Nope', true));
|
||||||
|
$this->assertNull($object->getHeader('Nope'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExpectedDataFromNonGuzzleBody() {
|
||||||
|
$parts = $this->testMessageProvider();
|
||||||
|
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||||
|
$object = $this->factory->fromMessage($message);
|
||||||
|
|
||||||
|
$this->assertEquals($parts['body'], (string)$object->getBody());
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WebSocket\Version;
|
namespace Ratchet\Tests\WebSocket\Version;
|
||||||
use Ratchet\Component\WebSocket\Version\Hixie76;
|
use Ratchet\WebSocket\Version\Hixie76;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WebSocket\Version\Hixie76
|
* @covers Ratchet\WebSocket\Version\Hixie76
|
||||||
*/
|
*/
|
||||||
class Hixie76Test extends \PHPUnit_Framework_TestCase {
|
class Hixie76Test extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_version;
|
protected $_version;
|
||||||
@ -13,7 +13,7 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testClassImplementsVersionInterface() {
|
public function testClassImplementsVersionInterface() {
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface');
|
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface');
|
||||||
$this->assertThat($this->_version, $constraint);
|
$this->assertThat($this->_version, $constraint);
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WebSocket\Version;
|
namespace Ratchet\Tests\WebSocket\Version;
|
||||||
use Ratchet\Component\WebSocket\Version\HyBi10;
|
use Ratchet\WebSocket\Version\HyBi10;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455\Frame;
|
use Ratchet\WebSocket\Version\RFC6455\Frame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WebSocket\Version\Hybi10
|
* @covers Ratchet\WebSocket\Version\Hybi10
|
||||||
*/
|
*/
|
||||||
class HyBi10Test extends \PHPUnit_Framework_TestCase {
|
class HyBi10Test extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_version;
|
protected $_version;
|
||||||
@ -17,7 +17,7 @@ class HyBi10Test extends \PHPUnit_Framework_TestCase {
|
|||||||
* Is this useful?
|
* Is this useful?
|
||||||
*/
|
*/
|
||||||
public function testClassImplementsVersionInterface() {
|
public function testClassImplementsVersionInterface() {
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface');
|
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface');
|
||||||
$this->assertThat($this->_version, $constraint);
|
$this->assertThat($this->_version, $constraint);
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WebSocket\Version\RFC6455;
|
namespace Ratchet\Tests\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455\Frame;
|
use Ratchet\WebSocket\Version\RFC6455\Frame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WebSocket\Version\RFC6455\Frame
|
* @covers Ratchet\WebSocket\Version\RFC6455\Frame
|
||||||
* @todo getMaskingKey, getPayloadStartingByte don't have tests yet
|
* @todo getMaskingKey, getPayloadStartingByte don't have tests yet
|
||||||
* @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry.
|
* @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry.
|
||||||
*/
|
*/
|
@ -1,13 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WebSocket\Version\RFC6455;
|
namespace Ratchet\Tests\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier;
|
use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier
|
* @covers Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier
|
||||||
*/
|
*/
|
||||||
class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
||||||
/**
|
/**
|
||||||
* @var Ratchet\Component\WebSocket\Version\RFC6455\HandshakeVerifier
|
* @var Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier
|
||||||
*/
|
*/
|
||||||
protected $_v;
|
protected $_v;
|
||||||
|
|
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Tests\Component\WebSocket\Version;
|
namespace Ratchet\Tests\WebSocket\Version;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455;
|
use Ratchet\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\Component\WebSocket\Version\RFC6455\Frame;
|
use Ratchet\WebSocket\Version\RFC6455\Frame;
|
||||||
use Guzzle\Http\Message\RequestFactory;
|
use Guzzle\Http\Message\RequestFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Component\WebSocket\Version\RFC6455
|
* @covers Ratchet\WebSocket\Version\RFC6455
|
||||||
*/
|
*/
|
||||||
class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_version;
|
protected $_version;
|
||||||
@ -18,7 +18,7 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
* Is this useful?
|
* Is this useful?
|
||||||
*/
|
*/
|
||||||
public function testClassImplementsVersionInterface() {
|
public function testClassImplementsVersionInterface() {
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface');
|
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface');
|
||||||
$this->assertThat($this->_version, $constraint);
|
$this->assertThat($this->_version, $constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
* @dataProvider headerHandshakeProvider
|
* @dataProvider headerHandshakeProvider
|
||||||
*/
|
*/
|
||||||
public function testVariousHeadersToCheckHandshakeTolerance($pass, $header) {
|
public function testVariousHeadersToCheckHandshakeTolerance($pass, $header) {
|
||||||
$request = RequestFactory::fromMessage($header);
|
$request = RequestFactory::getInstance()->fromMessage($header);
|
||||||
|
|
||||||
if ($pass) {
|
if ($pass) {
|
||||||
$this->assertInstanceOf('\\Guzzle\\Http\\Message\\Response', $this->_version->handshake($request));
|
$this->assertInstanceOf('\\Guzzle\\Http\\Message\\Response', $this->_version->handshake($request));
|
||||||
@ -125,4 +125,12 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
$this->_version->handshake($request);
|
$this->_version->handshake($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNewMessage() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->_version->newMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNewFrame() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->_version->newFrame());
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
|
||||||
|
|
||||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
|
Loading…
Reference in New Issue
Block a user