Skip to content

Commit

Permalink
Merge pull request #28 from Nekland/fix/wstest-4.1.3
Browse files Browse the repository at this point in the history
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
  • Loading branch information
folliked authored Nov 7, 2016
2 parents 0a3dd90 + 8dc874d commit e22a05d
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 118 deletions.
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);
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

0 comments on commit e22a05d

Please sign in to comment.