Current section
Files
Jump to
Current section
Files
lib/websocket.ex
defmodule Dialup.WebSocket do
@moduledoc false
@behaviour WebSock
# WebSocket接続確立時、既存セッションを引き継ぐか新規プロセスを起動する
# tab_id (sessionStorage) をregistry keyとして使うことで、
# 同一ブラウザの複数タブが互いのセッションプロセスを上書きしない
@impl WebSock
def init(%{app_module: app_module, session_id: session_id, tab_id: tab_id}) do
registry_key = tab_id || session_id
session_pid =
case Registry.lookup(Dialup.SessionRegistry, registry_key) do
[{pid, _}] ->
# session_id が一致する場合のみ引き継ぐ(tab_id が推測された場合の乗っ取りを防ぐ)
if Dialup.UserSessionProcess.session_id(pid) == session_id do
Dialup.UserSessionProcess.take_over(pid, self())
pid
else
{:ok, new_pid} =
DynamicSupervisor.start_child(
Dialup.SessionSupervisor,
{Dialup.UserSessionProcess, {self(), app_module, session_id, registry_key}}
)
new_pid
end
[] ->
# プロセスが存在しない(初回 or タイムアウト済み)→ 新規起動
{:ok, pid} =
DynamicSupervisor.start_child(
Dialup.SessionSupervisor,
{Dialup.UserSessionProcess, {self(), app_module, session_id, registry_key}}
)
pid
end
Dialup.Telemetry.websocket_connect(%{session_id: session_id})
{:ok, %{session_pid: session_pid, session_id: session_id}}
end
# クライアントからのメッセージを受け取りGenServerに委譲
@impl WebSock
def handle_in({text, [opcode: :text]}, state) do
case Jason.decode(text) do
{:ok, %{"event" => "__init", "value" => path}} ->
Dialup.UserSessionProcess.init_session(state.session_pid, path)
{:ok, state}
{:ok, %{"event" => "__reconnect", "value" => path}} ->
Dialup.UserSessionProcess.reconnect(state.session_pid, path)
{:ok, state}
{:ok, %{"event" => "__navigate", "value" => path}} ->
Dialup.UserSessionProcess.navigate(state.session_pid, path)
{:ok, state}
{:ok, %{"event" => event, "value" => value}} ->
Dialup.UserSessionProcess.event(state.session_pid, event, value)
{:ok, state}
{:error, _} ->
{:ok, state}
end
end
# GenServerからのHTMLをWebSocket経由でブラウザへ転送
@impl WebSock
def handle_info({:send_html, payload}, state) do
{:reply, :ok, {:text, payload}, state}
end
def handle_info(_msg, state), do: {:ok, state}
@impl WebSock
def terminate(_reason, state) do
Dialup.Telemetry.websocket_disconnect(%{session_id: state[:session_id]})
:ok
end
end