102 lines
3.4 KiB
PHP
102 lines
3.4 KiB
PHP
<?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);
|
|
}
|
|
}
|