Proper way to close channel from the server side?

In my application, clients establish a websocket connection to a phoenix channel. After some actions, the server needs to close the connection

Right now in order to accomplish this I do in my service module:

MyApp.Endpoint.broadcast("topic", "end_session", %{})

In channel:

  def handle_info("end_session", _params, socket) do
    Logger.debug("Ending session...")
    push socket, "end_session", %{}
    {:stop, :normal, socket}
  end

  def terminate(reason, _socket) do
    Logger.debug"Terminating socket... #{inspect reason}"
    :ok
  end

and in the socket.js:

channel.on("end_session", payload => {
  console.log("terminating session")
  channel.leave()
})
channel.onClose( () => console.log("channel has been closed") )

I’m wondering if this is the proper way to gracefully terminate the channel from the server side?
Thanks!

4 Likes

There is a disconnect event described in user_socket.ex.

  # Socket id's are topics that allow you to identify all sockets for a given user:
  #
  #     def id(socket), do: "user_socket:#{socket.assigns.user_id}"
  #
  # Would allow you to broadcast a "disconnect" event and terminate
  # all active sockets and channels for a given user:
  #
  #     BlahWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
  #
  # Returning `nil` makes this socket anonymous.

Never used it, but it might be what You are looking for.

1 Like

I tried that approach (broadcasting the disconnect event) and it works from the server side of things.

The problem is that the front-end can’t tell whether it should try to reconnect or not, because the wasClean attribute of the close event is sometimes false and sometimes true. I was expecting it to be always true in that case. Am I wrong?

I’m on Phoenix 1.7.9

What I usually do is send a “kicked” message to the front end before closing the channel. That way the front end can stop reconnecting or often redirect to the login page.

2 Likes