%% 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}.