Code cleanup, return value fix, comments
This commit is contained in:
parent
d603870d7f
commit
899dd3b7e4
@ -221,19 +221,33 @@ class WebSocketConnection implements WebSocketInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @see writeBinary() to write binary frames as opposed to text frames.
|
||||||
*/
|
*/
|
||||||
public function write($data)
|
public function write($data)
|
||||||
{
|
{
|
||||||
return $this->send(self::OP_FRAME_TEXT, $data);
|
return $this->send(self::OP_FRAME_TEXT, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write binary frames.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public function writeBinary($data)
|
public function writeBinary($data)
|
||||||
{
|
{
|
||||||
return $this->send(self::OP_FRAME_BINARY, $data);
|
return $this->send(self::OP_FRAME_BINARY, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Encode and send a frame.
|
||||||
*
|
*
|
||||||
|
* @param int $opcode
|
||||||
|
* @param string $data
|
||||||
|
* @param bool $final
|
||||||
|
* @param bool $masked
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function send(int $opcode, string $data, bool $final = true, bool $masked = false)
|
public function send(int $opcode, string $data, bool $final = true, bool $masked = false)
|
||||||
{
|
{
|
||||||
@ -245,9 +259,7 @@ class WebSocketConnection implements WebSocketInterface
|
|||||||
'masked' => $masked
|
'masked' => $masked
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->outStream->write($frame);
|
return $this->outStream->write($frame);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,7 +273,10 @@ class WebSocketConnection implements WebSocketInterface
|
|||||||
$this->emit('close', []);
|
$this->emit('close', []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function closeWithReason(string $reason, int $code=1000)
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function closeWithReason(string $reason, int $code=1000): void
|
||||||
{
|
{
|
||||||
$payload = chr(($code >> 8) & 0xFF) . chr($code & 0xFF) . $reason;
|
$payload = chr(($code >> 8) & 0xFF) . chr($code & 0xFF) . $reason;
|
||||||
$this->send(self::OP_CLOSE, $payload);
|
$this->send(self::OP_CLOSE, $payload);
|
||||||
|
@ -16,15 +16,46 @@ interface WebSocketInterface extends ConnectionInterface
|
|||||||
const EVENT_GROUP_JOIN = 'join';
|
const EVENT_GROUP_JOIN = 'join';
|
||||||
const EVENT_GROUP_LEAVE = 'leave';
|
const EVENT_GROUP_LEAVE = 'leave';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection with a reason and code.
|
||||||
|
*
|
||||||
|
* @param string $reason
|
||||||
|
* @param int $code
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function closeWithReason(string $reason, int $code=1000): void;
|
||||||
|
|
||||||
public function setGroup(?string $name): void;
|
/**
|
||||||
|
* Get the initial HTTP request sent to the server.
|
||||||
public function getGroupName(): ?string;
|
*
|
||||||
|
* @return ServerRequestInterface
|
||||||
public function getGroup(): ?ConnectionGroup;
|
*/
|
||||||
|
|
||||||
public function closeWithReason(string $reason, int $code=1000);
|
|
||||||
|
|
||||||
public function getServerRequest(): ServerRequestInterface;
|
public function getServerRequest(): ServerRequestInterface;
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Assign this connection to a connection group. If the connection is already
|
||||||
|
* part of a group, it will leave the current group before joining the new
|
||||||
|
* group.
|
||||||
|
*
|
||||||
|
* @param null|string $name The group name to join
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setGroup(?string $name): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current connection group.
|
||||||
|
*
|
||||||
|
* @see getGroupName() if you want the name of the group.
|
||||||
|
*
|
||||||
|
* @return null|ConnectionGroup
|
||||||
|
*/
|
||||||
|
public function getGroup(): ?ConnectionGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the current connection group
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function getGroupName(): ?string;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -18,6 +18,8 @@ class WebSocketMiddleware implements EventEmitterInterface
|
|||||||
|
|
||||||
const MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
const MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||||
|
|
||||||
|
const VERSION = 13;
|
||||||
|
|
||||||
use EventEmitterTrait;
|
use EventEmitterTrait;
|
||||||
|
|
||||||
private GroupManager $groupManager;
|
private GroupManager $groupManager;
|
||||||
@ -32,7 +34,7 @@ class WebSocketMiddleware implements EventEmitterInterface
|
|||||||
|
|
||||||
public function addRoute(string $path, callable $handler, array $allowedOrigins=[]): void
|
public function addRoute(string $path, callable $handler, array $allowedOrigins=[]): void
|
||||||
{
|
{
|
||||||
|
// TODO implement or remove
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request, callable $next)
|
public function __invoke(ServerRequestInterface $request, callable $next)
|
||||||
@ -62,12 +64,14 @@ class WebSocketMiddleware implements EventEmitterInterface
|
|||||||
$this->emit(self::EVENT_CONNECTION, [ $websocket ]);
|
$this->emit(self::EVENT_CONNECTION, [ $websocket ]);
|
||||||
//});
|
//});
|
||||||
|
|
||||||
|
// TODO would it be possible or rather useful for the 'connection' event to set additional response headers to be sent here?
|
||||||
return new Response(
|
return new Response(
|
||||||
Response::STATUS_SWITCHING_PROTOCOLS,
|
Response::STATUS_SWITCHING_PROTOCOLS,
|
||||||
array(
|
array(
|
||||||
'Upgrade' => 'websocket',
|
'Upgrade' => 'websocket',
|
||||||
'Connection' => 'upgrade',
|
'Connection' => 'upgrade',
|
||||||
'Sec-WebSocket-Accept' => $handshakeResponse
|
'Sec-WebSocket-Accept' => $handshakeResponse,
|
||||||
|
'Sec-WebSocket-Version' => self::VERSION
|
||||||
),
|
),
|
||||||
$stream
|
$stream
|
||||||
);
|
);
|
||||||
|
@ -125,6 +125,7 @@ class WebSocketProtocol
|
|||||||
$byte0 = ord($frame[0]);
|
$byte0 = ord($frame[0]);
|
||||||
$decoded['final'] = !!($byte0 & 0x80);
|
$decoded['final'] = !!($byte0 & 0x80);
|
||||||
$decoded['opcode'] = $byte0 & 0x0F;
|
$decoded['opcode'] = $byte0 & 0x0F;
|
||||||
|
|
||||||
// Peek at the second byte, holding mask bit and len
|
// Peek at the second byte, holding mask bit and len
|
||||||
$byte1 = ord($frame[1]);
|
$byte1 = ord($frame[1]);
|
||||||
$decoded['masked'] = $masked = !!($byte1 & 0x80);
|
$decoded['masked'] = $masked = !!($byte1 & 0x80);
|
||||||
@ -146,14 +147,16 @@ class WebSocketProtocol
|
|||||||
$header += 4;
|
$header += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now for the masking
|
// Now for the masking, if present.
|
||||||
if ($masked) {
|
if ($masked) {
|
||||||
$mask = substr($frame, $header, 4);
|
$mask = substr($frame, $header, 4);
|
||||||
$header += 4;
|
$header += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and unmask payload
|
// Extract the payload, and unmask it if needed. The mask() function handles
|
||||||
|
// both masking and unmasing as the algorithm uses xor.
|
||||||
$payload = substr($frame, $header, $len);
|
$payload = substr($frame, $header, $len);
|
||||||
|
// TODO check that extracted payload len equals expected len
|
||||||
if ($masked) {
|
if ($masked) {
|
||||||
$payload = $this->mask($payload, $mask);
|
$payload = $this->mask($payload, $mask);
|
||||||
$decoded['mask'] = $mask;
|
$decoded['mask'] = $mask;
|
||||||
@ -174,14 +177,17 @@ class WebSocketProtocol
|
|||||||
*/
|
*/
|
||||||
private function mask(string $payload, string $mask): string
|
private function mask(string $payload, string $mask): string
|
||||||
{
|
{
|
||||||
|
// Unpack the payload and mask into byte values
|
||||||
$payloadData = array_map("ord", str_split($payload,1));
|
$payloadData = array_map("ord", str_split($payload,1));
|
||||||
$maskData = array_map("ord", str_split($mask,1));
|
$maskData = array_map("ord", str_split($mask,1));
|
||||||
|
// TODO check that mask len==4
|
||||||
|
|
||||||
$unmasked = [];
|
$unmasked = [];
|
||||||
for ($n = 0; $n < count($payloadData); $n++) {
|
for ($n = 0; $n < count($payloadData); $n++) {
|
||||||
$unmasked[] = $payloadData[$n] ^ $maskData[$n % 4];
|
$unmasked[] = $payloadData[$n] ^ $maskData[$n % 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the masked byte values packed into a string
|
||||||
return join("", array_map("chr", $unmasked));
|
return join("", array_map("chr", $unmasked));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user