Skip to content

Commit 4687d96

Browse files
authored
add support for server path prefix (#311)
1 parent 22f9c39 commit 4687d96

File tree

5 files changed

+47
-17
lines changed

5 files changed

+47
-17
lines changed

config/reverb.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
'reverb' => [
3232
'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
3333
'port' => env('REVERB_SERVER_PORT', 8080),
34+
'path' => env('REVERB_SERVER_PATH', ''),
3435
'hostname' => env('REVERB_HOST'),
3536
'options' => [
3637
'tls' => [],

src/Servers/Reverb/Console/Commands/StartServer.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class StartServer extends Command implements SignalableCommandInterface
3131
protected $signature = 'reverb:start
3232
{--host= : The IP address the server should bind to}
3333
{--port= : The port the server should listen on}
34+
{--path= : The path the server should prefix to all routes}
3435
{--hostname= : The hostname the server is accessible from}
3536
{--debug : Indicates whether debug messages should be displayed in the terminal}';
3637

@@ -57,6 +58,7 @@ public function handle(): void
5758
$server = ServerFactory::make(
5859
$host = $this->option('host') ?: $config['host'],
5960
$port = $this->option('port') ?: $config['port'],
61+
$path = $this->option('path') ?: $config['path'] ?? '',
6062
$hostname = $this->option('hostname') ?: $config['hostname'],
6163
$config['max_request_size'] ?? 10_000,
6264
$config['options'] ?? [],
@@ -69,7 +71,7 @@ public function handle(): void
6971
$this->ensurePulseEventsAreCollected($loop, $config['pulse_ingest_interval']);
7072
$this->ensureTelescopeEntriesAreCollected($loop, $config['telescope_ingest_interval'] ?? 15);
7173

72-
$this->components->info('Starting '.($server->isSecure() ? 'secure ' : '')."server on {$host}:{$port}".(($hostname && $hostname !== $host) ? " ({$hostname})" : ''));
74+
$this->components->info('Starting '.($server->isSecure() ? 'secure ' : '')."server on {$host}:{$port}{$path}".(($hostname && $hostname !== $host) ? " ({$hostname})" : ''));
7375

7476
$server->start();
7577
}

src/Servers/Reverb/Factory.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Factory
3939
public static function make(
4040
string $host = '0.0.0.0',
4141
string $port = '8080',
42+
string $path = '',
4243
?string $hostname = null,
4344
int $maxRequestSize = 10_000,
4445
array $options = [],
@@ -48,7 +49,7 @@ public static function make(
4849
$loop = $loop ?: Loop::get();
4950

5051
$router = match ($protocol) {
51-
'pusher' => static::makePusherRouter(),
52+
'pusher' => static::makePusherRouter($path),
5253
default => throw new InvalidArgumentException("Unsupported protocol [{$protocol}]."),
5354
};
5455

@@ -67,7 +68,7 @@ public static function make(
6768
/**
6869
* Create a new WebSocket server for the Pusher protocol.
6970
*/
70-
public static function makePusherRouter(): Router
71+
public static function makePusherRouter(string $path): Router
7172
{
7273
app()->singleton(
7374
ChannelManager::class,
@@ -84,13 +85,13 @@ public static function makePusherRouter(): Router
8485
fn () => new PusherPubSubIncomingMessageHandler,
8586
);
8687

87-
return new Router(new UrlMatcher(static::pusherRoutes(), new RequestContext));
88+
return new Router(new UrlMatcher(static::pusherRoutes($path), new RequestContext));
8889
}
8990

9091
/**
9192
* Generate the routes required to handle Pusher requests.
9293
*/
93-
protected static function pusherRoutes(): RouteCollection
94+
protected static function pusherRoutes(string $path): RouteCollection
9495
{
9596
$routes = new RouteCollection;
9697

@@ -104,6 +105,8 @@ protected static function pusherRoutes(): RouteCollection
104105
$routes->add('users_terminate', Route::post('/apps/{appId}/users/{userId}/terminate_connections', new UsersTerminateController));
105106
$routes->add('health_check', Route::get('/up', new HealthCheckController));
106107

108+
$routes->addPrefix($path);
109+
107110
return $routes;
108111
}
109112

tests/Feature/Protocols/Pusher/Reverb/ServerTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Laravel\Reverb\Jobs\PruneStaleConnections;
66
use Laravel\Reverb\Tests\ReverbTestCase;
77
use Ratchet\RFC6455\Messaging\Frame;
8+
use React\Http\Message\ResponseException;
89
use React\Promise\Deferred;
910

1011
use function Ratchet\Client\connect as wsConnect;
@@ -265,7 +266,7 @@
265266
expect(channels()->all())->toHaveCount(4);
266267
collect(channels()->all())->each(function ($channel) use ($connection) {
267268
expect($channel->connections())->toHaveCount(1);
268-
expect(collect($channel->connections())->map(fn ($conn) => $conn->id()))->toContain($connection->socketId());
269+
expect(collect($channel->connections())->map(fn($conn) => $conn->id()))->toContain($connection->socketId());
269270
});
270271
});
271272

@@ -472,6 +473,29 @@
472473
expect($response->getBody()->getContents())->toBe('{}');
473474
});
474475

476+
it('server listens on a specific path', function () {
477+
$this->stopServer();
478+
$this->startServer(path: 'ws');
479+
$response = await($this->signedPostRequest('events', [
480+
'name' => 'NewEvent',
481+
'channel' => 'test-channel',
482+
'data' => json_encode(['data' => 'data']),
483+
], pathPrefix: '/ws', appId: '654321', key: 'reverb-key-2', secret: 'reverb-secret-2'));
484+
485+
expect($response->getStatusCode())->toBe(200);
486+
expect($response->getBody()->getContents())->toBe('{}');
487+
});
488+
489+
it('returns an error when the path prefix is not included in the request', function () {
490+
$this->stopServer();
491+
$this->startServer(path: 'ws');
492+
await($this->signedPostRequest('events', [
493+
'name' => 'NewEvent',
494+
'channel' => 'test-channel',
495+
'data' => json_encode(['data' => 'data']),
496+
], appId: '654321', key: 'reverb-key-2', secret: 'reverb-secret-2'));
497+
})->throws(ResponseException::class, exceptionCode: 404);
498+
475499
it('subscription_succeeded event contains unique list of users', function () {
476500
$data = ['user_id' => 1, 'user_info' => ['name' => 'Test User']];
477501
subscribe('presence-test-channel', data: $data);

tests/ReverbTestCase.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ public function usingRedis(): void
8383
/**
8484
* Start the WebSocket server.
8585
*/
86-
public function startServer(string $host = '0.0.0.0', string $port = '8080', int $maxRequestSize = 10_000): void
86+
public function startServer(string $host = '0.0.0.0', string $port = '8080', string $path = '', int $maxRequestSize = 10_000): void
8787
{
8888
$this->resetFiber();
89-
$this->server = Factory::make($host, $port, maxRequestSize: $maxRequestSize, loop: $this->loop);
89+
$this->server = Factory::make($host, $port, $path, maxRequestSize: $maxRequestSize, loop: $this->loop);
9090
}
9191

9292
/**
@@ -132,12 +132,12 @@ public function triggerEvent(string $channel, string $event, array $data = []):
132132
/**
133133
* Send a request to the server.
134134
*/
135-
public function request(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface
135+
public function request(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $pathPrefix = '', string $appId = '123456'): PromiseInterface
136136
{
137137
return (new Browser($this->loop))
138138
->request(
139139
$method,
140-
"http://{$host}:{$port}/apps/{$appId}/{$path}",
140+
"http://{$host}:{$port}{$pathPrefix}/apps/{$appId}/{$path}",
141141
[],
142142
($data) ? json_encode($data) : ''
143143
);
@@ -160,7 +160,7 @@ public function requestWithoutAppId(string $path, string $method = 'GET', mixed
160160
/**
161161
* Send a signed request to the server.
162162
*/
163-
public function signedRequest(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $appId = '123456', string $key = 'reverb-key', string $secret = 'reverb-secret'): PromiseInterface
163+
public function signedRequest(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $pathPrefix = '', string $appId = '123456', string $key = 'reverb-key', string $secret = 'reverb-secret'): PromiseInterface
164164
{
165165
$timestamp = time();
166166

@@ -179,25 +179,25 @@ public function signedRequest(string $path, string $method = 'GET', mixed $data
179179
$query .= "&body_md5={$hash}";
180180
}
181181

182-
$string = "{$method}\n/apps/{$appId}/{$path}\n$query";
182+
$string = "{$method}\n{$pathPrefix}/apps/{$appId}/{$path}\n$query";
183183
$signature = hash_hmac('sha256', $string, $secret);
184184

185-
return $this->request("{$path}?{$query}&auth_signature={$signature}", $method, $data, $host, $port, $appId);
185+
return $this->request("{$path}?{$query}&auth_signature={$signature}", $method, $data, $host, $port, $pathPrefix, $appId);
186186
}
187187

188188
/**
189189
* Send a POST request to the server.
190190
*/
191-
public function postRequest(string $path, ?array $data = [], string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface
191+
public function postRequest(string $path, ?array $data = [], string $host = '0.0.0.0', string $port = '8080', string $pathPrefix = '', string $appId = '123456'): PromiseInterface
192192
{
193-
return $this->request($path, 'POST', $data, $host, $port, $appId);
193+
return $this->request($path, 'POST', $data, $host, $port, $pathPrefix, $appId);
194194
}
195195

196196
/**
197197
* Send a signed POST request to the server.
198198
*/
199-
public function signedPostRequest(string $path, ?array $data = [], string $host = '0.0.0.0', string $port = '8080', string $appId = '123456', $key = 'reverb-key', $secret = 'reverb-secret'): PromiseInterface
199+
public function signedPostRequest(string $path, ?array $data = [], string $host = '0.0.0.0', string $port = '8080', string $pathPrefix = '', string $appId = '123456', $key = 'reverb-key', $secret = 'reverb-secret'): PromiseInterface
200200
{
201-
return $this->signedRequest($path, 'POST', $data, $host, $port, $appId, $key, $secret);
201+
return $this->signedRequest($path, 'POST', $data, $host, $port, $pathPrefix, $appId, $key, $secret);
202202
}
203203
}

0 commit comments

Comments
 (0)