Merge branch 'master' of github.com:beyondcode/laravel-websockets

This commit is contained in:
Marcel Pociot 2018-11-27 09:17:45 +01:00
commit 2685f48f1e
13 changed files with 75 additions and 82 deletions

View File

@ -1,6 +1,5 @@
<?php <?php
use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize;
use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider; use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider;
return [ return [

View File

@ -13,7 +13,6 @@ use Illuminate\Http\JsonResponse;
use GuzzleHttp\Psr7\ServerRequest; use GuzzleHttp\Psr7\ServerRequest;
use Ratchet\Http\HttpServerInterface; use Ratchet\Http\HttpServerInterface;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\RequestInterface;
use SebastianBergmann\ObjectEnumerator\Fixtures\ExceptionThrower;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;

View File

@ -12,7 +12,7 @@ class FetchChannel extends EchoController
$channel = $this->channelManager->find($request->appId, $request->channelName); $channel = $this->channelManager->find($request->appId, $request->channelName);
if (is_null($channel)) { if (is_null($channel)) {
throw new HttpException(404, 'Unknown channel "'.$request->channelName.'"'); throw new HttpException(404, "Unknown channel `{$request->channelName}`.");
} }
return $channel->toArray(); return $channel->toArray();

View File

@ -5,8 +5,7 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
use BeyondCode\LaravelWebSockets\Events\ChannelOccupied; use BeyondCode\LaravelWebSockets\Events\ChannelOccupied;
use BeyondCode\LaravelWebSockets\Events\ChannelVacated; use BeyondCode\LaravelWebSockets\Events\ChannelVacated;
use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel; use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignature;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignatureException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use stdClass; use stdClass;
@ -40,7 +39,7 @@ class Channel
} }
if (str_after($auth, ':') !== hash_hmac('sha256', $signature, $connection->client->appSecret)) { if (str_after($auth, ':') !== hash_hmac('sha256', $signature, $connection->client->appSecret)) {
throw new InvalidSignatureException(); throw new InvalidSignature();
} }
} }

View File

@ -28,7 +28,7 @@ class ChannelManager
return $this->channels[$appId][$channelId] ?? null; return $this->channels[$appId][$channelId] ?? null;
} }
protected function detectChannelClass($channelId): string protected function detectChannelClass(string $channelId): string
{ {
if (starts_with($channelId, 'private-')) { if (starts_with($channelId, 'private-')) {
return PrivateChannel::class; return PrivateChannel::class;

View File

@ -3,6 +3,7 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use stdClass;
class PresenceChannel extends Channel class PresenceChannel extends Channel
{ {
@ -16,7 +17,7 @@ class PresenceChannel extends Channel
/* /*
* @link https://pusher.com/docs/pusher_protocol#presence-channel-events * @link https://pusher.com/docs/pusher_protocol#presence-channel-events
*/ */
public function subscribe(ConnectionInterface $connection, $payload) public function subscribe(ConnectionInterface $connection, stdClass $payload)
{ {
$this->verifySignature($connection, $payload); $this->verifySignature($connection, $payload);

View File

@ -2,7 +2,7 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions;
class InvalidConnectionException extends PusherException class InvalidConnection extends PusherException
{ {
public function __construct() public function __construct()
{ {

View File

@ -2,7 +2,7 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions;
class InvalidSignatureException extends PusherException class InvalidSignature extends PusherException
{ {
public function __construct() public function __construct()
{ {

View File

@ -2,7 +2,7 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions;
class UnknownAppKeyException extends PusherException class UnknownAppKey extends PusherException
{ {
public function __construct(string $appKey) public function __construct(string $appKey)
{ {

View File

@ -4,7 +4,6 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket;
use BeyondCode\LaravelWebSockets\Events\ClientMessageSent; use BeyondCode\LaravelWebSockets\Events\ClientMessageSent;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use stdClass; use stdClass;
@ -30,12 +29,14 @@ class Message implements RespondableMessage
public function respond() public function respond()
{ {
if (starts_with($this->payload->event, 'client-')) { if (!starts_with($this->payload->event, 'client-')) {
return;
}
event(new ClientMessageSent($this->connection, $this->payload)); event(new ClientMessageSent($this->connection, $this->payload));
$channel = $this->channelManager->find($this->connection->client->appId, $this->payload->channel); $channel = $this->channelManager->find($this->connection->client->appId, $this->payload->channel);
optional($channel)->broadcast($this->payload); optional($channel)->broadcast($this->payload);
} }
}
} }

View File

@ -3,7 +3,6 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket; namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket;
use BeyondCode\LaravelWebSockets\Events\ConnectionEstablished; use BeyondCode\LaravelWebSockets\Events\ConnectionEstablished;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard;
use BeyondCode\LaravelWebSockets\QueryParameters; use BeyondCode\LaravelWebSockets\QueryParameters;
use Exception; use Exception;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
@ -12,7 +11,7 @@ use BeyondCode\LaravelWebSockets\WebSocketController;
use BeyondCode\LaravelWebSockets\ClientProviders\Client; use BeyondCode\LaravelWebSockets\ClientProviders\Client;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\PusherException; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\PusherException;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKeyException; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey;
class PusherServer extends WebSocketController class PusherServer extends WebSocketController
{ {
@ -26,11 +25,10 @@ class PusherServer extends WebSocketController
function onOpen(ConnectionInterface $connection) function onOpen(ConnectionInterface $connection)
{ {
$this->generateSocketId($connection); $this
->generateSocketId($connection)
$this->verifyConnection($connection); ->verifyConnection($connection)
->establishConnection($connection);
$this->establishConnection($connection);
} }
public function onMessage(ConnectionInterface $connection, MessageInterface $message) public function onMessage(ConnectionInterface $connection, MessageInterface $message)
@ -45,7 +43,7 @@ class PusherServer extends WebSocketController
$this->channelManager->removeFromAllChannels($connection); $this->channelManager->removeFromAllChannels($connection);
} }
function onError(ConnectionInterface $connection, Exception $exception) public function onError(ConnectionInterface $connection, Exception $exception)
{ {
if ($exception instanceof PusherException) { if ($exception instanceof PusherException) {
$connection->send(json_encode( $connection->send(json_encode(
@ -54,15 +52,26 @@ class PusherServer extends WebSocketController
} }
} }
protected function generateSocketId(ConnectionInterface $connection)
{
$socketId = sprintf("%d.%d", getmypid(), random_int(1, 100000000));
$connection->socketId = $socketId;
return $this;
}
protected function verifyConnection(ConnectionInterface $connection) protected function verifyConnection(ConnectionInterface $connection)
{ {
$appKey = QueryParameters::create($connection->httpRequest)->get('appKey'); $appKey = QueryParameters::create($connection->httpRequest)->get('appKey');
if (! $client = Client::findByAppKey($appKey)) { if (!$client = Client::findByAppKey($appKey)) {
throw new UnknownAppKeyException($appKey); throw new UnknownAppKey($appKey);
} }
$connection->client = $client; $connection->client = $client;
return $this;
} }
protected function establishConnection(ConnectionInterface $connection) protected function establishConnection(ConnectionInterface $connection)
@ -76,12 +85,7 @@ class PusherServer extends WebSocketController
])); ]));
event(new ConnectionEstablished($connection)); event(new ConnectionEstablished($connection));
}
protected function generateSocketId(ConnectionInterface $connection) return $this;
{
$socketId = sprintf("%d.%d", getmypid(), random_int(1, 100000000));
$connection->socketId = $socketId;
} }
} }

View File

@ -3,33 +3,37 @@
namespace BeyondCode\LaravelWebSockets\Tests; namespace BeyondCode\LaravelWebSockets\Tests;
use BeyondCode\LaravelWebSockets\ClientProviders\Client; use BeyondCode\LaravelWebSockets\ClientProviders\Client;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignatureException; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignature;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKeyException; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey;
use BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket\PusherServer; use BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket\PusherServer;
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message; use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
class ConnectionTest extends TestCase class ConnectionTest extends TestCase
{ {
/** @var \BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket\PusherServer */
protected $pusherServer;
public function setUp()
{
parent::setUp();
$this->pusherServer = app(PusherServer::class);
}
/** @test */ /** @test */
public function unknown_app_keys_can_not_connect() public function unknown_app_keys_can_not_connect()
{ {
$this->expectException(UnknownAppKeyException::class); $this->expectException(UnknownAppKey::class);
/** @var PusherServer $server */ $this->pusherServer->onOpen($this->getWebSocketConnection('/?appKey=test'));
$server = app(PusherServer::class);
$server->onOpen($this->getWebSocketConnection('/?appKey=test'));
} }
/** @test */ /** @test */
public function known_app_keys_can_connect() public function known_app_keys_can_connect()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$connection->assertSentEvent('pusher:connection_established'); $connection->assertSentEvent('pusher:connection_established');
} }
@ -37,12 +41,9 @@ class ConnectionTest extends TestCase
/** @test */ /** @test */
public function successful_connections_have_the_client_attached() public function successful_connections_have_the_client_attached()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$this->assertInstanceOf(Client::class, $connection->client); $this->assertInstanceOf(Client::class, $connection->client);
$this->assertSame(1234, $connection->client->appId); $this->assertSame(1234, $connection->client->appId);
@ -54,16 +55,13 @@ class ConnectionTest extends TestCase
/** @test */ /** @test */
public function ping_returns_pong() public function ping_returns_pong()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$message = new Message('{"event": "pusher:ping"}'); $message = new Message('{"event": "pusher:ping"}');
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
$connection->assertSentEvent('pusher:pong'); $connection->assertSentEvent('pusher:pong');
} }
@ -71,9 +69,6 @@ class ConnectionTest extends TestCase
/** @test */ /** @test */
public function clients_can_subscribe_to_basic_channels() public function clients_can_subscribe_to_basic_channels()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$message = new Message(json_encode([ $message = new Message(json_encode([
@ -83,9 +78,9 @@ class ConnectionTest extends TestCase
], ],
])); ]));
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
$connection->assertSentEvent('pusher_internal:subscription_succeeded', [ $connection->assertSentEvent('pusher_internal:subscription_succeeded', [
'channel' => 'basic-channel' 'channel' => 'basic-channel'
@ -95,10 +90,7 @@ class ConnectionTest extends TestCase
/** @test */ /** @test */
public function clients_need_valid_auth_signatures_for_private_channels() public function clients_need_valid_auth_signatures_for_private_channels()
{ {
$this->expectException(InvalidSignatureException::class); $this->expectException(InvalidSignature::class);
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
@ -110,20 +102,17 @@ class ConnectionTest extends TestCase
], ],
])); ]));
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
} }
/** @test */ /** @test */
public function clients_can_subscribe_to_private_channels() public function clients_can_subscribe_to_private_channels()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$signature = "{$connection->socketId}:private-channel"; $signature = "{$connection->socketId}:private-channel";
@ -135,7 +124,7 @@ class ConnectionTest extends TestCase
], ],
])); ]));
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
$connection->assertSentEvent('pusher_internal:subscription_succeeded', [ $connection->assertSentEvent('pusher_internal:subscription_succeeded', [
'channel' => 'private-channel' 'channel' => 'private-channel'
@ -145,10 +134,7 @@ class ConnectionTest extends TestCase
/** @test */ /** @test */
public function clients_need_valid_auth_signatures_for_presence_channels() public function clients_need_valid_auth_signatures_for_presence_channels()
{ {
$this->expectException(InvalidSignatureException::class); $this->expectException(InvalidSignature::class);
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
@ -160,20 +146,17 @@ class ConnectionTest extends TestCase
], ],
])); ]));
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
} }
/** @test */ /** @test */
public function clients_can_subscribe_to_presence_channels() public function clients_can_subscribe_to_presence_channels()
{ {
/** @var PusherServer $server */
$server = app(PusherServer::class);
$connection = $this->getWebSocketConnection(); $connection = $this->getWebSocketConnection();
$server->onOpen($connection); $this->pusherServer->onOpen($connection);
$channelData = [ $channelData = [
'user_id' => 1, 'user_id' => 1,
@ -193,7 +176,7 @@ class ConnectionTest extends TestCase
], ],
])); ]));
$server->onMessage($connection, $message); $this->pusherServer->onMessage($connection, $message);
$connection->assertSentEvent('pusher_internal:subscription_succeeded', [ $connection->assertSentEvent('pusher_internal:subscription_succeeded', [
'channel' => 'presence-channel', 'channel' => 'presence-channel',

View File

@ -13,6 +13,8 @@ class Connection implements ConnectionInterface
public $sentData = []; public $sentData = [];
public $closed = false;
function send($data) function send($data)
{ {
$this->sentData[] = json_decode($data, true); $this->sentData[] = json_decode($data, true);
@ -20,7 +22,7 @@ class Connection implements ConnectionInterface
function close() function close()
{ {
// TODO: Implement close() method. $this->closed = true;
} }
public function assertSentEvent(string $name, array $additionalParameters = []) public function assertSentEvent(string $name, array $additionalParameters = [])
@ -35,4 +37,9 @@ class Connection implements ConnectionInterface
PHPUnit::assertSame($event[$parameter], $value); PHPUnit::assertSame($event[$parameter], $value);
} }
} }
public function assertClosed()
{
PHPUnit::assertTrue($this->closed);
}
} }