From 54479da9d58b06ab5c3f7dfdc821dc506c6042d1 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 2 Jun 2012 22:08:27 -0400 Subject: [PATCH] [WebSocket] Messaging Fluent interface on MessageInterface::addFrame RFC6455 Message unit tests RFC handling TCP concatenation (refs #31) --- src/Ratchet/WebSocket/MessageParser.php | 6 +- .../WebSocket/Version/Hixie76/Message.php | 2 + .../WebSocket/Version/MessageInterface.php | 1 + .../WebSocket/Version/RFC6455/Frame.php | 5 -- .../WebSocket/Version/RFC6455/Message.php | 5 +- .../WebSocket/Version/RFC6455/FrameTest.php | 11 ++- .../WebSocket/Version/RFC6455/MessageTest.php | 70 +++++++++++++++++++ 7 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php diff --git a/src/Ratchet/WebSocket/MessageParser.php b/src/Ratchet/WebSocket/MessageParser.php index 017c4f5..b28592d 100644 --- a/src/Ratchet/WebSocket/MessageParser.php +++ b/src/Ratchet/WebSocket/MessageParser.php @@ -21,13 +21,17 @@ class MessageParser { return; } + // Check frame // If is control frame, do your thing // Else, add to message // Control frames (ping, pong, close) can be sent in between a fragmented message + $nextFrame = $from->WebSocket->version->newFrame(); + $nextFrame->addBuffer($from->WebSocket->frame->extractOverflow()); + $from->WebSocket->message->addFrame($from->WebSocket->frame); - unset($from->WebSocket->frame); + $from->WebSocket->frame = $nextFrame; } if ($from->WebSocket->message->isCoalesced()) { diff --git a/src/Ratchet/WebSocket/Version/Hixie76/Message.php b/src/Ratchet/WebSocket/Version/Hixie76/Message.php index f783e8b..818d95d 100644 --- a/src/Ratchet/WebSocket/Version/Hixie76/Message.php +++ b/src/Ratchet/WebSocket/Version/Hixie76/Message.php @@ -36,6 +36,8 @@ class Message implements MessageInterface { } $this->_frame = $fragment; + + return $this; } /** diff --git a/src/Ratchet/WebSocket/Version/MessageInterface.php b/src/Ratchet/WebSocket/Version/MessageInterface.php index 90d0179..4c21114 100644 --- a/src/Ratchet/WebSocket/Version/MessageInterface.php +++ b/src/Ratchet/WebSocket/Version/MessageInterface.php @@ -17,6 +17,7 @@ interface MessageInterface { /** * @param FragmentInterface + * @return MessageInterface */ function addFrame(FrameInterface $fragment); diff --git a/src/Ratchet/WebSocket/Version/RFC6455/Frame.php b/src/Ratchet/WebSocket/Version/RFC6455/Frame.php index af8e961..ec73393 100644 --- a/src/Ratchet/WebSocket/Version/RFC6455/Frame.php +++ b/src/Ratchet/WebSocket/Version/RFC6455/Frame.php @@ -35,7 +35,6 @@ class Frame implements FrameInterface { * @param bool Mask the payload * @return Frame * @throws InvalidArgumentException If the payload is not a valid UTF-8 string - * @throws BadMethodCallException If there is a problem with miss-matching parameters * @throws LengthException If the payload is too big */ public static function create($payload, $final = true, $opcode = 1, $mask = false) { @@ -45,10 +44,6 @@ class Frame implements FrameInterface { throw new \InvalidArgumentException("Payload is not a valid UTF-8 string"); } - if (false === (boolean)$final && $opcode !== static::OP_CONTINUE) { - throw new \BadMethodCallException("opcode MUST be 'continue' if the frame is not final"); - } - $raw = (int)(boolean)$final . sprintf('%07b', (int)$opcode); $plLen = strlen($payload); diff --git a/src/Ratchet/WebSocket/Version/RFC6455/Message.php b/src/Ratchet/WebSocket/Version/RFC6455/Message.php index 1071115..53bb8dd 100644 --- a/src/Ratchet/WebSocket/Version/RFC6455/Message.php +++ b/src/Ratchet/WebSocket/Version/RFC6455/Message.php @@ -39,6 +39,8 @@ class Message implements MessageInterface { */ public function addFrame(FrameInterface $fragment) { $this->_frames->push($fragment); + + return $this; } /** @@ -62,6 +64,7 @@ class Message implements MessageInterface { try { $len += $frame->getPayloadLength(); } catch (\UnderflowException $e) { + // Not an error, want the current amount buffered } } @@ -73,7 +76,7 @@ class Message implements MessageInterface { */ public function getPayload() { if (!$this->isCoalesced()) { - throw new \UnderflowMessage('Message has not been put back together yet'); + throw new \UnderflowException('Message has not been put back together yet'); } $buffer = ''; diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php index b011671..028c151 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php +++ b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php @@ -249,9 +249,8 @@ class FrameTest extends \PHPUnit_Framework_TestCase { $this->assertEquals($msg, $this->_frame->getPayload()); } - public function testCreate() { + public function testLongCreate() { $len = 65525; - $len = 65575; $pl = $this->generateRandomString($len); $frame = Frame::create($pl, true, Frame::OP_PING); @@ -263,6 +262,14 @@ class FrameTest extends \PHPUnit_Framework_TestCase { $this->assertEquals($pl, $frame->getPayload()); } + public function testReallyLongCreate() { + $len = 65575; + + $frame = Frame::create($this->generateRandomString($len)); + + $this->assertEquals($len, $frame->getPayloadLength()); + } + public function testExtractOverflow() { $string1 = $this->generateRandomString(); $frame1 = Frame::create($string1); diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php new file mode 100644 index 0000000..5a5d773 --- /dev/null +++ b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php @@ -0,0 +1,70 @@ +message = new Message; + } + + public function testNoFrames() { + $this->assertFalse($this->message->isCoalesced()); + } + + public function testNoFramesOpCode() { + $this->setExpectedException('UnderflowException'); + $this->message->getOpCode(); + } + + public function testFragmentationPayload() { + $a = 'Hello '; + $b = 'World!'; + + $f1 = Frame::create($a, false); + $f2 = Frame::create($b, true, Frame::OP_CONTINUE); + + $this->message->addFrame($f1)->addFrame($f2); + + $this->assertEquals(strlen($a . $b), $this->message->getPayloadLength()); + $this->assertEquals($a . $b, $this->message->getPayload()); + } + + public function testUnbufferedFragment() { + $this->message->addFrame(Frame::create('The quick brow', false)); + + $this->setExpectedException('UnderflowException'); + $this->message->getPayload(); + } + + public function testGetOpCode() { + $this->message + ->addFrame(Frame::create('The quick brow', false, Frame::OP_TEXT)) + ->addFrame(Frame::create('n fox jumps ov', false, Frame::OP_CONTINUE)) + ->addFrame(Frame::create('er the lazy dog', true, Frame::OP_CONTINUE)) + ; + + $this->assertEquals(Frame::OP_TEXT, $this->message->getOpCode()); + } + + public function testGetUnBufferedPayloadLength() { + $this->message + ->addFrame(Frame::create('The quick brow', false, Frame::OP_TEXT)) + ->addFrame(Frame::create('n fox jumps ov', false, Frame::OP_CONTINUE)) + ; + + $this->assertEquals(28, $this->message->getPayloadLength()); + } + + public function testToString() { + $msg = 'Who likes short shorts?'; + $this->message->addFrame(Frame::create($msg)); + + $this->assertEquals($msg, (string)$this->message); + } +} \ No newline at end of file