diff --git a/.travis.yml b/.travis.yml index 8f23f58..8dce43a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: php sudo: false +dist: precise php: - "7.0" - "5.6" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a977e9..34ee4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,4 +64,9 @@ Changelog stomp-php 3.0.5 ----- - - do not error when received header value is null (https://github.com/stomp-php/stomp-php/issues/30) \ No newline at end of file + - do not error when received header value is null (https://github.com/stomp-php/stomp-php/issues/30) + +3.0.6 +----- +- do not throw read exception if the next byte after a complete read is a zero byte. (https://github.com/stomp-php/stomp-php/issues/39) +- allow to reset the parser internal state and buffer. (https://github.com/stomp-php/stomp-php/issues/40) \ No newline at end of file diff --git a/src/Stomp/Connection.php b/src/Stomp/Connection.php index 87f4985..4d1f893 100644 --- a/src/Stomp/Connection.php +++ b/src/Stomp/Connection.php @@ -320,7 +320,7 @@ public function readFrame() do { $read = @stream_get_line($this->connection, 8192, Parser::FRAME_END); - if ($read === false || $read === '') { + if ($read === false) { throw new ConnectionException('Was not possible to read data from stream.', $this->activeHost); } @@ -409,4 +409,14 @@ private function connectionHasDataToRead($timeoutSec, $timeoutMicros) return !empty($read); } + + /** + * Returns the used parser. + * + * @return Parser + */ + public function getParser() + { + return $this->parser; + } } diff --git a/src/Stomp/Parser.php b/src/Stomp/Parser.php index e30fdd6..9020907 100644 --- a/src/Stomp/Parser.php +++ b/src/Stomp/Parser.php @@ -43,6 +43,13 @@ class Parser */ private $buffer = ''; + /** + * The frame header / content delimiter. + * + * @var string + */ + private $delimiter = null; + /** * Add data to parse. * @@ -104,7 +111,7 @@ public function parse() */ private function parseToFrame($source) { - list ($header, $body) = explode("\n\n", ltrim($source), 2); + list ($header, $body) = explode($this->delimiter, ltrim($source), 2); $header = explode("\n", $header); $headers = array(); $command = null; @@ -137,9 +144,12 @@ private function getFrameEnd($offset) $headerEnd = false; $activeMarker = false; foreach ($endMarkers as $marker) { - $activeMarker = $marker; - if (($headerEnd = strpos($this->buffer, $activeMarker, $offset)) !== false) { - break; + if (($markerPosition = strpos($this->buffer, $marker, $offset)) !== false) { + if ($headerEnd !== false && $headerEnd < $markerPosition) { + break; + } + $activeMarker = $marker; + $headerEnd = $markerPosition; } } @@ -148,6 +158,9 @@ private function getFrameEnd($offset) return false; } + // keep marker + $this->delimiter = $activeMarker; + $headers = substr($this->buffer, $offset, $headerEnd); // See if there is a content-length header @@ -164,4 +177,18 @@ private function getFrameEnd($offset) // No content-length, search for first 0-byte return strpos($this->buffer, self::FRAME_END, $offset); } + + /** + * Resets the buffer and all not delivered frames. + * Returns the not yet parsed buffer, which will be flushed. + * + * @return string + */ + public function flushBuffer() + { + $currentBuffer = $this->buffer; + $this->buffer = ''; + $this->frames = array(); + return $currentBuffer; + } } diff --git a/tests/Unit/Stomp/ParserTest.php b/tests/Unit/Stomp/ParserTest.php index 2abf9ca..51711c0 100644 --- a/tests/Unit/Stomp/ParserTest.php +++ b/tests/Unit/Stomp/ParserTest.php @@ -80,4 +80,51 @@ public function testParserOnFrameWithIncorrectHeaderValue() $this->assertEquals('var', $result->body); $this->assertEquals(true, $result->headers['header1 value1']); } + + public function testFlushResetsBufferAndReturnsCurrentlyNotParsedBuffer() + { + $msg = "CMD\nheader1:value1\n\n\nvar\x00"; + + $parser = new Parser(); + $parser->addData($msg); + $this->assertTrue($parser->parse()); + $this->assertTrue($parser->hasBufferedFrames()); + $parser->addData($msg); + $this->assertEquals($msg, $parser->flushBuffer()); + $this->assertFalse($parser->parse()); + $this->assertFalse($parser->hasBufferedFrames()); + + $parser->addData($msg); + $this->assertTrue($parser->parse()); + $result = $parser->getFrame(); + $this->assertInstanceOf('\Stomp\Frame', $result); + } + + /** + * @see https://github.com/stomp-php/stomp-php/issues/93 + */ + public function testParserWhenHeaderStopSequenceWithCarriageReturnIsPartOfBody() + { + $parser = new Parser(); + $body = "{ \n\"value1\" : \"hello world\"\n,\n\n \"value2\" : \"2002-02-01\"\r\n\r\n}"; + $header = "MESSAGE\n\n"; + $parser->addData($header . $body . "\x00"); + $this->assertTrue($parser->parse()); + $message = $parser->getFrame(); + $this->assertEquals($body, $message->body); + } + + /** + * @see https://github.com/stomp-php/stomp-php/issues/93 + */ + public function testParserWhenHeaderStopSequenceWithoutCarriageReturnIsPartOfBody() + { + $parser = new Parser(); + $body = "{ \n\"value1\" : \"hello world\"\n,\n\n \"value2\" : \"2002-02-01\"\n\n}"; + $header = "MESSAGE\r\n\r\n"; + $parser->addData($header . $body . "\x00"); + $this->assertTrue($parser->parse()); + $message = $parser->getFrame(); + $this->assertEquals($body, $message->body); + } }