add client provider

This commit is contained in:
freek 2018-11-24 01:25:40 +01:00
parent 193eb5eb4f
commit d7ea654c3d
11 changed files with 212 additions and 70 deletions

View File

@ -1,5 +1,7 @@
<?php <?php
use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider;
return [ return [
/* /*
@ -34,27 +36,26 @@ return [
'passphrase' => null 'passphrase' => null
], ],
/* /**
* TODO:: add client config * This package comes with multi tenancy out of the box. Here you can
* * configure the diffente clients that can use the webSockets server.
* Default: one item in the array with env PUSHER_APP_ID, _KEY, _SECRET
*
* Add notice app id should be numeric
*
* "clients": [
{
"appId": "cbf9b001405e51d4",
"key": "d886dd1900a5911d00996b41638d7026"
"secret":
}
],
`
* *
* You should make sure that the app id is numeric.
*/
'clients' => [ 'clients' => [
...[] [
'app_id' => env('WEBSOCKETS_APP_ID'),
'app_key' => env('WEBSOCKETS_APP_KEY'),
'app_secret' => env('WEBSOCKETS_APP_SECRET')
],
], ],
'client_provider' => ConfigProvider /**
*/ * This class is responsible for finding the clients. The default provider
* will use the clients defined in this config file.
*
* You can create a custom provider by implementing the
* `ClientProvier` interface.
*/
'client_provider' => ConfigClientProvider::class,
]; ];

View File

@ -0,0 +1,46 @@
<?php
namespace BeyondCode\LaravelWebSockets\ClientProviders;
use BeyondCode\LaravelWebSockets\Exceptions\InvalidClient;
class Client
{
/** @var int */
public $appId;
/** @var string */
public $appKey;
/** @var string */
public $appSecret;
public static function find(string $appKey): ?Client
{
return app(ClientProvider::class)->findClient($appKey);
}
public function __construct($appId, string $appKey, string $appSecret)
{
if (!is_numeric($appId)) {
throw InvalidClient::appIdIsNotNumeric($appId);
}
if ($appKey === '') {
throw InvalidClient::valueIsRequired('appKey', $appId);
}
if ($appSecret === '') {
throw InvalidClient::valueIsRequired('appSecret', $appId);
}
$this->appId = $appId;
$this->appKey = $appKey;
$this->appSecret = $appSecret;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace BeyondCode\LaravelWebSockets\ClientProviders;
interface ClientProvider
{
public function findClient(string $appId): ?Client;
}

View File

@ -0,0 +1,28 @@
<?php
namespace BeyondCode\LaravelWebSockets\ClientProviders;
class ConfigClientProvider implements ClientProvider
{
public function getClients(): array
{
return config('laravel-websockets.clients');
}
public function findClient(string $appKey): ?Client
{
$allClients = collect(config('websockets.clients'));
$client = $allClients->firstWhere('app_key', $appKey);
if (! $client) {
return null;
}
return new Client(
$client['app_id'],
$client['app_key'],
$client['app_secret']
);
}
}

View File

@ -3,6 +3,7 @@
namespace BeyondCode\LaravelWebSockets\Console; namespace BeyondCode\LaravelWebSockets\Console;
use BeyondCode\LaravelWebSockets\Facades\WebSocketRouter; use BeyondCode\LaravelWebSockets\Facades\WebSocketRouter;
use BeyondCode\LaravelWebSockets\Server\Logger;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use BeyondCode\LaravelWebSockets\Server\WebSocketServer; use BeyondCode\LaravelWebSockets\Server\WebSocketServer;
@ -16,30 +17,47 @@ class StartWebSocketServer extends Command
public function handle() public function handle()
{ {
// TODO: add an option to not start the echo server $this
WebSocketRouter::echo(); ->configureLogger()
->registerEchoRoutes()
// TODO: add flag for verbose mode, to send more things to console ->startWebSocketServer();
$websocketServer = $this->createWebsocketServer();
$websocketServer->run();
} }
protected function createWebsocketServer(): WebSocketServer protected function configureLogger()
{
app()->singleton(Logger::class, function() {
return (new Logger($this->output))
->enable(config('app.debug'))
//TODO: use real option
->verbose($this->hasOption('vvv'));
});
return $this;
}
protected function registerEchoRoutes()
{
WebSocketRouter::echo();
return $this;
}
protected function startWebSocketServer()
{ {
$routes = WebSocketRouter::getRoutes(); $routes = WebSocketRouter::getRoutes();
$loop = LoopFactory::create(); $loop = LoopFactory::create();
$loop->futureTick(function () { $loop->futureTick(function () {
$this->info('Started the WebSocket server on port '.$this->option('port')); $this->info("Started the WebSocket server on port {$this->option('port')}");
}); });
/** 🎩 Start the magic 🎩 */
return (new WebSocketServer($routes)) return (new WebSocketServer($routes))
->setHost($this->option('host')) ->setHost($this->option('host'))
->setPort($this->option('port')) ->setPort($this->option('port'))
->setConsoleOutput($this->output) ->setConsoleOutput($this->output)
->enableLogging(config('app.debug')) ->setLoop($loop)
->setLoop($loop); ->run();
} }
} }

View File

@ -0,0 +1,24 @@
<?php
namespace BeyondCode\LaravelWebSockets\Exceptions;
use Exception;
class InvalidClient extends Exception
{
public static function notFound(int $appId)
{
return new static("Could not find client for app id `{$appId}`.");
}
public static function appIdIsNotNumeric($appId)
{
return new static("Invalid app id `{$appId}` found. An app id should be numeric.");
}
public static function valueIsRequired($name, int $appId)
{
return new static("{$name} is required but was empty for app id {$appId}");
}
}

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket; namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket;
use BeyondCode\LaravelWebSockets\ClientProviders\Client;
use BeyondCode\LaravelWebsockets\LaravelEcho\Pusher\Exceptions\PusherException; use BeyondCode\LaravelWebsockets\LaravelEcho\Pusher\Exceptions\PusherException;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey;
use Exception; use Exception;
@ -59,12 +60,11 @@ class PusherServer extends WebSocketController
$queryParameters = []; $queryParameters = [];
parse_str($request->getUri()->getQuery(), $queryParameters); parse_str($request->getUri()->getQuery(), $queryParameters);
// Todo: Lookup app-id for multi-tenancy support if (! $client = Client::find($queryParameters['appKey'])) {
if ($queryParameters['appKey'] !== config('broadcasting.connections.pusher.key')) {
throw new UnknownAppKey($queryParameters['appKey']); throw new UnknownAppKey($queryParameters['appKey']);
} }
$connection->appId = config('broadcasting.connections.pusher.app_id'); $connection->appId = $client->appId;
} }
protected function establishConnection(ConnectionInterface $connection) protected function establishConnection(ConnectionInterface $connection)

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets; namespace BeyondCode\LaravelWebSockets;
use BeyondCode\LaravelWebSockets\ClientProviders\ClientProvider;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
@ -10,7 +11,7 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
$this->publishes([ $this->publishes([
__DIR__.'/../config/config.php' => base_path('config/websockets.php'), __DIR__.'/../config/websockets.php' => base_path('config/websockets.php'),
], 'config'); ], 'config');
$this->commands([ $this->commands([
@ -20,7 +21,7 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
public function register() public function register()
{ {
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'websockets'); $this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets');
$this->app->singleton('websockets.router', function() { $this->app->singleton('websockets.router', function() {
return new Router(); return new Router();
@ -29,5 +30,9 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
$this->app->singleton(ChannelManager::class, function() { $this->app->singleton(ChannelManager::class, function() {
return new ChannelManager(); return new ChannelManager();
}); });
$this->app->singleton(ClientProvider::class, function() {
return app(config('websockets.client_provider'));
});
} }
} }

View File

@ -7,7 +7,6 @@ use Ratchet\WebSocket\WsServer;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Ratchet\Http\HttpServerInterface; use Ratchet\Http\HttpServerInterface;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
use Ratchet\WebSocket\MessageComponentInterface;
use BeyondCode\LaravelWebSockets\Exceptions\InvalidWebSocketController; use BeyondCode\LaravelWebSockets\Exceptions\InvalidWebSocketController;
class Router class Router
@ -82,12 +81,13 @@ class Router
protected function wrapAction($action) protected function wrapAction($action)
{ {
if (is_subclass_of($action, WebSocketController::class)) { if (is_subclass_of($action, WebSocketController::class)) {
$app = app($action); $app = app($action);
$appThatLogs = new Logger($app); if (Logger::isEnabled()) {
$app = Logger::decorate($app);
}
return new WsServer($appThatLogs); return new WsServer($app);
} }
return app($action); return app($action);

View File

@ -27,22 +27,47 @@ class Logger implements MessageComponentInterface
protected $consoleOutput; protected $consoleOutput;
/** @var bool */ /** @var bool */
protected $enabled; protected $enabled = false;
public function __construct(MessageComponentInterface $app) /** @var bool */
protected $verbose = false;
public static function decorate(MessageComponentInterface $app): Logger
{
$logger = app(Logger::class);
return $logger->setApp($app);
}
public static function isEnabled(): bool
{
return app(Logger::class)->enabled;
}
public function __construct(OutputInterface $consoleOutput)
{
$this->consoleOutput = $consoleOutput;
}
public function enable($enabled = true)
{
$this->enabled = $enabled;
return $this;
}
public function verbose($verbose = false)
{
$this->verbose = $verbose;
return $this;
}
public function setApp(MessageComponentInterface $app)
{ {
$this->app = $app; $this->app = $app;
/* return $this;
$this->consoleOutput = $consoleOutput;
$this->enabled = $enabled;
*/
}
public function enable()
{
$this->enabled = true;
} }
public function onOpen(ConnectionInterface $connection) public function onOpen(ConnectionInterface $connection)
@ -75,14 +100,13 @@ class Logger implements MessageComponentInterface
{ {
$exceptionClass = get_class($exception); $exceptionClass = get_class($exception);
$message = "{$connection->appId}: execption `{$exceptionClass}` thrown: `{$exception->getMessage()}`"; $appId = $connection->appId ?? 'Unknown app id';
/* $message = "{$appId}: exception `{$exceptionClass}` thrown: `{$exception->getMessage()}`";
* TODO: add verbose option
if ($this->isVerbose) { if ($this->verbose) {
$message .= $exception->getTraceAsString(); $message .= $exception->getTraceAsString();
} }
*/
$this->error($message); $this->error($message);
@ -106,10 +130,6 @@ class Logger implements MessageComponentInterface
protected function line(string $message, string $style) protected function line(string $message, string $style)
{ {
echo $message;
return;
$styled = $style ? "<$style>$message</$style>" : $message; $styled = $style ? "<$style>$message</$style>" : $message;
$this->consoleOutput->writeln($styled); $this->consoleOutput->writeln($styled);

View File

@ -31,8 +31,6 @@ class WebSocketServer
/** @var Symfony\Component\Console\Output\OutputInterface */ /** @var Symfony\Component\Console\Output\OutputInterface */
protected $consoleOutput; protected $consoleOutput;
protected $enableLogging = false;
public function __construct(RouteCollection $routes) public function __construct(RouteCollection $routes)
{ {
$this->loop = LoopFactory::create(); $this->loop = LoopFactory::create();
@ -68,13 +66,6 @@ class WebSocketServer
return $this; return $this;
} }
public function enableLogging($enableLogging = true)
{
$this->enableLogging = $enableLogging;
return $this;
}
public function run() public function run()
{ {
$server = $this->createServer(); $server = $this->createServer();