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
use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider;
return [
/*
@ -34,27 +36,26 @@ return [
'passphrase' => null
],
/*
* TODO:: add client config
*
* 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":
}
],
`
/**
* This package comes with multi tenancy out of the box. Here you can
* configure the diffente clients that can use the webSockets server.
*
* You should make sure that the app id is numeric.
*/
'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;
use BeyondCode\LaravelWebSockets\Facades\WebSocketRouter;
use BeyondCode\LaravelWebSockets\Server\Logger;
use Illuminate\Console\Command;
use BeyondCode\LaravelWebSockets\Server\WebSocketServer;
@ -16,30 +17,47 @@ class StartWebSocketServer extends Command
public function handle()
{
// TODO: add an option to not start the echo server
WebSocketRouter::echo();
// TODO: add flag for verbose mode, to send more things to console
$websocketServer = $this->createWebsocketServer();
$websocketServer->run();
$this
->configureLogger()
->registerEchoRoutes()
->startWebSocketServer();
}
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();
$loop = LoopFactory::create();
$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))
->setHost($this->option('host'))
->setPort($this->option('port'))
->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;
use BeyondCode\LaravelWebSockets\ClientProviders\Client;
use BeyondCode\LaravelWebsockets\LaravelEcho\Pusher\Exceptions\PusherException;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey;
use Exception;
@ -59,12 +60,11 @@ class PusherServer extends WebSocketController
$queryParameters = [];
parse_str($request->getUri()->getQuery(), $queryParameters);
// Todo: Lookup app-id for multi-tenancy support
if ($queryParameters['appKey'] !== config('broadcasting.connections.pusher.key')) {
if (! $client = Client::find($queryParameters['appKey'])) {
throw new UnknownAppKey($queryParameters['appKey']);
}
$connection->appId = config('broadcasting.connections.pusher.app_id');
$connection->appId = $client->appId;
}
protected function establishConnection(ConnectionInterface $connection)

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets;
use BeyondCode\LaravelWebSockets\ClientProviders\ClientProvider;
use Illuminate\Support\ServiceProvider;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
@ -10,7 +11,7 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
public function boot()
{
$this->publishes([
__DIR__.'/../config/config.php' => base_path('config/websockets.php'),
__DIR__.'/../config/websockets.php' => base_path('config/websockets.php'),
], 'config');
$this->commands([
@ -20,7 +21,7 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'websockets');
$this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets');
$this->app->singleton('websockets.router', function() {
return new Router();
@ -29,5 +30,9 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
$this->app->singleton(ChannelManager::class, function() {
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 Ratchet\Http\HttpServerInterface;
use Symfony\Component\Routing\RouteCollection;
use Ratchet\WebSocket\MessageComponentInterface;
use BeyondCode\LaravelWebSockets\Exceptions\InvalidWebSocketController;
class Router
@ -82,12 +81,13 @@ class Router
protected function wrapAction($action)
{
if (is_subclass_of($action, WebSocketController::class)) {
$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);

View File

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

View File

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