diff --git a/.travis.yml b/.travis.yml index f51c586..09125d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,9 @@ php: - 5.4 - 5.5 - 5.6 - - 7 + - 7.0 - 7.1 - - hhvm - - nightly + - 7.2 before_install: - export PATH=$HOME/.local/bin:$PATH diff --git a/README.md b/README.md index 7c09148..0f17c14 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ This library a protocol handler for the RFC6455 specification. It contains components for both server and client side handshake and messaging protocol negotation. -Aspects that are left open to interpertation in the specification are also left open in this library. -It is up to the implementation to determine how those interpertations are to be dealt with. +Aspects that are left open to interpretation in the specification are also left open in this library. +It is up to the implementation to determine how those interpretations are to be dealt with. This library is independent, framework agnostic, and does not deal with any I/O. HTTP upgrade negotiation integration points are handled with PSR-7 interfaces. diff --git a/src/Handshake/ServerNegotiator.php b/src/Handshake/ServerNegotiator.php index 51752e3..3059f65 100644 --- a/src/Handshake/ServerNegotiator.php +++ b/src/Handshake/ServerNegotiator.php @@ -77,7 +77,7 @@ class ServerNegotiator implements NegotiatorInterface { 'Sec-WebSocket-Version' => $this->getVersionNumber() ]; if (count($this->_supportedSubProtocols) > 0) { - $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', $this->_supportedSubProtocols); + $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', array_keys($this->_supportedSubProtocols)); } if (true !== $this->verifier->verifyUpgradeRequest($request->getHeader('Upgrade'))) { return new Response(426, $upgradeSuggestion, null, '1.1', 'Upgrade header MUST be provided'); diff --git a/src/Messaging/MessageBuffer.php b/src/Messaging/MessageBuffer.php index 6ca82db..f5bf340 100644 --- a/src/Messaging/MessageBuffer.php +++ b/src/Messaging/MessageBuffer.php @@ -142,6 +142,10 @@ class MessageBuffer { if ($this->messageBuffer->isCoalesced()) { $msgCheck = $this->checkMessage($this->messageBuffer); + + $msgBuffer = $this->messageBuffer; + $this->messageBuffer = null; + if (true !== $msgCheck) { $onControl($this->newCloseFrame($msgCheck, 'Ratchet detected an invalid UTF-8 payload'), $this); } else { diff --git a/tests/unit/Handshake/ServerNegotiatorTest.php b/tests/unit/Handshake/ServerNegotiatorTest.php index 9c9aa8d..6fa8e64 100644 --- a/tests/unit/Handshake/ServerNegotiatorTest.php +++ b/tests/unit/Handshake/ServerNegotiatorTest.php @@ -19,7 +19,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -46,7 +48,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -71,7 +75,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -96,7 +102,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -128,7 +136,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -160,7 +170,9 @@ Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch, br -Accept-Language: en-US,en;q=0.8'; +Accept-Language: en-US,en;q=0.8 + +'; $request = \GuzzleHttp\Psr7\parse_request($requestText); @@ -172,4 +184,34 @@ Accept-Language: en-US,en;q=0.8'; $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); $this->assertFalse($response->hasHeader('Sec-WebSocket-Protocol')); } -} \ No newline at end of file + + public function testSuggestsAppropriateSubprotocol() + { + $negotiator = new ServerNegotiator(new RequestVerifier()); + $negotiator->setStrictSubProtocolCheck(true); + $negotiator->setSupportedSubProtocols(['someproto']); + + $requestText = 'GET / HTTP/1.1 +Host: localhost:8080 +Connection: Upgrade +Upgrade: websocket +Sec-WebSocket-Version: 13 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 +Accept-Encoding: gzip, deflate, br +Accept-Language: en-US,en;q=0.9 +Sec-WebSocket-Key: HGt8eQax7nAOlXUw0/asPQ== +Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits + +'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(426, $response->getStatusCode()); + $this->assertEquals('Upgrade', $response->getHeaderLine('Connection')); + $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); + $this->assertEquals('someproto', $response->getHeaderLine('Sec-WebSocket-Protocol')); + } +} diff --git a/tests/unit/Messaging/MessageBufferTest.php b/tests/unit/Messaging/MessageBufferTest.php index c33ff0c..567afa2 100644 --- a/tests/unit/Messaging/MessageBufferTest.php +++ b/tests/unit/Messaging/MessageBufferTest.php @@ -6,6 +6,7 @@ use Ratchet\RFC6455\Messaging\CloseFrameChecker; use Ratchet\RFC6455\Messaging\Frame; use Ratchet\RFC6455\Messaging\Message; use Ratchet\RFC6455\Messaging\MessageBuffer; +use React\EventLoop\Factory; class MessageBufferTest extends \PHPUnit_Framework_TestCase { @@ -36,4 +37,36 @@ class MessageBufferTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1000, $messageCount); } + + public function testProcessingMessagesAsynchronouslyWhileBlockingInMessageHandler() { + $loop = Factory::create(); + + $frameA = new Frame('a', true, Frame::OP_TEXT); + $frameB = new Frame('b', true, Frame::OP_TEXT); + + $bReceived = false; + + $messageBuffer = new MessageBuffer( + new CloseFrameChecker(), + function (Message $message) use (&$messageCount, &$bReceived, $loop) { + $payload = $message->getPayload(); + $bReceived = $payload === 'b'; + + if (!$bReceived) { + $loop->run(); + } + }, + null, + false + ); + + $loop->addPeriodicTimer(0.1, function () use ($messageBuffer, $frameB, $loop) { + $loop->stop(); + $messageBuffer->onData($frameB->getContents()); + }); + + $messageBuffer->onData($frameA->getContents()); + + $this->assertTrue($bReceived); + } } \ No newline at end of file