Hi,
I’m trying to implement a raw websocket endpoint to push data to some clients which may use any language (JavaScript, Java…). For that purpose I used Phoenix.Socket.Transport behaviour :
defmodule MyApp.WebsocketEndpoint do
@behaviour Phoenix.Socket.Transport
require Logger
def child_spec(_opts) do
# We won't spawn any process, so let's return a dummy task
%{id: __MODULE__, start: {Task, :start_link, [fn -> :ok end]}, restart: :transient}
end
def connect(%{params: params} = _state) do
Logger.info("Websocket connection established")
{:ok, params}
end
# Now we are effectively inside the process that maintains the socket.
def init(%{"queryName"=> queryName} = params) do
Logger.info("Websocket init with query_name : #{queryName} and params : #{inspect(params)}")
try do
validate_param_names(params, ControlsException)
state =
with {:ok, params} <- Tarams.cast(params, params_schema) do
{:ok, query} = Queries.create_query(params, queryName)
Queries.schedule_job(query, {:websocket, self()})
query
else
{:error, errors} ->
send(self(), {:error, errors.message})
end
{:ok, state}
rescue
e in ControlsException ->
send(self(), {:error, e.message})
end
end
def handle_info({:send_events, message}, state) do
message = Jason.encode!(message)
{:push, {:text, message}, state}
end
def handle_info({:error, reason}, state) do
{:stop, reason, state}
end
def handle_in({text, _opts}, state) do
{:reply, :ok, {:text, text}, state}
end
def terminate(reason, state) do
Logger.warn("Websocket connection terminated with reason : #{inspect(reason)}")
:ok
end
end
On the endpoint side :
socket "/ws/query/:queryName", MyApp.WebsocketEndpoint, websocket: [path: "", timeout: :infinity]
This work well with a test client (JavaScript) and I’m able to push messages correctly :
var WebSocketClient = require('websocket').client;
var client = new WebSocketClient();
client.on('connectFailed', function(error) {
console.log('Connect Error: ' + error.toString());
});
client.on('connect', function(connection) {
console.log('WebSocket Client Connected');
connection.on('error', function(error) {
console.log("Connection Error: " + error.toString());
});
connection.on('close', function() {
console.log('echo-protocol Connection Closed');
});
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log(JSON.stringify(JSON.parse(message.utf8Data), null, 2))
}
});
});
client.connect('ws://localhost:5000/ws/query/:MyQuery?send=true&&initialTime=2022-01-19T08%3A15%3A30.285639Z');
However, when errors occur (like on validation errors), the connection is immediatly closed and the terminate callback is called. The desired behavior would be to send the error reason to the client ( to console.log('Connect Error: ’ + error.toString())) and eventually close the connection after. I tried to send to self() an error message and implement an approriate handle_info function with the ‘:stop’ reply but it doesn’t work. Otherwise, I was wondering whether the timeout set to ‘:infinity’ is a good practice or not because without it I have timeouts with closed connection. Thanks for your help.