diff --git a/src/Pusher/Http/Controllers/ChannelsController.php b/src/Pusher/Http/Controllers/ChannelsController.php index f2c04ea3..04c5b3d1 100644 --- a/src/Pusher/Http/Controllers/ChannelsController.php +++ b/src/Pusher/Http/Controllers/ChannelsController.php @@ -2,6 +2,7 @@ namespace Laravel\Reverb\Pusher\Http\Controllers; +use Illuminate\Support\Str; use Laravel\Reverb\Http\Connection; use Psr\Http\Message\RequestInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -14,7 +15,16 @@ class ChannelsController extends Controller */ public function handle(RequestInterface $request, Connection $connection, ...$args): Response { - $channels = $this->channels->channels()->mapWithKeys(fn ($connections, $name) => [$name => ['user_count' => count($connections)]]); + $channels = $this->channels->channels(); + $info = explode(',', $this->query['info'] ?? ''); + + if (isset($this->query['filter_by_prefix'])) { + $channels = $channels->filter(fn ($connections, $name) => Str::startsWith($name, $this->query['filter_by_prefix'])); + } + + $channels = $channels->mapWithKeys(function ($connections, $name) use ($info) { + return [$name => array_filter(['user_count' => in_array('user_count', $info) ? count($connections) : null])]; + }); return new JsonResponse((object) ['channels' => $channels]); } diff --git a/src/Pusher/Http/Controllers/Controller.php b/src/Pusher/Http/Controllers/Controller.php index a19fc5a7..a2b5b62a 100644 --- a/src/Pusher/Http/Controllers/Controller.php +++ b/src/Pusher/Http/Controllers/Controller.php @@ -26,9 +26,13 @@ abstract class Controller protected $body; + protected $query; + public function __invoke(RequestInterface $request, Connection $connection, ...$args) { + parse_str($request->getUri()->getQuery(), $query); $this->body = $request->getBody()->getContents(); + $this->query = $query; try { $this->setApplication($args['appId'] ?? null); @@ -91,9 +95,7 @@ protected function setChannels() */ protected function verifySignature(RequestInterface $request): void { - parse_str($request->getUri()->getQuery(), $queryParams); - - $params = Arr::except($queryParams, [ + $params = Arr::except($this->query, [ 'auth_signature', 'body_md5', 'appId', 'appKey', 'channelName', ]); @@ -111,8 +113,8 @@ protected function verifySignature(RequestInterface $request): void $signature = hash_hmac('sha256', $signature, $this->application->secret()); - if ($signature !== $queryParams['auth_signature']) { - // throw new HttpException(401, 'Authentication signature invalid.'); + if ($signature !== $this->query['auth_signature']) { + throw new HttpException(401, 'Authentication signature invalid.'); } } diff --git a/tests/Feature/Ratchet/ChannelsControllerTest.php b/tests/Feature/Ratchet/ChannelsControllerTest.php new file mode 100644 index 00000000..1c1d3662 --- /dev/null +++ b/tests/Feature/Ratchet/ChannelsControllerTest.php @@ -0,0 +1,17 @@ +subscribe('test-channel-one'); + $this->subscribe('test-channel-two'); + + $response = await($this->getWithSignature('channels?info=user_count')); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('{"channels":{"test-channel-one":{"user_count":1},"test-channel-two":{"user_count":1}}}', $response->getBody()->getContents()); +}); diff --git a/tests/RatchetTestCase.php b/tests/RatchetTestCase.php index 210e098c..4e450707 100644 --- a/tests/RatchetTestCase.php +++ b/tests/RatchetTestCase.php @@ -274,6 +274,17 @@ public function triggerEvent(string $channel, string $event, array $data = []): $this->assertSame('{}', $response->getBody()->getContents()); } + public function request(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface + { + return (new Browser($this->loop)) + ->request( + $method, + "http://{$host}:{$port}/apps/{$appId}/{$path}", + [], + ($data) ? json_encode($data) : '' + ); + } + /** * Post a request to the server. */ @@ -284,12 +295,7 @@ public function postToServer( string $port = '8080', string $appId = '123456' ): PromiseInterface { - return (new Browser($this->loop)) - ->post( - "http://{$host}:{$port}/apps/{$appId}/{$path}", - [], - json_encode($data) - ); + return $this->request($path, 'POST', $data, $host, $port, $appId); } /** @@ -310,4 +316,22 @@ public function postToServerWithSignature( return $this->postToServer("{$path}?{$query}&auth_signature={$signature}", $data, $host, $port, $appId); } + + public function getWithSignature( + string $path, + array $data = [], + string $host = '0.0.0.0', + string $port = '8080', + string $appId = '123456' + ): PromiseInterface { + $hash = md5(json_encode($data)); + $timestamp = time(); + $query = "auth_key=pusher-key&auth_timestamp={$timestamp}&auth_version=1.0&body_md5={$hash}"; + $string = "POST\n/apps/{$appId}/{$path}\n$query"; + $signature = hash_hmac('sha256', $string, 'pusher-secret'); + + $path = Str::contains($path, '?') ? "{$path}&{$query}" : "{$path}?{$query}"; + + return $this->request("{$path}&auth_signature={$signature}", 'GET', '', $host, $port, $appId); + } }