[WebSockets] Handshake encoding + case insensitivity
Updated RFC6455 handshaker to check values case insensitively Made sure RFC6455 handshaker matches encoding properly Added mbstring as a requirement for Ratchet Refs #28, #30
This commit is contained in:
parent
27716fef78
commit
d075b99c26
@ -8,13 +8,14 @@ Build up your application through simple interfaces and re-use your application
|
|||||||
##WebSocket Compliance
|
##WebSocket Compliance
|
||||||
|
|
||||||
* 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 - 12, Safari 5, iOS 4.2, iOS 5
|
* Tested on Chrome 13 - 19, Firefox 6 - 12, Safari 5.0.1+, iOS 4.2, iOS 5
|
||||||
|
|
||||||
##Requirements
|
##Requirements
|
||||||
|
|
||||||
Shell access is required and a dedicated machine with root access is recommended.
|
Shell access is required and a dedicated machine with root access is recommended.
|
||||||
To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access.
|
To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access.
|
||||||
Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine (for now).
|
Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine (for now).
|
||||||
|
PHP 5.3.2 (or higher) is required with mbstring enabled (*--enable-mbstring* flag during compile time). PHP5.4 is recommended for its performance improvements.
|
||||||
|
|
||||||
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 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.
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
, "require": {
|
, "require": {
|
||||||
"php": ">=5.3.2"
|
"php": ">=5.3.2"
|
||||||
|
, "ext-mbstring": "*"
|
||||||
, "guzzle/guzzle": "2.5.*"
|
, "guzzle/guzzle": "2.5.*"
|
||||||
, "symfony/http-foundation": "2.1.*"
|
, "symfony/http-foundation": "2.1.*"
|
||||||
, "react/socket": "dev-master"
|
, "react/socket": "dev-master"
|
||||||
|
16
composer.lock
generated
16
composer.lock
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"hash": "cbea4e3e4d74a22ba34d4edf2ce44df3",
|
"hash": "253370657f067dacf104d5fae531f20a",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"package": "evenement/evenement",
|
"package": "evenement/evenement",
|
||||||
@ -32,13 +32,6 @@
|
|||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source-reference": "eb82542e8ec9506096caf7c528564c740a214f56"
|
"source-reference": "eb82542e8ec9506096caf7c528564c740a214f56"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"package": "symfony/event-dispatcher",
|
|
||||||
"version": "dev-master",
|
|
||||||
"source-reference": "0b58a4019befc0bd038bc0ec0165101d5dd31754",
|
|
||||||
"alias-pretty-version": "2.1.x-dev",
|
|
||||||
"alias-version": "2.1.9999999.9999999-dev"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"package": "symfony/http-foundation",
|
"package": "symfony/http-foundation",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
@ -50,13 +43,6 @@
|
|||||||
"package": "symfony/http-foundation",
|
"package": "symfony/http-foundation",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source-reference": "3d9f4ce435f6322b9720c209ad610202526373c0"
|
"source-reference": "3d9f4ce435f6322b9720c209ad610202526373c0"
|
||||||
},
|
|
||||||
{
|
|
||||||
"package": "symfony/http-foundation",
|
|
||||||
"version": "dev-master",
|
|
||||||
"source-reference": "cf8e8324c68ce584525502702866485f17f1c8a5",
|
|
||||||
"alias-pretty-version": "2.1.x-dev",
|
|
||||||
"alias-version": "2.1.9999999.9999999-dev"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": null,
|
"packages-dev": null,
|
||||||
|
@ -2,12 +2,6 @@
|
|||||||
namespace Ratchet\WebSocket\Version;
|
namespace Ratchet\WebSocket\Version;
|
||||||
|
|
||||||
interface FrameInterface {
|
interface FrameInterface {
|
||||||
/**
|
|
||||||
* Dunno if I'll use this
|
|
||||||
* Thinking could be used if a control frame?
|
|
||||||
*/
|
|
||||||
// function __invoke();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -32,10 +32,9 @@ class HandshakeVerifier {
|
|||||||
* Test the HTTP method. MUST be "GET"
|
* Test the HTTP method. MUST be "GET"
|
||||||
* @param string
|
* @param string
|
||||||
* @return bool
|
* @return bool
|
||||||
* @todo Look into STD if "get" is valid (am I supposed to do case conversion?)
|
|
||||||
*/
|
*/
|
||||||
public function verifyMethod($val) {
|
public function verifyMethod($val) {
|
||||||
return ('GET' === $val);
|
return ('get' === mb_strtolower($val, 'ASCII'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +49,6 @@ class HandshakeVerifier {
|
|||||||
/**
|
/**
|
||||||
* @param string
|
* @param string
|
||||||
* @return bool
|
* @return bool
|
||||||
* @todo Verify the logic here is correct
|
|
||||||
*/
|
*/
|
||||||
public function verifyRequestURI($val) {
|
public function verifyRequestURI($val) {
|
||||||
if ($val[0] != '/') {
|
if ($val[0] != '/') {
|
||||||
@ -80,7 +78,7 @@ class HandshakeVerifier {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function verifyUpgradeRequest($val) {
|
public function verifyUpgradeRequest($val) {
|
||||||
return ('websocket' === $val);
|
return ('websocket' === mb_strtolower($val, 'ASCII'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,12 +87,16 @@ class HandshakeVerifier {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function verifyConnection($val) {
|
public function verifyConnection($val) {
|
||||||
if ('Upgrade' === $val) {
|
$val = mb_strtolower($val, 'ASCII');
|
||||||
|
|
||||||
|
if ('upgrade' === $val) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo change this to mb_eregi_replace
|
||||||
$vals = explode(',', str_replace(', ', ',', $val));
|
$vals = explode(',', str_replace(', ', ',', $val));
|
||||||
return (false !== array_search('Upgrade', $vals));
|
|
||||||
|
return (false !== array_search('upgrade', $vals));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,8 @@ class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
|||||||
public static function methodProvider() {
|
public static function methodProvider() {
|
||||||
return array(
|
return array(
|
||||||
array(true, 'GET')
|
array(true, 'GET')
|
||||||
, array(false, 'get') // I'm not sure if this is valid or not, need to check standard
|
, array(true, 'get')
|
||||||
|
, array(true, 'Get')
|
||||||
, array(false, 'POST')
|
, array(false, 'POST')
|
||||||
, array(false, 'DELETE')
|
, array(false, 'DELETE')
|
||||||
, array(false, 'PUT')
|
, array(false, 'PUT')
|
||||||
@ -64,6 +65,7 @@ class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
|||||||
, array(false, '/chat#bad')
|
, array(false, '/chat#bad')
|
||||||
, array(false, 'nope')
|
, array(false, 'nope')
|
||||||
, array(false, '/ ಠ_ಠ ')
|
, array(false, '/ ಠ_ಠ ')
|
||||||
|
, array(false, '/✖')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +93,8 @@ class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
|||||||
public static function upgradeProvider() {
|
public static function upgradeProvider() {
|
||||||
return array(
|
return array(
|
||||||
array(true, 'websocket')
|
array(true, 'websocket')
|
||||||
, array(false, 'Websocket')
|
, array(true, 'Websocket')
|
||||||
|
, array(true, 'webSocket')
|
||||||
, array(false, null)
|
, array(false, null)
|
||||||
, array(false, '')
|
, array(false, '')
|
||||||
);
|
);
|
||||||
@ -107,7 +110,7 @@ class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
|||||||
public static function connectionProvider() {
|
public static function connectionProvider() {
|
||||||
return array(
|
return array(
|
||||||
array(true, 'Upgrade')
|
array(true, 'Upgrade')
|
||||||
, array(false, 'upgrade')
|
, array(true, 'upgrade')
|
||||||
, array(true, 'keep-alive, Upgrade')
|
, array(true, 'keep-alive, Upgrade')
|
||||||
, array(true, 'Upgrade, keep-alive')
|
, array(true, 'Upgrade, keep-alive')
|
||||||
, array(true, 'keep-alive, Upgrade, something')
|
, array(true, 'keep-alive, Upgrade, something')
|
||||||
@ -133,6 +136,8 @@ class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase {
|
|||||||
, array(false, 'Hello World')
|
, array(false, 'Hello World')
|
||||||
, array(false, '1234567890123456')
|
, array(false, '1234567890123456')
|
||||||
, array(false, '123456789012345678901234')
|
, array(false, '123456789012345678901234')
|
||||||
|
, array(true, base64_encode('UTF8allthngs+✓'))
|
||||||
|
, array(true, 'dGhlIHNhbXBsZSBub25jZQ==')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,33 +3,26 @@ namespace Ratchet\Tests\WebSocket\Version;
|
|||||||
use Ratchet\WebSocket\Version\RFC6455;
|
use Ratchet\WebSocket\Version\RFC6455;
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Frame;
|
use Ratchet\WebSocket\Version\RFC6455\Frame;
|
||||||
use Guzzle\Http\Message\RequestFactory;
|
use Guzzle\Http\Message\RequestFactory;
|
||||||
|
use Guzzle\Http\Message\EntityEnclosingRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\WebSocket\Version\RFC6455
|
* @covers Ratchet\WebSocket\Version\RFC6455
|
||||||
*/
|
*/
|
||||||
class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_version;
|
protected $version;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_version = new RFC6455();
|
$this->version = new RFC6455;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this useful?
|
* @dataProvider handshakeProvider
|
||||||
*/
|
|
||||||
public function testClassImplementsVersionInterface() {
|
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface');
|
|
||||||
$this->assertThat($this->_version, $constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider HandshakeProvider
|
|
||||||
*/
|
*/
|
||||||
public function testKeySigningForHandshake($key, $accept) {
|
public function testKeySigningForHandshake($key, $accept) {
|
||||||
$this->assertEquals($accept, $this->_version->sign($key));
|
$this->assertEquals($accept, $this->version->sign($key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function HandshakeProvider() {
|
public static function handshakeProvider() {
|
||||||
return array(
|
return array(
|
||||||
array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=')
|
array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=')
|
||||||
, array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=')
|
, array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=')
|
||||||
@ -57,7 +50,7 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
public function testUnframeMatchesPreFraming() {
|
public function testUnframeMatchesPreFraming() {
|
||||||
$string = 'Hello World!';
|
$string = 'Hello World!';
|
||||||
$framed = $this->_version->frame($string);
|
$framed = $this->version->frame($string);
|
||||||
|
|
||||||
$frame = new Frame;
|
$frame = new Frame;
|
||||||
$frame->addBuffer($framed);
|
$frame->addBuffer($framed);
|
||||||
@ -77,6 +70,26 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
, 'Sec-WebSocket-Version' => 13
|
, 'Sec-WebSocket-Version' => 13
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public function caseVariantProvider() {
|
||||||
|
return array(
|
||||||
|
array('Sec-Websocket-Version')
|
||||||
|
, array('sec-websocket-version')
|
||||||
|
, array('SEC-WEBSOCKET-VERSION')
|
||||||
|
, array('sEC-wEBsOCKET-vERSION')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider caseVariantProvider
|
||||||
|
*/
|
||||||
|
public function testIsProtocolWithCaseInsensitivity($headerName) {
|
||||||
|
$header = static::$good_header;
|
||||||
|
unset($header['Sec-WebSocket-Version']);
|
||||||
|
$header[$headerName] = 13;
|
||||||
|
|
||||||
|
$this->assertTrue($this->version->isProtocol(new EntityEnclosingRequest('get', '/', $header)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper function to try and quickly put together a valid WebSocket HTTP handshake
|
* A helper function to try and quickly put together a valid WebSocket HTTP handshake
|
||||||
* but optionally replace a piece to an invalid value for failure testing
|
* but optionally replace a piece to an invalid value for failure testing
|
||||||
@ -119,18 +132,18 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
|||||||
$request = RequestFactory::getInstance()->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));
|
||||||
} else {
|
} else {
|
||||||
$this->setExpectedException('InvalidArgumentException');
|
$this->setExpectedException('InvalidArgumentException');
|
||||||
$this->_version->handshake($request);
|
$this->version->handshake($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNewMessage() {
|
public function testNewMessage() {
|
||||||
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->_version->newMessage());
|
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->version->newMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNewFrame() {
|
public function testNewFrame() {
|
||||||
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->_version->newFrame());
|
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->version->newFrame());
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user