Skip to content

Commit

Permalink
Support XEP-0198 pings
Browse files Browse the repository at this point in the history
If stream management is enabled, let mod_ping trigger XEP-0198
<r/>equests rather than sending XEP-0199 pings.  This avoids the
overhead of the ping IQ stanzas, which, if stream management is enabled,
are accompanied by XEP-0198 elements anyway.

Thanks to MoyaApp (<https://moya.app>) for sponsoring this work.
  • Loading branch information
weiss committed Jan 15, 2024
1 parent 74cb2e0 commit 6c691a7
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
41 changes: 26 additions & 15 deletions src/mod_ping.erl
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@
-export([init/1, terminate/2, handle_call/3,
handle_cast/2, handle_info/2, code_change/3]).

-export([iq_ping/1, user_online/3, user_offline/3, mod_doc/0,
user_send/1, mod_opt_type/1, mod_options/1, depends/2]).
-export([iq_ping/1, user_online/3, user_offline/3, mod_doc/0, user_send/1,
c2s_handle_cast/2, mod_opt_type/1, mod_options/1, depends/2]).

-record(state,
{host :: binary(),
send_pings :: boolean(),
ping_interval :: pos_integer(),
ping_ack_timeout :: undefined | non_neg_integer(),
timeout_action :: none | kill,
timers :: timers()}).

Expand Down Expand Up @@ -167,13 +166,8 @@ handle_info({timeout, _TRef, {ping, JID}}, State) ->
JID#jid.lresource) of
none ->
del_timer(JID, State#state.timers);
_ ->
Host = State#state.host,
From = jid:make(Host),
IQ = #iq{from = From, to = JID, type = get, sub_els = [#ping{}]},
ejabberd_router:route_iq(IQ, JID,
gen_mod:get_module_proc(Host, ?MODULE),
State#state.ping_ack_timeout),
Pid ->
ejabberd_c2s:cast(Pid, send_ping),
add_timer(JID, State#state.ping_interval,
State#state.timers)
end,
Expand Down Expand Up @@ -214,19 +208,29 @@ user_send({Packet, #{jid := JID} = C2SState}) ->
start_ping(JID#jid.lserver, JID),
{Packet, C2SState}.

-spec c2s_handle_cast(ejabberd_c2s:state(), send_ping | term())
-> ejabberd_c2s:state() | {stop, ejabberd_c2s:state()}.
c2s_handle_cast(#{lserver := Host, jid := JID} = C2SState, send_ping) ->
From = jid:make(Host),
IQ = #iq{from = From, to = JID, type = get, sub_els = [#ping{}]},
Proc = gen_mod:get_module_proc(Host, ?MODULE),
PingAckTimeout = mod_ping_opt:ping_ack_timeout(Host),
ejabberd_router:route_iq(IQ, JID, Proc, PingAckTimeout),
{stop, C2SState};
c2s_handle_cast(C2SState, _Msg) ->
C2SState.

%%====================================================================
%% Internal functions
%%====================================================================
init_state(Host, Opts) ->
SendPings = mod_ping_opt:send_pings(Opts),
PingInterval = mod_ping_opt:ping_interval(Opts),
PingAckTimeout = mod_ping_opt:ping_ack_timeout(Opts),
TimeoutAction = mod_ping_opt:timeout_action(Opts),
#state{host = Host,
send_pings = SendPings,
ping_interval = PingInterval,
timeout_action = TimeoutAction,
ping_ack_timeout = PingAckTimeout,
timers = #{}}.

register_hooks(Host) ->
Expand All @@ -235,15 +239,19 @@ register_hooks(Host) ->
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, user_offline, 100),
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
user_send, 100).
user_send, 100),
ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE,
c2s_handle_cast, 99).

unregister_hooks(Host) ->
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
?MODULE, user_offline, 100),
ejabberd_hooks:delete(sm_register_connection_hook, Host,
?MODULE, user_online, 100),
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
user_send, 100).
user_send, 100),
ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE,
c2s_handle_cast, 99).

register_iq_handlers(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING,
Expand Down Expand Up @@ -315,7 +323,10 @@ mod_doc() ->
#{value => "timeout()",
desc =>
?T("How long to wait before deeming that a client "
"has not answered a given server ping request. "
"has not answered a given server ping request. NOTE: when "
"_`mod_stream_mgmt`_ is loaded and stream management is "
"enabled by a client, this value is ignored, and the "
"`ack_timeout` applies instead. "
"The default value is 'undefined'.")}},
{send_pings,
#{value => "true | false",
Expand Down
12 changes: 10 additions & 2 deletions src/mod_stream_mgmt.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
-export([c2s_stream_started/2, c2s_stream_features/2,
c2s_authenticated_packet/2, c2s_unauthenticated_packet/2,
c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2,
c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3,
c2s_handle_recv/3, c2s_inline_features/2,
c2s_handle_send/3, c2s_handle_info/2, c2s_handle_cast/2,
c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/2,
c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3,
c2s_handle_bind2_inline/1]).
%% adjust pending session timeout / access queue
Expand Down Expand Up @@ -77,6 +77,7 @@ start(_Host, Opts) ->
{hook, c2s_handle_send, c2s_handle_send, 50},
{hook, c2s_handle_recv, c2s_handle_recv, 50},
{hook, c2s_handle_info, c2s_handle_info, 50},
{hook, c2s_handle_cast, c2s_handle_cast, 50},
{hook, c2s_handle_call, c2s_handle_call, 50},
{hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 50},
{hook, c2s_handle_sasl2_inline_post, c2s_handle_sasl2_inline_post, 50},
Expand Down Expand Up @@ -258,6 +259,13 @@ c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod,
c2s_handle_send(State, _Pkt, _Result) ->
State.

c2s_handle_cast(#{mgmt_state := active} = State, send_ping) ->
{stop, send_rack(State)};
c2s_handle_cast(#{mgmt_state := pending} = State, send_ping) ->
{stop, State};
c2s_handle_cast(State, _Msg) ->
State.

c2s_handle_call(#{mgmt_id := MgmtID, mgmt_queue := Queue, mod := Mod} = State,
{resume_session, MgmtID}, From) ->
State1 = State#{mgmt_queue => p1_queue:file_to_ram(Queue)},
Expand Down

0 comments on commit 6c691a7

Please sign in to comment.