defmodule Dexcord.FakeGateway.Plug do @moduledoc false # Upgrades every request to a websocket handled by `Dexcord.FakeGateway.Handler`, # threading the controlling `Dexcord.FakeGateway` server pid into the handler. @behaviour Plug @impl true def init(opts), do: opts @impl true def call(conn, opts) do server = Keyword.fetch!(opts, :server) conn |> WebSockAdapter.upgrade(Dexcord.FakeGateway.Handler, %{server: server}, timeout: 60_000) |> Plug.Conn.halt() end end defmodule Dexcord.FakeGateway.Handler do @moduledoc false # Thin WebSock relay. Runs in the Bandit/ThousandIsland connection process, so it # *is* the connection: killing it drops the raw TCP socket. All logic lives in the # `Dexcord.FakeGateway` server; this module only forwards frames and pushes what # the server tells it to. @behaviour WebSock @impl true def init(%{server: server}) do state = %{server: server} case Dexcord.FakeGateway.register(server, self()) do {_conn_id, {:push, msg}} -> {:push, msg, state} {_conn_id, {:close, code}} -> {:stop, :normal, code, state} {_conn_id, :none} -> {:ok, state} end end @impl true def handle_in({data, opcode: :text}, %{server: server} = state) do send(server, {:client_frame, self(), data}) {:ok, state} end def handle_in({_data, opcode: :binary}, state), do: {:ok, state} @impl true def handle_info({:push, {:text, _} = msg}, state), do: {:push, msg, state} def handle_info({:close, {code, reason}}, state), do: {:stop, :normal, {code, reason}, state} def handle_info({:close, code}, state), do: {:stop, :normal, code, state} def handle_info(_msg, state), do: {:ok, state} end