- fix heartbeat timer loss in :hello_wait (re-arm during identify-gap wait) - honor server op 1 in :identifying/:hello_wait, not just :connected/:resuming - full op 9 handling: d:false in :resuming re-IDENTIFYs on the same socket after the mandated 1-5s; d:false elsewhere floors the reconnect; d:true keeps session - resume-loop cap (5 consecutive failed resumes -> fresh IDENTIFY) via Session ETS - explicit conn_gen/stale-socket guard backstopping Mint's :unknown - bounded TCP connect + configurable handshake watchdogs so a stalled reconnect self-heals instead of wedging - FakeGateway scripted Bandit/WebSock test server + TestHandler - integration suite (happy/zombie/kill/close-codes/op7/op9/replay/cap/backoff/gap/ stale), fatal-path end-to-end, and unit tests for resume counter + backoff math
54 lines
1.7 KiB
Elixir
54 lines
1.7 KiB
Elixir
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
|