Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tests 4.1.3, 4.1.4, 4.2.1, 4.2.2, 4.2.3, 4.2.4 and way to process data in woketo #28

Merged
merged 2 commits into from
Nov 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ branches:

install:
- composer install --prefer-source

script:
- phpunit
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
},
"require": {
"react/event-loop": "~0.4.0",
"react/socket": "~0.4.0"
"react/socket": "~0.4.0",
"nekland/tools": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.3"
Expand Down
24 changes: 23 additions & 1 deletion src/Exception/Frame/TooBigFrameException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,39 @@
/**
* This file is a part of Woketo package.
*
* (c) Ci-tron <dev@ci-tron.org>
* (c) Nekland <dev@nekland.fr>
*
* For the full license, take a look to the LICENSE file
* on the root directory of this project
*/

namespace Nekland\Woketo\Exception\Frame;

use Exception;
use Nekland\Woketo\Exception\LimitationException;

class TooBigFrameException extends LimitationException
{
/**
* @var int
*/
private $maxLength;

/**
* @param int $maxLength
* @param string $message
*/
public function __construct(int $maxLength, string $message = 'The frame is too big to be processed.')
{
parent::__construct($message, null, null);
$this->maxLength = $maxLength;
}

/**
* @return int
*/
public function getMaxLength()
{
return $this->maxLength;
}
}
123 changes: 74 additions & 49 deletions src/Rfc6455/Frame.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* on the root directory of this project
*/

declare(strict_types=1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why ? no good for performance ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because sometimes it's really important. (and it is for some parts of woketo)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand but it is in a central class, so it should be interesting a load tests for the impact. But ok

namespace Nekland\Woketo\Rfc6455;

use Nekland\Woketo\Exception\Frame\ControlFrameException;
Expand Down Expand Up @@ -89,7 +90,7 @@ class Frame
* @var int
*/
private $secondByte;

/**
* @var bool
*/
Expand Down Expand Up @@ -133,25 +134,35 @@ public function __construct($data=null)
{
if (null !== $data) {
$this->setRawData($data);
$this->checkFrameSize();
}
}

/**
* It also run checks on data.
*
* @param string|int $rawData Probably more likely a string than an int, but well... why not.
* @return self
* @throws InvalidFrameException
*/
public function setRawData($rawData)
{
$this->rawData = $rawData;
$this->frameSize = strlen($rawData);
$this->frameSize = BitManipulation::frameSize($rawData);

if ($this->frameSize < 2) {
throw new InvalidFrameException('Not enough data to be a frame.');
}
$this->getInformationFromRawData();

try {
$this->checkFrameSize();
} catch (TooBigFrameException $e) {
$this->frameSize = $e->getMaxLength();
$this->rawData = BitManipulation::bytesFromToString($this->rawData, 0, $this->frameSize, BitManipulation::MODE_PHP);
}

Frame::checkFrame($this);

return $this;
}

Expand Down Expand Up @@ -188,12 +199,12 @@ public function getRawData() : string
}
if ($this->isMasked()) {
$data .= $this->getMaskingKey();
$data .= $this->applyMask($this->getPayload());
return $data;
$data .= $this->applyMask();

return $this->rawData = $data;
}
return $data . $this->getPayload();

return $this->rawData = $data . $this->getPayload();
}

/**
Expand Down Expand Up @@ -221,23 +232,23 @@ public function setFinal(bool $final) : Frame
*/
public function getRsv1() : bool
{
return BitManipulation::nthBit($this->firstByte, 2);
return (bool) BitManipulation::nthBit($this->firstByte, 2);
}

/**
* @return boolean
*/
public function getRsv2() : bool
{
return BitManipulation::nthBit($this->firstByte, 3);
return (bool) BitManipulation::nthBit($this->firstByte, 3);
}

/**
* @return boolean
*/
public function getRsv3() : bool
{
return BitManipulation::nthBit($this->firstByte, 4);
return (bool) BitManipulation::nthBit($this->firstByte, 4);
}

/**
Expand All @@ -247,15 +258,15 @@ public function getOpcode() : int
{
return BitManipulation::partOfByte($this->firstByte, 2);
}

public function setOpcode(int $opcode) : Frame
{
if (!in_array($opcode, [Frame::OP_TEXT, Frame::OP_BINARY, Frame::OP_CLOSE, Frame::OP_CONTINUE, Frame::OP_PING, Frame::OP_PONG])) {
throw new \InvalidArgumentException('Wrong opcode !');
}

$this->opcode = $opcode;

return $this;
}

Expand Down Expand Up @@ -287,7 +298,7 @@ public function getMaskingKey() : string

$value = BitManipulation::bytesFromTo($this->rawData, $start, $start + 3);

return $this->mask = BitManipulation::intToString($value);
return $this->mask = BitManipulation::intToString($value, 4);
}

public function getPayload()
Expand All @@ -296,13 +307,13 @@ public function getPayload()
return $this->payload;
}

$this->checkFrameSize();

$infoBytesLen = $this->getInfoBytesLen();
$payload = (string) substr($this->rawData, $infoBytesLen, $this->payloadLen);
$payload = (string) BitManipulation::bytesFromToString($this->rawData, $infoBytesLen, $this->payloadLen, BitManipulation::MODE_PHP);

if ($this->isMasked()) {
return $this->payload = $this->applyMask($payload);
$this->payload = $payload;

return $this->payload = $this->applyMask();
}

return $this->payload = $payload;
Expand All @@ -319,37 +330,18 @@ public function getInfoBytesLen()
return $this->infoBytesLen = (9 + $this->payloadLenSize) / 8 + ($this->isMasked() ? 4 : 0);
}

public function checkFrameSize()
{
$infoBytesLen = $this->getInfoBytesLen();
$realDataLength = BitManipulation::frameSize($this->rawData);
$theoricDataLength = $infoBytesLen + $this->payloadLen;

if ($realDataLength < $theoricDataLength) {
throw new IncompleteFrameException(
sprintf('Impossible to retrieve %s bytes of payload when the full frame is %s bytes long.', $theoricDataLength, $realDataLength)
);
}

if ($realDataLength > $theoricDataLength) {
throw new TooBigFrameException();
}
}



public function setPayload(string $payload) : Frame
{
$this->payload = $payload;
$this->payloadLen = BitManipulation::frameSize($this->payload);
$this->payloadLenSize = 7;

if ($this->payloadLen > 126 && $this->payloadLen < 65536) {
$this->payloadLenSize += 16;
} else if ($this->payloadLen > 126) {
$this->payloadLenSize += 64;
}

return $this;
}

Expand Down Expand Up @@ -382,7 +374,7 @@ public function getPayloadLength() : int

// Check < 0 because 64th bit is the negative one in PHP.
if ($payloadLen < 0 || $payloadLen > Frame::$maxPayloadSize) {
throw new TooBigFrameException;
throw new TooBigFrameException(Frame::$maxPayloadSize);
}

return $this->payloadLen = $payloadLen;
Expand All @@ -393,22 +385,26 @@ public function isMasked() : bool
if ($this->mask !== null) {
return true;
}

if ($this->rawData !== null) {
return (bool) BitManipulation::nthBit($this->secondByte, 1);
}

return false;
}

public function applyMask(string $payload) : string
/**
* This method works for mask and unmask (it's the same operation)
*
* @return string
*/
public function applyMask() : string
{
$res = '';
$mask = $this->getMaskingKey();


for ($i = 0; $i < $this->payloadLen; $i++) {
$payloadByte = $payload[$i];
$payloadByte = $this->payload[$i];
$res .= $payloadByte ^ $mask[$i % 4];
}

Expand All @@ -422,10 +418,39 @@ private function getInformationFromRawData()

$this->final = (bool) BitManipulation::nthBit($this->firstByte, 1);
$this->payloadLen = $this->getPayloadLength();
}

Frame::checkFrame($this);
/**
* Check if the frame have the good size based on payload size.
*
* @throws IncompleteFrameException
* @throws TooBigFrameException
*/
public function checkFrameSize()
{
$infoBytesLen = $this->getInfoBytesLen();
$this->frameSize = BitManipulation::frameSize($this->rawData);
$theoricDataLength = $infoBytesLen + $this->payloadLen;

if ($this->frameSize < $theoricDataLength) {
throw new IncompleteFrameException(
sprintf('Impossible to retrieve %s bytes of payload when the full frame is %s bytes long.', $theoricDataLength, $this->frameSize)
);
}

if ($this->frameSize > $theoricDataLength) {
throw new TooBigFrameException($theoricDataLength);
}
}

/**
* Validate a frame with RFC criteria
*
* @param Frame $frame
* @throws ControlFrameException
* @throws InvalidFrameException
* @throws TooBigControlFrameException
*/
public static function checkFrame(Frame $frame)
{
if ($frame->getOpcode() === Frame::OP_TEXT && !mb_check_encoding($frame->getPayload())) {
Expand All @@ -438,14 +463,14 @@ public static function checkFrame(Frame $frame)
}

if ($frame->getPayloadLength() > Frame::MAX_CONTROL_FRAME_SIZE) {
throw new TooBigControlFrameException('A control frame cannot be larger than 125 bytes.');
throw new TooBigControlFrameException(Frame::MAX_CONTROL_FRAME_SIZE, 'A control frame cannot be larger than 125 bytes.');
}
}
}

/**
* You can call this method to be sure your frame is valid before trying to get the raw data.
*
*
* @return bool
*/
public function isValid() : bool
Expand Down
22 changes: 14 additions & 8 deletions src/Rfc6455/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

namespace Nekland\Woketo\Rfc6455;

use Nekland\Tools\StringTools;
use Nekland\Woketo\Exception\Frame\IncompleteFrameException;
use Nekland\Woketo\Exception\LimitationException;
use Nekland\Woketo\Exception\MissingDataException;
use Nekland\Woketo\Utils\BitManipulation;

class Message
{
Expand Down Expand Up @@ -40,17 +42,21 @@ public function __construct()

public function addData($data)
{
try {
if ('' === $this->buffer) {
$this->addFrame(new Frame($data));
} else {
$this->addFrame(new Frame($this->buffer . $data));
$this->buffer .= $data;
do {
try {
$this->addFrame($frame = new Frame($this->buffer));
$this->buffer = StringTools::removeStart($this->buffer, $frame->getRawData(), '8bit');
} catch (IncompleteFrameException $e) {
return ''; // There is no more frame we can generate, the data is saved as buffer.
}
$this->buffer = '';
} while(!$this->isComplete() && !empty($this->buffer));

} catch (IncompleteFrameException $e) {
$this->buffer .= $data;
if ($this->isComplete()) {
return $this->buffer;
}

return '';
}

/**
Expand Down
Loading