aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHolger Weiss <holger@zedat.fu-berlin.de>2014-03-25 23:23:38 +0100
committerHolger Weiß <holger@zedat.fu-berlin.de>2014-03-25 23:23:38 +0100
commita97c716352c3baec1416f5e0ee303c996bb5900c (patch)
tree5467e575056ef37863369efaca0fa6384f934419
parentRemove "fun" element from c2s #state (diff)
XEP-0198: Bounce unacked stanzas by default
If the new "resend_on_timeout" option is set to false (which it is by default), bounce any unacknowledged stanzas instead of re-routing them.
-rw-r--r--doc/guide.tex15
-rw-r--r--src/ejabberd_c2s.erl36
2 files changed, 36 insertions, 15 deletions
diff --git a/doc/guide.tex b/doc/guide.tex
index 23752a27d..714a62a57 100644
--- a/doc/guide.tex
+++ b/doc/guide.tex
@@ -871,8 +871,9 @@ The available modules, their purpose and the options allowed by each one are:
Handles c2s connections.\\
Options: \texttt{access}, \texttt{certfile}, \texttt{ciphers},
\texttt{max\_ack\_queue}, \texttt{max\_fsm\_queue},
- \texttt{max\_stanza\_size}, \texttt{resume\_timeout},
- \texttt{shaper}, \texttt{starttls}, \texttt{starttls\_required},
+ \texttt{max\_stanza\_size}, \texttt{resend\_on\_timeout},
+ \texttt{resume\_timeout}, \texttt{shaper},
+ \texttt{starttls}, \texttt{starttls\_required},
\texttt{stream\_management}, \texttt{tls},
\texttt{zlib}, \texttt{tls\_compression}
\titem{\texttt{ejabberd\_s2s\_in}}
@@ -1007,6 +1008,16 @@ request_handlers:
/"a"/"b": mod_foo
/"http-bind": mod_http_bind
\end{verbatim}
+ \titem{resend\_on\_timeout: true|false}
+ If \term{stream\_management} is enabled and this option is set to
+ \term{true}, any stanzas that weren't acknowledged by the client
+ will be resent on session timeout. This behavior might often be
+ desired, but could have unexpected results under certain
+ circumstances. For example, a message that was sent to two resources
+ might get resent to one of them if the other one timed out.
+ Therefore, the default value for this option is \term{false}, which
+ tells ejabberd to generate an error message instead. The option can
+ be specified for \term{ejabberd\_c2s} listeners.
\titem{resume\_timeout: Seconds}
This option configures the number of seconds until a session times
out if the connection is lost. During this period of time, a client
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 4955f6e42..0051266c7 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -115,6 +115,7 @@
max_ack_queue,
pending_since,
resume_timeout,
+ resend_on_timeout,
n_stanzas_in = 0,
n_stanzas_out = 0,
lang}).
@@ -304,6 +305,7 @@ init([{SockMod, Socket}, Opts]) ->
Timeout when is_integer(Timeout), Timeout >= 0 -> Timeout;
_ -> 300
end,
+ ResendOnTimeout = proplists:get_bool(resend_on_timeout, Opts),
IP = peerip(SockMod, Socket),
%% Check if IP is blacklisted:
case is_ip_blacklisted(IP) of
@@ -328,7 +330,8 @@ init([{SockMod, Socket}, Opts]) ->
access = Access, shaper = Shaper, ip = IP,
sm_state = StreamMgmtState,
max_ack_queue = MaxAckQueue,
- resume_timeout = ResumeTimeout},
+ resume_timeout = ResumeTimeout,
+ resend_on_timeout = ResendOnTimeout},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end.
@@ -1757,7 +1760,7 @@ terminate(_Reason, StateName, StateData) ->
StateData#state.pres_a, Packet),
presence_broadcast(StateData, From,
StateData#state.pres_i, Packet),
- resend_unacked_stanzas(StateData);
+ handle_unacked_stanzas(StateData);
_ ->
?INFO_MSG("(~w) Close session for ~s",
[StateData#state.socket,
@@ -1787,7 +1790,7 @@ terminate(_Reason, StateName, StateData) ->
presence_broadcast(StateData, From,
StateData#state.pres_i, Packet)
end,
- resend_unacked_stanzas(StateData)
+ handle_unacked_stanzas(StateData)
end,
bounce_messages();
true ->
@@ -2714,7 +2717,7 @@ handle_resume(StateData, Attrs) ->
{<<"previd">>, AttrId}],
children = []}),
SendFun = fun(_F, _T, El) -> send_element(NewState, El) end,
- resend_unacked_stanzas(NewState, SendFun),
+ handle_unacked_stanzas(NewState, SendFun),
send_element(NewState,
#xmlel{name = <<"r">>,
attrs = [{<<"xmlns">>, AttrXmlns}],
@@ -2782,33 +2785,40 @@ limit_queue_length(#state{jid = JID,
StateData
end.
-resend_unacked_stanzas(StateData, F) when StateData#state.sm_state == active;
+handle_unacked_stanzas(StateData, F) when StateData#state.sm_state == active;
StateData#state.sm_state == pending ->
Queue = StateData#state.ack_queue,
case queue:len(Queue) of
0 ->
ok;
N ->
- ?INFO_MSG("Resending ~B unacknowledged stanzas to ~s",
+ ?INFO_MSG("~B stanzas were not acknowledged by ~s",
[N, jlib:jid_to_string(StateData#state.jid)]),
lists:foreach(
- fun({Num, #xmlel{attrs = Attrs} = El}) ->
+ fun({_, #xmlel{attrs = Attrs} = El}) ->
From_s = xml:get_attr_s(<<"from">>, Attrs),
From = jlib:string_to_jid(From_s),
To_s = xml:get_attr_s(<<"to">>, Attrs),
To = jlib:string_to_jid(To_s),
- ?DEBUG("Resending unacknowledged stanza #~B from ~s to ~s",
- [Num, From_s, To_s]),
F(From, To, El)
end, queue:to_list(Queue))
end;
-resend_unacked_stanzas(_StateData, _F) ->
+handle_unacked_stanzas(_StateData, _F) ->
ok.
-resend_unacked_stanzas(StateData) when StateData#state.sm_state == active;
+handle_unacked_stanzas(StateData) when StateData#state.sm_state == active;
StateData#state.sm_state == pending ->
- resend_unacked_stanzas(StateData, fun ejabberd_router:route/3);
-resend_unacked_stanzas(_StateData) ->
+ F = case StateData#state.resend_on_timeout of
+ true ->
+ fun ejabberd_router:route/3;
+ false ->
+ fun(From, To, El) ->
+ Err = jlib:make_error_reply(El, ?ERR_SERVICE_UNAVAILABLE),
+ ejabberd_router:route(To, From, Err)
+ end
+ end,
+ handle_unacked_stanzas(StateData, F);
+handle_unacked_stanzas(_StateData) ->
ok.
inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->