Create MessageStreamer, move some things

This commit is contained in:
matt 2015-03-15 23:15:41 -04:00
parent d0202c8c71
commit 8653b92115
4 changed files with 178 additions and 173 deletions

View File

@ -1,15 +1,9 @@
<?php
namespace Ratchet\RFC6455\Handshake;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
// TODO remove all these
use Ratchet\ConnectionInterface;
use Ratchet\MessageInterface;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ServerRequestInterface;
use Ratchet\RFC6455\Encoding\ValidatorInterface;
use Ratchet\RFC6455\Message\Message;
use Ratchet\RFC6455\Message\Frame;
use Ratchet\RFC6455\Message\Connection;
/**
* The latest version of the WebSocket protocol
@ -31,7 +25,7 @@ class Negotiator implements NegotiatorInterface {
* @var array
* @deprecated
*/
private $closeCodes = array();
private $closeCodes = [];
public function __construct(ValidatorInterface $validator) {
$this->verifier = new RequestVerifier;
@ -44,7 +38,7 @@ class Negotiator implements NegotiatorInterface {
/**
* {@inheritdoc}
*/
public function isProtocol(RequestInterface $request) {
public function isProtocol(ServerRequestInterface $request) {
$version = (int)(string)$request->getHeader('Sec-WebSocket-Version');
return ($this->getVersionNumber() === $version);
@ -60,34 +54,16 @@ class Negotiator implements NegotiatorInterface {
/**
* {@inheritdoc}
*/
public function handshake(RequestInterface $request) {
public function handshake(ServerRequestInterface $request) {
if (true !== $this->verifier->verifyAll($request)) {
return new Response(400);
}
return new Response(101, array(
return new Response(101, [
'Upgrade' => 'websocket'
, 'Connection' => 'Upgrade'
, 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key'))
));
}
/**
* @deprecated
* @param \Ratchet\ConnectionInterface $conn
* @param \Ratchet\MessageInterface $coalescedCallback
* @return \Ratchet\WebSocket\Version\RFC6455\Connection
*/
public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) {
$upgraded = new Connection($conn);
if (!isset($upgraded->WebSocket)) {
$upgraded->WebSocket = new \StdClass;
}
$upgraded->WebSocket->coalescedCallback = $coalescedCallback;
return $upgraded;
]);
}
/**
@ -96,102 +72,7 @@ class Negotiator implements NegotiatorInterface {
* @param string $data
*/
public function onMessage(ConnectionInterface $from, $data) {
$overflow = '';
if (!isset($from->WebSocket->message)) {
$from->WebSocket->message = $this->newMessage();
}
// There is a frame fragment attached to the connection, add to it
if (!isset($from->WebSocket->frame)) {
$from->WebSocket->frame = $this->newFrame();
}
$from->WebSocket->frame->addBuffer($data);
if ($from->WebSocket->frame->isCoalesced()) {
$frame = $from->WebSocket->frame;
if ($opcode > 2) {
switch ($opcode) {
case $frame::OP_CLOSE:
$closeCode = 0;
$bin = $frame->getPayload();
if (empty($bin)) {
return $from->close();
}
if (strlen($bin) >= 2) {
list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2)));
}
if (!$this->isValidCloseCode($closeCode)) {
return $from->close($frame::CLOSE_PROTOCOL);
}
if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) {
return $from->close($frame::CLOSE_BAD_PAYLOAD);
}
return $from->close($frame);
break;
case $frame::OP_PING:
$from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG));
break;
case $frame::OP_PONG:
break;
default:
return $from->close($frame::CLOSE_PROTOCOL);
break;
}
$overflow = $from->WebSocket->frame->extractOverflow();
unset($from->WebSocket->frame, $frame, $opcode);
if (strlen($overflow) > 0) {
$this->onMessage($from, $overflow);
}
return;
}
$overflow = $from->WebSocket->frame->extractOverflow();
$from->WebSocket->message->addFrame($from->WebSocket->frame);
unset($from->WebSocket->frame);
}
if ($from->WebSocket->message->isCoalesced()) {
$parsed = $from->WebSocket->message->getPayload();
unset($from->WebSocket->message);
$from->WebSocket->coalescedCallback->onMessage($from, $parsed);
}
if (strlen($overflow) > 0) {
$this->onMessage($from, $overflow);
}
}
/**
* @deprecated
* @return RFC6455\Message
*/
public function newMessage() {
return new Message;
}
/**
* @deprecated
* @param string|null $payload
* @param bool|null $final
* @param int|null $opcode
* @return RFC6455\Frame
*/
public function newFrame($payload = null, $final = null, $opcode = null) {
return new Frame($payload, $final, $opcode);
}
/**
@ -203,41 +84,4 @@ class Negotiator implements NegotiatorInterface {
public function sign($key) {
return base64_encode(sha1($key . static::GUID, true));
}
/**
* @deprecated
* Determine if a close code is valid
* @param int|string
* @return bool
*/
public function isValidCloseCode($val) {
if (array_key_exists($val, $this->closeCodes)) {
return true;
}
if ($val >= 3000 && $val <= 4999) {
return true;
}
return false;
}
/**
* @deprecated
* Creates a private lookup of valid, private close codes
*/
protected function setCloseCodes() {
$this->closeCodes[Frame::CLOSE_NORMAL] = true;
$this->closeCodes[Frame::CLOSE_GOING_AWAY] = true;
$this->closeCodes[Frame::CLOSE_PROTOCOL] = true;
$this->closeCodes[Frame::CLOSE_BAD_DATA] = true;
//$this->closeCodes[Frame::CLOSE_NO_STATUS] = true;
//$this->closeCodes[Frame::CLOSE_ABNORMAL] = true;
$this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true;
$this->closeCodes[Frame::CLOSE_POLICY] = true;
$this->closeCodes[Frame::CLOSE_TOO_BIG] = true;
$this->closeCodes[Frame::CLOSE_MAND_EXT] = true;
$this->closeCodes[Frame::CLOSE_SRV_ERR] = true;
//$this->closeCodes[Frame::CLOSE_TLS] = true;
}
}

View File

@ -1,8 +1,8 @@
<?php
namespace Ratchet\RFC6455\Handshake;
use Ratchet\MessageInterface;
use Ratchet\ConnectionInterface;
use Guzzle\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* A standard interface for interacting with the various version of the WebSocket protocol
@ -13,10 +13,10 @@ interface NegotiatorInterface {
/**
* Given an HTTP header, determine if this version should handle the protocol
* @param \Guzzle\Http\Message\RequestInterface $request
* @param ServerRequestInterface $request
* @return bool
*/
function isProtocol(RequestInterface $request);
function isProtocol(ServerRequestInterface $request);
/**
* Although the version has a name associated with it the integer returned is the proper identification
@ -26,10 +26,10 @@ interface NegotiatorInterface {
/**
* Perform the handshake and return the response headers
* @param \Guzzle\Http\Message\RequestInterface $request
* @return \Guzzle\Http\Message\Response
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
function handshake(RequestInterface $request);
function handshake(ServerRequestInterface $request);
/**
* Add supported protocols. If the request has any matching the response will include one

View File

@ -1,6 +1,5 @@
<?php
namespace Ratchet\RFC6455\Handshake;
use Guzzle\Http\Message\RequestInterface;
/**
* These are checks to ensure the client requested handshake are valid
@ -12,7 +11,7 @@ class RequestVerifier {
/**
* Given an array of the headers this method will run through all verification methods
* @param \Guzzle\Http\Message\RequestInterface $request
* @param RequestInterface $request
* @return bool TRUE if all headers are valid, FALSE if 1 or more were invalid
*/
public function verifyAll(RequestInterface $request) {

View File

@ -0,0 +1,162 @@
<?php
namespace Ratchet\RFC6455\Messaging\Streaming;
use Evenement\EventEmitterInterface;
use Evenement\EventEmitterTrait;
use Ratchet\RFC6455\Messaging\Protocol\Frame;
use Ratchet\RFC6455\Messaging\Protocol\Message;
class MessageStreamer implements EventEmitterInterface {
use EventEmitterTrait;
/** @var Frame */
private $currentFrame;
/** @var Message */
private $currentMessage;
/** @var array */
private $closeCodes = [];
public function onData($data) {
$overflow = '';
if (!isset($this->currentMessage)) {
$this->currentMessage = $this->newMessage();
}
// There is a frame fragment attached to the connection, add to it
if (!isset($this->currentFrame)) {
$this->currentFrame = $this->newFrame();
}
$frame = $this->currentFrame;
$frame->addBuffer($data);
if ($frame->isCoalesced()) {
$opcode = $frame->getOpcode();
if ($opcode > 2) {
switch ($opcode) {
case $frame::OP_CLOSE:
$closeCode = 0;
$bin = $frame->getPayload();
if (empty($bin)) {
$this->emit('close', [null]);
return;
}
if (strlen($bin) >= 2) {
list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2)));
}
if (!$this->isValidCloseCode($closeCode)) {
$this->emit('close', [$frame::CLOSE_PROTOCOL]);
return;
}
// todo:
//if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) {
// $this->emit('close', [$frame::CLOSE_BAD_PAYLOAD]);
// return;
//}
$this->emit('close', [$closeCode]);
return;
break;
case $frame::OP_PING:
// this should probably be automatic
//$from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG));
$this->emit('ping', [$frame]);
break;
case $frame::OP_PONG:
$this->emit('pong', [$frame]);
break;
default:
$this->emit('close', [$frame::CLOSE_PROTOCOL]);
return;
break;
}
$overflow = $frame->extractOverflow();
unset($this->currentFrame, $frame, $opcode);
if (strlen($overflow) > 0) {
$this->onData($overflow);
}
return;
}
$overflow = $frame->extractOverflow();
$this->currentMessage->addFrame($this->currentFrame);
unset($this->currentFrame);
}
if ($this->currentMessage->isCoalesced()) {
$this->emit('message', [$this->currentMessage]);
//$parsed = $from->WebSocket->message->getPayload();
unset($this->currentMessage);
}
if (strlen($overflow) > 0) {
$this->onData($overflow);
}
}
/**
* @return Message
*/
public function newMessage() {
return new Message;
}
/**
* @param string|null $payload
* @param bool|null $final
* @param int|null $opcode
* @return Frame
*/
public function newFrame($payload = null, $final = null, $opcode = null) {
return new Frame($payload, $final, $opcode);
}
/**
* Determine if a close code is valid
* @param int|string
* @return bool
*/
public function isValidCloseCode($val) {
if (array_key_exists($val, $this->closeCodes)) {
return true;
}
if ($val >= 3000 && $val <= 4999) {
return true;
}
return false;
}
/**
* Creates a private lookup of valid, private close codes
*/
protected function setCloseCodes() {
$this->closeCodes[Frame::CLOSE_NORMAL] = true;
$this->closeCodes[Frame::CLOSE_GOING_AWAY] = true;
$this->closeCodes[Frame::CLOSE_PROTOCOL] = true;
$this->closeCodes[Frame::CLOSE_BAD_DATA] = true;
//$this->closeCodes[Frame::CLOSE_NO_STATUS] = true;
//$this->closeCodes[Frame::CLOSE_ABNORMAL] = true;
$this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true;
$this->closeCodes[Frame::CLOSE_POLICY] = true;
$this->closeCodes[Frame::CLOSE_TOO_BIG] = true;
$this->closeCodes[Frame::CLOSE_MAND_EXT] = true;
$this->closeCodes[Frame::CLOSE_SRV_ERR] = true;
//$this->closeCodes[Frame::CLOSE_TLS] = true;
}
}