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