Skip to content

Commit ef41446

Browse files
authoredMar 6, 2025
Merge pull request #4501 from esl/fix-sm-h-counting
Fix SM counting
2 parents eca6541 + 4364cdb commit ef41446

File tree

2 files changed

+67
-21
lines changed

2 files changed

+67
-21
lines changed
 

‎big_tests/tests/sm_SUITE.erl

+48-11
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ parallel_cases() ->
103103
h_ok_after_session_enabled_before_session,
104104
h_ok_after_session_enabled_after_session,
105105
h_ok_after_a_chat,
106+
h_ok_after_presence,
107+
h_ok_after_iq,
108+
h_ok_after_non_xmpp_stanza,
106109
h_non_given_closes_stream_gracefully,
107110
resend_unacked_on_reconnection,
108111
session_established,
@@ -401,18 +404,14 @@ basic_ack(Config) ->
401404
%% - <r/> is sent *before* the session is established
402405
h_ok_before_session(Config) ->
403406
User = connect_fresh(Config, ?config(user, Config), sm_after_bind),
404-
escalus_connection:send(User, escalus_stanza:sm_request()),
405-
escalus:assert(is_sm_ack, [0],
406-
escalus_connection:get_stanza(User, stream_mgmt_ack)).
407+
assert_h(User, 0).
407408

408409
%% Test that "h" value is valid when:
409410
%% - SM is enabled *before* the session is established
410411
%% - <r/> is sent *after* the session is established
411412
h_ok_after_session_enabled_before_session(Config) ->
412413
User = connect_fresh(Config, ?config(user, Config), sm_before_session),
413-
escalus_connection:send(User, escalus_stanza:sm_request()),
414-
escalus:assert(is_sm_ack, [1],
415-
escalus_connection:get_stanza(User, stream_mgmt_ack)).
414+
assert_h(User, 1).
416415

417416
%% Test that "h" value is valid when:
418417
%% - SM is enabled *after* the session is established
@@ -422,15 +421,14 @@ h_ok_after_session_enabled_after_session(Config) ->
422421
escalus_connection:send(User, escalus_stanza:roster_get()),
423422
escalus:assert(is_roster_result,
424423
escalus_connection:get_stanza(User, roster_result)),
425-
escalus_connection:send(User, escalus_stanza:sm_request()),
426-
escalus:assert(is_sm_ack, [1],
427-
escalus_connection:get_stanza(User, stream_mgmt_ack)).
424+
assert_h(User, 1).
428425

429426
%% Test that "h" value is valid after exchanging a few messages.
430427
h_ok_after_a_chat(ConfigIn) ->
431428
Config = escalus_users:update_userspec(ConfigIn, ?config(user, ConfigIn),
432429
stream_management, true),
433430
escalus:fresh_story(Config, [{?config(user, Config), 1}, {bob,1}], fun(User, Bob) ->
431+
assert_h(User, 1),
434432
escalus:send(User, escalus_stanza:chat_to(Bob, <<"Hi, Bob!">>)),
435433
escalus:assert(is_chat_message, [<<"Hi, Bob!">>],
436434
escalus:wait_for_stanza(Bob)),
@@ -443,12 +441,47 @@ h_ok_after_a_chat(ConfigIn) ->
443441
escalus:send(User, escalus_stanza:chat_to(Bob, <<"Pretty !@#$%^$">>)),
444442
escalus:assert(is_chat_message, [<<"Pretty !@#$%^$">>],
445443
escalus:wait_for_stanza(Bob)),
446-
escalus:send(User, escalus_stanza:sm_request()),
447-
escalus:assert(is_sm_ack, [3], escalus:wait_for_stanza(User)),
444+
assert_h(User, 3),
448445
%% Ack, so that unacked messages don't go into offline store.
449446
escalus:send(User, escalus_stanza:sm_ack(3))
450447
end).
451448

449+
h_ok_after_presence(Config) ->
450+
User = connect_fresh(Config, ?config(user, Config), sm_before_session),
451+
assert_h(User, 1),
452+
Presence = escalus_stanza:presence(<<"available">>),
453+
escalus:send(User, Presence),
454+
escalus:assert(is_presence, escalus:wait_for_stanza(User)),
455+
assert_h(User, 2),
456+
escalus:send(User, Presence),
457+
escalus:assert(is_presence, escalus:wait_for_stanza(User)),
458+
assert_h(User, 3).
459+
460+
h_ok_after_iq(Config) ->
461+
User = connect_fresh(Config, ?config(user, Config), sm_before_session),
462+
assert_h(User, 1),
463+
Iq = escalus_stanza:iq_get(<<"invalid_ns">>, []),
464+
escalus_client:send(User, Iq),
465+
escalus:assert(is_iq_error, escalus:wait_for_stanza(User)),
466+
assert_h(User, 2),
467+
escalus_client:send(User, Iq),
468+
escalus:assert(is_iq_error, escalus:wait_for_stanza(User)),
469+
assert_h(User, 3).
470+
471+
h_ok_after_non_xmpp_stanza(Config) ->
472+
User = connect_fresh(Config, ?config(user, Config), sm_before_session),
473+
assert_h(User, 1),
474+
%% SM stanzas are not counted
475+
assert_h(User, 1),
476+
%% CSI stanzas are not counted
477+
CsiActive = csi_helper:csi_stanza(<<"active">>),
478+
escalus_client:send(User, CsiActive),
479+
assert_h(User, 1),
480+
%% any non-xmpp stanza is not counted
481+
Stanza = #xmlel{name = <<"dummy_stanza">>},
482+
escalus_client:send(User, Stanza),
483+
assert_h(User, 1).
484+
452485
h_non_given_closes_stream_gracefully(ConfigIn) ->
453486
AStanza = #xmlel{name = <<"a">>,
454487
attrs = #{<<"xmlns">> => <<"urn:xmpp:sm:3">>}},
@@ -1601,3 +1634,7 @@ maybe_ack_initial_presence(User, ack) ->
16011634
ack_initial_presence(User);
16021635
maybe_ack_initial_presence(_User, no_ack) ->
16031636
ok.
1637+
1638+
assert_h(User, H) ->
1639+
escalus:send(User, escalus_stanza:sm_request()),
1640+
escalus:assert(is_sm_ack, [H], escalus:wait_for_stanza(User)).

‎src/stream_management/mod_stream_management.erl

+19-10
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ stale_h_config_spec() ->
178178
-spec user_send_packet(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) ->
179179
mongoose_c2s_hooks:result().
180180
user_send_packet(Acc, #{c2s_data := StateData}, _Extra) ->
181-
case {get_mod_state(StateData), is_sm_element(Acc)} of
182-
{#sm_state{counter_in = Counter} = SmState, false} ->
181+
case {get_mod_state(StateData), is_stanza_element(Acc)} of
182+
{#sm_state{counter_in = Counter} = SmState, true} ->
183183
NewSmState = SmState#sm_state{counter_in = incr_counter(Counter)},
184184
{ok, mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewSmState})};
185185
{_, _} ->
@@ -856,7 +856,7 @@ sm_state_to_keep(SmState, From) ->
856856
recover_messages(SmState) ->
857857
receive
858858
{route, Acc} ->
859-
recover_messages(maybe_buffer_acc(SmState, Acc, is_message(mongoose_acc:stanza_name(Acc))))
859+
recover_messages(maybe_buffer_acc(SmState, Acc, is_message(Acc)))
860860
after 0 ->
861861
SmState
862862
end.
@@ -868,14 +868,23 @@ maybe_buffer_acc(SmState, _Acc, false) ->
868868
SmState.
869869

870870
%% IQs and presences are allowed to come to the same SID only
871-
-spec is_message(binary()) -> boolean().
872-
is_message(<<"message">>) -> true;
873-
is_message(_) -> false.
871+
-spec is_message(mongoose_acc:t()) -> boolean().
872+
is_message(Acc) ->
873+
case mongoose_acc:stanza_name(Acc) of
874+
<<"message">> -> true;
875+
_ -> false
876+
end.
874877

875-
-spec is_sm_element(mongoose_acc:t()) -> boolean().
876-
is_sm_element(Acc) ->
877-
El = mongoose_acc:element(Acc),
878-
?NS_STREAM_MGNT_3 =:= exml_query:attr(El, <<"xmlns">>).
878+
%% XEP-0198 states that only XMPP stanzas (i.e. <iq/>, <message/> or <presence/>
879+
%% stanzas as defined in RFC 6120) should be counted or acked in stream management
880+
-spec is_stanza_element(mongoose_acc:t()) -> boolean().
881+
is_stanza_element(Acc) ->
882+
case mongoose_acc:stanza_name(Acc) of
883+
<<"message">> -> true;
884+
<<"iq">> -> true;
885+
<<"presence">> -> true;
886+
_ -> false
887+
end.
879888

880889
-spec make_smid() -> smid().
881890
make_smid() ->

0 commit comments

Comments
 (0)