diff options
Diffstat (limited to 'src/web/ejabberd_http_wsjson.erl')
-rw-r--r-- | src/web/ejabberd_http_wsjson.erl | 235 |
1 files changed, 104 insertions, 131 deletions
diff --git a/src/web/ejabberd_http_wsjson.erl b/src/web/ejabberd_http_wsjson.erl index 2abd12b3b..1a07252e8 100644 --- a/src/web/ejabberd_http_wsjson.erl +++ b/src/web/ejabberd_http_wsjson.erl @@ -24,196 +24,169 @@ %%% %%%---------------------------------------------------------------------- --module (ejabberd_http_wsjson). +-module(ejabberd_http_wsjson). + -author('ecestari@process-one.net'). -behaviour(gen_fsm). % External exports --export([ - start/1, - start_link/1, - init/1, - handle_event/3, - handle_sync_event/4, - code_change/4, - handle_info/3, - terminate/3, - send_xml/2, - setopts/2, - sockname/1, peername/1, - controlling_process/2, - become_controller/2, +-export([start/1, start_link/1, init/1, handle_event/3, + handle_sync_event/4, code_change/4, handle_info/3, + terminate/3, send_xml/2, setopts/2, sockname/1, + peername/1, controlling_process/2, become_controller/2, close/1]). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("ejabberd_http.hrl"). --record(state, { - socket, - timeout, - timer, - input = [], - waiting_input = false, %% {ReceiverPid, Tag} - last_receiver, - ws}). +-define(WEBSOCKET_TIMEOUT, 300). + +-record(state, + {socket :: ejabberd_http_ws:ws_socket(), + timeout = ?WEBSOCKET_TIMEOUT :: pos_integer(), + timer = make_ref() :: reference(), + input = [] :: list(), + waiting_input = false :: false | pid(), + last_receiver :: pid(), + ws :: atom()}). %-define(DBGFSM, true). -ifdef(DBGFSM). + -define(FSMOPTS, [{debug, [trace]}]). + -else. + -define(FSMOPTS, []). + -endif. --define(WEBSOCKET_TIMEOUT, 300000). -% -% -%%%%---------------------------------------------------------------------- -%%%% API -%%%%---------------------------------------------------------------------- start(WS) -> supervisor:start_child(ejabberd_wsloop_sup, [WS]). start_link(WS) -> - gen_fsm:start_link(?MODULE, [WS],?FSMOPTS). + gen_fsm:start_link(?MODULE, [WS], ?FSMOPTS). send_xml({http_ws, FsmRef, _IP}, Packet) -> - gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}). + gen_fsm:sync_send_all_state_event(FsmRef, + {send, Packet}). setopts({http_ws, FsmRef, _IP}, Opts) -> case lists:member({active, once}, Opts) of - true -> - gen_fsm:send_all_state_event(FsmRef, {activate, self()}); - _ -> - ok + true -> + gen_fsm:send_all_state_event(FsmRef, + {activate, self()}); + _ -> ok end. -sockname(_Socket) -> - {ok, {{0, 0, 0, 0}, 0}}. +sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}. -peername({http_ws, _FsmRef, IP}) -> - {ok, IP}. +peername({http_ws, _FsmRef, IP}) -> {ok, IP}. -controlling_process(_Socket, _Pid) -> - ok. +controlling_process(_Socket, _Pid) -> ok. become_controller(FsmRef, C2SPid) -> - gen_fsm:send_all_state_event(FsmRef, {become_controller, C2SPid}). + gen_fsm:send_all_state_event(FsmRef, + {become_controller, C2SPid}). close({http_ws, FsmRef, _IP}) -> - catch gen_fsm:sync_send_all_state_event(FsmRef, close). - -%%% Internal + catch gen_fsm:sync_send_all_state_event(FsmRef, close). +%%% Internal init([WS]) -> - %% Read c2s options from the first ejabberd_c2s configuration in - %% the config file listen section - %% TODO: We should have different access and shaper values for - %% each connector. The default behaviour should be however to use - %% the default c2s restrictions if not defined for the current - %% connector. - Opts = [{xml_socket, true}|ejabberd_c2s_config:get_c2s_limits()], - - WSTimeout = case ejabberd_config:get_local_option({websocket_timeout, - ?MYNAME}) of - %% convert seconds of option into milliseconds - Int when is_integer(Int) -> Int*1000; - undefined -> ?WEBSOCKET_TIMEOUT - end, - + Opts = [{xml_socket, true} + | ejabberd_c2s_config:get_c2s_limits()], + WSTimeout = ejabberd_config:get_local_option( + {websocket_timeout, ?MYNAME}, + fun(I) when is_integer(I), I>0 -> I end, + ?WEBSOCKET_TIMEOUT) * 1000, Socket = {http_ws, self(), WS:get(ip)}, - ?DEBUG("Client connected through websocket ~p", [Socket]), - ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, Opts), + ?DEBUG("Client connected through websocket ~p", + [Socket]), + ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, + Opts), Timer = erlang:start_timer(WSTimeout, self(), []), - {ok, loop, #state{ - socket = Socket, - timeout = WSTimeout, - timer = Timer, - ws = WS}}. + {ok, loop, + #state{socket = Socket, timeout = WSTimeout, + timer = Timer, ws = WS}}. handle_event({activate, From}, StateName, StateData) -> case StateData#state.input of - [] -> - {next_state, StateName, - StateData#state{waiting_input = {From, ok}}}; - Input -> - Receiver = From, - lists:reverse(lists:map(fun(Packet)-> - Receiver ! {tcp, StateData#state.socket, [Packet]} - end, Input)), - {next_state, StateName, StateData#state{input = "", - waiting_input = false, - last_receiver = Receiver - }} + [] -> + {next_state, StateName, + StateData#state{waiting_input = From}}; + Input -> + Receiver = From, + lists:reverse(lists:map(fun (Packet) -> + Receiver ! + {tcp, StateData#state.socket, + [Packet]} + end, + Input)), + {next_state, StateName, + StateData#state{input = [], waiting_input = false, + last_receiver = Receiver}} end. -handle_sync_event({send, Packet}, _From, StateName, #state{ws = WS} = StateData) -> +handle_sync_event({send, Packet}, _From, StateName, + #state{ws = WS} = StateData) -> EJson = xmpp_json:to_json(Packet), - Json = mochijson2:encode(EJson), - WS:send(iolist_to_binary(Json)), + Json = jiffy:encode(EJson), + WS:send(Json), {reply, ok, StateName, StateData}; - -handle_sync_event(close, _From, _StateName, StateData) -> - Reply = ok, - {stop, normal, Reply, StateData}. - -handle_info({browser, <<"\n">>}, StateName, StateData)-> +handle_sync_event(close, _From, _StateName, + StateData) -> + Reply = ok, {stop, normal, Reply, StateData}. + +handle_info({browser, <<"\n">>}, StateName, + StateData) -> NewState = case StateData#state.waiting_input of - false -> - ok; - {Receiver, _Tag} -> - Receiver ! {tcp, StateData#state.socket,<<"\n">>}, - cancel_timer(StateData#state.timer), - Timer = erlang:start_timer(StateData#state.timeout, self(), []), - StateData#state{waiting_input = false, - last_receiver = Receiver, - timer = Timer} - end, - {next_state, StateName, NewState}; -handle_info({browser, JsonPacket}, StateName, StateData)-> + false -> ok; + Receiver -> + Receiver ! {tcp, StateData#state.socket, <<"\n">>}, + cancel_timer(StateData#state.timer), + Timer = erlang:start_timer(StateData#state.timeout, + self(), []), + StateData#state{waiting_input = false, + last_receiver = Receiver, timer = Timer} + end, + {next_state, StateName, NewState}; +handle_info({browser, JsonPacket}, StateName, + StateData) -> NewState = case StateData#state.waiting_input of - false -> - EJson = mochijson2:decode(JsonPacket), - Packet = xmpp_json:from_json(EJson), - Input = [Packet | StateData#state.input], - StateData#state{input = Input}; - {Receiver, _Tag} -> - %?DEBUG("Received from browser : ~p", [JsonPacket]), - EJson = mochijson2:decode(JsonPacket), - %?DEBUG("decoded : ~p", [EJson]), - Packet = xmpp_json:from_json(EJson), - %?DEBUG("sending to c2s : ~p", [Packet]), - Receiver ! {tcp, StateData#state.socket,[Packet]}, - cancel_timer(StateData#state.timer), - Timer = erlang:start_timer(StateData#state.timeout, self(), []), - StateData#state{waiting_input = false, - last_receiver = Receiver, - timer = Timer} - end, - {next_state, StateName, NewState}; - - + false -> + EJson = jiffy:decode(JsonPacket), + Packet = xmpp_json:from_json(EJson), + Input = [Packet | StateData#state.input], + StateData#state{input = Input}; + Receiver -> + EJson = jiffy:decode(JsonPacket), + Packet = xmpp_json:from_json(EJson), + Receiver ! {tcp, StateData#state.socket, [Packet]}, + cancel_timer(StateData#state.timer), + Timer = erlang:start_timer(StateData#state.timeout, + self(), []), + StateData#state{waiting_input = false, + last_receiver = Receiver, timer = Timer} + end, + {next_state, StateName, NewState}; handle_info({timeout, Timer, _}, _StateName, #state{timer = Timer} = StateData) -> {stop, normal, StateData}; - handle_info(_, StateName, StateData) -> {next_state, StateName, StateData}. - code_change(_OldVsn, StateName, StateData, _Extra) -> - {ok, StateName, StateData}. - + {ok, StateName, StateData}. + terminate(_Reason, _StateName, _StateData) -> ok. cancel_timer(Timer) -> erlang:cancel_timer(Timer), - receive - {timeout, Timer, _} -> - ok - after 0 -> - ok - end. + receive {timeout, Timer, _} -> ok after 0 -> ok end. |