This commit is contained in:
Marcel Pociot 2018-11-21 12:13:40 +01:00
parent a12c3ba8d0
commit e94e70b734
14 changed files with 220 additions and 9 deletions

View File

@ -27,7 +27,8 @@
"illuminate/console": "5.6.*|5.7.*", "illuminate/console": "5.6.*|5.7.*",
"illuminate/http": "5.6.*|5.7.*", "illuminate/http": "5.6.*|5.7.*",
"illuminate/routing": "5.6.*|5.7.*", "illuminate/routing": "5.6.*|5.7.*",
"illuminate/support": "5.6.*|5.7.*" "illuminate/support": "5.6.*|5.7.*",
"symfony/psr-http-message-bridge": "^1.1"
}, },
"require-dev": { "require-dev": {
"larapack/dd": "^1.0", "larapack/dd": "^1.0",

View File

@ -1,6 +1,6 @@
<?php <?php
namespace BeyondCode\LaravelWebsockets\Console; namespace BeyondCode\LaravelWebSockets\Console;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use BeyondCode\LaravelWebSockets\Server\WebSocketServer; use BeyondCode\LaravelWebSockets\Server\WebSocketServer;

View File

@ -1,11 +1,14 @@
<?php <?php
namespace BeyondCode\LaravelWebsockets\LaravelEcho\Http\Controllers; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use Illuminate\Http\Request;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use GuzzleHttp\Psr7\ServerRequest;
use Ratchet\Http\HttpServerInterface; use Ratchet\Http\HttpServerInterface;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\RequestInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
abstract class EchoController implements HttpServerInterface abstract class EchoController implements HttpServerInterface
{ {
@ -39,7 +42,18 @@ abstract class EchoController implements HttpServerInterface
*/ */
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) public function onOpen(ConnectionInterface $conn, RequestInterface $request = null)
{ {
$response = $this($request); $queryParameters = [];
parse_str($request->getUri()->getQuery(), $queryParameters);
$serverRequest = (new ServerRequest(
$request->getMethod(),
$request->getUri(),
$request->getHeaders(),
$request->getBody(),
$request->getProtocolVersion()
))->withQueryParams($queryParameters);
$response = $this(Request::createFromBase((new HttpFoundationFactory)->createRequest($serverRequest)));
$conn->send(JsonResponse::create($response)->send()); $conn->send(JsonResponse::create($response)->send());
$conn->close(); $conn->close();
@ -56,5 +70,5 @@ abstract class EchoController implements HttpServerInterface
// //
} }
abstract public function __invoke($request); abstract public function __invoke(Request $request);
} }

View File

@ -0,0 +1,14 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use Illuminate\Http\Request;
class EventController extends EchoController
{
public function __invoke(Request $request)
{
return $request->json()->all();
}
}

View File

@ -2,10 +2,12 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use Illuminate\Http\Request;
class StatusController extends EchoController class StatusController extends EchoController
{ {
public function __invoke($request) public function __invoke(Request $request)
{ {
return [ return [
'subscription_count' => 10 'subscription_count' => 10

View File

@ -0,0 +1,21 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
use Ratchet\ConnectionInterface;
class Channel
{
protected $channelId;
public function __construct($channelId)
{
$this->channelId = $channelId;
}
public function subscribe(ConnectionInterface $conn, $payload)
{
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
class ChannelManager
{
protected $channels = [];
public function findOrCreate(string $channelId)
{
if (! isset($this->channels[$channelId])) {
$channelClass = $this->detectChannelClass($channelId);
$this->channels[$channelId] = new $channelClass($channelId);
}
return $this->channels[$channelId];
}
protected function detectChannelClass($channelId) : string
{
if (starts_with($channelId, 'private-')) {
return PrivateChannel::class;
} elseif(starts_with($channelId, 'presence-')) {
return PresenceChannel::class;
}
return Channel::class;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
use Ratchet\ConnectionInterface;
class PresenceChannel extends Channel
{
protected $subscriptions = [];
public function subscribe(ConnectionInterface $conn, $payload)
{
$channelData = json_decode($payload->channel_data);
$this->subscriptions[$channelData->user_id] = $channelData;
// Send the success event
$conn->send(json_encode([
'event' => 'pusher_internal:subscription_succeeded',
'channel' => $this->channelId,
'data' => json_encode($this->getChannelData())
]));
}
protected function getChannelData()
{
return [
'presence' => [
'ids' => array_keys($this->subscriptions),
'hash' => array_map(function($channelData) { return $channelData->user_info; }, $this->subscriptions),
'count' => count($this->subscriptions)
]
];
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels;
class PrivateChannel extends Channel
{
}

View File

@ -0,0 +1,82 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket;
use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;
use BeyondCode\LaravelWebSockets\WebSocketController;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
class EchoServer extends WebSocketController
{
/** @var ChannelManager */
protected $channelManager;
public function __construct(ChannelManager $channelManager)
{
$this->channelManager = $channelManager;
}
/**
* When a new connection is opened it will be passed to this method
* @param ConnectionInterface $conn The socket/connection that just connected to your application
* @throws \Exception
*/
function onOpen(ConnectionInterface $conn)
{
dump("Client connected");
$socketId = sprintf("%d.%d", getmypid(), random_int(1, 100000000));
// Store the socketId along with the connection so we can retrieve it.
$conn->socketId = $socketId;
$conn->send($this->buildPayload('pusher:connection_established', [
'socket_id' => $socketId,
'activity_timeout' => 60,
]));
}
public function onMessage(ConnectionInterface $conn, MessageInterface $msg)
{
$payload = json_decode($msg->getPayload());
dump("Received payload", $payload);
// todo: validate payload
$event = camel_case(str_replace(':', '_', $payload->event));
if (method_exists($this, $event)) {
call_user_func([$this, $event], $conn, $payload->data);
}
}
/**
* @link https://pusher.com/docs/pusher_protocol#ping-pong
* @param ConnectionInterface $conn
* @param $payload
*/
protected function pusherPing(ConnectionInterface $conn, $payload)
{
$conn->send($this->buildPayload('pusher:pong'));
}
/**
* @link https://pusher.com/docs/pusher_protocol#pusher-subscribe
* @param ConnectionInterface $conn
* @param $payload
*/
protected function pusherSubscribe(ConnectionInterface $conn, $payload)
{
$channel = $this->channelManager->findOrCreate($payload->channel);
$channel->subscribe($conn, $payload);
}
protected function buildPayload($event, $data = [])
{
return json_encode([
'event' => $event,
'data' => json_encode($data)
]);
}
}

View File

@ -3,6 +3,7 @@
namespace BeyondCode\LaravelWebSockets; namespace BeyondCode\LaravelWebSockets;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
class LaravelWebSocketsServiceProvider extends ServiceProvider class LaravelWebSocketsServiceProvider extends ServiceProvider
{ {
@ -24,5 +25,8 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
$this->app->singleton('websockets.router', function() { $this->app->singleton('websockets.router', function() {
return new Router(); return new Router();
}); });
$this->app->singleton(ChannelManager::class, function() {
return new ChannelManager();
});
} }
} }

View File

@ -6,6 +6,7 @@ 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
@ -73,11 +74,12 @@ class Router
*/ */
public function echo() public function echo()
{ {
//$this->get('/', EchoWebsocketServer::class); $this->get('/app/{appId}', LaravelEcho\WebSocket\EchoServer::class);
$this->get('/apps/{appId}/status', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/status', LaravelEcho\Http\Controllers\StatusController::class);
$this->get('/apps/{appId}/channels', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels', LaravelEcho\Http\Controllers\StatusController::class);
$this->get('/apps/{appId}/channels/{channelName}', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels/{channelName}', LaravelEcho\Http\Controllers\StatusController::class);
$this->get('/apps/{appId}/channels/{channelName}/users', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels/{channelName}/users', LaravelEcho\Http\Controllers\StatusController::class);
$this->post('/apps/{appId}/events', LaravelEcho\Http\Controllers\EventController::class);
} }
/** /**

View File

@ -1,6 +1,6 @@
<?php <?php
namespace BeyondCode\LaravelWebsockets\Server; namespace BeyondCode\LaravelWebSockets\Server;
use Ratchet\Http\Router; use Ratchet\Http\Router;
use Ratchet\Http\HttpServer; use Ratchet\Http\HttpServer;

View File

@ -16,7 +16,7 @@ class WebSocketController implements MessageComponentInterface
*/ */
function onOpen(ConnectionInterface $conn) function onOpen(ConnectionInterface $conn)
{ {
// dump("Client connected");
} }
/** /**