From: Holger Weiss Date: Tue, 25 Mar 2014 22:23:38 +0000 (+0100) Subject: XEP-0198: Bounce unacked stanzas by default X-Git-Tag: 14.05~11^2~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a97c716352c3baec1416f5e0ee303c996bb5900c;p=ejabberd 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. --- 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) ->