rfc6455/HandshakeNegotiator.php
Chris Boden 6cbf0eb186 [WebSocket] Refactoring
Updated deps; React Socket notify client of shutdown
Separated core interfaces into many
Removed initial version support out of handshake negotiator
Moved message parser responsibility to each version
Removed __toString method from MessageInterface as to not confuse message from payload
Support for RFC control frames
Support message concatenation
[BCB] (temporary) WsConnection hard coded to RFC version
Handshake checks for \r\n\r\n anywhere, not just at end of string
2012-06-09 19:38:44 -04:00

135 lines
4.3 KiB
PHP

<?php
namespace Ratchet\WebSocket;
use Ratchet\MessageInterface;
use Ratchet\ConnectionInterface;
use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory;
use Ratchet\WebSocket\Version\VersionInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
class HandshakeNegotiator implements MessageInterface {
const EOM = "\r\n\r\n";
/**
* The maximum number of bytes the request can be
* This is a security measure to prevent attacks
* @var int
*/
public $maxSize = 4096;
private $versionString = '';
protected $versions = array();
/**
* @param WsConnection
*/
public function onOpen(ConnectionInterface $conn) {
$conn->WebSocket->handshakeBuffer = '';
}
/**
* @param WsConnection
* @param string Data stream to buffer
* @return Guzzle\Http\Message\Response|null Response object if it's done parsing, null if there's more to be buffered
* @throws HttpException
*/
public function onMessage(ConnectionInterface $conn, $data) {
$conn->WebSocket->handshakeBuffer .= $data;
if (strlen($conn->WebSocket->handshakeBuffer) >= (int)$this->maxSize) {
return new Response(413, array('X-Powered-By' => \Ratchet\VERSION));
}
if ($this->isEom($conn->WebSocket->handshakeBuffer)) {
$conn->WebSocket->request = RequestFactory::getInstance()->fromMessage($conn->WebSocket->handshakeBuffer);
if (null === ($version = $this->getVersion($conn->WebSocket->request))) {
return new Response(400, array(
'Sec-WebSocket-Version' => $this->getSupportedVersionString()
, 'X-Powered-By' => \Ratchet\VERSION
));
}
// TODO: confirm message is buffered
// Hixie requires the body to complete the handshake (6 characters long) - is that 6 ASCII or UTF-8 characters?
// Update VersionInterface to check for this, ::canHandshake() maybe
// return if can't, continue buffering
$response = $version->handshake($conn->WebSocket->request);
$response->setHeader('X-Powered-By', \Ratchet\VERSION);
// This needs to be decoupled
$conn->WebSocket->version = $version;
unset($conn->WebSocket->handshakeBuffer);
return $response;
}
}
/**
* Determine if the message has been buffered as per the HTTP specification
* @param string
* @return boolean
*/
public function isEom($message) {
//return (static::EOM === substr($message, 0 - strlen(static::EOM)));
return (boolean)strpos($message, static::EOM);
}
/**
* Get the protocol negotiator for the request, if supported
* @param Guzzle\Http\Message\RequestInterface
* @return Ratchet\WebSocket\Version\VersionInterface
*/
public function getVersion(RequestInterface $request) {
foreach ($this->versions as $version) {
if ($version->isProtocol($request)) {
return $version;
}
}
}
/**
* Enable support for a specific version of the WebSocket protocol
* @param Ratchet\WebSocket\Vesion\VersionInterface
* @return HandshakeNegotiator
*/
public function enableVersion(VersionInterface $version) {
$this->versions[$version->getVersionNumber()] = $version;
if (empty($this->versionString)) {
$this->versionString = (string)$version->getVersionNumber();
} else {
$this->versionString .= ", {$version->getVersionNumber()}";
}
return $this;
}
/**
* Disable support for a specific WebSocket protocol version
* @param int The version ID to un-support
* @return HandshakeNegotiator
*/
public function disableVersion($versionId) {
unset($this->versions[$versionId]);
$this->versionString = '';
foreach ($this->versions as $id => $object) {
$this->versionString .= "{$id}, ";
}
$this->versionString = substr($this->versionString, 0, -2);
return $this;
}
/**
* Get a string of version numbers supported (comma delimited)
* @return string
*/
public function getSupportedVersionString() {
return $this->versionString;
}
}