Separate negotiation and validation
This commit is contained in:
parent
d1376d824a
commit
5e79598448
@ -54,7 +54,7 @@ class Negotiator implements NegotiatorInterface {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getVersionNumber() {
|
public function getVersionNumber() {
|
||||||
return 13;
|
return RequestVerifier::VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,25 +111,7 @@ class Negotiator implements NegotiatorInterface {
|
|||||||
if ($from->WebSocket->frame->isCoalesced()) {
|
if ($from->WebSocket->frame->isCoalesced()) {
|
||||||
$frame = $from->WebSocket->frame;
|
$frame = $from->WebSocket->frame;
|
||||||
|
|
||||||
if (false !== $frame->getRsv1() ||
|
|
||||||
false !== $frame->getRsv2() ||
|
|
||||||
false !== $frame->getRsv3()
|
|
||||||
) {
|
|
||||||
return $from->close($frame::CLOSE_PROTOCOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is server-side specific logic
|
|
||||||
if (!$frame->isMasked()) {
|
|
||||||
return $from->close($frame::CLOSE_PROTOCOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
$opcode = $frame->getOpcode();
|
|
||||||
|
|
||||||
if ($opcode > 2) {
|
if ($opcode > 2) {
|
||||||
if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) {
|
|
||||||
return $from->close($frame::CLOSE_PROTOCOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($opcode) {
|
switch ($opcode) {
|
||||||
case $frame::OP_CLOSE:
|
case $frame::OP_CLOSE:
|
||||||
$closeCode = 0;
|
$closeCode = 0;
|
||||||
@ -177,14 +159,6 @@ class Negotiator implements NegotiatorInterface {
|
|||||||
|
|
||||||
$overflow = $from->WebSocket->frame->extractOverflow();
|
$overflow = $from->WebSocket->frame->extractOverflow();
|
||||||
|
|
||||||
if ($frame::OP_CONTINUE == $frame->getOpcode() && 0 == count($from->WebSocket->message)) {
|
|
||||||
return $from->close($frame::CLOSE_PROTOCOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($from->WebSocket->message) > 0 && $frame::OP_CONTINUE != $frame->getOpcode()) {
|
|
||||||
return $from->close($frame::CLOSE_PROTOCOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
$from->WebSocket->message->addFrame($from->WebSocket->frame);
|
$from->WebSocket->message->addFrame($from->WebSocket->frame);
|
||||||
unset($from->WebSocket->frame);
|
unset($from->WebSocket->frame);
|
||||||
}
|
}
|
||||||
@ -193,10 +167,6 @@ class Negotiator implements NegotiatorInterface {
|
|||||||
$parsed = $from->WebSocket->message->getPayload();
|
$parsed = $from->WebSocket->message->getPayload();
|
||||||
unset($from->WebSocket->message);
|
unset($from->WebSocket->message);
|
||||||
|
|
||||||
if (!$this->validator->checkEncoding($parsed, 'UTF-8')) {
|
|
||||||
return $from->close(Frame::CLOSE_BAD_PAYLOAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
$from->WebSocket->coalescedCallback->onMessage($from, $parsed);
|
$from->WebSocket->coalescedCallback->onMessage($from, $parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ interface NegotiatorInterface {
|
|||||||
* Given an HTTP header, determine if this version should handle the protocol
|
* Given an HTTP header, determine if this version should handle the protocol
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request
|
* @param \Guzzle\Http\Message\RequestInterface $request
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws \UnderflowException If the protocol thinks the headers are still fragmented
|
|
||||||
*/
|
*/
|
||||||
function isProtocol(RequestInterface $request);
|
function isProtocol(RequestInterface $request);
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ interface NegotiatorInterface {
|
|||||||
* Perform the handshake and return the response headers
|
* Perform the handshake and return the response headers
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request
|
* @param \Guzzle\Http\Message\RequestInterface $request
|
||||||
* @return \Guzzle\Http\Message\Response
|
* @return \Guzzle\Http\Message\Response
|
||||||
* @throws \UnderflowException If the message hasn't finished buffering (not yet implemented, theoretically will only happen with Hixie version)
|
|
||||||
*/
|
*/
|
||||||
function handshake(RequestInterface $request);
|
function handshake(RequestInterface $request);
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ use Guzzle\Http\Message\RequestInterface;
|
|||||||
* @todo Currently just returning invalid - should consider returning appropriate HTTP status code error #s
|
* @todo Currently just returning invalid - should consider returning appropriate HTTP status code error #s
|
||||||
*/
|
*/
|
||||||
class RequestVerifier {
|
class RequestVerifier {
|
||||||
|
const VERSION = 13;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an array of the headers this method will run through all verification methods
|
* Given an array of the headers this method will run through all verification methods
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request
|
* @param \Guzzle\Http\Message\RequestInterface $request
|
||||||
@ -23,9 +25,9 @@ class RequestVerifier {
|
|||||||
$passes += (int)$this->verifyUpgradeRequest((string)$request->getHeader('Upgrade'));
|
$passes += (int)$this->verifyUpgradeRequest((string)$request->getHeader('Upgrade'));
|
||||||
$passes += (int)$this->verifyConnection((string)$request->getHeader('Connection'));
|
$passes += (int)$this->verifyConnection((string)$request->getHeader('Connection'));
|
||||||
$passes += (int)$this->verifyKey((string)$request->getHeader('Sec-WebSocket-Key'));
|
$passes += (int)$this->verifyKey((string)$request->getHeader('Sec-WebSocket-Key'));
|
||||||
//$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality
|
$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']);
|
||||||
|
|
||||||
return (7 === $passes);
|
return (8 === $passes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,10 +119,9 @@ class RequestVerifier {
|
|||||||
* Verify the version passed matches this RFC
|
* Verify the version passed matches this RFC
|
||||||
* @param string|int MUST equal 13|"13"
|
* @param string|int MUST equal 13|"13"
|
||||||
* @return bool
|
* @return bool
|
||||||
* @todo Ran in to a problem here...I'm having HyBi use the RFC files, this breaks it! oops
|
|
||||||
*/
|
*/
|
||||||
public function verifyVersion($val) {
|
public function verifyVersion($val) {
|
||||||
return (13 === (int)$val);
|
return (static::VERSION === (int)$val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
105
src/Messaging/Validation/MessageValidator.php
Normal file
105
src/Messaging/Validation/MessageValidator.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\RFC6455\Messaging\Validation;
|
||||||
|
use Ratchet\RFC6455\Encoding\ValidatorInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\Protocol\MessageInterface;
|
||||||
|
|
||||||
|
class MessageValidator {
|
||||||
|
public $checkForMask = true;
|
||||||
|
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
public function __construct(ValidatorInterface $validator) {
|
||||||
|
$this->validator = $validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a message is valid
|
||||||
|
* @param \Ratchet\RFC6455\Messaging\Protocol\MessageInterface
|
||||||
|
* @return bool|int true if valid - false if incomplete - int of recomended close code
|
||||||
|
*/
|
||||||
|
public function checkMessage(MessageInterface $message) {
|
||||||
|
// Need a progressive and complete check...this is only satisfying complete
|
||||||
|
if (!$message->isCoalesced()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$frame = $message[0];
|
||||||
|
|
||||||
|
$frameCheck = $this->checkFrame($frame);
|
||||||
|
if (true !== $frameCheck) {
|
||||||
|
return $frameCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This seems incorrect - how could a frame exist with message count being 0?
|
||||||
|
if ($frame::OP_CONTINUE === $frame->getOpcode() && 0 === count($message)) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($message) > 0 && $frame::OP_CONTINUE !== $frame->getOpcode()) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsed = $message->getPayload();
|
||||||
|
if (!$this->validator->checkEncoding($parsed, 'UTF-8')) {
|
||||||
|
return $frame::CLOSE_BAD_PAYLOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateFrame(FrameInterface $frame) {
|
||||||
|
if (false !== $frame->getRsv1() ||
|
||||||
|
false !== $frame->getRsv2() ||
|
||||||
|
false !== $frame->getRsv3()
|
||||||
|
) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be checking all frames
|
||||||
|
if ($this->checkForMask && !$frame->isMasked()) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opcode = $frame->getOpcode();
|
||||||
|
|
||||||
|
if ($opcode > 2) {
|
||||||
|
if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($opcode) {
|
||||||
|
case $frame::OP_CLOSE:
|
||||||
|
$closeCode = 0;
|
||||||
|
|
||||||
|
$bin = $frame->getPayload();
|
||||||
|
|
||||||
|
if (empty($bin)) {
|
||||||
|
return $frame::CLOSE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($bin) >= 2) {
|
||||||
|
list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->isValidCloseCode($closeCode)) {
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) {
|
||||||
|
return $frame::CLOSE_BAD_PAYLOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $frame::CLOSE_NORMAL;
|
||||||
|
break;
|
||||||
|
case $frame::OP_PING:
|
||||||
|
case $frame::OP_PONG:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return $frame::CLOSE_PROTOCOL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user