]> granicus.if.org Git - ejabberd/commitdiff
XEP-0198: Bounce unacked stanzas by default
authorHolger Weiss <holger@zedat.fu-berlin.de>
Tue, 25 Mar 2014 22:23:38 +0000 (23:23 +0100)
committerHolger Weiß <holger@zedat.fu-berlin.de>
Tue, 25 Mar 2014 22:23:38 +0000 (23:23 +0100)
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.

doc/guide.tex
src/ejabberd_c2s.erl

index 23752a27d43ad55c421553dbb1bbc73b4068a523..714a62a573afba5e240d0c886c6334c3d2392e5c 100644 (file)
@@ -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
index 4955f6e424d857030eb75253c5512cff575775f8..0051266c7d1c3d14a9e597fce414fdcac3638484 100644 (file)
                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) ->