Moved frame logic to WebSocketCodec

This commit is contained in:
2024-02-21 21:23:24 +01:00
parent 065d96f90a
commit 1caeb3c29f
6 changed files with 341 additions and 64 deletions

View File

@ -27,6 +27,8 @@ class WebSocketConnection implements WebSocketInterface
private ?string $groupName = null;
private WebSocketCodec $codec;
private ?ConnectionGroup $group = null;
private GroupManager $groupManager;
@ -43,6 +45,9 @@ class WebSocketConnection implements WebSocketInterface
public function __construct(ServerRequestInterface $request, ReadableStreamInterface $inStream, WritableStreamInterface $outStream, GroupManager $groupManager)
{
// The codec is used to encode and decode frames
$this->codec = new WebSocketCodec();
$this->request = $request;
$this->inStream = $inStream;
$this->outStream = $outStream;
@ -54,42 +59,10 @@ class WebSocketConnection implements WebSocketInterface
private function onWebSocketData($data)
{
// Keep track of the number of bytes in the header
$header = 2;
// Peek at the first byte, holding flags and opcode
$byte0 = ord($data[0]);
$final = !!($byte0 & 0x80);
$opcode = $byte0 & 0x0F;
// Peek at the second byte, holding mask bit and len
$byte1 = ord($data[1]);
$masked = !!($byte1 & 0x80);
$len = $byte1 & 0x7F;
// Read extended length if present
if ($len == 126) {
$len = (ord($data[$header+0]) << 8)
| (ord($data[$header+1]));
$header += 2;
} elseif ($len == 127) {
$len = (ord($data[$header+0]) << 24)
| (ord($data[$header+1]) << 16)
| (ord($data[$header+2]) << 8)
| (ord($data[$header+3]));
$header += 4;
}
// Now for the masking
if ($masked) {
$mask = substr($data, $header, 4);
$header += 4;
}
// Extract and unmask payload
$payload = substr($data, $header, $len);
if ($masked) {
$payload = $this->unmask($payload, $mask);
}
$decoded = $this->codec->decode($data);
$opcode = $decoded['opcode'];
$final = $decoded['final'];
$payload = $decoded['payload'];
if (!$final) {
if ($this->bufferedOp === null) {
@ -111,7 +84,12 @@ class WebSocketConnection implements WebSocketInterface
switch ($opcode) {
case self::OP_PING:
$this->sendPong();
$this->sendPong($payload);
return;
case self::OP_PONG:
return;
case self::OP_CLOSE:
// TODO implement
return;
case self::OP_CONTINUATION:
$this->buffer .= $payload;
@ -125,24 +103,9 @@ class WebSocketConnection implements WebSocketInterface
}
}
private function unmask(string $payload, string $mask): string
private function sendPong(string $data): void
{
$payloadData = array_map("ord", str_split($payload,1));
$maskData = array_map("ord", str_split($mask,1));
//printf("Mask: %02x %02x %02x %02x\n", ...$maskData);
$unmasked = [];
for ($n = 0; $n < count($payloadData); $n++) {
$unmasked[] = $payloadData[$n] ^ $maskData[$n % 4];
//printf("%02x ^ %02x = %02x %s\n", $payloadData[$n], $maskData[$n%4], $payloadData[$n]^$maskData[$n%4], chr($payloadData[$n]^$maskData[$n%4]));
}
return join("", array_map("chr", $unmasked));
}
private function sendPong(): void
{
$this->send(self::OP_PONG, $data, true);
}
public function setGroup(?string $name): void
@ -221,17 +184,15 @@ class WebSocketConnection implements WebSocketInterface
/**
*
*/
public function send(int $opcode, string $data, bool $final = true)
public function send(int $opcode, string $data, bool $final = true, bool $masked = false)
{
$frame = chr(($final?0x80:0x00) | ($opcode & 0xF));
$len = strlen($data);
if ($len > 126) {
$frame .= chr(0x7E) . chr(($len >> 8) & 0xFF) . chr($len & 0xFF);
} else {
$frame .= chr($len);
}
$frame .= $data;
$frame = $this->codec->encode([
'opcode' => $opcode,
'payload' => $data,
'final' => $final,
'masked' => $masked
]);
$this->outStream->write($frame);