Support XEP-0198 pings

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.
This commit is contained in:
Holger Weiss 2024-01-15 21:38:54 +01:00
parent 74cb2e054f
commit 6c691a73bd
2 changed files with 36 additions and 17 deletions

View File

@ -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()}).
@ -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,
@ -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) ->
@ -235,7 +239,9 @@ 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,
@ -243,7 +249,9 @@ unregister_hooks(Host) ->
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,
@ -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",

View File

@ -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
@ -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},
@ -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)},