./ct_report/coverage/mod_stream_management.COVER.html

1 -module(mod_stream_management).
2 -xep([{xep, 198}, {version, "1.6"}]).
3 -behaviour(gen_mod).
4 -behaviour(mongoose_module_metrics).
5
6 %% `gen_mod' callbacks
7 -export([start/2,
8 stop/1,
9 config_spec/0,
10 supported_features/0,
11 process_buffer_and_ack/1]).
12
13 %% hooks handlers
14 -export([c2s_stream_features/3,
15 remove_smid/5,
16 session_cleanup/5]).
17
18 %% API for `ejabberd_c2s'
19 -export([make_smid/0,
20 get_session_from_smid/2,
21 get_buffer_max/1,
22 get_ack_freq/1,
23 get_resume_timeout/1,
24 register_smid/3]).
25
26 %% API for inspection and tests
27 -export([get_sid/2,
28 get_stale_h/2,
29 register_stale_smid_h/3,
30 remove_stale_smid_h/2]).
31
32 -ignore_xref([c2s_stream_features/3, get_sid/2, get_stale_h/2, remove_smid/5,
33 register_stale_smid_h/3, remove_stale_smid_h/2, session_cleanup/5]).
34
35 -type smid() :: base64:ascii_binary().
36
37 -export_type([smid/0]).
38
39 -include("mongoose.hrl").
40 -include("jlib.hrl").
41 -include("mongoose_config_spec.hrl").
42
43 -type buffer_max() :: pos_integer() | infinity | no_buffer.
44 -type ack_freq() :: pos_integer() | never.
45 %%
46 %% `gen_mod' callbacks
47 %%
48
49 start(HostType, Opts) ->
50 387 mod_stream_management_backend:init(HostType, Opts),
51 387 ?LOG_INFO(#{what => stream_management_starting}),
52 387 ejabberd_hooks:add(hooks(HostType)),
53 387 ok.
54
55 stop(HostType) ->
56 387 ?LOG_INFO(#{what => stream_management_stopping}),
57 387 ejabberd_hooks:delete(hooks(HostType)),
58 387 ok.
59
60 hooks(HostType) ->
61 774 [{sm_remove_connection_hook, HostType, ?MODULE, remove_smid, 50},
62 {c2s_stream_features, HostType, ?MODULE, c2s_stream_features, 50},
63 {session_cleanup, HostType, ?MODULE, session_cleanup, 50}].
64
65 -spec config_spec() -> mongoose_config_spec:config_section().
66 config_spec() ->
67 166 #section{
68 items = #{<<"backend">> => #option{type = atom, validate = {module, ?MODULE}},
69 <<"buffer">> => #option{type = boolean},
70 <<"buffer_max">> => #option{type = int_or_infinity,
71 validate = positive},
72 <<"ack">> => #option{type = boolean},
73 <<"ack_freq">> => #option{type = integer,
74 validate = positive},
75 <<"resume_timeout">> => #option{type = integer,
76 validate = positive},
77 <<"stale_h">> => stale_h_config_spec()
78 },
79 process = fun ?MODULE:process_buffer_and_ack/1,
80 format_items = map,
81 defaults = #{<<"backend">> => mnesia,
82 <<"buffer">> => true,
83 <<"buffer_max">> => 100,
84 <<"ack">> => true,
85 <<"ack_freq">> => 1,
86 <<"resume_timeout">> => 600 % seconds
87 }
88 }.
89
90 148 supported_features() -> [dynamic_domains].
91
92 process_buffer_and_ack(Opts = #{buffer := Buffer, ack := Ack}) ->
93 83 OptsWithBuffer = check_buffer(Buffer, Opts),
94 83 check_ack(Ack, OptsWithBuffer).
95
96 check_buffer(false, Opts) ->
97
:-(
Opts#{buffer_max => no_buffer};
98 check_buffer(_, Opts) ->
99 83 Opts.
100
101 check_ack(false, Opts) ->
102
:-(
Opts#{ack_freq => never};
103 check_ack(_, Opts) ->
104 83 Opts.
105
106 stale_h_config_spec() ->
107 166 #section{
108 items = #{<<"enabled">> => #option{type = boolean},
109 <<"repeat_after">> => #option{type = integer,
110 validate = positive},
111 <<"geriatric">> => #option{type = integer,
112 validate = positive}},
113 format_items = map,
114 include = always,
115 defaults = #{<<"enabled">> => false,
116 <<"repeat_after">> => 1800, % seconds
117 <<"geriatric">> => 3600 % seconds
118 }
119 }.
120
121 %%
122 %% hooks handlers
123 %%
124
125 -spec c2s_stream_features([exml:element()], mongooseim:host_type(), jid:lserver()) ->
126 [exml:element()].
127 c2s_stream_features(Acc, _HostType, _Lserver) ->
128 6067 lists:keystore(<<"sm">>, #xmlel.name, Acc, sm()).
129
130 sm() ->
131 6067 #xmlel{name = <<"sm">>,
132 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3}]}.
133
134 -spec remove_smid(Acc, SID, JID, Info, Reason) -> Acc1 when
135 Acc :: mongoose_acc:t(),
136 SID :: ejabberd_sm:sid(),
137 JID :: undefined | jid:jid(),
138 Info :: undefined | [any()],
139 Reason :: undefined | ejabberd_sm:close_reason(),
140 Acc1 :: mongoose_acc:t().
141 remove_smid(Acc, SID, _JID, _Info, _Reason) ->
142 2801 HostType = mongoose_acc:host_type(Acc),
143 2801 do_remove_smid(Acc, HostType, SID).
144
145 -spec session_cleanup(Acc :: map(), LUser :: jid:luser(), LServer :: jid:lserver(),
146 LResource :: jid:lresource(), SID :: ejabberd_sm:sid()) -> any().
147 session_cleanup(Acc, _LUser, _LServer, _LResource, SID) ->
148
:-(
HostType = mongoose_acc:host_type(Acc),
149
:-(
do_remove_smid(Acc, HostType, SID).
150
151 -spec do_remove_smid(mongoose_acc:t(), mongooseim:host_type(), ejabberd_sm:sid()) ->
152 mongoose_acc:t().
153 do_remove_smid(Acc, HostType, SID) ->
154 2801 H = mongoose_acc:get(stream_mgmt, h, undefined, Acc),
155 2801 MaybeSMID = unregister_smid(HostType, SID),
156 2801 case MaybeSMID of
157 {ok, SMID} when H =/= undefined ->
158 32 register_stale_smid_h(HostType, SMID, H);
159 _ ->
160 2769 ok
161 end,
162 2801 mongoose_acc:set(stream_mgmt, smid, MaybeSMID, Acc).
163
164 %%
165 %% API for `ejabberd_c2s'
166 %%
167
168 -spec make_smid() -> smid().
169 make_smid() ->
170 33 base64:encode(crypto:strong_rand_bytes(21)).
171
172 %% Getters
173 -spec get_session_from_smid(mongooseim:host_type(), smid()) ->
174 {sid, ejabberd_sm:sid()} | {stale_h, non_neg_integer()} | {error, smid_not_found}.
175 get_session_from_smid(HostType, SMID) ->
176 17 case get_sid(HostType, SMID) of
177 12 {sid, SID} -> {sid, SID};
178 5 {error, smid_not_found} -> get_stale_h(HostType, SMID)
179 end.
180
181 -spec get_stale_h(mongooseim:host_type(), SMID :: smid()) ->
182 {stale_h, non_neg_integer()} | {error, smid_not_found}.
183 get_stale_h(HostType, SMID) ->
184 25 case is_stale_h_enabled(HostType) of
185 1 false -> {error, smid_not_found};
186 24 true -> read_stale_h(HostType, SMID)
187 end.
188
189 -spec get_buffer_max(mongooseim:host_type()) -> buffer_max().
190 get_buffer_max(HostType) ->
191 60 gen_mod:get_module_opt(HostType, ?MODULE, buffer_max).
192
193 -spec get_ack_freq(mongooseim:host_type()) -> ack_freq().
194 get_ack_freq(HostType) ->
195 60 gen_mod:get_module_opt(HostType, ?MODULE, ack_freq).
196
197 -spec get_resume_timeout(mongooseim:host_type()) -> pos_integer().
198 get_resume_timeout(HostType) ->
199 60 gen_mod:get_module_opt(HostType, ?MODULE, resume_timeout).
200
201
202 register_stale_smid_h(HostType, SMID, H) ->
203 38 case is_stale_h_enabled(HostType) of
204 30 false -> ok;
205 8 true -> write_stale_h(HostType, SMID, H)
206 end.
207
208 remove_stale_smid_h(HostType, SMID) ->
209
:-(
case is_stale_h_enabled(HostType) of
210
:-(
false -> ok;
211
:-(
true -> delete_stale_h(HostType, SMID)
212 end.
213
214 is_stale_h_enabled(HostType) ->
215 63 gen_mod:get_module_opt(HostType, ?MODULE, [stale_h, enabled]).
216
217 %% Backend operations
218
219 -spec register_smid(HostType, SMID, SID) ->
220 ok | {error, term()} when
221 HostType :: mongooseim:host_type(),
222 SMID :: mod_stream_management:smid(),
223 SID :: ejabberd_sm:sid().
224 register_smid(HostType, SMID, SID) ->
225 44 mod_stream_management_backend:register_smid(HostType, SMID, SID).
226
227 -spec unregister_smid(mongooseim:host_type(), ejabberd_sm:sid()) ->
228 {ok, SMID :: mod_stream_management:smid()} | {error, smid_not_found}.
229 unregister_smid(HostType, SID) ->
230 2801 mod_stream_management_backend:unregister_smid(HostType, SID).
231
232 -spec get_sid(mongooseim:host_type(), mod_stream_management:smid()) ->
233 {sid, ejabberd_sm:sid()} | {error, smid_not_found}.
234 get_sid(HostType, SMID) ->
235 18 mod_stream_management_backend:get_sid(HostType, SMID).
236
237 %% stale_h
238
239 -spec write_stale_h(HostType, SMID, H) -> ok | {error, any()} when
240 HostType :: mongooseim:host_type(),
241 SMID :: mod_stream_management:smid(),
242 H :: non_neg_integer().
243 write_stale_h(HostType, SMID, H) ->
244 8 mod_stream_management_backend:write_stale_h(HostType, SMID, H).
245
246 -spec delete_stale_h(HostType, SMID) -> ok | {error, any()} when
247 HostType :: mongooseim:host_type(),
248 SMID :: mod_stream_management:smid().
249 delete_stale_h(HostType, SMID) ->
250
:-(
mod_stream_management_backend:delete_stale_h(HostType, SMID).
251
252 -spec read_stale_h(HostType, SMID) ->
253 {stale_h, non_neg_integer()} | {error, smid_not_found} when
254 HostType :: mongooseim:host_type(),
255 SMID :: mod_stream_management:smid().
256 read_stale_h(HostType, SMID) ->
257 24 mod_stream_management_backend:read_stale_h(HostType, SMID).
Line Hits Source