laravel-webrtc/tests/WebSocketRelayServerTest.php

102 lines
3.4 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
namespace Blax\WebRtc\Tests;
use Blax\WebRtc\Signaling\RelaySignalingHandler;
use Blax\Ws\WebSocketServer;
use PHPUnit\Framework\TestCase;
use React\EventLoop\StreamSelectLoop;
use React\Socket\ConnectionInterface;
use React\Socket\Connector;
/**
* End-to-end proof that the WebRTC relay composes with the laravel-ws
* WebSocketServer: a browser (hand-rolled RFC6455 client) connecting to
* `webrtc:serve` gets a `welcome` with its peer id over a real WebSocket.
*/
final class WebSocketRelayServerTest extends TestCase
{
public function test_a_browser_receives_welcome_over_a_real_websocket(): void
{
$loop = new StreamSelectLoop;
$server = new WebSocketServer('127.0.0.1', 0, new RelaySignalingHandler);
$server->boot($loop);
$address = $server->address();
$this->assertNotNull($address);
$port = (int) parse_url($address, PHP_URL_PORT);
$handshakeOk = false;
$welcome = null;
(new Connector([], $loop))
->connect("127.0.0.1:{$port}")
->then(function (ConnectionInterface $conn) use (&$handshakeOk, &$welcome, $loop) {
$buffer = '';
$upgraded = false;
$conn->on('data', function ($data) use (&$buffer, &$upgraded, &$handshakeOk, &$welcome, $conn, $loop) {
$buffer .= $data;
if (! $upgraded) {
$end = strpos($buffer, "\r\n\r\n");
if ($end === false) {
return;
}
$handshakeOk = str_contains(substr($buffer, 0, $end), ' 101 ');
$buffer = substr($buffer, $end + 4);
$upgraded = true;
if (! $handshakeOk) {
$loop->stop();
return;
}
}
// The server pushes a `welcome` frame on connect (no client frame needed).
$decoded = self::decodeServerTextFrame($buffer);
if ($decoded !== null) {
$welcome = json_decode($decoded, true);
$conn->close();
$loop->stop();
}
});
$key = base64_encode(random_bytes(16));
$conn->write(
"GET / HTTP/1.1\r\n".
"Host: 127.0.0.1\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Key: {$key}\r\n".
"Sec-WebSocket-Version: 13\r\n\r\n"
);
});
$loop->addTimer(3.0, fn () => $loop->stop());
$loop->run();
$server->shutdown();
$this->assertTrue($handshakeOk, 'server should return a 101 upgrade');
$this->assertSame('welcome', $welcome['type'] ?? null);
$this->assertArrayHasKey('peer', (array) $welcome);
}
/** Decode one unmasked server->client text frame (payload < 126 bytes), or null. */
private static function decodeServerTextFrame(string $data): ?string
{
if (strlen($data) < 2) {
return null;
}
$length = ord($data[1]) & 0x7F;
if ($length >= 126 || strlen($data) < 2 + $length) {
return null;
}
return substr($data, 2, $length);
}
}