Current section

Files

Jump to
wampex_router lib router transports web_socket.ex
Raw

lib/router/transports/web_socket.ex

defmodule Wampex.Router.Transports.WebSocket do
@moduledoc false
@behaviour :cowboy_websocket
require Logger
alias __MODULE__
alias Wampex.Router
alias Wampex.Router.Realms
alias Wampex.Serializers.{JSON, MessagePack}
@protocol_header "sec-websocket-protocol"
@json "wamp.2.json"
@msgpack "wamp.2.msgpack"
defstruct [:serializer, :session, :last_message]
def send_request(transport, message) do
send(transport, {:send_request, message})
end
@impl true
def init(req, %{db: db, name: name, authentication_module: atm, authorization_module: azm}) do
{:ok, serializer, protocol} = get_serializer(req)
req = :cowboy_req.set_resp_header(@protocol_header, protocol, req)
{:cowboy_websocket, req, {%WebSocket{serializer: serializer}, db, name, atm, azm}}
end
@impl true
def websocket_init({state, db, name, atm, azm}) do
{:ok, session} = Realms.start_session(Router.realms_name(name), db, name, WebSocket, self(), atm, azm)
Process.link(session)
Logger.debug("#{__MODULE__} Websocket Initialized: #{inspect(state)}")
{:ok, %WebSocket{state | session: session}}
end
@impl true
def websocket_handle({type, payload}, state) do
Logger.debug("Received #{type} Payload: #{payload}")
handle_payload({payload, state})
end
@impl true
def websocket_handle(:ping, state) do
{[:pong], state}
end
@impl true
def websocket_handle(unknown, state) do
Logger.warn("#{__MODULE__} Unknown websocket frame #{inspect(unknown)}")
{:ok, state}
end
@impl true
def websocket_info({:send_request, message}, %WebSocket{serializer: s} = state) do
{[{s.data_type(), s.serialize!(message)}], state}
end
@impl true
def websocket_info(
{:EXIT, _pid = session_pid, reason},
%WebSocket{session: session_pid, last_message: lm} = state
) do
Logger.warn(
"#{__MODULE__} Session ended because #{inspect(reason)}, closing connection. Last message #{inspect(lm)}"
)
{:stop, state}
end
@impl true
def websocket_info(event, state) do
Logger.debug("#{__MODULE__} Received unhandled event: #{inspect(event)}")
{:ok, state}
end
@impl true
def terminate(reason, _req, _state) do
Logger.debug("#{__MODULE__} Websocket closing for reason #{inspect(reason)}")
:ok
end
defp handle_payload({payload, %WebSocket{serializer: s} = state}) do
payload
|> s.deserialize!()
|> handle_event(state)
end
defp handle_event(ms, %WebSocket{session: s} = state) do
send(s, {:set_message, ms})
{:ok, %WebSocket{state | last_message: ms}}
end
defp get_serializer(req) do
@protocol_header
|> :cowboy_req.parse_header(req)
|> parse_protocol()
end
defp parse_protocol([]), do: Logger.error("Unknown protocol")
defp parse_protocol([@json | _]), do: {:ok, JSON, @json}
defp parse_protocol([@msgpack | _]), do: {:ok, MessagePack, @msgpack}
defp parse_protocol([_ | t]), do: parse_protocol(t)
end