Current section

Files

Jump to
bandit lib bandit websocket handler.ex
Raw

lib/bandit/websocket/handler.ex

defmodule Bandit.WebSocket.Handler do
@moduledoc false
# A WebSocket handler conforming to RFC6455, structured as a ThousandIsland.Handler
use ThousandIsland.Handler
alias Bandit.Extractor
alias Bandit.WebSocket.{Connection, Frame}
@impl ThousandIsland.Handler
def handle_connection(socket, state) do
{websock, websock_opts, connection_opts} = state.upgrade_opts
connection_opts
|> Keyword.take([:fullsweep_after, :max_heap_size])
|> Enum.each(fn {key, value} -> :erlang.process_flag(key, value) end)
connection_opts = Keyword.merge(state.opts.websocket, connection_opts)
primitive_ops_module =
Keyword.get(state.opts.websocket, :primitive_ops_module, Bandit.PrimitiveOps.WebSocket)
state =
state
|> Map.take([:handler_module])
|> Map.put(:extractor, Extractor.new(Frame, primitive_ops_module, connection_opts))
case Connection.init(websock, websock_opts, connection_opts, socket) do
{:continue, connection} ->
case Keyword.get(connection_opts, :timeout) do
nil -> {:continue, Map.put(state, :connection, connection)}
timeout -> {:continue, Map.put(state, :connection, connection), {:persistent, timeout}}
end
{:error, reason, connection} ->
{:error, reason, Map.put(state, :connection, connection)}
end
end
@impl ThousandIsland.Handler
def handle_data(data, socket, state) do
state.extractor
|> Extractor.push_data(data)
|> pop_frame(socket, state)
end
defp pop_frame(extractor, socket, state) do
case Extractor.pop_frame(extractor) do
{extractor, {:ok, frame}} ->
case Connection.handle_frame(frame, socket, state.connection) do
{:continue, connection} ->
pop_frame(extractor, socket, %{state | extractor: extractor, connection: connection})
{:close, connection} ->
{:close, %{state | extractor: extractor, connection: connection}}
{:error, reason, connection} ->
{:error, reason, %{state | extractor: extractor, connection: connection}}
end
{extractor, {:error, reason}} ->
{:error, {:deserializing, reason}, %{state | extractor: extractor}}
{extractor, :more} ->
{:continue, %{state | extractor: extractor}}
end
end
@impl ThousandIsland.Handler
def handle_close(socket, %{connection: connection}),
do: Connection.handle_close(socket, connection)
def handle_close(_socket, _state), do: :ok
@impl ThousandIsland.Handler
def handle_shutdown(socket, state), do: Connection.handle_shutdown(socket, state.connection)
@impl ThousandIsland.Handler
def handle_error(reason, socket, state),
do: Connection.handle_error(reason, socket, state.connection)
@impl ThousandIsland.Handler
def handle_timeout(socket, state), do: Connection.handle_timeout(socket, state.connection)
def handle_info({:plug_conn, :sent}, {socket, state}),
do: {:noreply, {socket, state}, socket.read_timeout}
def handle_info(msg, {socket, state}) do
case Connection.handle_info(msg, socket, state.connection) do
{:continue, connection_state} ->
{:noreply, {socket, %{state | connection: connection_state}}, socket.read_timeout}
{:error, reason, connection_state} ->
{:stop, reason, {socket, %{state | connection: connection_state}}}
end
end
end