src/yaws_bridge_modules/yaws_simple_bridge_anchor.erl

%% vim: ts=4 sw=4 et
-module(yaws_simple_bridge_anchor).
-export([
    out/1,
    init/1,
    handle_message/2,
    handle_info/2,
    terminate/2
]).

-record(ws_state, {handler, bridge, state}).

out(Arg) ->
    Bridge = simple_bridge:make(yaws, Arg),
    Handler = simple_bridge_util:get_env(handler),
    Upgrade = sbw:header_lower(upgrade, Bridge),
    {KAInterval, KATimeout} = simple_bridge_util:get_websocket_keepalive_interval_timeout(yaws),
    case Upgrade of
        "websocket" ->
            %% Pass the Handler module and the Bridge as the initial State but
            %% use the current module as the actual Websocket handler
            Opts = setup_websocket_opts(Handler, Bridge, KAInterval, KATimeout),
            {websocket, ?MODULE, Opts};
        _ ->
            Handler:run(Bridge)
    end.

setup_websocket_opts(Handler, Bridge, KAInterval, KATimeout) ->
    KeepAlive = is_integer(KAInterval) andalso KAInterval > 0,
    [
        %% Standard Yaws websocket handler.
        {callback, {basic, #ws_state{handler=Handler, bridge=Bridge}}},

        %% Boolean turning on or off keepalive pings
        {keepalive, KeepAlive},

        %% The interval to send keepalive pings (even though it's
        %% labeled 'keepalive_timeout').
        {keepalive_timeout, KAInterval},

        %% The actual timeout. If we don't hear back about our ping in
        %% KATimeout milliseconds, yaws will kill the connection.
        {keepalive_grace_period, KATimeout},

	%% If the websocket times out, should we drop the websocket connection?
	{drop_on_timeout, true}
    ].

init([_Arg, WSState=#ws_state{handler=Handler, bridge=Bridge}]) ->
    NewState = simple_bridge_websocket:call_init(Handler, Bridge),
    {ok, WSState#ws_state{state=NewState}}.

handle_message({close, Status, Reason}, State) ->
    {close, {Status, Reason}, State};

handle_message(Data, WSState=#ws_state{handler=Handler, bridge=Bridge, state=State}) ->
    Result = Handler:ws_message(Data, Bridge, State),
    massage_reply(Result, WSState).

handle_info(Data, WSState=#ws_state{handler=Handler, bridge=Bridge, state=State}) ->
    case erlang:function_exported(Handler, ws_info, 3) of
        true -> 
            Result = Handler:ws_info(Data, Bridge, State),
            massage_reply(Result, WSState);
        false ->
            {noreply, WSState}
    end.

terminate(Reason, #ws_state{handler=Handler, bridge=Bridge, state=State}) ->
    ok = Handler:ws_terminate(Reason, Bridge, State).

massage_reply(noreply, WSState) ->
    {noreply, WSState};
massage_reply({noreply, NewState}, WSState) ->
    {noreply, WSState#ws_state{state=NewState}};
massage_reply({reply, Reply}, WSState) ->
    massage_reply({reply, Reply, WSState#ws_state.state}, WSState);
massage_reply({reply, {Type, Data}, NewState}, WSState)
        when Type==binary; Type==text ->
    {reply, {Type, iolist_to_binary(Data)}, WSState#ws_state{state=NewState}};
massage_reply({reply, List, NewState}, WSState) ->
    FixedList = [{Type, iolist_to_binary(Data)} || {Type, Data} <- List],
    {reply, FixedList, WSState#ws_state{state=NewState}};
massage_reply({close, Reason}, WSState) when is_integer(Reason) ->
    {close, Reason, WSState};
massage_reply(close, WSState) ->
    {close, 1000, WSState}.