I created custom websockethandler and its work fine when APP_DEBUG=true in .env
but when i set APP_DEBUG=false its stop working
class CustomSocketHandler extends WebSocketHandler { }
i think that is bad idea if i have to enable APP_DEBUG=true
In the documentation the custom handler is a separate class, it does not extends WebSocketHandler.
class MyCustomWebSocketHandler implements MessageComponentInterface { }
It works with APP_DEBUG=false.
WebSocketHandler also implements the MessageComponentInterface. My guess is that @ayuningnareswari is extending that class, just to override the onMessage with custom logic, without having to rewrite the other methods (onOpen, onClose and onError).
Changing the code from extends WebSocketHandler to implements MessageComponentInterface (and copying over the implementations of those methods) yields the same result: The custom handler code is simply bypassed entirely when APP_DEBUG is set to false.
I've been trying to pin-point where it goes wrong, but I don't see any exceptions being logged. Turns out that debugging an app with debugging disabled is quite a hassle :-)
Changing the code from
extends WebSocketHandlertoimplements MessageComponentInterfaceyields the same result: The custom handler code is simply bypassed entirely whenAPP_DEBUGis set tofalse.
This happens because of how the WebsocketsLogger singleton is applied.
I'm using the code from here as a workaround.
Yeah, I came across that one, and I think I tried that already, and it did not make any difference. But I've had a good night's sleep, so I'm gonna try again this morning with fresh eyes. I'll let you know.
Applying that workaround didn't yield any difference for me. Since the code was doing an array search of the action in the customRoutes, I figured: why not dump this stuff in the log and see what happens.
protected function createWebSocketsServer(string $action): WsServer
{
$app = app($action);
\Log::info("action: $action");
\Log::info("custom routes: ");
\Log::info($this->customRoutes->all());
\Log::info("websockets logger is enabled: ".WebsocketsLogger::isEnabled());
if (WebsocketsLogger::isEnabled()) {
$app = WebsocketsLogger::decorate($app);
}
return new WsServer($app);
}
With APP_DEBUG=true, this yields:
[2020-03-03 09:02:37] production.INFO: action: BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
[2020-03-03 09:02:37] production.INFO: custom routes:
[2020-03-03 09:02:37] production.INFO: array (
'/ws' => 'App\\WebSocket\\WebSocketHandler',
)
[2020-03-03 09:02:37] production.INFO: websockets logger is enabled: 1
[2020-03-03 09:02:37] production.INFO: action: App\WebSocket\WebSocketHandler
[2020-03-03 09:02:37] production.INFO: custom routes:
[2020-03-03 09:02:37] production.INFO: array (
'/ws' => 'App\\WebSocket\\WebSocketHandler',
)
[2020-03-03 09:02:37] production.INFO: websockets logger is enabled: 1
With APP_DEBUG=false, this yields:
[2020-03-03 09:03:19] production.INFO: action: BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
[2020-03-03 09:03:19] production.INFO: custom routes:
[2020-03-03 09:03:19] production.INFO: array (
'/ws' => 'App\\WebSocket\\WebSocketHandler',
)
[2020-03-03 09:03:19] production.INFO: websockets logger is enabled:
[2020-03-03 09:03:19] production.INFO: action: App\WebSocket\WebSocketHandler
[2020-03-03 09:03:19] production.INFO: custom routes:
[2020-03-03 09:03:19] production.INFO: array (
'/ws' => 'App\\WebSocket\\WebSocketHandler',
)
[2020-03-03 09:03:19] production.INFO: websockets logger is enabled:
So in order for my custom handler to work, I need to basically do this:
protected function createWebSocketsServer(string $action): WsServer
{
$app = app($action);
$app = WebsocketsLogger::decorate($app);
return new WsServer($app);
}
If I dig a bit further, I found that the $app that is resolved based on the $action is something different than the $app which is decorated by means of the WebsocketsLogger.
$app = app($action):
BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler {#2933
#channelManager: BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager {#2935
#appId: null
#channels: []
}
}
$app = WebsocketsLogger::decorate($app):
BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger {#2937
#app: BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler {#2933
#channelManager: BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager {#2935
#appId: null
#channels: []
}
}
[...]
My custom route:
$app = app($action):
App\WebSocket\WebSocketHandler {#2963
#channelManager: BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager {#2932
#appId: null
#channels: []
}
}
$app = WebsocketsLogger::decorate($app):
BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger {#2936
#app: App\WebSocket\WebSocketHandler {#2963
#channelManager: BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager {#2932
#appId: null
#channels: []
}
}
[...]
In my case, whenever the $app resolved from the $action is passed to Ratchet's WsServer, stuff stops working. If that WebsocketLogger decorated version is passed, everything works peachy.
As far as I can tell, the only thing that WebsocketLogger does is access some stuff in order to get some data to output to the console, but ultimately they defer to the methods on the #app.
Really frustrating to debug like this.
A colleague of mine ultimately resorted in creating a CustomRouter that overrides echo() to use our own WebSocketHandler::class for the default GET /app/{appKey} route. This also required him to create a separate Console Command to start our custom websocket server.
I thought that was bollocks, so I finally settled for:
WebSocketsRouter::webSocket('/app/{appKey}', \App\WebSocket\WebSocketHandler::class);
Simply overwriting the default app route to force it through my own handler.
My Custom Handler still checks if it's pusher:protocol messages or our own events that need our own logic, so basically we still have Pusher compatibility as well as custom logic. Which I believe was another question #309
@axit-joost Thanks for your update
Workaround by @matheusb-comp works (at least in my case). I needed to add a route to / for HAProxy healthchecks.
class Router extends \BeyondCode\LaravelWebSockets\Server\Router
{
/**
* @param string $action
* @return WsServer
*/
protected function createWebSocketsServer(string $action): WsServer
{
$app = app($action);
if (WebsocketsLogger::isEnabled() && !$this->isCustomAction($action)) {
$app = WebsocketsLogger::decorate($app);
}
return new WsServer($app);
}
/**
* @param string $action
* @return bool
*/
private function isCustomAction(string $action): bool
{
return false !== array_search($action, $this->customRoutes->all());
}
}
AppServiceProvider):$this->app->singleton('websockets.router', function () {
return new Router();
});
WebSocketsRouter::webSocket('/', IndexHandler::class);
Most helpful comment
I thought that was bollocks, so I finally settled for:
Simply overwriting the default app route to force it through my own handler.