From 036921d6c31231b95db813882b68bb96f18492c9 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 11 Mar 2016 17:14:02 +1300 Subject: [PATCH 1/4] added close opcode --- src/WebSocket.php | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/WebSocket.php b/src/WebSocket.php index 745df94..50a082f 100644 --- a/src/WebSocket.php +++ b/src/WebSocket.php @@ -55,9 +55,9 @@ public function __construct(DuplexStreamInterface $stream, Response $response, R } }); - $stream->on('close', function() { - $this->emit('close', [$this]); - }); + // $stream->on('close', function() { + // $this->emit('close', [$this]); + // }); $stream->on('error', function($error) { $this->emit('error', [$error, $this]); @@ -80,6 +80,8 @@ public function close($code = 1000) { $this->_stream->write($frame->getContents()); $this->_stream->end(); + + $this->emit('close', [$code, '', $this]); } private function handleData($data) { @@ -111,6 +113,16 @@ private function handleData($data) { case Frame::OP_CLOSE: $this->close($frame->getPayload()); + $opcode = unpack('n', $frame->getContents()); + $opcode = reset($opcode); + $reason = ''; + + if (strlen($frame->getContents()) > 2) { + $reason = substr($frame->getContents(), 2, strlen($frame->getContents())); + } + + $this->emit('close', [$opcode, $reason, $this]); + return; case Frame::OP_PING: $this->send(new Frame($frame->getPayload(), true, Frame::OP_PONG)); @@ -120,6 +132,17 @@ private function handleData($data) { break; default: $this->close($frame->getPayload()); + + $opcode = unpack('n', $frame->getContents()); + $opcode = reset($opcode); + $reason = ''; + + if (strlen($frame->getContents()) > 2) { + $reason = substr($frame->getContents(), 2, strlen($frame->getContents())); + } + + $this->emit('close', [$opcode, $reason, $this]); + return; } } From d7522c988cd17008f1efe5fe8e4b6a1872ba866c Mon Sep 17 00:00:00 2001 From: David Date: Mon, 14 Mar 2016 21:59:32 +1300 Subject: [PATCH 2/4] re-implemented close code and reason --- src/WebSocket.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/WebSocket.php b/src/WebSocket.php index fd9b70b..57dec0a 100644 --- a/src/WebSocket.php +++ b/src/WebSocket.php @@ -56,12 +56,32 @@ function(MessageInterface $msg) { function(FrameInterface $frame) use (&$streamer) { switch ($frame->getOpcode()) { case Frame::OP_CLOSE: + $reason = ''; + $code = unpack('n', $frame->getContents()); + $code = reset($code); + + if (($frameLen = strlen($frame->getContents())) > 2) { // has reason + $reason = substr($frame->getContents(), 2, $frameLen); + } + + $this->emit('close', [$code, $reason, $this]); + return $this->_stream->end($streamer->newFrame($frame->getPayload(), true, Frame::OP_CLOSE)->maskPayload()->getContents()); case Frame::OP_PING: return $this->send($streamer->newFrame($frame->getPayload(), true, Frame::OP_PONG)); case Frame::OP_PONG: return $this->emit('pong', [$frame, $this]); default: + $reason = ''; + $code = unpack('n', $frame->getContents()); + $code = reset($code); + + if (($frameLen = strlen($frame->getContents())) > 2) { // has reason + $reason = substr($frame->getContents(), 2, $frameLen); + } + + $this->emit('close', [$code, $reason, $this]); + return $this->_stream->end($streamer->newFrame(Frame::CLOSE_PROTOCOL, true, Frame::OP_CLOSE)->maskPayload()->getContents()); } }, From 01c87e0da06379bc703988e8607e5c5faedba948 Mon Sep 17 00:00:00 2001 From: David Cole Date: Tue, 15 Mar 2016 21:20:37 +1300 Subject: [PATCH 3/4] Fixed opcode and reason --- src/WebSocket.php | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/WebSocket.php b/src/WebSocket.php index 57dec0a..e20ebf1 100644 --- a/src/WebSocket.php +++ b/src/WebSocket.php @@ -56,12 +56,14 @@ function(MessageInterface $msg) { function(FrameInterface $frame) use (&$streamer) { switch ($frame->getOpcode()) { case Frame::OP_CLOSE: + $frameContents = substr($frame->getContents(), 2, strlen($frame->getContents())); // for some reason, the contents have 2 extra bytes at the front + $reason = ''; - $code = unpack('n', $frame->getContents()); + $code = unpack('n', substr($frameContents, 0, 2)); $code = reset($code); - if (($frameLen = strlen($frame->getContents())) > 2) { // has reason - $reason = substr($frame->getContents(), 2, $frameLen); + if (($frameLen = strlen($frameContents)) > 2) { // has reason + $reason = substr($frameContents, 2, $frameLen); } $this->emit('close', [$code, $reason, $this]); @@ -72,16 +74,6 @@ function(FrameInterface $frame) use (&$streamer) { case Frame::OP_PONG: return $this->emit('pong', [$frame, $this]); default: - $reason = ''; - $code = unpack('n', $frame->getContents()); - $code = reset($code); - - if (($frameLen = strlen($frame->getContents())) > 2) { // has reason - $reason = substr($frame->getContents(), 2, $frameLen); - } - - $this->emit('close', [$code, $reason, $this]); - return $this->_stream->end($streamer->newFrame(Frame::CLOSE_PROTOCOL, true, Frame::OP_CLOSE)->maskPayload()->getContents()); } }, @@ -100,10 +92,6 @@ function() use ($reusableUAException) { } }); - $stream->on('close', function() { - $this->emit('close', [$this]); - }); - $stream->on('error', function($error) { $this->emit('error', [$error, $this]); }); From 5d012a9ad6275c0c51898dca46f72104f02fe5af Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 15 Mar 2016 11:32:23 -0400 Subject: [PATCH 4/4] Emit close code once --- src/WebSocket.php | 36 ++++++++++++++++++++++++++++++------ tests/autobahn/runner.php | 2 +- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/WebSocket.php b/src/WebSocket.php index e20ebf1..5631944 100644 --- a/src/WebSocket.php +++ b/src/WebSocket.php @@ -31,6 +31,11 @@ class WebSocket implements EventEmitterInterface { */ protected $_stream; + /** + * @var \Closure + */ + protected $_close; + /** * WebSocket constructor. * @param \React\Stream\DuplexStreamInterface $stream @@ -46,6 +51,18 @@ public function __construct(DuplexStreamInterface $stream, ResponseInterface $re $this->response = $response; $this->request = $request; + $self = $this; + $this->_close = function($code = null, $reason = null) use ($self) { + static $sent = false; + + if ($sent) { + return; + } + $sent = true; + + $self->emit('close', [$code, $reason, $self]); + }; + $reusableUAException = new \UnderflowException; $streamer = new MessageBuffer( @@ -56,17 +73,18 @@ function(MessageInterface $msg) { function(FrameInterface $frame) use (&$streamer) { switch ($frame->getOpcode()) { case Frame::OP_CLOSE: - $frameContents = substr($frame->getContents(), 2, strlen($frame->getContents())); // for some reason, the contents have 2 extra bytes at the front + $frameContents = $frame->getPayload(); $reason = ''; $code = unpack('n', substr($frameContents, 0, 2)); $code = reset($code); - if (($frameLen = strlen($frameContents)) > 2) { // has reason + if (($frameLen = strlen($frameContents)) > 2) { $reason = substr($frameContents, 2, $frameLen); } - $this->emit('close', [$code, $reason, $this]); + $closeFn = $this->_close; + $closeFn($code, $reason); return $this->_stream->end($streamer->newFrame($frame->getPayload(), true, Frame::OP_CLOSE)->maskPayload()->getContents()); case Frame::OP_PING: @@ -74,7 +92,7 @@ function(FrameInterface $frame) use (&$streamer) { case Frame::OP_PONG: return $this->emit('pong', [$frame, $this]); default: - return $this->_stream->end($streamer->newFrame(Frame::CLOSE_PROTOCOL, true, Frame::OP_CLOSE)->maskPayload()->getContents()); + return $this->close(Frame::CLOSE_PROTOCOL); } }, false, @@ -92,6 +110,8 @@ function() use ($reusableUAException) { } }); + $stream->on('close', $this->_close); + $stream->on('error', function($error) { $this->emit('error', [$error, $this]); }); @@ -112,9 +132,13 @@ public function send($msg) { $this->_stream->write($msg->getContents()); } - public function close($code = 1000) { - $frame = new Frame(pack('n', $code), true, Frame::OP_CLOSE); + public function close($code = 1000, $reason = '') { + $frame = new Frame(pack('n', $code) . $reason, true, Frame::OP_CLOSE); $this->_stream->write($frame->getContents()); + + $closeFn = $this->_close; + $closeFn($code, $reason); + $this->_stream->end(); } } \ No newline at end of file diff --git a/tests/autobahn/runner.php b/tests/autobahn/runner.php index 57b43dd..5676bef 100644 --- a/tests/autobahn/runner.php +++ b/tests/autobahn/runner.php @@ -4,7 +4,7 @@ require __DIR__ . '/../../vendor/autoload.php'; - define('AGENT', 'Pawl/0.0.1'); + define('AGENT', 'Pawl/0.2.1'); $loop = React\EventLoop\Factory::create();