Skip to content

Commit

Permalink
Implements Autobahn tests against the WebSocket spec (#22)
Browse files Browse the repository at this point in the history
* implement spec tests

* update action

* formatting

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* check for frame

* Fix code styling
  • Loading branch information
joedixon authored Nov 27, 2023
1 parent 2a67c99 commit d1bfbeb
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 31 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/spec-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: spec tests

on:
push:
branches:
- main
- '*.x'
pull_request:

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: true
matrix:
php: [8.2, 8.3]
laravel: [10]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip
ini-values: error_reporting=E_ALL
tools: composer:v2
coverage: none

- name: Install dependencies
run: |
composer require "illuminate/contracts=^${{ matrix.laravel }}" --no-update
composer update --prefer-dist --no-interaction --no-progress
- name: Pull Autobahn Docker image
run: docker pull crossbario/autobahn-testsuite

- name: Start WebSocket server
working-directory: tests/Specification
run: php spec-server.php &

- name: Run specification tests
working-directory: tests/Specification
run: |
docker run --rm \
-v $PWD:/mnt/autobahn \
-v $PWD/reports:/mnt/autobahn/reports \
--add-host host.docker.internal:host-gateway \
crossbario/autobahn-testsuite \
wstest -m fuzzingclient -s /mnt/autobahn/client-spec.json
- name: Analyze test results
working-directory: tests/Specification
run: php spec-analyze.php
18 changes: 13 additions & 5 deletions src/WebSockets/WsConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Evenement\EventEmitter;
use Laravel\Reverb\Http\Connection;
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
use Ratchet\RFC6455\Messaging\DataInterface;
use Ratchet\RFC6455\Messaging\Frame;
use Ratchet\RFC6455\Messaging\FrameInterface;
use Ratchet\RFC6455\Messaging\MessageBuffer;
Expand Down Expand Up @@ -56,10 +57,12 @@ public function openBuffer(): void
/**
* Send a message to the connection.
*/
public function send(string $message): void
public function send(mixed $message): void
{
$this->connection->send(
(new Frame($message))->getContents()
$message instanceof DataInterface ?
$message->getContents() :
(new Frame($message))->getContents()
);
}

Expand All @@ -69,8 +72,9 @@ public function send(string $message): void
public function control(FrameInterface $message): void
{
match ($message->getOpcode()) {
Frame::OP_PING => $this->send(new Frame('pong', opcode: Frame::OP_PONG)),
Frame::OP_CLOSE => $this->close(),
Frame::OP_PING => $this->send(new Frame($message->getPayload(), opcode: Frame::OP_PONG)),
Frame::OP_PONG => fn () => null,
Frame::OP_CLOSE => $this->close($message),
};
}

Expand All @@ -93,8 +97,12 @@ public function onClose(callable $callback): void
/**
* Close the connection.
*/
public function close(): void
public function close(FrameInterface $frame = null): void
{
if ($frame) {
$this->send($frame);
}

$this->connection->close();
}

Expand Down
8 changes: 0 additions & 8 deletions tests/Feature/Reverb/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use Laravel\Reverb\Jobs\PingInactiveConnections;
use Laravel\Reverb\Jobs\PruneStaleConnections;
use Laravel\Reverb\Tests\ReverbTestCase;
use Ratchet\RFC6455\Messaging\Frame;
use React\Promise\Deferred;

use function Ratchet\Client\connect;
Expand Down Expand Up @@ -315,13 +314,6 @@
$this->connect();
});

it('can receive a pong control frame', function () {
$frame = new Frame('ping', true, Frame::OP_PING);
$response = $this->sendRaw($frame);

expect($response)->toBe('pong');
});

it('clears application state between requests', function () {
$this->subscribe('test-channel');

Expand Down
18 changes: 0 additions & 18 deletions tests/ReverbTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,24 +182,6 @@ public function send(array $message, WebSocket $connection = null): string
return await($promise->promise());
}

/**
* Send a message to the connected client.
*/
public function sendRaw(mixed $message, WebSocket $connection = null): string
{
$promise = new Deferred;

$connection = $connection ?: $this->connect();

$connection->on('message', function ($message) use ($promise) {
$promise->resolve((string) $message);
});

$connection->send($message);

return await($promise->promise());
}

/**
* Disconnect the connected client.
*/
Expand Down
11 changes: 11 additions & 0 deletions tests/Specification/client-spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"options": { "failByDrop": false },
"outdir": "/mnt/autobahn/reports",
"servers": [{ "agent": "Reverb", "url": "ws://host.docker.internal:8080", "options": { "version": 18 } }],
"cases": ["*"],
"exclude-cases": [
"12.*",
"13.*"
],
"exclude-agent-cases": {}
}
33 changes: 33 additions & 0 deletions tests/Specification/spec-analyze.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Ratchet\RFC6455\Test;

use Illuminate\Support\Arr;

require __DIR__.'/../../vendor/autoload.php';

$hasFailures = false;

if (! file_exists($file = __DIR__.'/reports/index.json')) {
echo 'No test results found.'.PHP_EOL;

exit(1);
}

$results = file_get_contents($file);
$results = Arr::first(json_decode($results, true));

foreach ($results as $name => $result) {
if ($result['behavior'] === 'INFORMATIONAL') {
continue;
}

if (in_array($result['behavior'], ['OK', 'NON-STRICT'])) {
echo '✅ Test case '.$name.' passed.'.PHP_EOL;
} else {
$hasFailures = true;
echo '❌ Test case '.$name.' failed.'.PHP_EOL;
}
}

exit($hasFailures ? 1 : 0);
40 changes: 40 additions & 0 deletions tests/Specification/spec-server.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

use Laravel\Reverb\Http\Route;
use Laravel\Reverb\Http\Router;
use Laravel\Reverb\Http\Server;
use Laravel\Reverb\WebSockets\WsConnection;
use Psr\Http\Message\RequestInterface;
use React\EventLoop\Loop;
use React\Socket\SocketServer;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;

require __DIR__.'/../../vendor/autoload.php';

$loop = Loop::get();
$socket = new SocketServer('0.0.0.0:8080', [], $loop);
$router = new Router(new UrlMatcher(routes(), new RequestContext));

$server = new Server($socket, $router, $loop);

echo "Server running at 0.0.0.0:8080\n";

$server->start();

function routes()
{
$routes = new RouteCollection;
$routes->add(
'sockets',
Route::get('/', function (RequestInterface $request, WsConnection $connection) {
$connection->onMessage(function ($message) use ($connection) {
$connection->send($message);
});
$connection->openBuffer();
})
);

return $routes;
}

0 comments on commit d1bfbeb

Please sign in to comment.