Current section
Files
Jump to
Current section
Files
src/ow_websocket.erl
-module(ow_websocket).
-behavior(cowboy_handler).
%-export([init/2]).
-export([init/2, terminate/3]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).
-include_lib("kernel/include/logger.hrl").
-type ws_result() :: {ok, any()} | {reply, [binary(), ...], any()}.
%%===========================================================================
%% Cowboy Handler Callbacks
%%===========================================================================
%%---------------------------------------------------------------------------
%% @doc Handler is initialized for any new connection and logs the foreign IP
%% @end
%%---------------------------------------------------------------------------
init(Req, _St0) ->
#{peer := {IP, _Port}} = Req,
logger:notice("~p: client connected.", [IP]),
SessionID = erlang:unique_integer(),
St1 = ow_session:set_id(SessionID, ow_session:new()),
St2 = ow_session:set_pid(self(), St1),
St3 = ow_session:set_serializer(protobuf, St2),
{cowboy_websocket, Req, St3}.
%%---------------------------------------------------------------------------
%% @doc Terminate callback for cleanup processes
%% @end
%%---------------------------------------------------------------------------
terminate(_Reason, Req, State) ->
#{peer := {IP, _Port}} = Req,
logger:notice("~p: client disconnected", [IP]),
logger:debug("Client state at disconnect: ~p", [State]),
% Check for a termination callback in the client state
case ow_session:get_termination_callback(State) of
{M, F, 1} ->
erlang:apply(M, F, [State]);
_ ->
ok
end.
%%---------------------------------------------------------------------------
%% @doc Set up the initial state of the websocket handler
%% The greeting handler is spawned, which can be helpful debug
%% information sent to the client to ensure that the connection
%% (preferably over TLS) is working correctly.
%% @end
%%---------------------------------------------------------------------------
websocket_init(State) ->
gproc:reg({p, l, client_session}),
St1 = ow_session:set_pid(self(), State),
Msg = ow_session:encode_log("CONNECTION ESTABLISHED"),
{reply, {binary, Msg}, St1}.
%%---------------------------------------------------------------------------
%% @doc The websocket handler passes any binary message down to the protocol
%% decoder for further processing. All other messages are discarded.
%% @end
%%---------------------------------------------------------------------------
-spec websocket_handle(
{binary, [binary(), ...]}, ow_session:session()
) -> ws_result().
websocket_handle({binary, Msg}, Session) ->
% protocol decoding will reply with an 'ok' for asynchronous messages or
% will give us a binary to send back to the client
case ow_protocol:decode(Msg, Session) of
% Table of possible returns
% ok -> No reply, keep old session
% {ok, Session1} -> No reply, update session
% {Msg1, Session1} -> Reply message, update session (which may
% ==Session)
ok ->
{ok, Session};
{ok, Session1} ->
{ok, Session1};
{Msg1, Session1} ->
{reply, {binary, Msg1}, Session1}
end;
websocket_handle(Frame, State) ->
logger:debug("Received some other kind of frame: ~p", [Frame]),
{ok, State}.
%%--------------------------------------------------------------------------
%% @doc websocket_info is triggered when a message from another erlang
%% process comes into this handler process.
%% @end
%%--------------------------------------------------------------------------
websocket_info({_Pid, zone_msg, Msg}, Session) ->
{reply, {binary, Msg}, Session};
websocket_info({_Pid, broadcast, Msg}, Session) ->
{reply, {binary, Msg}, Session};
websocket_info(Info, State) ->
logger:debug("Got a message from another process: ~p", [Info]),
{ok, State}.