Merge branch '0.4-merge-0.3.4' into 0.4
This commit is contained in:
commit
e463de0c6b
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
phpunit.xml
|
phpunit.xml
|
||||||
reports
|
reports
|
||||||
sandbox
|
sandbox
|
||||||
vendor
|
vendor
|
||||||
composer.lock
|
composer.lock
|
@ -4,6 +4,8 @@ php:
|
|||||||
- 5.3
|
- 5.3
|
||||||
- 5.4
|
- 5.4
|
||||||
- 5.5
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- 7
|
||||||
- hhvm
|
- hhvm
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -8,6 +8,21 @@ CHANGELOG
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
* 0.3.4 (2015-12-23)
|
||||||
|
|
||||||
|
* BF: Edge case where version check wasn't run on message coalesce
|
||||||
|
* BF: Session didn't start when using pdo_sqlite
|
||||||
|
* BF: WAMP currie prefix check when using '#'
|
||||||
|
* Compatibility with Symfony 3
|
||||||
|
|
||||||
|
* 0.3.3 (2015-05-26)
|
||||||
|
|
||||||
|
* BF: Framing bug on large messages upon TCP fragmentation
|
||||||
|
* BF: Symfony Router query parameter defaults applied to Request
|
||||||
|
* BF: WAMP CURIE on all URIs
|
||||||
|
* OriginCheck rules applied to FlashPolicy
|
||||||
|
* Switched from PSR-0 to PSR-4
|
||||||
|
|
||||||
* 0.3.2 (2014-06-08)
|
* 0.3.2 (2014-06-08)
|
||||||
|
|
||||||
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
||||||
@ -111,4 +126,4 @@ CHANGELOG
|
|||||||
* 0.1 (2012-05-11)
|
* 0.1 (2012-05-11)
|
||||||
|
|
||||||
* First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
|
* First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
|
||||||
* I/O now handled by React, making Ratchet fully asynchronous
|
* I/O now handled by React, making Ratchet fully asynchronous
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2011-2014 Chris Boden
|
Copyright (c) 2011-2016 Chris Boden
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#Ratchet
|
#Ratchet
|
||||||
|
|
||||||
[](http://travis-ci.org/cboden/Ratchet)
|
[](http://travis-ci.org/ratchetphp/Ratchet)
|
||||||
[](https://packagist.org/packages/cboden/Ratchet)
|
[](https://packagist.org/packages/cboden/ratchet)
|
||||||
|
|
||||||
A PHP 5.3 library for asynchronously serving WebSockets.
|
A PHP 5.3 library for asynchronously serving WebSockets.
|
||||||
Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components.
|
Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components.
|
||||||
|
@ -14,19 +14,19 @@
|
|||||||
]
|
]
|
||||||
, "support": {
|
, "support": {
|
||||||
"forum": "https://groups.google.com/forum/#!forum/ratchet-php"
|
"forum": "https://groups.google.com/forum/#!forum/ratchet-php"
|
||||||
, "issues": "https://github.com/cboden/Ratchet/issues"
|
, "issues": "https://github.com/ratchetphp/Ratchet/issues"
|
||||||
, "irc": "irc://irc.freenode.org/reactphp"
|
, "irc": "irc://irc.freenode.org/reactphp"
|
||||||
}
|
}
|
||||||
, "autoload": {
|
, "autoload": {
|
||||||
"psr-0": {
|
"psr-4": {
|
||||||
"Ratchet": "src"
|
"Ratchet\\": "src/Ratchet"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
, "require": {
|
, "require": {
|
||||||
"php": ">=5.3.9"
|
"php": ">=5.3.9"
|
||||||
, "react/socket": "0.3.*|0.4.*"
|
, "react/socket": "^0.3 || ^0.4"
|
||||||
, "guzzle/http": "~3.6"
|
, "guzzle/http": "^3.6"
|
||||||
, "symfony/http-foundation": "~2.2"
|
, "symfony/http-foundation": "^2.2|^3.0"
|
||||||
, "symfony/routing": "~2.2"
|
, "symfony/routing": "^2.2|^3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,12 @@ class App {
|
|||||||
*/
|
*/
|
||||||
protected $httpHost;
|
protected $httpHost;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The port the socket is listening
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $port;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
@ -56,7 +62,7 @@ class App {
|
|||||||
*/
|
*/
|
||||||
public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) {
|
public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) {
|
||||||
if (extension_loaded('xdebug')) {
|
if (extension_loaded('xdebug')) {
|
||||||
trigger_error("XDebug extension detected. Remember to disable this if performance testing or going live!", E_USER_WARNING);
|
trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (3 !== strlen('✓')) {
|
if (3 !== strlen('✓')) {
|
||||||
@ -68,6 +74,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->httpHost = $httpHost;
|
$this->httpHost = $httpHost;
|
||||||
|
$this->port = $port;
|
||||||
|
|
||||||
$socket = new Reactor($loop);
|
$socket = new Reactor($loop);
|
||||||
$socket->listen($port, $address);
|
$socket->listen($port, $address);
|
||||||
@ -80,7 +87,6 @@ class App {
|
|||||||
$policy->addAllowedAccess($httpHost, $port);
|
$policy->addAllowedAccess($httpHost, $port);
|
||||||
$flashSock = new Reactor($loop);
|
$flashSock = new Reactor($loop);
|
||||||
$this->flashServer = new IoServer($policy, $flashSock);
|
$this->flashServer = new IoServer($policy, $flashSock);
|
||||||
|
|
||||||
if (80 == $port) {
|
if (80 == $port) {
|
||||||
$flashSock->listen(843, '0.0.0.0');
|
$flashSock->listen(843, '0.0.0.0');
|
||||||
} else {
|
} else {
|
||||||
@ -89,7 +95,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an endpiont/application to the server
|
* Add an endpoint/application to the server
|
||||||
* @param string $path The URI the client will connect to
|
* @param string $path The URI the client will connect to
|
||||||
* @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
|
* @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
|
||||||
* @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
|
* @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
|
||||||
@ -119,6 +125,13 @@ class App {
|
|||||||
$decorated = new OriginCheck($decorated, $allowedOrigins);
|
$decorated = new OriginCheck($decorated, $allowedOrigins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//allow origins in flash policy server
|
||||||
|
if(empty($this->flashServer) === false) {
|
||||||
|
foreach($allowedOrigins as $allowedOrgin) {
|
||||||
|
$this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost));
|
$this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost));
|
||||||
|
|
||||||
return $decorated;
|
return $decorated;
|
||||||
@ -130,4 +143,4 @@ class App {
|
|||||||
public function run() {
|
public function run() {
|
||||||
$this->_server->run();
|
$this->_server->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ namespace Ratchet;
|
|||||||
* The version of Ratchet being used
|
* The version of Ratchet being used
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const VERSION = 'Ratchet/0.3.2';
|
const VERSION = 'Ratchet/0.3.4';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A proxy object representing a connection to the application
|
* A proxy object representing a connection to the application
|
||||||
|
@ -53,6 +53,8 @@ class Router implements HttpServerInterface {
|
|||||||
$parameters[$key] = $value;
|
$parameters[$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$parameters = array_merge($parameters, $request->getQuery()->getAll());
|
||||||
|
|
||||||
$url = Url::factory($request->getPath());
|
$url = Url::factory($request->getPath());
|
||||||
$url->setQuery($parameters);
|
$url->setQuery($parameters);
|
||||||
$request->setUrl($url);
|
$request->setUrl($url);
|
||||||
|
@ -30,6 +30,12 @@ class VirtualSessionStorage extends NativeSessionStorage {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use
|
||||||
|
// pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object
|
||||||
|
// in the constructor. The method arguments are filled with the values, which are also used by the symfony
|
||||||
|
// framework in this case. This must not be the best choice, but it works.
|
||||||
|
$this->saveHandler->open(session_save_path(), session_name());
|
||||||
|
|
||||||
$rawData = $this->saveHandler->read($this->saveHandler->getId());
|
$rawData = $this->saveHandler->read($this->saveHandler->getId());
|
||||||
$sessionData = $this->_serializer->unserialize($rawData);
|
$sessionData = $this->_serializer->unserialize($rawData);
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
|||||||
$json = $json[0];
|
$json = $json[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_decorating->onCall($from, $callID, $procURI, $json);
|
$this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case static::MSG_SUBSCRIBE:
|
case static::MSG_SUBSCRIBE:
|
||||||
|
@ -17,7 +17,7 @@ class WampConnection extends AbstractConnectionDecorator {
|
|||||||
parent::__construct($conn);
|
parent::__construct($conn);
|
||||||
|
|
||||||
$this->WAMP = new \StdClass;
|
$this->WAMP = new \StdClass;
|
||||||
$this->WAMP->sessionId = uniqid();
|
$this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true));
|
||||||
$this->WAMP->prefixes = array();
|
$this->WAMP->prefixes = array();
|
||||||
|
|
||||||
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
||||||
@ -26,10 +26,10 @@ class WampConnection extends AbstractConnectionDecorator {
|
|||||||
/**
|
/**
|
||||||
* Successfully respond to a call made by the client
|
* Successfully respond to a call made by the client
|
||||||
* @param string $id The unique ID given by the client to respond to
|
* @param string $id The unique ID given by the client to respond to
|
||||||
* @param array $data An array of data to return to the client
|
* @param array $data an object or array
|
||||||
* @return WampConnection
|
* @return WampConnection
|
||||||
*/
|
*/
|
||||||
public function callResult($id, array $data = array()) {
|
public function callResult($id, $data = array()) {
|
||||||
return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,19 @@ class WampConnection extends AbstractConnectionDecorator {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getUri($uri) {
|
public function getUri($uri) {
|
||||||
return (array_key_exists($uri, $this->WAMP->prefixes) ? $this->WAMP->prefixes[$uri] : $uri);
|
$curieSeperator = ':';
|
||||||
|
|
||||||
|
if (preg_match('/http(s*)\:\/\//', $uri) == false) {
|
||||||
|
if (strpos($uri, $curieSeperator) !== false) {
|
||||||
|
list($prefix, $action) = explode($curieSeperator, $uri);
|
||||||
|
|
||||||
|
if(isset($this->WAMP->prefixes[$prefix]) === true){
|
||||||
|
return $this->WAMP->prefixes[$prefix] . '#' . $action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,7 @@ class WampServer implements MessageComponentInterface, WsServerInterface {
|
|||||||
/**
|
/**
|
||||||
* @var ServerProtocol
|
* @var ServerProtocol
|
||||||
*/
|
*/
|
||||||
private $wampProtocol;
|
protected $wampProtocol;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class just makes it 1 step easier to use Topic objects in WAMP
|
* This class just makes it 1 step easier to use Topic objects in WAMP
|
||||||
@ -41,8 +41,6 @@ class WampServer implements MessageComponentInterface, WsServerInterface {
|
|||||||
$this->wampProtocol->onMessage($conn, $msg);
|
$this->wampProtocol->onMessage($conn, $msg);
|
||||||
} catch (Exception $we) {
|
} catch (Exception $we) {
|
||||||
$conn->close(1007);
|
$conn->close(1007);
|
||||||
} catch (JsonException $je) {
|
|
||||||
$conn->close(1007);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +379,7 @@ class Frame implements FrameInterface {
|
|||||||
|
|
||||||
$byte_length = $this->getNumPayloadBytes();
|
$byte_length = $this->getNumPayloadBytes();
|
||||||
if ($this->bytesRecvd < 1 + $byte_length) {
|
if ($this->bytesRecvd < 1 + $byte_length) {
|
||||||
|
$this->defPayLen = -1;
|
||||||
throw new \UnderflowException('Not enough data buffered to determine payload length');
|
throw new \UnderflowException('Not enough data buffered to determine payload length');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,14 +102,14 @@ class WsServer implements HttpServerInterface {
|
|||||||
protected function attemptUpgrade(ConnectionInterface $conn, $data = '') {
|
protected function attemptUpgrade(ConnectionInterface $conn, $data = '') {
|
||||||
if ('' !== $data) {
|
if ('' !== $data) {
|
||||||
$conn->WebSocket->request->getBody()->write($data);
|
$conn->WebSocket->request->getBody()->write($data);
|
||||||
} else {
|
|
||||||
if (!$this->versioner->isVersionEnabled($conn->WebSocket->request)) {
|
|
||||||
return $this->close($conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->WebSocket->version = $this->versioner->getVersion($conn->WebSocket->request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->versioner->isVersionEnabled($conn->WebSocket->request)) {
|
||||||
|
return $this->close($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->WebSocket->version = $this->versioner->getVersion($conn->WebSocket->request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = $conn->WebSocket->version->handshake($conn->WebSocket->request);
|
$response = $conn->WebSocket->version->handshake($conn->WebSocket->request);
|
||||||
} catch (\UnderflowException $e) {
|
} catch (\UnderflowException $e) {
|
||||||
|
@ -8,7 +8,7 @@ interface WsServerInterface {
|
|||||||
/**
|
/**
|
||||||
* If any component in a stack supports a WebSocket sub-protocol return each supported in an array
|
* If any component in a stack supports a WebSocket sub-protocol return each supported in an array
|
||||||
* @return array
|
* @return array
|
||||||
* @temporary This method may be removed in future version (note that will not break code, just make some code obsolete)
|
* @todo This method may be removed in future version (note that will not break code, just make some code obsolete)
|
||||||
*/
|
*/
|
||||||
function getSubProtocols();
|
function getSubProtocols();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$loader = require __DIR__ . '/../vendor/autoload.php';
|
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||||
$loader->add('Ratchet', __DIR__ . '/helpers');
|
$loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
|
||||||
$loader->register();
|
|
||||||
|
@ -15,8 +15,18 @@ class RouterTest extends \PHPUnit_Framework_TestCase {
|
|||||||
protected $_req;
|
protected $_req;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
|
$queryMock = $this->getMock('Guzzle\Http\QueryString');
|
||||||
|
$queryMock
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getAll')
|
||||||
|
->will($this->returnValue(array()));
|
||||||
|
|
||||||
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
$this->_req = $this->getMock('\Guzzle\Http\Message\RequestInterface');
|
$this->_req = $this->getMock('\Guzzle\Http\Message\RequestInterface');
|
||||||
|
$this->_req
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getQuery')
|
||||||
|
->will($this->returnValue($queryMock));
|
||||||
$this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
|
$this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
|
||||||
$this->_matcher
|
$this->_matcher
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
@ -88,8 +98,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$this->_router->onError($this->_conn, $e);
|
$this->_router->onError($this->_conn, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRouterGeneratesRouteParameters()
|
public function testRouterGeneratesRouteParameters() {
|
||||||
{
|
|
||||||
/** @var $controller WsServerInterface */
|
/** @var $controller WsServerInterface */
|
||||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
/** @var $matcher UrlMatcherInterface */
|
/** @var $matcher UrlMatcherInterface */
|
||||||
@ -110,4 +119,22 @@ class RouterTest extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
$this->assertEquals(array('foo' => 'bar', 'baz' => 'qux'), $request->getQuery()->getAll());
|
$this->assertEquals(array('foo' => 'bar', 'baz' => 'qux'), $request->getQuery()->getAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testQueryParams() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will(
|
||||||
|
$this->returnValue(array('_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'))
|
||||||
|
);
|
||||||
|
|
||||||
|
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||||
|
$request = $this->getMock('Guzzle\Http\Message\Request', array('getPath'), array('GET', ''), '', false);
|
||||||
|
|
||||||
|
$request->setHeaderFactory($this->getMock('Guzzle\Http\Message\Header\HeaderFactoryInterface'));
|
||||||
|
$request->setUrl('ws://doesnt.matter?hello=world&foo=nope');
|
||||||
|
|
||||||
|
$router = new Router($this->_matcher);
|
||||||
|
$router->onOpen($conn, $request);
|
||||||
|
|
||||||
|
$this->assertEquals(array('foo' => 'nope', 'baz' => 'qux', 'hello' => 'world'), $request->getQuery()->getAll());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,14 +70,17 @@ class SessionProviderTest extends AbstractMessageComponentTestCase {
|
|||||||
, 'db_id_col' => 'sess_id'
|
, 'db_id_col' => 'sess_id'
|
||||||
, 'db_data_col' => 'sess_data'
|
, 'db_data_col' => 'sess_data'
|
||||||
, 'db_time_col' => 'sess_time'
|
, 'db_time_col' => 'sess_time'
|
||||||
|
, 'db_lifetime_col' => 'sess_lifetime'
|
||||||
);
|
);
|
||||||
|
|
||||||
$pdo = new \PDO("sqlite::memory:");
|
$pdo = new \PDO("sqlite::memory:");
|
||||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
$pdo->exec(vsprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY, %s TEXT, %s INTEGER)", $dbOptions));
|
$pdo->exec(vsprintf("CREATE TABLE %s (%s TEXT NOT NULL PRIMARY KEY, %s BLOB NOT NULL, %s INTEGER NOT NULL, %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()));
|
|
||||||
|
|
||||||
$component = new SessionProvider($this->getMock($this->getComponentClassString()), new PdoSessionHandler($pdo, $dbOptions), array('auto_start' => 1));
|
$pdoHandler = new PdoSessionHandler($pdo, $dbOptions);
|
||||||
|
$pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}');
|
||||||
|
|
||||||
|
$component = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1));
|
||||||
$connection = $this->getMock('Ratchet\\ConnectionInterface');
|
$connection = $this->getMock('Ratchet\\ConnectionInterface');
|
||||||
|
|
||||||
$headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array()));
|
$headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array()));
|
||||||
|
53
tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
Normal file
53
tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage;
|
||||||
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||||
|
|
||||||
|
class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
/**
|
||||||
|
* @var VirtualSessionStorage
|
||||||
|
*/
|
||||||
|
protected $_virtualSessionStorage;
|
||||||
|
|
||||||
|
protected $_pathToDB;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||||
|
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = <<<SQL
|
||||||
|
CREATE TABLE `sessions` (
|
||||||
|
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
|
||||||
|
`sess_data` BLOB NOT NULL,
|
||||||
|
`sess_time` INTEGER UNSIGNED NOT NULL,
|
||||||
|
`sess_lifetime` MEDIUMINT NOT NULL
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
$this->_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');;
|
||||||
|
$dsn = 'sqlite:' . $this->_pathToDB;
|
||||||
|
|
||||||
|
$pdo = new \PDO($dsn);
|
||||||
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->exec($schema);
|
||||||
|
$pdo = null;
|
||||||
|
|
||||||
|
$sessionHandler = new PdoSessionHandler($dsn);
|
||||||
|
$serializer = new PhpHandler();
|
||||||
|
$this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer);
|
||||||
|
$this->_virtualSessionStorage->registerBag(new FlashBag());
|
||||||
|
$this->_virtualSessionStorage->registerBag(new AttributeBag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
unlink($this->_pathToDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStartWithDSN() {
|
||||||
|
$this->_virtualSessionStorage->start();
|
||||||
|
|
||||||
|
$this->assertTrue($this->_virtualSessionStorage->isStarted());
|
||||||
|
}
|
||||||
|
}
|
@ -211,13 +211,14 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase {
|
|||||||
$conn = new WampConnection($this->newConn());
|
$conn = new WampConnection($this->newConn());
|
||||||
$this->_comp->onOpen($conn);
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
$shortIn = 'incoming';
|
$prefix = 'incoming';
|
||||||
$longIn = 'http://example.com/incoming/';
|
$fullURI = "http://example.com/$prefix";
|
||||||
|
$method = 'call';
|
||||||
|
|
||||||
$this->_comp->onMessage($conn, json_encode(array(1, $shortIn, $longIn)));
|
$this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI)));
|
||||||
|
|
||||||
$this->assertEquals($longIn, $conn->WAMP->prefixes[$shortIn]);
|
$this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]);
|
||||||
$this->assertEquals($longIn, $conn->getUri($shortIn));
|
$this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMessageMustBeJson() {
|
public function testMessageMustBeJson() {
|
||||||
|
@ -80,4 +80,24 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase {
|
|||||||
$mockApp->expects($this->once())->method('onOpen');
|
$mockApp->expects($this->once())->method('onOpen');
|
||||||
$server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf);
|
$server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTcpFragmentedBodyUpgrade() {
|
||||||
|
$headers = $this->headerProvider();
|
||||||
|
$body = base64_decode($this->_body);
|
||||||
|
$body1 = substr($body, 0, 4);
|
||||||
|
$body2 = substr($body, 4);
|
||||||
|
|
||||||
|
$mockConn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$mockApp = $this->getMock('\Ratchet\MessageComponentInterface');
|
||||||
|
|
||||||
|
$server = new HttpServer(new WsServer($mockApp));
|
||||||
|
$server->onOpen($mockConn);
|
||||||
|
$server->onMessage($mockConn, $headers);
|
||||||
|
|
||||||
|
$mockApp->expects($this->once())->method('onOpen');
|
||||||
|
|
||||||
|
$server->onMessage($mockConn, $body1);
|
||||||
|
$server->onMessage($mockConn, $body2);
|
||||||
|
$server->onMessage($mockConn, $this->_crlf . $this->_crlf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,4 +500,44 @@ class FrameTest extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
return $randomString;
|
return $randomString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There was a frame boundary issue when the first 3 bytes of a frame with a payload greater than
|
||||||
|
* 126 was added to the frame buffer and then Frame::getPayloadLength was called. It would cause the frame
|
||||||
|
* to set the payload length to 126 and then not recalculate it once the full length information was available.
|
||||||
|
*
|
||||||
|
* This is fixed by setting the defPayLen back to -1 before the underflow exception is thrown.
|
||||||
|
*
|
||||||
|
* @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength
|
||||||
|
* @covers Ratchet\WebSocket\Version\RFC6455\Frame::extractOverflow
|
||||||
|
*/
|
||||||
|
public function testFrameDeliveredOneByteAtATime() {
|
||||||
|
$startHeader = "\x01\x7e\x01\x00"; // header for a text frame of 256 - non-final
|
||||||
|
$framePayload = str_repeat("*", 256);
|
||||||
|
$rawOverflow = "xyz";
|
||||||
|
$rawFrame = $startHeader . $framePayload . $rawOverflow;
|
||||||
|
|
||||||
|
$frame = new Frame();
|
||||||
|
$payloadLen = 256;
|
||||||
|
|
||||||
|
for ($i = 0; $i < strlen($rawFrame); $i++) {
|
||||||
|
$frame->addBuffer($rawFrame[$i]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// payloadLen will
|
||||||
|
$payloadLen = $frame->getPayloadLength();
|
||||||
|
} catch (\UnderflowException $e) {
|
||||||
|
if ($i > 2) { // we should get an underflow on 0,1,2
|
||||||
|
$this->fail("Underflow exception when the frame length should be available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($payloadLen !== 256) {
|
||||||
|
$this->fail("Payload length of " . $payloadLen . " should have been 256.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the overflow is good
|
||||||
|
$this->assertEquals($rawOverflow, $frame->extractOverflow());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user