]> granicus.if.org Git - ejabberd/commitdiff
XEP-0198: Terminate session on queue overflow
authorHolger Weiss <holger@zedat.fu-berlin.de>
Tue, 27 May 2014 20:56:33 +0000 (22:56 +0200)
committerHolger Weiss <holger@zedat.fu-berlin.de>
Tue, 27 May 2014 20:56:33 +0000 (22:56 +0200)
On queue overflow, terminate the c2s session instead of just dropping
items from the queue.  This makes sure all stanzas are either delivered
or bounced.

doc/guide.tex
src/ejabberd_c2s.erl

index a7ff11fff4292b5ea0d2274e64e5493d7fe6a322..9abf48343ce7326cf359d22b710c7c1bc3022c19 100644 (file)
@@ -983,10 +983,10 @@ This is a detailed description of each option allowed by the listening modules:
   \titem{max\_ack\_queue: Size}
     This option specifies the maximum number of unacknowledged stanzas
     queued for possible retransmission if \term{stream\_management} is
-    enabled. When the limit is reached, the first stanza is dropped from
-    the queue before adding the next one. This option can be specified
-    for \term{ejabberd\_c2s} listeners. The allowed values are positive
-    integers and \term{infinity}. Default value: \term{500}.
+    enabled. When the limit is exceeded, the client session is
+    terminated. This option can be specified for \term{ejabberd\_c2s}
+    listeners. The allowed values are positive integers and
+    \term{infinity}. Default value: \term{500}.
   \titem{max\_fsm\_queue: Size}
     This option specifies the maximum number of elements in the queue of the FSM
     (Finite State Machine).
index e6667aa8defc32d00965ea60b20b611863041d7a..1263af5b3298b826ed5918e97b3badd4457c607f 100644 (file)
@@ -1284,6 +1284,10 @@ wait_for_resume(Event, StateData) ->
 %%          {next_state, NextStateName, NextStateData, Timeout} |
 %%          {stop, Reason, NewStateData}
 %%----------------------------------------------------------------------
+handle_event({abort, Xmlelement}, _StateName, StateData) ->
+    send_element(StateData, Xmlelement),
+    send_trailer(StateData),
+    {stop, normal, StateData};
 handle_event(_Event, StateName, StateData) ->
     fsm_next_state(StateName, StateData).
 
@@ -2779,27 +2783,30 @@ mgmt_queue_add(StateData, El) ->
               Num ->
                   Num + 1
             end,
-    NewState = limit_queue_length(StateData),
-    NewQueue = queue:in({NewNum, El}, NewState#state.mgmt_queue),
-    NewState#state{mgmt_queue = NewQueue, mgmt_stanzas_out = NewNum}.
+    NewQueue = queue:in({NewNum, El}, StateData#state.mgmt_queue),
+    NewState = StateData#state{mgmt_queue = NewQueue,
+                              mgmt_stanzas_out = NewNum},
+    check_queue_length(NewState).
 
 mgmt_queue_drop(StateData, NumHandled) ->
     NewQueue = jlib:queue_drop_while(fun({N, _Stanza}) -> N =< NumHandled end,
                                     StateData#state.mgmt_queue),
     StateData#state{mgmt_queue = NewQueue}.
 
-limit_queue_length(#state{mgmt_max_queue = Limit} = StateData)
+check_queue_length(#state{mgmt_max_queue = Limit} = StateData)
     when Limit == infinity;
         Limit == unlimited ->
     StateData;
-limit_queue_length(#state{jid = JID,
-                         mgmt_queue = Queue,
+check_queue_length(#state{mgmt_queue = Queue,
                          mgmt_max_queue = Limit} = StateData) ->
-    case queue:len(Queue) >= Limit of
+    case queue:len(Queue) > Limit of
       true ->
-         ?WARNING_MSG("Dropping stanza from too long ACK queue for ~s",
-                      [jlib:jid_to_string(JID)]),
-         limit_queue_length(StateData#state{mgmt_queue = queue:drop(Queue)});
+         ?WARNING_MSG("ACK queue too long, terminating session for ~s",
+                      [jlib:jid_to_string(StateData#state.jid)]),
+         Lang = StateData#state.lang,
+         Err = ?SERRT_POLICY_VIOLATION(Lang, <<"Too many unacked stanzas">>),
+         (?GEN_FSM):send_all_state_event(self(), {abort, Err}),
+         StateData#state{mgmt_resend = false}; % Don't resend the flood!
       false ->
          StateData
     end.