Get the PID of a Phoenix.Socket.Transport process that was started by endpoint.ex?

Though this question has been asked before, there is no answer there so here goes again.

I have implemented a nice little ping websocket to talk to Python:

defmodule Exwstest.EchoSocket do
  @behaviour Phoenix.Socket.Transport

  def child_spec(opts) do
    # We won't spawn any process, so let's return a dummy task
    %{id: Task, start: {Task, :start_link, [fn -> :ok end]}, restart: :transient}
  end

  def connect(%{params: %{"token" => token}}) do
    # Callback to retrieve relevant data from the connection.
    # The map contains options, params, transport and endpoint keys.
    state = %{number: 1}
    {:ok, state}
  end

  def init(state) do
    # Now we are effectively inside the process that maintains the socket.
    send(self(), :sendback)
    {:ok, state}
  end

  def handle_in({text, _opts}, state) do
    {:reply, :ok, {:text, "#{text} to you too"}, state}
  end

  def handle_info(:sendback, %{number: number} = state) do
    # here look at the push thing in the website
    Process.send_after(self(), :sendback, 1000)
    {:push, {:text, Integer.to_string(number)}, %{state | number: number + 1}}
  end

  def handle_info(_, state) do
    {:ok, state}
  end

  def terminate(_reason, _state) do
    :ok
  end
end

However I want to send messages to it from inside Elixir, and I have no clue how to obtain its pid because it’s started by my endpoint.ex:

 socket "/echo/:token", Exwstest.EchoSocket

I suppose I could hack around and get init to send a message to an external process with the pid, but is that the best way?

(for interest here’s the toy python client)

from websockets.sync.client import connect

def hello():
    with connect("ws://localhost:4000/echo/token123/websocket") as websocket:
        websocket.send("Hello Thomas")
        while True:
            message = websocket.recv()
            print(f"Received: {message}")
            # must send ping at least every 60 seconds
            if "ping" not in message:
                print("Sending ping")
                websocket.send("ping!")
            if message == "Bye!":
                break
        print("Connection closed.")

hello()

You could use a Registry to register the process in init, so that the code that wants to message the transport can find it.

It’s ultimately not much more than sending a PID to a centralized place, but it takes care of a lot of cleanup etc.

1 Like